Verified Commit 5cb0e777 authored by Jakob Moser's avatar Jakob Moser
Browse files

Also load groups (using somewhat buggy heuristic)

parent 931bfcbc
Loading
Loading
Loading
Loading
+1 −1
Changes for muffin/__main__.py: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ output_rows = [
        remaining := item.amount - spent,
        item.amount,
    )
    for item in budget_plan.items.values()
    for item in budget_plan.items
]

formatted_output_rows = [
+22 −1
Changes for muffin/budget_plan/BudgetPlan.py: 22 added lines, 1 removed line.
Original line number Diff line number Diff line
from dataclasses import dataclass
from typing import Self
from collections.abc import Iterable, Sequence

from .BudgetaryItem import BudgetaryItem
from .BudgetaryGroup import BudgetaryGroup


@dataclass(frozen=True, eq=False)
class BudgetPlan:

    items: dict[str, BudgetaryItem]
    _groups: dict[str, BudgetaryGroup]

    @classmethod
    def from_groups(cls, groups: Iterable[BudgetaryGroup]) -> Self:
        return cls({group.id: group for group in groups})

    @property
    def groups(self) -> Sequence[BudgetaryGroup]:
        return tuple(self._groups.values())

    def group(self, id: str) -> BudgetaryGroup:
        return self._groups

    @property
    def items(self) -> Sequence[BudgetaryItem]:
        return [item for group in self.groups for item in group.items]

    def item(self, id: str) -> BudgetaryItem:
        pass  # TODO
+61 −7
Changes for muffin/budget_plan/BudgetPlanLoader.py: 61 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -3,25 +3,79 @@ import subprocess
from pathlib import Path
from collections.abc import Sequence, Iterable

from ..amount.AmountParser import parse_euro_amount
from ..cash_flow.CashFlowDirection import CashFlowDirection

from .BudgetPlan import BudgetPlan
from .BudgetaryGroup import BudgetaryGroup
from .BudgetaryItem import BudgetaryItem
from ..amount.AmountParser import parse_euro_amount


def _is_budgetary_item_row(row: Sequence[str]) -> bool:
    return len(row) >= 3 and row[0] and row[1] and row[2]


def _parse_budgetary_item_row(row: Sequence[str]) -> BudgetaryItem:
    return BudgetaryItem(id=row[0], name=row[1], amount=parse_euro_amount(row[2]))


def _is_combined_row(row: Sequence[str]) -> bool:
    return row[0] and all(not row[i] for i in range(1, len(row)))


def _parse_combined_row(row: Sequence[str]) -> ...:
    lines = row[0].split("\n")

    return (lines[0][0], lines[0][1:])  # TODO


def _load_csv_lines(csv_lines: Iterable[str]) -> BudgetPlan:
    reader = csv.reader(csv_lines)
    items = [
        BudgetaryItem(id=row[0], name=row[1], amount=parse_euro_amount(row[2]))
        for row in reader
        if _is_budgetary_item_row(row)
    ]

    return BudgetPlan({item.id: item for item in items})
    groups: list[BudgetaryGroup] = []

    current_group_id: Optional[str] = None
    current_group_name: Optional[str] = None
    current_cash_flow_direction: Optional[CashFlowDirection] = None
    current_items: list[BudgetaryItem] = []

    def add_group():
        nonlocal current_group_id
        nonlocal current_group_name
        nonlocal current_items

        if current_group_id is None:
            return

        groups.append(
            BudgetaryGroup.from_items(
                id=current_group_id,
                name=current_group_name,
                cash_flow_direction=current_cash_flow_direction,
                items=current_items,
            )
        )

        current_group_id = None
        current_group_name = None
        current_items = []

    for row in reader:
        if not row:
            continue
        elif row[0] == "Einnahmen":
            current_cash_flow_direction = CashFlowDirection.INCOMING
        elif row[0] == "Ausgaben":
            current_cash_flow_direction = CashFlowDirection.OUTGOING
        elif _is_budgetary_item_row(row):
            current_items.append(_parse_budgetary_item_row(row))
        elif _is_combined_row(row):
            add_group()  # Add the previous group, which is now completed
            current_group_id, current_group_name = _parse_combined_row(row)

    add_group()  # Add the last group

    return BudgetPlan.from_groups(groups)


def load_csv(csv_path: Path | str) -> BudgetPlan:
+6 −0
Changes for muffin/cash_flow/CashFlowDirection.py: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
from enum import Enum


class CashFlowDirection(Enum):
    INCOMING = 1
    OUTGOING = -1