"""The reusable section-field library (字段表). A *section field* is one heading a paper can contain — "1. Introduction", "2.1 Dataset", "0 Abstract". Fields live in this one global library and are never owned by a template. Why there is no ``parent_id`` ----------------------------- A tree would make a field usable under exactly one parent, so a level-2 heading such as "Background" could not sit under both "1. Introduction" and "2. Related Work". Hierarchy is therefore expressed only by :attr:`SectionField.level` (1, 2, 3 ...), which is a *rendering hint* — it drives indentation and numbering semantics in the UI — while a field stays free to be attached to any number of templates and any number of parents within them. The number that the reader sees is part of :attr:`name` and is written by the user ("1. Introduction", "0 Abstract"). Nothing derives or rewrites it. Ordering inside a template is *not* stored here: it lives in ``template_field.sort``. This table has no ``sort`` column on purpose, so that one library field can occupy a different position in every template. """ from decimal import Decimal from sqlalchemy import Integer, Numeric, String, text from sqlalchemy.orm import Mapped, mapped_column from app.db.base import Base from app.models.mixins import TimestampMixin class SectionField(TimestampMixin, Base): """A single reusable heading, with the typography it should render in.""" __tablename__ = "section_field" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) #: Display name, numbering included. Stored and rendered verbatim. name: Mapped[str] = mapped_column(String(255), nullable=False) #: Heading depth: 1 for "1.", 2 for "1.1", 3 for "1.1.1". Rendering hint #: only — it does not link fields to each other. level: Mapped[int] = mapped_column( Integer, nullable=False, default=1, server_default=text("1"), ) #: Font size in points. ``Numeric`` rather than ``Integer`` because the #: conventional Chinese sizes are fractional (五号 = 10.5pt, #: 小四 = 12pt). font_size: Mapped[Decimal] = mapped_column( Numeric(4, 1), nullable=False, default=Decimal("12.0"), server_default=text("12.0"), ) #: Font colour as ``#RRGGBB`` — the canonical RGB encoding. The API #: normalises ``rgb(20, 30, 40)``, ``#abc`` and bare ``aabbcc`` to it on #: write, so the column always holds one comparable format. font_color: Mapped[str] = mapped_column( String(7), nullable=False, default="#000000", server_default=text("'#000000'"), ) def __repr__(self) -> str: # pragma: no cover - debugging aid return ( f"" )