"""The one rule deciding which nomenclatures are visible for a given extra_data. A nomenclature is hidden if it is revealed by some selector option and that selector's current value is not the option revealing it. Anything no selector references is always visible, so installations without selectors are unchanged. Mirrored in frontend/src/utils/nomenclatureVisibility.js - keep the two in step. """ from .models import Nomenclature, SelectorOption def build_reveal_map(nomenclatures=None): """{revealed_code: {selector_code: {value, ...}}} for the given nomenclatures.""" options = ( SelectorOption.objects .select_related('selector') .prefetch_related('reveals') ) if nomenclatures is not None: codes = {n.code for n in nomenclatures} options = [o for o in options if o.selector.code in codes] reveal_map = {} for option in options: for revealed in option.reveals.all(): reveal_map.setdefault(revealed.code, {}).setdefault( option.selector.code, set() ).add(option.value) return reveal_map def is_visible(code, extra_data, reveal_map): """True if the nomenclature `code` should be shown/accepted for this data.""" controllers = reveal_map.get(code) if not controllers: return True for selector_code, revealing_values in controllers.items(): if extra_data.get(selector_code) in revealing_values: return True return False def visible_codes(codes, extra_data, reveal_map=None): """Filter `codes` down to those visible for `extra_data`.""" if reveal_map is None: reveal_map = build_reveal_map() return [c for c in codes if is_visible(c, extra_data or {}, reveal_map)]