Source code for cadrumo.application.wizard.compiler
"""Pure projection of the wizard descriptor catalogue into profile-key records.``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 callers can safely cache the compiled profile-keytuple when an application projection needs it."""from__future__importannotationsfromcollections.abcimportIterator,Sequencefrom...core.i18n.translatableimportTranslatableastrfrom...core.requirementimportRequirementfrom..user_profile.profile_keyimportProfileKeyfrom.errorsimportWizardCompileErrorfrom.modelsimportWizardCondition,WizardFlow,WizardQuestion,WizardVisibility
[docs]defcompile_profile_keys(flows:Sequence[WizardFlow])->tuple[ProfileKey,...]:"""Project the wizard catalogue into application profile-key records. 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(translated_message="errors.error.error_wizard_compile",context={"profile_key":question.profile_key,"question_id":question.id},)def_compile_one(question:WizardQuestion,by_id:dict[str,WizardQuestion],)->ProfileKey:requirement=Requirement.REQUIREDifquestion.requiredandquestion.visible_whenisNoneelseRequirement.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(translated_message="errors.error.error_wizard_compile",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`` catalogue 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.equals__all__=["compile_profile_keys"]