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,176 @@
# -*- coding: utf-8 -*-
"""Panel Analysis Handling page pre-fill: `face_lifters` / `edge_lifters`
read straight off Panel Lift's own `job['panel']['lifters']`, split face vs
edge the way `knowledge.handling.lifter_groups` groups them for the rest of
the tool.
"""
from vibe import knowledge
from vibe import pa_lifters
def _know():
know = knowledge._deep_copy(knowledge.DEFAULTS)
know['handling']['lifter_groups'] = {
'face': {'families': ['FACE_X']},
'edge_top': {'families': ['EDGE_Y']},
}
return know
def _lifter(family, x, y, on_edge=False, mark='m'):
return {'family': family, 'mark': mark, 'x': x, 'y': y,
'on_edge': on_edge}
# --------------------------------------------------------------- lifter_lines
def test_face_and_edge_split_by_knowledge_groups():
know = _know()
lifters = [
_lifter('FACE_X_ANCHOR', 2.5, 0.75, mark='f1'),
_lifter('EDGE_Y_ANCHOR', 25.0, 2.25, mark='e1'),
]
face_text, edge_text, why = pa_lifters.lifter_lines(lifters, 3.0, know)
assert face_text == '2.50 @ 0.25'
assert edge_text == '25.00 @ 0.75'
assert 'FACE_X_ANCHOR' in why
assert 'EDGE_Y_ANCHOR' in why
assert '1 face lifter' in why
assert '1 edge lifter' in why
def test_on_edge_flag_counts_as_edge_even_off_the_edge_top_family_list():
know = _know()
lifters = [_lifter('SOME_OTHER_FAMILY', 10.0, 1.5, on_edge=True)]
face_text, edge_text, why = pa_lifters.lifter_lines(lifters, 3.0, know)
assert face_text == ''
assert edge_text == '10.00 @ 0.50'
def test_family_matching_neither_group_and_not_on_edge_is_unclassified():
know = _know()
lifters = [_lifter('MYSTERY_FAMILY', 5.0, 1.0)]
face_text, edge_text, why = pa_lifters.lifter_lines(lifters, 2.0, know)
assert face_text == ''
assert edge_text == ''
assert 'MYSTERY_FAMILY' in why
assert 'matched neither' in why
def test_lines_sorted_by_x_then_frac_same_x_is_a_row():
know = _know()
lifters = [
_lifter('FACE_X_A', 10.0, 2.0, mark='a'),
_lifter('FACE_X_B', 2.0, 1.0, mark='b'),
_lifter('FACE_X_C', 10.0, 1.0, mark='c'),
]
face_text, _edge_text, _why = pa_lifters.lifter_lines(lifters, 4.0, know)
assert face_text == ('2.00 @ 0.25\n'
'10.00 @ 0.25\n'
'10.00 @ 0.50')
def test_missing_x_or_y_is_skipped_and_counted_in_why():
know = _know()
lifters = [
_lifter('FACE_X_A', None, 1.0, mark='a'),
_lifter('FACE_X_B', 2.0, None, mark='b'),
_lifter('FACE_X_C', 2.0, 1.0, mark='c'),
]
face_text, _edge_text, why = pa_lifters.lifter_lines(lifters, 4.0, know)
assert face_text == '2.00 @ 0.25'
assert '2 lifters skipped' in why
def test_no_width_skips_every_lifter_and_says_so():
know = _know()
lifters = [_lifter('FACE_X_A', 2.0, 1.0)]
face_text, edge_text, why = pa_lifters.lifter_lines(lifters, None, know)
assert face_text == '' and edge_text == ''
assert '1 lifter skipped' in why
def test_empty_lifter_list():
know = _know()
face_text, edge_text, why = pa_lifters.lifter_lines([], 4.0, know)
assert face_text == '' and edge_text == ''
assert '0 face lifters' in why
assert '0 edge lifters' in why
# ---------------------------------------------------------------- read_lifters
def test_read_lifters_from_a_job_dict():
know = _know()
job = {'panel': {'width_ft': 3.0, 'lifters': [
_lifter('FACE_X_ANCHOR', 2.5, 0.75),
_lifter('EDGE_Y_ANCHOR', 25.0, 2.25),
]}}
out = pa_lifters.read_lifters(job, know)
assert out['face_lifters'] == '2.50 @ 0.25'
assert out['edge_lifters'] == '25.00 @ 0.75'
assert 'lifters_basis' in out
def test_read_lifters_omits_empty_text_but_keeps_basis():
know = _know()
job = {'panel': {'width_ft': 3.0, 'lifters': [
_lifter('FACE_X_ANCHOR', 2.5, 0.75),
]}}
out = pa_lifters.read_lifters(job, know)
assert 'face_lifters' in out
assert 'edge_lifters' not in out
assert 'lifters_basis' in out
def test_read_lifters_no_job_is_empty_dict():
know = _know()
assert pa_lifters.read_lifters(None, know) == {}
def test_read_lifters_no_lifters_key_is_empty_dict():
know = _know()
assert pa_lifters.read_lifters({'panel': {'width_ft': 3.0}}, know) == {}
assert pa_lifters.read_lifters(
{'panel': {'width_ft': 3.0, 'lifters': []}}, know) == {}
def test_read_lifters_is_defensive_on_bad_input():
know = _know()
# 'panel' is not a dict -- .get on a list raises AttributeError inside;
# this must come back {} rather than propagate.
assert pa_lifters.read_lifters({'panel': ['not', 'a', 'dict']}, know) \
== {}
# ----------------------------------------------------------- parse_lifter_lines
def test_parse_lifter_lines_round_trip():
text = '2.50 @ 0.25\n10.00 @ 0.50'
rows, problems = pa_lifters.parse_lifter_lines(text)
assert problems == []
assert rows == [{'x_ft': 2.5, 'frac': 0.25}, {'x_ft': 10.0, 'frac': 0.5}]
def test_parse_lifter_lines_blank_text():
assert pa_lifters.parse_lifter_lines('') == ([], [])
assert pa_lifters.parse_lifter_lines(None) == ([], [])
assert pa_lifters.parse_lifter_lines(' \n \n') == ([], [])
def test_parse_lifter_lines_refuses_the_whole_field_on_one_bad_line():
text = '2.50 @ 0.25\nnot a lifter line\n10.00 @ 0.50'
rows, problems = pa_lifters.parse_lifter_lines(text)
assert rows == []
assert len(problems) == 1
assert 'line 2' in problems[0]
def test_parse_lifter_lines_bad_numbers_named_by_line():
rows, problems = pa_lifters.parse_lifter_lines('abc @ 0.25')
assert rows == []
assert 'line 1' in problems[0] and 'x_ft' in problems[0]
rows, problems = pa_lifters.parse_lifter_lines('2.5 @ xyz')
assert rows == []
assert 'line 1' in problems[0] and 'frac' in problems[0]