cadrumo.domain.user_profile.schema module

Strict Pydantic records for the centralized user-profile schema.

Each schema section declares a SensitivityClass that governs the encryption tier applied when the section’s data is persisted to the secure DB backend.

class ProfileFieldType(*values)[source]

Bases: StrEnum

Closed catalogue of field types allowed by the profile schema TOML.

STRING
BOOLEAN
INTEGER
DECIMAL
MONEY
DATE
EMAIL
ENUM
ARRAY
OBJECT
class ProfileSnapshotPolicy(*values)[source]

Bases: StrEnum

Accepted profile snapshot stale-check policies.

IMMUTABLE_SECURE_SNAPSHOT_HASH
class ProfileRemovePolicy(*values)[source]

Bases: StrEnum

Accepted profile removal policies.

LIVE_PROFILE_TOMBSTONE_RETAIN_SNAPSHOTS
class ProfileFieldDefinition(**data)[source]

Bases: BaseModel

One field declared inside a user-profile schema section.

Parameters:

data (Any)

key: _FieldKey
type: ProfileFieldType
required: bool
nullable: bool
sensitivity: SensitivityClass
effective_dated: bool
description: _Description
enum_values: tuple[str, ...]
model_selectors: tuple[_Selector, ...]
required_for_modelos: tuple[Modelo, ...]

Modelos that require this field even though it is not globally required.

required is a property of the field across the whole profile: it drives completeness, overview and presentation, so setting it to satisfy one modelo demands the fact from every taxpayer, including those with no such obligation. This axis carries the modelo-scoped requirement instead, and ONLY the filing-preflight walk consults it.

schedule_predicates: tuple[_Selector, ...]
legal_refs: tuple[_Description, ...]
minimum: Decimal | None
maximum: Decimal | None
class ProfileSectionDefinition(**data)[source]

Bases: BaseModel

A top-level section in the user-profile schema.

Parameters:

data (Any)

key: _SectionKey
title: _Description
sensitivity: SensitivityClass
effective_dated: bool
repeatable: bool
fields: tuple[ProfileFieldDefinition, ...]
class ProfileDerivedSelectorDefinition(**data)[source]

Bases: BaseModel

A namespace of profile paths the calculation engine owns and computes.

A derived path is NOT taxpayer data. Its value is computed at calculate time from the source facts named in derived_from, so the schema declares the namespace as a pattern rather than one field per filing year. The pattern exists so registry binding selectors targeting these paths still resolve once the per-year field declarations are gone.

This is a DECLARATION of engine ownership, never a resolution route for values: nothing here supplies a value to the binding resolver.

Parameters:

data (Any)

pattern: _DerivedSelectorPattern
derived_from: tuple[_FieldPath, ...]
entry_surface: _Description
description: _Description
legal_refs: tuple[_Description, ...]
matches(selector)[source]

Return whether selector falls inside this derived namespace.

Return type:

bool

Parameters:

selector (str)

derived_selector_for_path(path, derived_selectors)[source]

Return the declared namespace owning path, or None if it is not derived.

The single written-once judgment on whether a profile path is engine-derived. Every consumer – the registry contract validator and the write-door refusal – asks through here, so the two can never disagree about what “derived” means.

Deliberately NOT routed through profile_value_refusal(), which is the authority on whether a VALUE may be stored at a declared field. That judgment is value-scoped against a ProfileFieldDefinition and expressly declines to judge absence; this one is path-scoped, refuses every value including a clear, and must keep answering once the per-year field declarations are gone and there is no declaration left to judge against. The two live in one module because they are both schema-level judgments, not because they are the same judgment.

The definition is returned rather than a bare bool so a caller can name the surface that edits the real source facts without re-scanning the namespace.

Return type:

ProfileDerivedSelectorDefinition | None

Parameters:
class ProfileSchemaDefinition(**data)[source]

Bases: BaseModel

The committed centralized user-profile schema.

Parameters:
id: _SchemaId
version: int
title: _Description
snapshot_policy: ProfileSnapshotPolicy
remove_policy: ProfileRemovePolicy
sections: tuple[ProfileSectionDefinition, ...]
derived_selectors: tuple[ProfileDerivedSelectorDefinition, ...]
property field_paths: tuple[str, ...]

Canonical dotted field paths declared by the schema.

section(key)[source]

Return a ProfileSectionDefinition by canonical section key.

Return type:

ProfileSectionDefinition

Parameters:

key (str)

field(path)[source]

Return a field by canonical dotted path.

Return type:

ProfileFieldDefinition

Returns:

The ProfileFieldDefinition for the given path.

Parameters:

path (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=3, max_length=160, pattern=^[a-z][a-z0-9_]*(?:\.[a-z0-9_]+)+$, ascii_only=None)])

path_for_model_selector(selector)[source]

Return the canonical dotted path of the field declaring selector.

The inverse of the model_selectors declaration. Surfaces that hold a selector token rather than a path - a deadline-engine gating key, a registry binding’s consumed profile key - need the path before they can resolve the field’s operator label and legal grounding, and every other schema lookup here runs path-first.

Ambiguity is refused rather than guessed: the schema does not constrain a token to one declaring field, so a token declared by two fields has no single correct answer and returning either would mislabel one of them. An unknown token is likewise not an error - the callers mix selector tokens with identifiers from other namespaces and must be able to ask without knowing which they hold.

Return type:

str | None

Returns:

The section.field path when exactly one field declares selector, otherwise None.

Parameters:

selector (str)

NUMERIC_PROFILE_FIELD_TYPES: frozenset[ProfileFieldType]

The field types whose values are numbers, and whose bounds therefore bind.

Mirrors the set ProfileFieldDefinition._validate_enum_values() already uses to decide where minimum / maximum may be declared, so the types that may CARRY a bound and the types that are CHECKED against one cannot drift apart.

numeric_value_refusal(field, value)[source]

Return why value fails field’s numeric declaration, or None.

The single authority for “is this a legal value for this numeric field”. It exists because the declaration was inert: minimum and maximum were validated for their own coherence at schema build and then never compared to anything, so a participation percentage declared 0..100 accepted 999 on write and carried it into an attribution calculation unchallenged. Both the write door and the readers that consume these facts ask this one function, rather than each forming its own opinion.

Numbers are Decimal (or int), never float. These are financial quantities – a participation percentage and an assigned base that divide a taxable amount between members – so binary floating point is the wrong carrier: its rounding is invisible at the point of entry and shows up as a cent that does not reconcile in a filing. bool is rejected despite being an int subclass, because True is an answer to a different question and would otherwise silently satisfy a numeric field as 1.

Absence is not this rule’s business. A None value is a cleared or unanswered field, which the required-field check judges; refusing it here would report one missing field as two unrelated faults.

Non-finite values need no check: the fact carrier’s own union rejects a NaN or infinite Decimal before it can be constructed, so an unorderable value cannot reach a bound comparison.

Parameters:
  • field (ProfileFieldDefinition) – The declaration the value must satisfy.

  • value (object) – The value carried by the fact, after the fact carrier has restored its type.

Return type:

str | None

Returns:

An instructive message naming the field and the range it accepts, or None when the value is admissible or the field is not numeric.

boolean_value_refusal(field, value)[source]

Return why value fails field’s boolean declaration, or None.

The single authority for “is this a legal value for this boolean field”, the counterpart to numeric_value_refusal(). It exists because the declaration was inert: a field declared boolean accepted banana, placeholder and '' on write and stored them unchallenged, while every reader then had to decide for itself what an unreadable value meant. Readers that resolve a stored boolean default an unreadable one to False – safe only if a value this door admitted can always be read, which is what this function makes true.

Readability, not spelling, is the test. A taxpayer answering sí is saying yes, and refusing that while accepting true would be a vocabulary rule dressed up as a type rule. The judgement therefore comes from cadrumo.core.parsing.parse_bool, the one boolean vocabulary the rest of the codebase already reads answers with, rather than a set spelled out here that could drift from it.

A real bool is admissible by construction: it needs no parsing and carries no ambiguity.

Absence is not this rule’s business. A None value is a cleared or unanswered field, which the required-field check judges. An empty string is NOT absence – it is an answer that says nothing – and is refused, as the enum and date declarations already refuse it.

Parameters:
  • field (ProfileFieldDefinition) – The declaration the value must satisfy.

  • value (object) – The value carried by the fact, after the fact carrier has restored its type.

Return type:

str | None

Returns:

An instructive message naming the field and what it accepts, or None when the value is admissible or the field is not boolean.

class ProfileValueRefusalKind(*values)[source]

Bases: StrEnum

Which declaration a refused value failed.

A closed set, because it is the join between the one rule that judges a value and the two surfaces that have to say something about the verdict: the validation service turns a kind into its issue code, and an operator surface turns the same kind into the sentence it shows and the editor it offers. Both read the kind rather than matching on the message, so the prose stays free to change without either one silently stopping working.

ENUM
DATE
NUMERIC
BOOLEAN
EMAIL
class ProfileValueRefusal(**data)[source]

Bases: BaseModel

Why one value fails its field’s declaration.

Carries the kind as well as the message so a caller can classify the refusal without parsing the sentence: the codes an issue report uses and the copy an operator reads are both decided from kind.

Parameters:

data (Any)

kind: ProfileValueRefusalKind
message: str
date_value_refusal(field, value)[source]

Return why value fails field’s date declaration, or None.

The sibling of boolean_value_refusal() and numeric_value_refusal(), and here for the same reason: the rule lived only inside the validation service, so a surface wanting to refuse a bad date at the box the operator typed it into had nowhere to ask and would have had to restate the layout itself.

A real datetime.date is admissible by construction. A string is accepted only when it matches the zero-padded extended layout AND parses as a calendar day, which together reject a non-ISO layout (15/03/1978), the compact basic form (19780315), an impossible month or day (1978-13-45), a non-calendar day (1978-02-30), and plain garbage – without any hand-rolled calendar maths.

Return type:

str | None

Parameters:
email_value_refusal(field, value)[source]

Return why value fails field’s email declaration, or None.

The declaration was inert before this: a field declared email accepted banana and stored it, so the one type in the schema that names its own content format was the one type nothing checked.

Only a string can be an address. A non-string reaching an email field is a fact carrier that promoted the value to something else — a bare numeral, a date-shaped token — and is refused as the same fault rather than allowed through for want of a branch.

Return type:

str | None

Parameters:
enum_value_refusal(field, value)[source]

Return why value is outside field’s declared token set, or None.

Comparison is exact rather than case-folded. The declared sets are case-significant and inconsistent between fields by design – some carry AEAT’s own uppercase tokens, others the profile’s lowercase vocabulary – so folding would silently admit a spelling the declaring authority does not use.

Return type:

str | None

Parameters:
profile_value_refusal(field, value)[source]

Return why value fails field’s declaration, or None.

THE authority on whether a value may be stored at a field, gathering the four per-type rules behind one call so that every surface asks the same question in the same words. The write door asks it to build its issue report; an operator surface asks it to refuse a value at the box it was typed into, before a round trip to storage. Two surfaces judging a value apart is how one comes to accept what the other rejects, and the operator meets that as a dialog that closes on a value the record then refuses.

Absence is not this rule’s business. A None value is a cleared or unanswered field, which the required-field check judges.

Parameters:
  • field (ProfileFieldDefinition) – The declaration the value must satisfy.

  • value (object) – The value carried by the fact, after the fact carrier has restored its type. Passing a raw string a fact would have promoted ("true", "1978-03-15") asks a different question from the one the write door asks.

Return type:

ProfileValueRefusal | None

Returns:

A ProfileValueRefusal naming what failed and what the field accepts, or None when the value is admissible.