WillTools: precast shop-ticket QC and panel handling tools
ci / test (push) Has been cancelled

Public demo snapshot of an in-house pyRevit extension for precast concrete
shop-ticket QC (37 checks / 46 checklist rows) and a standalone panel
handling solver (PCI Chapter 8).

- No client, job, plant or product data: knowledge layers ship as
  structural templates only.
- Ticket QC's model-read escalation targets a LAN model server endpoint
  (configurable), not a cloud provider.
- Built from the private development repo via tools/make_dist.py.
This commit is contained in:
2026-09-03 16:33:47 -05:00
commit 52d94303bc
281 changed files with 155656 additions and 0 deletions
@@ -0,0 +1,338 @@
# -*- coding: utf-8 -*-
"""Tests for `vibe.pa_criteria.page` -- the Design Criteria section layout.
No Revit and no WPF renderer are available here (see `conftest.py`'s
docstring for why `vibe` imports this way), so this suite cannot check that
the page LOOKS right -- nobody has run this inside Revit and this file does
not claim otherwise. What it can and does check mechanically:
* `page()` never raises, on an empty dict, a filled dict, `None` values,
`None` reads, or a `values` object that is not even a dict.
* every block returned is a well-formed member of the block vocabulary
`page()`'s docstring promises (`heading`, `columns`, `field`, `kv`,
`sentence`, `table`, `rule`, `space`) -- an unknown `kind` renders as
nothing on the real form, so a typo here would be a silently missing
block that only this test would catch.
* every `field` block carries a non-empty, plain-English `label` and a
non-empty `hint` -- the owner's whole complaint about the old form was
that a field carried only its engineering symbol.
* a field key with no backing entry in `panelanalysisform.FIELDS` still
renders (an empty editor), and is named in `problems` -- never silently
dropped.
* `values` is never mutated by a call to `page()`.
* the block list is stable: calling `page()` twice on the same input
produces the same output.
"""
import copy
import sys
from pathlib import Path
TESTS_DIR = Path(__file__).resolve().parent
if str(TESTS_DIR) not in sys.path:
sys.path.insert(0, str(TESTS_DIR))
from vibe import pa_criteria as pc # noqa: E402
KNOWN_KINDS = set(['heading', 'columns', 'field', 'kv', 'sentence', 'table',
'rule', 'space'])
KNOWN_EDITORS = set(['text', 'num', 'check', 'choice'])
def _walk(blocks):
"""Yield every block in the tree, including nested `columns` children."""
for b in blocks:
yield b
if b.get('kind') == 'columns':
for col in b.get('columns') or []:
for sub in _walk(col):
yield sub
def _assert_well_formed(blocks):
for b in blocks:
assert isinstance(b, dict), 'block is not a dict: {0!r}'.format(b)
kind = b.get('kind')
assert kind in KNOWN_KINDS, 'unknown block kind {0!r}'.format(kind)
if kind == 'heading':
assert isinstance(b['text'], str) and b['text'].strip()
assert b['level'] in (1, 2, 3)
elif kind == 'columns':
assert isinstance(b['columns'], list) and b['columns']
for col in b['columns']:
assert isinstance(col, list)
_assert_well_formed(col)
elif kind == 'field':
assert isinstance(b['key'], str) and b['key']
assert isinstance(b['label'], str) and b['label'].strip(), (
'field {0!r} has no plain-English label'.format(b['key']))
assert isinstance(b['hint'], str) and b['hint'].strip(), (
'field {0!r} has no hint'.format(b['key']))
assert b['symbol'] is None or isinstance(b['symbol'], str)
assert isinstance(b['required'], bool)
assert b['editor'] in KNOWN_EDITORS, (
'field {0!r} has unknown editor {1!r}'
.format(b['key'], b['editor']))
if b['editor'] == 'choice':
assert b['choices'], (
'choice field {0!r} has no choices'.format(b['key']))
else:
assert b['choices'] is None
elif kind == 'kv':
assert isinstance(b['rows'], list)
for row in b['rows']:
assert len(row) == 2
elif kind == 'sentence':
assert isinstance(b['text'], str) and b['text'].strip()
elif kind == 'table':
assert isinstance(b['headers'], list) and b['headers']
assert isinstance(b['rows'], list)
for row in b['rows']:
assert len(row) == len(b['headers']), (
'table row {0!r} does not match headers {1!r}'
.format(row, b['headers']))
for cell in row:
if isinstance(cell, dict):
assert cell.get('editor') == 'check'
assert cell.get('key')
else:
assert isinstance(cell, str)
elif kind == 'rule':
assert set(b.keys()) == set(['kind'])
elif kind == 'space':
assert isinstance(b['h'], int)
def _field_keys(blocks):
return [b['key'] for b in _walk(blocks) if b.get('kind') == 'field']
# --------------------------------------------------------------------- tests
def test_empty_values_never_raises_and_is_well_formed():
blocks, problems = pc.page({})
assert isinstance(blocks, list) and blocks
assert isinstance(problems, list)
_assert_well_formed(blocks)
def test_none_values_and_none_reads_never_raises():
blocks, problems = pc.page(None, None)
assert isinstance(blocks, list) and blocks
_assert_well_formed(blocks)
def test_non_dict_values_degrades_rather_than_raising():
blocks, problems = pc.page('not a dict')
assert isinstance(blocks, list) and blocks
_assert_well_formed(blocks)
def test_filled_values_never_raises_and_is_well_formed():
values = {
'fc_psi': '6000', 'fci_psi': '3500', 'unit_weight_pcf': '150',
'concrete_type': 'Normalweight', 'rebar_fy_ksi': '60',
'rebar_es_ksi': '29000', 'ps_fpu_ksi': '270', 'strand_ep_ksi': '28500',
'ps_loss_method': 'PCI', 'ps_loss_humidity_pct': '75',
'design_criteria.rvalue_calc_enabled': 'yes',
'design_criteria.min_reinf_checked': 'true',
}
blocks, problems = pc.page(values, {'mark': 'sample_mark',
'design_number': 'PNL1.0'})
assert isinstance(blocks, list) and blocks
_assert_well_formed(blocks)
def test_values_dict_is_never_mutated():
values = {'fc_psi': '6000'}
before = copy.deepcopy(values)
pc.page(values, {'mark': 'sample_mark'})
assert values == before
def test_reads_dict_is_never_mutated():
reads = {'mark': 'sample_mark', 'design_number': 'PNL1.0'}
before = copy.deepcopy(reads)
pc.page({}, reads)
assert reads == before
def test_block_list_is_stable_for_the_same_input():
values = {'fc_psi': '6000', 'unit_weight_pcf': '150'}
reads = {'mark': 'sample_mark'}
a, pa = pc.page(values, reads)
b, pb = pc.page(values, reads)
assert a == b
assert pa == pb
def test_missing_key_degrades_to_a_field_and_names_a_problem():
blocks, problems = pc.page({})
keys = set(_field_keys(blocks))
# A key this file invented because Eriksson's page has no home for it
# yet in panelanalysisform.FIELDS.
assert 'design_criteria.analysis_beta_d' in keys
joined = ' '.join(problems)
assert 'design_criteria.analysis_beta_d' in joined
def test_existing_form_key_is_not_reported_as_missing():
blocks, problems = pc.page({})
joined = ' '.join(problems)
# fc_psi is a real, long-standing panelanalysisform.FIELDS key.
assert "'fc_psi'" not in joined or 'fc_psi) has no backing' not in joined
for p in problems:
assert 'fc_psi)' not in p
def test_every_field_key_appears_at_most_once():
blocks, _problems = pc.page({})
keys = _field_keys(blocks)
dupes = [k for k in set(keys) if keys.count(k) > 1]
assert not dupes, 'field key(s) rendered more than once: {0}'.format(dupes)
def test_section_heading_is_present():
blocks, _problems = pc.page({})
headings = [b['text'] for b in blocks if b.get('kind') == 'heading']
assert 'Design Criteria' in headings
assert headings[0] == 'Design Criteria'
def test_prime_notation_used_not_ascii_apostrophe():
blocks, _problems = pc.page({})
for b in _walk(blocks):
if b.get('kind') != 'field':
continue
symbol = b.get('symbol') or ''
# f'c / f'ci style ASCII apostrophes must never appear; the real
# prime character (U+2032) is what the renderer turns into a
# subscript.
assert "f'c" not in symbol and "f'ci" not in symbol
if u"f′" in symbol or u"E′" in symbol:
assert True # sanity: the prime character is reachable at all
def test_load_combination_tables_have_include_checkboxes():
blocks, problems = pc.page({})
tables = [b for b in _walk(blocks) if b.get('kind') == 'table']
assert len(tables) == 2
for t in tables:
assert t['headers'][-1] == 'Include'
for row in t['rows']:
cell = row[-1]
assert isinstance(cell, dict) and cell['editor'] == 'check'
joined = ' '.join(problems)
assert 'load_combo.strength' in joined
assert 'load_combo.service' in joined
def test_soil_blocks_are_sentences_not_editors():
blocks, problems = pc.page({})
sentences = [b['text'] for b in _walk(blocks) if b.get('kind') == 'sentence']
assert any('no exterior soil loads' in s for s in sentences)
assert any('no interior soil loads' in s for s in sentences)
assert any('soil loads are not collected' in p for p in problems)
def test_prestress_loss_group_is_a_sentence_when_blank():
blocks, _problems = pc.page({})
sentences = [b['text'] for b in _walk(blocks) if b.get('kind') == 'sentence']
assert any('No prestress loss assumptions' in s for s in sentences)
# And its fields must NOT be rendered while blank -- shown only once
# something in the group is filled in.
keys = set(_field_keys(blocks))
assert 'ps_loss_method' not in keys
def test_prestress_loss_group_becomes_fields_when_filled():
blocks, _problems = pc.page({'ps_loss_method': 'PCI'})
keys = set(_field_keys(blocks))
assert 'ps_loss_method' in keys
assert 'ps_loss_humidity_pct' in keys
def test_filling_one_group_field_does_not_file_a_phantom_problem_for_others():
# Regression guard: `_group_or_sentence` must not evaluate the
# filled-in branch (and thus call `F(key)`, which files a "missing"
# problem) when the sentence branch is the one actually shown.
_blocks, problems_blank = pc.page({})
joined = ' '.join(problems_blank)
assert 'ps_loss_method' not in joined
assert 'ps_loss_humidity_pct' not in joined
def test_rvalue_and_min_reinforcement_default_sentences_present():
blocks, _problems = pc.page({})
sentences = [b['text'] for b in _walk(blocks) if b.get('kind') == 'sentence']
assert any('R-value is not being calculated' in s for s in sentences)
assert any('Minimum steel is not being checked' in s for s in sentences)
def test_concrete_grid_has_release_and_inplace_strength_fields():
blocks, _problems = pc.page({})
keys = set(_field_keys(blocks))
assert 'fci_psi' in keys
assert 'fc_psi' in keys
assert 'conc_eci_psi' in keys
assert 'conc_ec_psi' in keys
def test_ksi_reconciliation_hint_on_psi_keys():
blocks, _problems = pc.page({})
for b in _walk(blocks):
if b.get('kind') == 'field' and b['key'] in ('fc_psi', 'fci_psi'):
assert 'ksi' in b['hint'].lower()
def test_required_fields_match_panelanalysisform_where_it_exists():
from vibe import panelanalysisform as pf
blocks, _problems = pc.page({})
required_here = dict((b['key'], b['required'])
for b in _walk(blocks) if b.get('kind') == 'field')
for f in pf.FIELDS:
key = f['key']
if key in required_here:
assert required_here[key] == f['required'], (
'required flag mismatch for {0!r}'.format(key))
def test_panel_header_kv_shown_only_when_reads_present():
blocks_no_reads, _ = pc.page({}, {})
kvs_no_reads = [b for b in blocks_no_reads if b.get('kind') == 'kv']
blocks_with_reads, _ = pc.page({}, {'mark': 'sample_mark'})
kvs_with_reads = [b for b in blocks_with_reads if b.get('kind') == 'kv']
assert len(kvs_with_reads) >= len(kvs_no_reads) + 1
def test_page_import_survives_a_broken_panelanalysisform(monkeypatch):
"""Simulates the sibling agent's file being mid-edit / unimportable.
`_existing_keys()` is the one function that reaches for
`panelanalysisform`; monkeypatching it directly to behave the way it
would if the import raised (returning `None`) exercises the same
"could not judge" path `page()` must degrade through, without depending
on the interpreter's module-cache mechanics for a package (`vibe`)
that other tests in this same process have already imported
`panelanalysisform` onto as an attribute.
"""
monkeypatch.setattr(pc, '_existing_keys', lambda: None)
blocks, problems = pc.page({})
assert isinstance(blocks, list) and blocks
assert any('could not read panelanalysisform' in p for p in problems)
def test_choice_field_has_choices():
blocks, _problems = pc.page({})
for b in _walk(blocks):
if b.get('kind') == 'field' and b['key'] == 'concrete_type':
assert b['editor'] == 'choice'
assert 'Normalweight' in b['choices']