cadrumo.application.wizard.persistence module

Persistence adapter for wizard answers.

Serialises a typed answers model back to canonical-token strings, then persists profile facts through canonical user-profile orchestration. The reverse projection (project_answers) builds the typed answers model from a raw canonical-token dict.

Command ownership for create and edit lives in commands; this module provides only the canonical projections and the explicit patch writer those commands consume.

WizardPersistMode

Which wizard verb is persisting — create registers a new profile, edit upserts facts on an existing one. The verb is the authority for the create-vs-edit branch; it is never re-derived at runtime.

alias of Literal[‘create’, ‘edit’]

serialise_answers(flow, answers, *, only_question_ids=None)[source]

Project a typed answers model into the canonical-token dict.

Only profile-bound questions contribute a key. When only_question_ids is supplied, the projection is restricted to those question ids — the patch behaviour the edit verb relies on so an unsupplied field is never written back at its default.

Return type:

dict[str, str]

Parameters:
profile_values_from_patch(flow, supplied)[source]

Project a non-interactive edit patch to schema-path keyed values.

Return type:

dict[str, str]

Parameters:
project_answers(flow, values)[source]

Reverse projection: build the typed answers model from canonical tokens.

Values absent from values fall back to the descriptor’s default or the answers model’s own field default; the answers model then runs its strict validation.

Return type:

BaseModel

Parameters:
parse_canonical(question, raw)[source]

Parse a canonical token into the question’s declared answer type.

For an optional CONFIRM, a blank canonical projects to the empty string (the undeclared three-state arm of the answers-model union), never to False. Collapsing blank onto False here would erase the distinction between “the operator did not declare this fact” and “the operator positively declined”, and would defeat the persistence-layer’s drop-blank filter — the projected False would round-trip to a stored "false" token and reload as a declared decline.

Return type:

object

Parameters:
descendant_facts_from_answers(answers, *, operation)[source]

Project the descendant repeating-group answers into profile facts.

The setup flow’s descendant repeating group keys each instance answer as descendientes#<index>.<page-id>. This reads the live instance count from the count page, reconstructs one DescendantInfo per index, and delegates to descendant_facts_from_list() so the emitted renta_family.descendiente.{n}.* paths and the derived aggregates are the single canonical projection the _minimo_descendientes_facts injector and the registry selectors consume. Returns an empty list when the group was never reached (the count page carries no answer), so a descendant-free profile writes no descendant fact.

Return type:

list[tuple[str, str]]

Parameters:
descendant_answers_from_record(record, *, operation)[source]

Re-project a record’s descendant facts into repeating-group answers.

The inverse of descendant_facts_from_answers(): reads the renta_family.descendiente.{n}.* facts a record carries, reconstructs each DescendantInfo through the canonical descendant_list_from_facts(), and emits the descendientes-count answer plus one descendientes#<index>.<page-id> answer per populated field. This is the exact page-keyed shape resume_flow() re-walks to re-instantiate the group: the count answer commits first (the familia section orders the count page before the group), revealing the instance pages the remaining answers then seed against the current definition. Returns an empty map when the record declares no descendants, so a childless profile seeds no group.

The per-field emission mirrors _descendant_from_row() exactly, so a facts-to-answers re-projection preserves an identical fact set: an absent optional field stays absent on both legs, never coerced to a stored default.

Parameters:
Return type:

dict[str, str]