# -*- 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']