# -*- coding: utf-8 -*-"""SQLAlchemy ORM models for tix entities.This module defines the database schema and domain logic for Story and Taskentities. The ORM models serve dual purpose: database persistence andruntime domain objects with file I/O capabilities."""importtypingasTimportjsonfrompathlibimportPathimportsqlalchemyassaimportsqlalchemy.ormasormfrom.utilsimportsafe_write,build_folder_name,Ticketfrom.constantsimportStatusEnum,MetadataKeyEnum,WordsEnum
[docs]classStoryOrTask(Base):""" Abstract base class for Story and Task ORM models. Provides common fields (id, date, title, path) and file I/O methods for metadata, description, and report files. :param id: Primary key, the global ID :param date: Creation date in YYYY-MM-DD format :param title: Sanitized title from folder name :param path: Absolute filesystem path to the entity directory """__abstract__=Trueid:orm.Mapped[int]=orm.mapped_column(primary_key=True)date:orm.Mapped[str]=orm.mapped_column(sa.String(10))title:orm.Mapped[str]=orm.mapped_column(sa.String(255))path:orm.Mapped[str]=orm.mapped_column(sa.String(1024))@propertydefdir_root(self)->Path:"""Get the filesystem directory for this entity as a Path object."""returnPath(self.path)@propertydefpath_metadata(self)->Path:returnself.dir_root/"metadata.json"@propertydeffile_metadata(self)->dict[str,T.Any]:"""Read metadata from metadata.json file."""try:content=self.path_metadata.read_text(encoding="utf-8")returnjson.loads(content)exceptFileNotFoundError:return{}defwrite_metadata(self,status:StatusEnum=StatusEnum.TODO,):data={MetadataKeyEnum.status.value:status.value,}safe_write(self.path_metadata,json.dumps(data,indent=4,ensure_ascii=False))@propertydefstatus(self)->str:returnself.file_metadata.get(MetadataKeyEnum.status.value,StatusEnum.TODO.value)@propertydefpath_description(self)->Path:returnself.dir_root/"description.md"defwrite_description(self,content:str):safe_write(self.path_description,content)defread_description(self)->str:try:returnself.path_description.read_text(encoding="utf-8")exceptFileNotFoundError:returnf"{self.path_description} doesn't exists!"@propertydefpath_report(self)->Path:returnself.dir_root/"report.md"defwrite_report(self,content:str):safe_write(self.path_report,content)defread_report(self)->str:try:returnself.path_report.read_text(encoding="utf-8")exceptFileNotFoundError:returnf"{self.path_report} doesn't exists!"
[docs]classStory(StoryOrTask):""" SQLAlchemy ORM model for Story entities. :param id: Primary key, the global story ID :param date: Creation date in YYYY-MM-DD format :param title: Sanitized title from folder name """__tablename__="stories"tasks:orm.Mapped[list["Task"]]=orm.relationship(back_populates="story",cascade="all, delete-orphan",)@propertydefdir_tasks(self)->Path:returnself.dir_root/WordsEnum.tasks.value
[docs]classTask(StoryOrTask):""" SQLAlchemy ORM model for Task entities. :param id: Primary key, the global task ID :param story_id: Foreign key to parent story :param date: Creation date in YYYY-MM-DD format :param title: Sanitized title from folder name """__tablename__="tasks"story_id:orm.Mapped[int]=orm.mapped_column(sa.ForeignKey("stories.id"))story:orm.Mapped["Story"]=orm.relationship(back_populates="tasks")