162 lines
4.9 KiB
Python
162 lines
4.9 KiB
Python
"""High-levle api."""
|
|
|
|
import logging
|
|
from datetime import date
|
|
from dataclasses import dataclass
|
|
from functools import cached_property
|
|
|
|
from typing import Final
|
|
from .db import AdmidioDB
|
|
|
|
|
|
MEMBER_ROLE = "Member"
|
|
FIELD_PAID_DATE = "PMB_PAID"
|
|
FIELD_FEE = "PMB_FEE"
|
|
FIELD_DUEDATE = "PMB_DUEDATE"
|
|
FIELD_EMAIL = "SYS_EMAIL"
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class AdmidioDBSettings:
|
|
"""Database settings class."""
|
|
|
|
user: str
|
|
password: str
|
|
host: str
|
|
database: str
|
|
port: int = 3306
|
|
|
|
|
|
class AdmidioMemberFee:
|
|
"""Admidio member fee manager."""
|
|
|
|
def __init__(self, user_id: int, connection: AdmidioDBSettings) -> None:
|
|
"""Create member fee manager class."""
|
|
self.db: AdmidioDB = AdmidioDB(
|
|
connection.user,
|
|
connection.password,
|
|
connection.host,
|
|
connection.database,
|
|
connection.port,
|
|
)
|
|
self.user_id: Final = user_id
|
|
self._user_data: dict[str, str] | None = None
|
|
|
|
@property
|
|
def is_member(self) -> bool:
|
|
"""Check if user is a member."""
|
|
role_ids = self.db.get_user_roles(self.user_id)
|
|
role_map = self.db.get_roles()
|
|
roles = [role_map[role_id] for role_id in role_ids]
|
|
return MEMBER_ROLE in roles
|
|
|
|
@property
|
|
def member_fee(self) -> int:
|
|
"""Get membership fee."""
|
|
roles = self.db.get_roles()
|
|
member_role_id = next(
|
|
iter(
|
|
[
|
|
role_id
|
|
for role_id, role_name in roles.items()
|
|
if role_name == MEMBER_ROLE
|
|
]
|
|
)
|
|
)
|
|
role_payments = self.db.get_role_payments()
|
|
fee = role_payments.get(member_role_id)
|
|
if not fee:
|
|
raise RuntimeError("Error: No membership cost set on Member role.")
|
|
return int(fee)
|
|
|
|
@property
|
|
def user_data(self) -> dict[str, str]:
|
|
"""Get user data."""
|
|
if not self._user_data:
|
|
self._user_data = self._get_user_data()
|
|
return self._user_data
|
|
|
|
def _get_user_data(self) -> dict[str, str]:
|
|
"""Get user data."""
|
|
user_fields = self.db.get_custom_fields()
|
|
user_field_ids = {value: key for key, value in user_fields.items()}
|
|
adm_user_data = self.db.get_adm_user_data(self.user_id)
|
|
user_data: dict[str, str] = {}
|
|
for field_id, data in adm_user_data.items():
|
|
if field_name := user_field_ids.get(field_id):
|
|
user_data[field_name] = data
|
|
else:
|
|
LOG.warning("Could not find a field definition for %s", field_id)
|
|
return user_data
|
|
|
|
def _lookup_user_field(self, name: str) -> int:
|
|
"""Lookup the ID of a user field."""
|
|
user_fields = self.db.get_custom_fields()
|
|
field_id = user_fields.get(name)
|
|
if not field_id:
|
|
raise RuntimeError(f"Unable to find field ID for field {name}")
|
|
return field_id
|
|
|
|
@property
|
|
def last_paid(self) -> date | None:
|
|
"""Get the date of last payment."""
|
|
paid_data = self.user_data.get(FIELD_PAID_DATE)
|
|
if not paid_data:
|
|
return None
|
|
paid_date = date.fromisoformat(paid_data)
|
|
return paid_date
|
|
|
|
@property
|
|
def has_paid(self) -> bool:
|
|
"""Check if a user has paid."""
|
|
if not self.last_paid:
|
|
return False
|
|
paid_days = date.today() - self.last_paid
|
|
if paid_days.days > 365:
|
|
return False
|
|
return True
|
|
|
|
@property
|
|
def amount_due(self) -> int:
|
|
"""Get amount due."""
|
|
if fee := self.user_data.get(FIELD_FEE):
|
|
return int(fee)
|
|
if not self.is_member:
|
|
raise RuntimeError(
|
|
"User does not seem to be a member, and has no due fees."
|
|
)
|
|
return self.member_fee
|
|
|
|
def register_payment(self, amount_paid: int | None = None) -> None:
|
|
"""Register payment,"""
|
|
date_field = self._lookup_user_field(FIELD_PAID_DATE)
|
|
amount_field = self._lookup_user_field(FIELD_FEE)
|
|
date_paid = date.today().isoformat()
|
|
if not amount_paid:
|
|
amount_paid = self.member_fee
|
|
self.db.create_user_data(self.user_id, date_field, date_paid)
|
|
self.db.create_user_data(self.user_id, amount_field, str(amount_paid))
|
|
|
|
@classmethod
|
|
def lookup_email(
|
|
cls, email: str, settings: AdmidioDBSettings
|
|
) -> "AdmidioMemberFee | None":
|
|
"""Create instance by lookup up user email address."""
|
|
db = AdmidioDB(
|
|
settings.user,
|
|
settings.password,
|
|
settings.host,
|
|
settings.database,
|
|
settings.port,
|
|
)
|
|
fields = db.get_custom_fields()
|
|
email_field_id = fields.get(FIELD_EMAIL)
|
|
if not email_field_id:
|
|
raise RuntimeError("Could not resolve email address field in database.")
|
|
if user_id := db.get_user_id_by_field(email_field_id, email):
|
|
return cls(user_id, settings)
|
|
|
|
return None
|