WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s
ci / test (push) Successful in 6m14s
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.
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
# -*- 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']
|
||||
Reference in New Issue
Block a user