sipa/model/sample/user.py

Summary

Maintainability
B
4 hrs
Test Coverage
import typing as t
from datetime import datetime, date
from random import random

from flask import current_app
from flask_login import AnonymousUserMixin
from werkzeug.local import LocalProxy

from sipa.model.exceptions import PasswordInvalid, UserNotFound
from sipa.model.fancy_property import (
    ActiveProperty,
    Capabilities,
    UnsupportedProperty,
)
from sipa.model.finance import BaseFinanceInformation
from sipa.model.misc import PaymentDetails
from sipa.model.user import BaseUser
from sipa.utils import argstr


class SampleUserData(t.TypedDict):
    name: str
    id: str
    uid: str
    password: str
    address: str
    mail: str
    mail_forwarded: bool
    mail_confirmed: bool
    mac: str
    ip: str
    status: str
    membership_end_date: str | None
    is_member: bool


def init_app(app):
    app.extensions['sample_users'] = {
        'test': {
            'name': 'Test User',
            'id': '1337-0',
            'uid': 'test',
            'password': 'test',
            'address': "Keller, Wundtstr. 5",
            'mail': 'test@agdsn.de',
            'mail_forwarded': True,
            'mail_confirmed': True,
            'mac': 'aa:bb:cc:dd:ee:ff',
            'ip': '141.30.228.39',
            'status': "OK",
            'membership_end_date': None,
            'is_member': True,
        }
    }


config = LocalProxy(lambda: current_app.extensions['sample_users'])


class SampleFinanceInformation(BaseFinanceInformation):
    has_to_pay = True

    @property
    def raw_balance(self):
        """Some random balance"""
        return random() * 10 - 5

    @property
    def history(self):
        return [
            (datetime(2016, 4, 1), 21, "Desc 1"),
            (datetime(2016, 4, 30), -3.5, "Desc 2"),
            (datetime(2023, 12, 23), -3.5, "Desc 3"),
        ]

    @property
    def last_update(self):
        return max(l[0] for l in self.history)

# noinspection PyMethodMayBeStatic
class User(BaseUser):
    def __init__(self, uid):
        super().__init__(uid)
        self.config: SampleUserData = config[uid]
        self._realname = self.config["name"]
        self.old_mail = self.config["mail"]
        self._ip = "127.0.0.1"

    def __repr__(self):
        return "{}.{}({})".format(__name__, type(self).__name__, argstr(
            uid=self.uid,
            realname=self._realname,
            mail=self.mail,
            ip=self._ip,
        ))

    can_change_password = True

    login_list = {
        'test': ('test', 'Test Nutzer', 'test@agdsn.de'),
    }

    @classmethod
    def get(cls, username):
        """Static method for flask-login user_loader,
        used before _every_ request.
        """
        if username in config:
            return cls(username)
        else:
            return AnonymousUserMixin()

    @classmethod
    def authenticate(cls, username, password):
        if username in config:
            if config[username]['password'] == password:
                return cls.get(username)
            else:
                raise PasswordInvalid
        else:
            raise UserNotFound

    @classmethod
    def from_ip(cls, ip):
        return cls.get('test')

    def change_password(self, old, new):
        self.config["password"] = new

    @property
    def traffic_history(self):
        def rand():
            return random() * 7 * 1024**2
        return [{
            'day': day,
            **(lambda i, o: {
                'input': i,
                'output': o,
                'throughput': i + o,
            })(rand(), rand()*0.04),
        } for day in range(7)]

    @property
    def realname(self):
        return ActiveProperty[str, str](name="realname", value=self._realname)

    @property
    def login(self):
        return ActiveProperty[str, str](name="login", value=self.uid)

    @property
    def mac(self):
        return ActiveProperty[str, str](
            name="mac",
            value=self.config["mac"],
            capabilities=Capabilities(edit=True, delete=False),
        )

    @mac.setter
    def mac(self, value):
        self.config["mac"] = value

    @property
    def mail(self):
        return ActiveProperty[str, str](
            name="mail",
            value=self.config["mail"],
            capabilities=Capabilities(edit=True, delete=False),
        )

    def change_mail(self, password: str, new_mail: str, mail_forwarded: bool):
        self.config["mail"] = new_mail

    @property
    def mail_forwarded(self):
        return ActiveProperty[bool, bool](
            name="mail_forwarded", value=self.config["mail_forwarded"]
        )

    @property
    def mail_confirmed(self):
        return ActiveProperty[bool, bool](
            name="mail_confirmed", value=self.config["mail_confirmed"]
        )

    def resend_confirm_mail(self) -> bool:
        """ Resend the confirmation mail."""
        pass

    @mail.setter
    def mail(self, value: str) -> None:
        self.config["mail"] = value

    @property
    def network_access_active(self):
        return ActiveProperty[bool, bool](
            name="network_access_active",
            value=True,
            capabilities=Capabilities(edit=True, delete=False),
        )

    @property
    def address(self):
        return ActiveProperty[str, str](name="address", value=self.config["address"])

    @property
    def ips(self):
        return ActiveProperty[str, str](name="ips", value=self.config["ip"])

    @property
    def status(self):
        status_str = self.config["status"]
        value = (
            status_str
            if not self.membership_end_date
            else f"{status_str} (ends at {self.membership_end_date.value})"
        )
        return ActiveProperty[str, str](name="status", value=value)

    has_connection = True

    @property
    def id(self):
        return ActiveProperty[str, str](name="id", value=self.config["id"])

    @property
    def userdb_status(self):
        return UnsupportedProperty("userdb_status")

    @property
    def birthdate(self):
        return UnsupportedProperty("birthdate")

    def payment_details(self) -> PaymentDetails:
        return PaymentDetails(
            recipient="Donald Duck",
            bank="Geldspeicher GbR",
            iban="EH12432543209523",
            bic="ENTHAUS123",
            purpose=self.id.value,
        )

    @property
    def membership_end_date(self):
        print(self.config)
        return ActiveProperty[date | None, date | None](
            name="membership_end_date",
            value=self.config["membership_end_date"],
            capabilities=Capabilities.edit_if(self.is_member),
        )

    @property
    def is_member(self) -> bool:
        return self.config["is_member"]

    def estimate_balance(self, end_date):
        return random() * 10 - 5

    def terminate_membership(self, end_date):
        self.config["membership_end_date"] = end_date
        print(self.config)

    def continue_membership(self):
        self.config["membership_end_date"] = None

    @property
    def wifi_password(self):
        return ActiveProperty[str, str](name="wifi_password", value="password")

    @classmethod
    def request_password_reset(cls, user_ident, email):
        raise NotImplementedError

    @classmethod
    def password_reset(cls, token, new_password):
        raise NotImplementedError

    userdb = None

    finance_information = SampleFinanceInformation()