"""Typed report contracts for registry query surfaces.
These frozen pydantic DTOs are emitted by
:class:`~domain.calculations.registry.queries.RegistryQueryService` for read-only
registry introspection: modelo listings, revision descriptions, casilla
details, binding selector projections, formula dependency rows, support-matrix
summaries, and the registry-wide binding-source inventory.
The contracts stay in the domain layer and are deliberately not CLI payload
schemas. Application facades return these reports unchanged; CLI modules then
project them into strict ``--json`` envelopes. The source-inventory report is
also intentionally disposition-free: it records the committed
:class:`~core.aggregation.BindingSourceKind` declarations and leaves enrolled/deferred/
reserved mesh classification to application-layer gates.
See Also:
:class:`~domain.calculations.registry.queries.RegistryQueryService`
Builder of every report class defined here.
:mod:`~application.modelo.registry_discovery`
Application facade used by CLI discovery commands.
:mod:`~entrypoints.cli._modelo_discovery_cli`
Typer command group that renders these reports to text and JSON.
:mod:`~entrypoints.cli._modelo_payloads`
CLI-side ``OutputSchema`` projections for discovery command envelopes.
:class:`~domain.calculations.registry.schema.ModeloRevision`
Revision record from which casilla, binding, and formula rows are
projected.
:class:`~domain.calculations.registry.support_matrix.ModeloEntry`
Support-matrix row carried by :class:`ModeloSupportMatrixReport`.
"""
from __future__ import annotations
from collections.abc import Mapping
from datetime import date
from enum import StrEnum
from typing import Literal
from pydantic import BaseModel, NonNegativeInt
from ....core.aggregation import BindingSourceKind
from ....core.casilla_id import CasillaId
from ....core.filing_year import FilingYear
from ....core.identity.aeat_box import AeatBoxNumber
from ....core.models import STRICT_FROZEN_CONFIG
from ....core.period import Period, RegistrySelectorPeriodCode
from ....core.text_bounds import PositiveCount
from .binding_provider import BindingProvider
from .binding_selector_utils import BooleanBindingEncodedValue
from .ids import BindingId, FormulaId, LegalRefId, ParameterId, RevisionId, SourceRefId
from .schema_input_kind import InputKind
from .support_matrix import ModeloEntry
BindingInputChannelValue = Literal[
BindingInputChannel.DECIMAL,
BindingInputChannel.ENUM,
]
"""The same vocabulary for a strict model field."""
[docs]
class ModeloListRow(BaseModel):
"""One entry in a modelo catalogue listing."""
model_config = STRICT_FROZEN_CONFIG
code: str
title: str
cadence: str
tax_domain: str
revision_count: int
[docs]
class ModeloListReport(BaseModel):
"""Complete result set for a modelo catalogue query."""
model_config = STRICT_FROZEN_CONFIG
modelos: tuple[ModeloListRow, ...]
[docs]
class ModeloDescribeReport(BaseModel):
"""Full describe view for one resolved modelo revision.
Every field here is regulatory grounding an operator may need to justify a
revision selection, so the counts are bounded and ``filing_year`` shares the
:class:`~core.period.Period` year range: a describe view is projected verbatim into
the CLI ``--json`` envelope, and a negative count or an out-of-range year
reaching that surface is a defect in the projection, not a legitimate value.
"""
model_config = STRICT_FROZEN_CONFIG
code: str
title: str
official_name: str
tax_domain: str
cadence: str
jurisdiction: str
revision: str
revision_ids: tuple[str, ...]
filing_year: FilingYear | None
filing_period: Period | None = None
period: RegistrySelectorPeriodCode | None
valid_from: date
valid_to: date | None
periods: tuple[str, ...]
casilla_count: NonNegativeInt
manual_casilla_count: NonNegativeInt
bound_casilla_count: NonNegativeInt
computed_casilla_count: NonNegativeInt
binding_count: NonNegativeInt
formula_count: NonNegativeInt
legal_refs: tuple[LegalRefId, ...]
source_refs: tuple[SourceRefId, ...]
[docs]
class CasillaGroundingReport(BaseModel):
"""Semantic identity and regulatory grounding of one casilla.
The casilla *list* row and the casilla *detail* report describe the same
casilla. Everything a reader needs to identify it and to justify its value
-- its id, number, label, section path, value shape, input kind,
requiredness, its bound binding, its localized label/help, and its
``legal_refs`` / ``source_refs`` grounding -- is declared once here, so the
two projections cannot disagree about the same casilla.
Before this base existed the list row typed its references as unconstrained
``tuple[str, ...]`` while the detail report used the canonical
:data:`~domain.calculations.registry.ids.LegalRefId` /
:data:`~domain.calculations.registry.ids.SourceRefId`, so a reference shape the
detail projection refused passed silently through operator list JSON.
Subclasses add only what genuinely differs: the resolved formula reference
and form number for a listing row, the query context and full formula
expression for a detail view.
"""
model_config = STRICT_FROZEN_CONFIG
casilla_id: CasillaId
number: str
label: str
help_text: str | None = None
section: tuple[str, ...]
data_type: str
input_kind: InputKind
required: bool
binding: BindingId | None
legal_refs: tuple[LegalRefId, ...]
source_refs: tuple[SourceRefId, ...]
[docs]
class ModeloCasillaRow(CasillaGroundingReport):
"""One row in a casilla listing for a resolved modelo revision.
Adds the listing-shaped formula reference and the official form number to
the shared :class:`CasillaGroundingReport` identity and grounding.
"""
formula: str | None
form_number: AeatBoxNumber | None
[docs]
class ModeloCasillasReport(BaseModel):
"""Full casilla listing for a resolved modelo revision."""
model_config = STRICT_FROZEN_CONFIG
code: str
revision: str
filing_year: int | None
filing_period: Period | None = None
period: RegistrySelectorPeriodCode | None
rows: tuple[ModeloCasillaRow, ...]
[docs]
class ModeloCasillaDetailReport(CasillaGroundingReport):
"""Full semantic detail for one casilla on a resolved modelo revision.
Adds the resolving query context and the full formula reference plus its
expression to the shared :class:`CasillaGroundingReport` identity and
grounding.
"""
code: str
revision: str
filing_year: int | None
filing_period: Period | None = None
period: RegistrySelectorPeriodCode | None
formula_id: FormulaId | None
formula_expression: Mapping[str, object] | None
[docs]
class ModeloBindingQueryRow(BaseModel):
"""One row in a binding listing for a resolved modelo revision."""
model_config = STRICT_FROZEN_CONFIG
binding_id: BindingId
typed_enum: str | None
input_channel: BindingInputChannelValue
provider: BindingProvider
"""The binding's own provider union member, carried whole.
The public surface used to flatten the declaration into an untagged
key/value entry list, which erased the member's identity and its field
types. Carrying the member itself keeps the discriminator and every typed
field; a JSON consumer serialises it with ``model_dump(mode="json")``.
"""
aggregation: Mapping[str, object] | None
legal_refs: tuple[str, ...]
source_refs: tuple[str, ...]
borrador_capable: bool = False
#: The fold slots feeding this row. A relation-prefill binding names
#: ITSELF here, because the fold and the slot are one declaration; every
#: other binding names none.
relation_inputs: tuple[BindingId, ...] = ()
encoded_options: tuple[BooleanBindingEncodedValue, ...] = ()
operator_input_required: bool = True
[docs]
class ModeloBindingsReport(BaseModel):
"""Full binding listing for a single resolved modelo revision."""
model_config = STRICT_FROZEN_CONFIG
code: str
revision: str
filing_year: int | None
filing_period: Period | None = None
period: RegistrySelectorPeriodCode | None
rows: tuple[ModeloBindingQueryRow, ...]
[docs]
class RegistrySourceSite(BaseModel):
"""One committed modelo revision that declares a binding source kind."""
model_config = STRICT_FROZEN_CONFIG
modelo: str
revision_id: RevisionId
binding_count: PositiveCount
[docs]
class RegistrySourceInventoryRow(BaseModel):
"""Every committed revision that declares one binding source kind."""
model_config = STRICT_FROZEN_CONFIG
source_kind: BindingSourceKind
sites: tuple[RegistrySourceSite, ...]
total_binding_count: PositiveCount
[docs]
class RegistrySourceInventoryReport(BaseModel):
"""Registry-wide inventory of every declared binding source kind."""
model_config = STRICT_FROZEN_CONFIG
rows: tuple[RegistrySourceInventoryRow, ...]
[docs]
class ModeloSupportMatrixReport(BaseModel):
"""Registry-wide support/capability matrix."""
model_config = STRICT_FROZEN_CONFIG
entries: tuple[ModeloEntry, ...]
__all__ = [
"ModeloBindingQueryRow",
"ModeloBindingsReport",
"ModeloCasillaDetailReport",
"ModeloCasillaRow",
"ModeloCasillasReport",
"ModeloDescribeReport",
"ModeloFormulaRow",
"ModeloFormulasReport",
"ModeloListReport",
"ModeloListRow",
"ModeloSupportMatrixReport",
"RegistrySourceInventoryReport",
"RegistrySourceInventoryRow",
"RegistrySourceSite",
]