Category overhaul (#20)
Tests / test (push) Successful in 16s
Tests / lint (push) Successful in 16s

Reviewed-on: #20
Co-authored-by: Mathias <mathias2784@web.de>
This commit was merged in pull request #20.
This commit is contained in:
2026-10-27 18:08:10 +00:00
committed by mathias2784
parent 4efef98c9c
commit 6babc560c7
36 changed files with 3021 additions and 176 deletions
+12 -55
View File
@@ -2,19 +2,26 @@ from datetime import date
from decimal import Decimal
from pydantic import Field, computed_field, field_validator
from sqlalchemy import Date, ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy import (
Date,
ForeignKey,
Integer,
String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from src.constants import IBAN_PATTERN, Currency
from src.models._common import (
BaseModelDb,
from src.models.category import Category, CategoryDb
from src.models.common import (
ExportBaseModel,
IngestdbBase,
Sign,
_hash,
date2str,
hashify,
)
__all__ = ["B4wDb", "B4wExport", "Category", "CategoryDb"]
class B4wExport(ExportBaseModel):
id: int = Field(alias="Id1", description="internal B4w ID")
@@ -59,7 +66,7 @@ class B4wExport(ExportBaseModel):
@computed_field # type: ignore[prop-decorator]
@property
def hash_id(self) -> str:
return _hash(self.id)
return hashify(self.id)
@property
def amount_signed(self) -> Decimal:
@@ -87,56 +94,6 @@ class B4wExport(ExportBaseModel):
return category
class Category(ExportBaseModel):
name: str = Field(pattern=r"[^:\r\n]+")
parent: Category | None = None
@computed_field # type: ignore[prop-decorator]
@property
def hash_id(self) -> str:
return _hash(self.name, self.parent)
@classmethod
def from_str(cls, s: str) -> Category | None:
if not s:
return None
components = s.split(":")
name = components[-1]
rest = ":".join(components[:-1])
return cls(name=name, parent=cls.from_str(rest))
def to_str(self) -> str | None:
if not self.parent:
return self.name
else:
return f"{self.parent.to_str()}:{self.name}"
def __str__(self):
return self.to_str()
def __repr__(self):
return f'Category.from_str("{self.__str__()}")'
class CategoryDb(BaseModelDb):
__tablename__ = "category"
__table_args__ = (
UniqueConstraint("name", "parent_id", name="uq_category_name_parent"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String, nullable=False)
parent_id: Mapped[int | None] = mapped_column(
ForeignKey("category.id"), nullable=True
)
# Relationships
parent = relationship(
"CategoryDb", remote_side="CategoryDb.id", back_populates="children"
)
children = relationship("CategoryDb", back_populates="parent")
class B4wDb(IngestdbBase):
__tablename__ = "import_b4w"