Source code for finalynx.budget.expense

from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Any
from typing import Dict
from typing import List
from typing import Optional

import pytz


[docs]class Status(Enum): UNKNOWN = "UNKNOWN" TODO = "TODO" DONE = "DONE" SKIP = "SKIP"
[docs]class Period(Enum): UNKNOWN = "UNKNOWN" MONTHLY = "MONTHLY" YEARLY = "YEARLY"
[docs]class Constraint(Enum): # TODO rename to Necessity UNKNOWN = "UNKNOWN" FIXED = "FIXED" MODULAR = "MODULAR" NOCHOICE = "NOCHOICE" VARIABLE = "VARIABLE" OPTIONAL = "OPTIONAL" HOBBIES = "HOBBIES" FUN = "FUN"
[docs]@dataclass class Expense: # Information from N26 and/or manual input timestamp: int amount: float merchant_name: str merchant_category: str # Information from user input to manage the status status: Status = Status.UNKNOWN i_paid: Optional[float] = None payback: str = "" constraint: Constraint = Constraint.UNKNOWN period: Period = Period.UNKNOWN comment: str = "" # Internal information cell_number: int = -1
[docs] def as_datetime(self, timezone: str = "Europe/Paris") -> datetime: """Return the timestamp as a datetime object in the given timezone.""" return ( datetime.utcfromtimestamp(int(self.timestamp) / 1000) .replace(tzinfo=pytz.UTC) .astimezone(pytz.timezone(timezone)) )
[docs] def to_list(self) -> List[Any]: """Return the expense as a list of values. Used for Google Sheets export/import.""" return [ int(self.timestamp), float(self.amount), str(self.merchant_name), str(self.merchant_category), str(self.status.value) if self.status != Status.UNKNOWN else "", float(self.i_paid) if self.i_paid is not None else "", str(self.payback), str(self.constraint.value) if self.constraint != Constraint.UNKNOWN else "", str(self.period.value) if self.period != Period.UNKNOWN else "", str(self.comment), ]
[docs] @staticmethod def from_list(list: List[Any], cell_number: int = -1) -> "Expense": """Create an Expense object from a list of values. Used for Google Sheets export/import.""" if len(list) < 4: raise ValueError("List must have at least 4 elements, got", len(list)) return Expense( timestamp=int(list[0]), amount=float(str(list[1]).replace("€", "").replace(",", "").strip()), merchant_name=list[2], merchant_category=list[3], status=Status(list[4]) if list[4] else Status.UNKNOWN, i_paid=float(str(list[5]).replace("€", "").replace(",", "").strip()) if list[5] else None, payback=list[6], constraint=Constraint(list[7]) if list[7] else Constraint.UNKNOWN, period=Period(list[8]) if list[8] else Period.UNKNOWN, comment=list[9], cell_number=cell_number, )
[docs] def to_dict(self) -> Dict[str, Any]: """Return the expense as a dictionary. Used for JSON export/import.""" return { "timestamp": str(self.timestamp), "amount": str(self.amount), "merchant_name": self.merchant_name, "merchant_category": self.merchant_category, "status": self.status.value, "i_paid": str(self.i_paid), "payback": self.payback, "constraint": self.constraint.value, "period": self.period.value, "comment": self.comment, "cell_number": str(self.cell_number), }
[docs] @staticmethod def from_dict(dict_: Dict[str, Any]) -> "Expense": """Create an Expense object from a dictionary. Used for JSON export/import.""" return Expense( timestamp=int(dict_["timestamp"]), amount=float(str(dict_["amount"]).replace("€", "").strip()), merchant_name=dict_["merchant_name"], merchant_category=dict_["merchant_category"], status=Status(dict_["status"]) if dict_["status"] else Status.UNKNOWN, i_paid=float(str(dict_["i_paid"]).replace("€", "").strip()) if dict_["i_paid"] != "None" else None, payback=dict_["payback"], constraint=Constraint(dict_["constraint"]) if dict_["constraint"] else Constraint.UNKNOWN, period=Period(dict_["period"]) if dict_["period"] else Period.UNKNOWN, comment=dict_["comment"], cell_number=int(dict_["cell_number"]), )