Files
willtools-demo/WillTools.extension/tests/test_ticketpage_opening_diagonal.py
T
will 25592fa6c2
ci / test (push) Successful in 6m14s
WillTools 2026-09-09 -- clean snapshot of the development repo for review
History-free snapshot of the private development repository at 77a82eb.
Code, docs and the public catalogue only: no plant or job knowledge
layers, no client, job or producer names (tools/scrub.py clean against
the widened term list; a second word-level grep clean). The intro deck is
left out because its slide images cannot be checked for job drawings.
2026-09-09 08:35:15 -05:00

196 lines
8.7 KiB
Python

# -*- coding: utf-8 -*-
"""The opening-bar rule: pure decision, no Revit.
ONE CHECK that judges TWO things about a piece's door openings / blockouts:
(1) BAR TYPE -- do any bars carry the plant's opening flag? The bars are
MODELLED assembly members of a scheduled rebar family (the tickets
carry no bar list), each with an instance flag named like
'BAR_TYPE_BLOCKOUT' reading 'Yes'/'No'.
(2) DIAGONAL -- among those flagged bars, are the plant's diagonal edge
bars present in number? What the diagonal must be is plant knowledge,
reached at `reinforce.panel_standard.opening.diagonal_edge_bar` -- a
string like '#3 x 3'-0" DIAG. E.L.' -- and it is never written into
the code. These tests exercise the SHAPE of the answer: nothing here
pins a real plant value, and every literal below is an invented
stand-in for whatever the plant maps at that key.
Both findings are WARN at worst, never a FAIL (owner: "很多时候开口太小我给不了
这些钢筋" -- a small opening often cannot take the bars; a person decides). One
bar per free corner is an upper bound, and an edge opening has fewer free
corners.
The verdict lives in ticketpage (Revit-free), imported the way
test_ticketpage.py imports it.
"""
# pylint: disable=import-error,invalid-name,broad-except
from vibe import ticketpage
SPEC = "#3 x 3'-0\" DIAG. E.L."
def _verdict(*args, **kw):
short_severity = kw.pop('short_severity', 'WARN')
findings, reason = ticketpage.opening_diagonal_status(
*args, short_severity=short_severity)
assert reason is None, reason
return findings
# ------------------------------------------- the size-map helper
def test_bar_size_fraction_maps_the_specs_hash_number():
"""'#3 x 3'-0"' names #3, and the modelled diameter parameter prints it
as the fraction '3/8' -- the two join here and nowhere else."""
assert ticketpage.bar_size_fraction(SPEC) == '3/8'
assert ticketpage.bar_size_fraction("#5 x 4'-0\" DIAG.") == '5/8'
assert ticketpage.bar_size_fraction('#8 x 6-0') == '1'
def test_bar_size_fraction_is_none_when_the_spec_names_no_known_size():
"""None for no '#N' and for a size the map does not know: the caller
then counts the flagged bars for the BAR TYPE half and lets the
DIAGONAL half report its shortfall instead of guessing a size."""
assert ticketpage.bar_size_fraction(None) is None
assert ticketpage.bar_size_fraction('') is None
assert ticketpage.bar_size_fraction("3'-0\" DIAG. E.L.") is None
assert ticketpage.bar_size_fraction('#14 x 10-0') is None
# --------------------------------------- the could-not-judge gates
def test_no_plant_value_is_could_not_judge_and_names_the_key():
"""Falsy required spec: nothing to count is the plant's mapping being
absent, not a clean piece -- and the sentence names the key."""
findings, reason = ticketpage.opening_diagonal_status(2, 4, 4, True, None)
assert findings == []
assert 'reinforce.panel_standard.opening.diagonal_edge_bar' in reason
findings, reason = ticketpage.opening_diagonal_status(2, 4, 4, True, '')
assert findings == []
assert 'reinforce.panel_standard.opening.diagonal_edge_bar' in reason
def test_an_unreadable_opening_count_is_could_not_judge():
"""None count is 'the openings could not be read', never a pass and
never judged against bars the assembly happens to model."""
findings, reason = ticketpage.opening_diagonal_status(None, 4, 4, True,
SPEC)
assert findings == []
assert 'could not be read' in reason
def test_an_unreadable_member_walk_is_could_not_judge():
"""`members_readable` False is NOT a piece that models zero bars: the
member walk being unreadable is a could-not-judge, and the sentence says
the assembly members could not be read."""
findings, reason = ticketpage.opening_diagonal_status(1, 0, 0, False,
SPEC)
assert findings == []
assert 'assembly members could not be read' in reason
def test_a_none_count_is_could_not_judge_never_a_verdict():
"""A None count must never reach the sentences -- formatting None into
'None bar(s) carry the opening bar-type flag' is a false verdict. Either
count being None is could-not-judge, whatever the other one says."""
findings, reason = ticketpage.opening_diagonal_status(2, None, None, True,
SPEC)
assert findings == []
assert 'could not be counted' in reason
assert 'opening bars could not be judged' in reason
# only ONE of the two counts missing is the same could-not-judge
findings, reason = ticketpage.opening_diagonal_status(2, 6, None, True,
SPEC)
assert findings == []
assert 'could not be counted' in reason
# ------------------------------------------------------- the verdicts
def test_zero_openings_is_a_judged_ok():
"""No door opening or blockout -> no opening bars required. A real
verdict, reached even when the assembly models none."""
findings = _verdict(0, 0, 0, True, SPEC)
assert findings[0][0] == 'OK'
assert 'no opening bars required' in findings[0][1]
def test_openings_with_no_flagged_bars_are_a_single_warn():
"""flagged_total 0 with openings present: ONE finding -- the opening
bars are missing or their bar type is not set, and the diagonal half is
not asked (there is nothing to ask it of)."""
findings = _verdict(2, 0, 0, True, SPEC)
assert [f[0] for f in findings] == ['WARN']
assert len(findings) == 1
assert 'opening bar-type flag' in findings[0][1]
assert 'missing or their bar type is not set' in findings[0][1]
assert 'a person decides' in findings[0][1]
def test_one_opening_flagged_six_with_four_diagonals_is_ok_ok():
"""6 flagged bars, 4 of them the #3 diagonal against 1 opening (4 >= 4):
BAR TYPE OK, DIAGONAL OK."""
findings = _verdict(1, 6, 4, True, SPEC)
assert [f[0] for f in findings] == ['OK', 'OK']
assert '6 bar(s) carry the opening bar-type flag' in findings[0][1]
assert '4 x' in findings[1][1]
assert '(>= 4)' in findings[1][1]
def test_one_opening_flagged_six_with_two_diagonals_is_ok_then_warn():
"""6 flagged bars but only 2 diagonals against 1 opening (2 < 4): the
BAR TYPE half is OK and the DIAGONAL half is a WARN naming the bound."""
findings = _verdict(1, 6, 2, True, SPEC)
assert [f[0] for f in findings] == ['OK', 'WARN']
assert '6 bar(s) carry the opening bar-type flag' in findings[0][1]
assert 'only 2 x' in findings[1][1]
assert 'up to 4' in findings[1][1]
assert 'a person decides' in findings[1][1]
def test_one_opening_flagged_six_with_no_diagonal_is_ok_then_warn():
"""6 flagged bars, none of them the #3 diagonal: BAR TYPE OK, then the
DIAGONAL half warns that none of the flagged bars is the diagonal."""
findings = _verdict(1, 6, 0, True, SPEC)
assert [f[0] for f in findings] == ['OK', 'WARN']
assert '6 bar(s) carry the opening bar-type flag' in findings[0][1]
assert 'none of the flagged opening bars' in findings[1][1]
assert 'is a' in findings[1][1]
assert 'a person decides' in findings[1][1]
def test_the_diagonal_message_names_the_spec_string():
"""The DIAGONAL message carries the required bar as the plant wrote it,
so a reader can match it against the modelled bars on the piece."""
findings = _verdict(1, 2, 0, True, SPEC)
assert SPEC in findings[1][1]
# --------------------------------------------- severity is the caller's
def test_short_severity_is_honoured():
"""The shortfall severity is a plant fact injected by the caller --
'OK' here must come back as OK, and the function never decides FAIL."""
findings = _verdict(1, 0, 0, True, SPEC, short_severity='OK')
assert [f[0] for f in findings] == ['OK']
def test_short_severity_defaults_to_warn():
findings, reason = ticketpage.opening_diagonal_status(1, 0, 0, True, SPEC)
assert reason is None
assert [f[0] for f in findings] == ['WARN']
def test_fail_never_appears_unless_passed():
"""The function never emits 'FAIL' on its own. Passing it through is the
only way FAIL can appear, and even then only for a shortfall."""
findings = _verdict(1, 0, 0, True, SPEC, short_severity='FAIL')
assert [f[0] for f in findings] == ['FAIL']
# a met expectation stays OK however the caller set the severity
findings = _verdict(1, 6, 4, True, SPEC, short_severity='FAIL')
assert [f[0] for f in findings] == ['OK', 'OK']
# and the OK gates above never take the shortfall severity either
findings = _verdict(0, 0, 0, True, SPEC, short_severity='FAIL')
assert [f[0] for f in findings] == ['OK']