"""ProductWizard payload -> CanonicalProductRegulatoryProfile (lossless). The gap-analysis ProductWizard POSTs exactly the gap.ProductProfile JSON shape (see admin-compliance/.../ProductWizard.tsx handleSubmit). This mapper copies every gap field verbatim so that `to_gap_profile(from_product_wizard(p))` reproduces the gap subset of `p` byte-for-byte (acceptance #1). New Navigator fields the wizard does not ask stay None. """ from __future__ import annotations from typing import Any, Dict, Optional from .canonical import CanonicalProductRegulatoryProfile, CanonicalProductType def _as_product_type(value: Any) -> Optional[CanonicalProductType]: try: return CanonicalProductType(value) except ValueError: return None def from_product_wizard(payload: Dict[str, Any]) -> CanonicalProductRegulatoryProfile: g = payload.get return CanonicalProductRegulatoryProfile( name=g("name", ""), description=g("description", ""), product_type=_as_product_type(g("product_type")), technologies=list(g("technologies") or []), data_processing=list(g("data_processing") or []), markets=list(g("markets") or []), existing_certifications=list(g("existing_certifications") or []), applied_norms=list(g("applied_norms") or []), connected_to_internet=g("connected_to_internet"), has_software_updates=g("has_software_updates"), uses_ai=g("uses_ai"), processes_personal_data=g("processes_personal_data"), is_critical_infra_supplier=g("is_critical_infra_supplier"), has_risk_assessment=g("has_risk_assessment"), has_technical_file=g("has_technical_file"), has_operating_manual=g("has_operating_manual"), has_sbom=g("has_sbom"), has_vuln_management=g("has_vuln_management"), has_update_mechanism=g("has_update_mechanism"), has_incident_response=g("has_incident_response"), has_supply_chain_mgmt=g("has_supply_chain_mgmt"), ce_marking_since=g("ce_marking_since"), product_age=g("product_age"), )