8e6ee31596
ci / test (push) Successful in 6m18s
History-free snapshot of the private development repository at cbe45c5 (2026-09-09). Code, docs and public catalogue only; no plant or job knowledge layers, no client or job names (tools/scrub.py clean). The intro deck is left out of this snapshot because its slide images could not be checked for job drawings.
65 lines
3.1 KiB
Python
65 lines
3.1 KiB
Python
# -*- 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
|