"""Interpretation-in-Map adapter (step 5). Evaluates a customer interpretation WITHIN the already-built RegulatoryMap. It reuses the existing `assess_interpretation` (no new legal engine), restricts the affected regulations/obligations to those present in the map, and reports any touched unsupported domain (wastewater/chemicals/...) as future_corpus_needed rather than pseudo-evaluating it. """ from __future__ import annotations from typing import Dict, List from compliance.reasoning.enums import InterpretationVerdict from compliance.reasoning.interpretation_engine import assess_interpretation from compliance.regulatory_map.schemas import RegulatoryMap from .schemas import InterpretationInMapResult _LABEL: Dict[InterpretationVerdict, str] = { InterpretationVerdict.PLAUSIBLE: "plausibel", InterpretationVerdict.TOO_NARROW: "zu eng", InterpretationVerdict.TOO_BROAD: "zu weit", InterpretationVerdict.PARTIALLY_CORRECT: "teilweise korrekt", InterpretationVerdict.UNSUPPORTED: "nicht belegt", InterpretationVerdict.UNCERTAIN: "unsicher", } # domain -> keywords that signal the interpretation is ABOUT that (uncovered) domain. _ENV_KEYWORDS: Dict[str, List[str]] = { "environment_water": ["abwasser", "wastewater", "gewässer", "gewaesser", "einleitung", "abfluss"], "chemicals": ["chemikalie", "reach", "clp", "reinigungsmittel", "biozid", "gefahrstoff", "detergenz", "lösemittel", "loesemittel"], "environment_air": ["luft", "emission", "voc", "immission", "abluft", "verbrennung"], "waste": ["abfall", "entsorgung", "weee", "recycling"], "energy_resources": ["energie", "ökodesign", "oekodesign", "verbrauch"], } def _touches(text: str, domain: str) -> bool: low = text.lower() return any(kw in low for kw in _ENV_KEYWORDS.get(domain, [])) def _explain(label: str, detail: str, affected_regs: List[str], future_domains: List[str], in_scope: bool) -> str: base = "Ihre Interpretation ist wahrscheinlich %s." % label if detail: base += " " + detail if affected_regs: base += " Betroffen in Ihrer Map: %s." % ", ".join(affected_regs) if future_domains: base += ( " Für %s liegt noch kein Regelkorpus vor — diese Aspekte werden nicht bewertet (future_corpus_needed)." % ", ".join(future_domains) ) if not in_scope and not future_domains: base += " Diese Auslegung betrifft kein Regelwerk Ihrer aktuellen Produkt-Map." return base def interpret_in_map(reg_map: RegulatoryMap, interpretation: str) -> InterpretationInMapResult: a = assess_interpretation(interpretation) # existing engine — no new reasoning map_reg_ids = ( {v.regulation_id for v in reg_map.applicable_regulations} | {v.regulation_id for v in reg_map.uncertain_regulations} | {v.regulation_id for v in reg_map.excluded_regulations} ) map_ob_ids = {o.obligation_id for v in reg_map.applicable_regulations for o in v.obligations} uncertain_ids = {v.regulation_id for v in reg_map.uncertain_regulations} affected_regs = [r for r in a.affected_regulations if r in map_reg_ids] affected_obs = [o for o in a.affected_obligations if o in map_ob_ids] related_unc = [r for r in a.affected_regulations if r in uncertain_ids] future = [d for d in reg_map.unsupported_domains if _touches(interpretation, d.domain)] in_scope = bool(affected_regs or affected_obs) return InterpretationInMapResult( raw_interpretation=interpretation, assessment=a.assessment, in_scope_of_map=in_scope, affected_regulations=affected_regs, affected_obligations=affected_obs, related_uncertainties=related_unc, future_corpus_domains=future, corrected_interpretation=a.corrected_interpretation, risks=a.risks, legal_basis_refs=a.legal_basis_refs, explanation=_explain(_LABEL[a.assessment], a.explanation, affected_regs, [d.domain for d in future], in_scope), confidence=a.confidence, )