# -*- coding: utf-8 -*- """Panel Analysis: the pure merge rule for the model-measured pre-fill. `script.py`'s `_reads_for` reads three separate things off one panel -- `pa_supports.read_supports`, `pa_lifters.read_lifters`, `pa_reinf.read_ reinforcement_prefill` -- each returning its own small dict of form-field values (or {}, defensively, when it could not measure anything). This module holds the one rule for folding all three into the button's running `reads` dict, kept apart from `script.py` because that file imports the Revit API at module scope and cannot be imported under `pytest` on host CPython (see `tests/conftest.py`'s docstring) -- so the part that is pure logic, and therefore worth a fast host test, lives here instead. THE RULE, in one sentence: everywhere else in `_reads_for` a value already read off the piece wins over a model guess (`setdefault` -- fill only what is still blank); `OVERRIDE_KEYS` are the exception, because for those keys the NEW read *is* the measurement and anything already sitting in `reads` is a weaker guess from an earlier, cruder pass -- `mild_as` is the clearest case: `_assembly_reinforcement`'s old marks-list (a bare comma list of piece marks) is worth less than `pa_reinf.rebar_prefill`'s `(bar size, type name)` summary with a real total area, so the new value has to win even though `reads` already carries something under that key. `None` never overwrites anything, for any key, override or not -- a module that could not measure something says so by leaving the key out (or handing back `None`), never by asserting a blank. """ # The new measurement wins over whatever is already in `reads`, blank or not. # Every other key follows plain `setdefault` -- only a still-empty slot gets # filled. See the module docstring for why `mild_as` is here: the new # `(bar size, type name)` summary from `pa_reinf.rebar_prefill` outranks the # old marks-list `_assembly_reinforcement` may already have written under the # same key. OVERRIDE_KEYS = ('supports_ft', 'face_lifters', 'edge_lifters', 'ps_rows', 'mild_as') def merge_model_reads(reads, parts): """Fold `parts` into `reads` IN PLACE, per `OVERRIDE_KEYS`, and return it. `parts` is either one dict or a list/tuple of dicts (`pa_supports. read_supports`'s, `pa_lifters.read_lifters`'s, `pa_reinf.read_ reinforcement_prefill`'s, in whatever order the caller has them handy -- order only matters between two dicts that both carry the same key, which none of the three do today). A `None` value in any part is skipped outright; it never counts as "the model answered" and never overwrites an existing value OR fills a blank. Returns `reads` (the same object, mutated) so a caller can chain it, but the mutation is the real effect -- this is not written to be used as a pure expression. """ if isinstance(parts, dict): parts = [parts] for part in (parts or []): if not part: continue for key, value in part.items(): if value is None: continue if key in OVERRIDE_KEYS or reads.get(key) is None: reads[key] = value return reads