WillTools 2026-09-09 -- clean snapshot of the development repo for review
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.
This commit is contained in:
2026-09-09 08:32:15 -05:00
commit 8e6ee31596
406 changed files with 223435 additions and 0 deletions
@@ -0,0 +1,64 @@
# -*- 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