"""Pure projection of the wizard descriptor catalogue into the ``PROFILE_KEYS`` registry shape.``compile_profile_keys`` walks every :class:`WizardFlow` in thecatalogue, emits one :class:`ProfileKey` per distinct``WizardQuestion.profile_key``, and derives the requirement flag plusthe conditional `required_when_*` pair from the question's``required`` and ``visible_when`` declarations. The function isimport-time pure: it performs no file I/O, no environment lookups,and no side effects, so :data:`PROFILE_KEYS` can be assigned to itsoutput at module-load time."""from__future__importannotationsfromcollections.abcimportIterator,Sequencefrom...core.i18nimportTranslatableastrfrom...domain.contribuyenteimport(ProfileKey,ProfileKeyRequirement,)from._errorsimportWizardCompileErrorfrom._modelsimportWizardCondition,WizardFlow,WizardQuestion,WizardVisibility
[docs]defcompile_profile_keys(flows:Sequence[WizardFlow])->tuple[ProfileKey,...]:"""Project the wizard catalogue into the ``PROFILE_KEYS`` shape. Args: flows: The wizard catalogue to walk. Returns: A tuple of :class:`ProfileKey` records, one per distinct ``WizardQuestion.profile_key``. None-bound questions are skipped. """by_id={question.id:questionforquestionin_iter_catalogue_questions(flows)}keys:dict[str,ProfileKey]={}forquestionin_iter_catalogue_questions(flows):ifquestion.profile_keyisNone:continue_reject_duplicate_profile_key(question,keys)keys[question.profile_key]=_compile_one(question,by_id)returntuple(keys.values())
def_iter_catalogue_questions(flows:Sequence[WizardFlow])->Iterator[WizardQuestion]:"""Flatten flow -> section -> question into a single question stream."""forflowinflows:forsectioninflow.sections:yield fromsection.questionsdef_reject_duplicate_profile_key(question:WizardQuestion,keys:dict[str,ProfileKey])->None:ifquestion.profile_keyinkeys:raiseWizardCompileError(f"wizard compile duplicate profile_key {question.profile_key!r}",context={"profile_key":question.profile_key,"question_id":question.id},)def_compile_one(question:WizardQuestion,by_id:dict[str,WizardQuestion],)->ProfileKey:requirement=(ProfileKeyRequirement.REQUIREDifquestion.requiredandquestion.visible_whenisNoneelseProfileKeyRequirement.OPTIONAL)# The ``required_when_*`` pair expresses a *conditional requirement*:# the key is REQUIRED only while its gate predicate holds. It is# meaningful solely for a question that is itself ``required`` —# a ``required=False`` gated question is optional whether or not# its visibility gate is satisfied, so it carries no conditional# requirement. Emitting the pair for an optional gated question# wrongly promotes it to required as soon as the gate matches.required_when_key:str|None=Nonerequired_when_value:str|None=Noneifquestion.requiredandquestion.visible_whenisnotNone:required_when_key,required_when_value=_resolve_condition(question.visible_when,by_id)ifquestion.profile_keyisNone:raiseWizardCompileError(f"question {question.id!r} reached _compile_one without a profile_key",context={"question_id":question.id},)returnProfileKey(key=question.profile_key,requirement=requirement,description=tr(f"profile.keys.{question.profile_key}"),required_when_key=required_when_key,required_when_value=required_when_value,)def_resolve_condition(condition:WizardCondition|WizardVisibility,by_id:dict[str,WizardQuestion],)->tuple[str|None,str|None]:"""Resolve a ``visible_when`` gate into the ``required_when_*`` pair. The ``ProfileKey`` registry expresses a conditional requirement as a single parent-key / parent-value pair. Only a single-clause ``equals`` :class:`WizardCondition` maps to that shape. A multi-clause :class:`WizardVisibility` disjunction and a ``contains`` checkbox-membership clause have no single-pair representation, so the conditional-requirement projection is left empty; the key is still emitted as ``OPTIONAL``. """ifisinstance(condition,WizardVisibility):returnNone,Noneifcondition.equalsisNone:returnNone,Noneparent=by_id.get(condition.question_id)ifparentisNoneorparent.profile_keyisNone:returnNone,Nonereturnparent.profile_key,condition.equalsdef_register_compiled_keys()->None:"""Register compiled PROFILE_KEYS into the domain registry at import time. Called once when this module is first imported. The domain's :func:`~aeat.domain.contribuyente._keys.register_profile_keys` receives the compiled tuple so the domain layer never needs to import application modules to populate its registry. """from...domain.contribuyenteimportregister_profile_keysfrom.import_catalogue# local import to avoid circular dependency at module levelregister_profile_keys(compile_profile_keys(_catalogue.WIZARD_FLOWS))_register_compiled_keys()__all__=["compile_profile_keys"]