8e6ee31596
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.
197 lines
8.6 KiB
Python
197 lines
8.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Which end of the piece is up, when it came from a fallback -- and the red
|
|
line telling the reader to go check TOT on the model.
|
|
|
|
`report.up_end_disclosure()` / `report._up_end_block()` already print the
|
|
fallback's own explanation, unconditionally, whenever `source.up_end_basis`
|
|
is anything but the ruled mechanism (`RULED_UP_END_BASIS`). What is new here
|
|
is `report._up_end_is_origin_defect()`: of the three reasons the ruled
|
|
mechanism can decline (see `up_end_from_origin()` in
|
|
`WillTools.extension/lib/vibe/knowledge.py`), only the FIRST -- the origin's
|
|
own up vector does not point up -- is a defect on the assembly's origin
|
|
itself. The other two (near-square; not yet confirmed for this piece's
|
|
orientation) are gaps in what the rule covers, not something wrong with the
|
|
model, and must never carry the same red call to action.
|
|
"""
|
|
import re
|
|
|
|
from conftest import make_panel
|
|
|
|
from panellift import report
|
|
|
|
|
|
# Verbatim from `up_end_from_origin()`'s FIRST refusal
|
|
# (WillTools.extension/lib/vibe/knowledge.py) -- the exact shape
|
|
# `liftexport` writes into `source.up_end_basis_why`, and the real text on
|
|
# WP401 (2026-09-01).
|
|
ORIGIN_NOT_UP_WHY = (
|
|
"The origin of assembly WP401 does not stand the piece up: its up "
|
|
"basis is (0.0, 1.0, 0.0) rather than world up. One assembly in 282 is "
|
|
"like this -- its face normal points up instead -- and on such a "
|
|
"piece every reading off the frame is wrong, so nothing is read off "
|
|
"this one. Which end is up was therefore read from GLOBAL ELEVATION "
|
|
"instead -- the end of the piece that is higher in the model. That is "
|
|
"a fallback, not the rule.")
|
|
|
|
# Verbatim shape of the SECOND refusal (near square) -- must NOT flag.
|
|
NEAR_SQUARE_WHY = (
|
|
"Assembly PNL127 is near square (11.63 x 11.58 ft, ratio 1.00), and "
|
|
"below 1.25 the long direction cannot be told from the up direction.")
|
|
|
|
# Verbatim shape of the THIRD refusal (not yet confirmed for this
|
|
# orientation) -- must NOT flag either; this is a coverage gap, not a
|
|
# defect on the model. Matches `up_end_from_origin()`'s wording as fixed
|
|
# 2026-09-01: green (BasisY/world-up) is now confirmed for a vertical wall
|
|
# panel too, so the message no longer claims nobody has said how vertical
|
|
# panels work -- only that this function has not been extended to run
|
|
# there, which is a coverage gap and NOT a defect on the origin.
|
|
NOT_HORIZONTAL_WHY = (
|
|
"The origin rule is confirmed for HORIZONTAL panels only, and the "
|
|
"origin of assembly PNL1 stands its length up, which is not one. The "
|
|
"owner has confirmed the same green-arrow reading also holds on a "
|
|
"vertical wall panel (2026-09-01), but this rule has not been "
|
|
"extended to run there yet, so the origin is not read on this piece.")
|
|
|
|
|
|
def _panel_with_fallback(why, basis='global elevation'):
|
|
panel = make_panel()
|
|
panel.source = {
|
|
'up_end_basis': basis,
|
|
'up_end_basis_why': why,
|
|
'up_end_affects': 'erection -- which row of face lifters the crane '
|
|
'cannot reach, and which edge the anchors are on',
|
|
}
|
|
return panel
|
|
|
|
|
|
# ------------------------------------------------- _up_end_is_origin_defect
|
|
|
|
def test_the_origin_not_up_refusal_is_flagged():
|
|
assert report._up_end_is_origin_defect(ORIGIN_NOT_UP_WHY) is True
|
|
|
|
|
|
def test_the_near_square_refusal_is_not_flagged():
|
|
assert report._up_end_is_origin_defect(NEAR_SQUARE_WHY) is False
|
|
|
|
|
|
def test_the_not_yet_confirmed_orientation_refusal_is_not_flagged():
|
|
assert report._up_end_is_origin_defect(NOT_HORIZONTAL_WHY) is False
|
|
|
|
|
|
def test_empty_or_none_why_is_not_flagged():
|
|
assert report._up_end_is_origin_defect('') is False
|
|
assert report._up_end_is_origin_defect(None) is False
|
|
|
|
|
|
# --------------------------------------------------------- _up_end_block()
|
|
|
|
def test_the_red_line_appears_when_the_origin_itself_is_wrong():
|
|
"""WP401's real shape: the origin does not point up at all, so this is
|
|
a defect on the assembly's own TOT, not a caveat about the analysis."""
|
|
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
|
|
block = report._up_end_block(panel)
|
|
assert 'tot-flag' in block
|
|
assert 'TOT' in block
|
|
# The short fact is always visible; the measured reasoning ("does not
|
|
# stand the piece up") is inside the fold, not repeated at top level.
|
|
assert 'read from global elevation, not the assembly origin' in block
|
|
assert 'does not stand the piece up' in block
|
|
|
|
|
|
def test_the_block_is_short_a_few_lines_not_a_wall_of_text():
|
|
"""The owner's own words, 2026-09-01: '把这一段从lift的结果里面删掉,
|
|
或者缩短...太长了压根不会看的' -- delete this section or shorten it, it
|
|
is so long nobody will read it at all. The block he cut was ~700
|
|
characters; the always-visible text (outside the fold) must be a small
|
|
fraction of that."""
|
|
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
|
|
block = report._up_end_block(panel)
|
|
# Everything inside the fold is closed by default and does not count
|
|
# against "what a reader sees at a glance" -- strip it out before
|
|
# measuring, the same way a browser would hide it on load. Tags do not
|
|
# count either: they are never visible text, only the characters
|
|
# between them are.
|
|
fold_start = block.index('<details')
|
|
fold_end = block.index('</details>') + len('</details>')
|
|
always_visible = block[:fold_start] + block[fold_end:]
|
|
visible_text = re.sub(r'<[^>]+>', '', always_visible)
|
|
assert len(visible_text) < 250, (len(visible_text), visible_text)
|
|
|
|
|
|
def test_fallback_not_the_rule_appears_at_most_once():
|
|
"""The old block said this exact clause TWICE in one paragraph -- once
|
|
as the report's own intro sentence, once again from the end of the
|
|
producer string it quoted. That repetition, not the length alone, was
|
|
what the owner named as the reason he stopped reading."""
|
|
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
|
|
block = report._up_end_block(panel)
|
|
assert block.count('fallback, not the rule') <= 1
|
|
|
|
|
|
def test_the_full_reasoning_is_folded_using_the_report_s_own_mechanism():
|
|
"""Not deleted, not moved to a different wall of visible text -- folded
|
|
behind the SAME <details>/_sec() idiom every other section already
|
|
uses, closed by default."""
|
|
panel = _panel_with_fallback(ORIGIN_NOT_UP_WHY)
|
|
block = report._up_end_block(panel)
|
|
assert '<details class="section upend-why">' in block
|
|
assert '<summary>Why</summary>' in block
|
|
assert 'open' not in block.split('<details')[1].split('>')[0]
|
|
# The full producer reasoning is still there -- just inside the fold.
|
|
assert 'One assembly in 282' in block
|
|
assert 'That is a fallback, not the rule.' in block
|
|
|
|
|
|
def test_the_red_line_is_absent_for_the_near_square_refusal():
|
|
"""A near-square piece is genuinely undecidable, not a broken origin --
|
|
the red 'go check TOT' line would send the reader to fix a model that
|
|
is not wrong."""
|
|
panel = _panel_with_fallback(NEAR_SQUARE_WHY)
|
|
block = report._up_end_block(panel)
|
|
assert block # the disclosure itself still prints
|
|
assert 'tot-flag' not in block
|
|
assert 'TOT' not in block
|
|
|
|
|
|
def test_the_red_line_is_absent_for_the_not_yet_confirmed_orientation():
|
|
"""A vertical panel the rule has not been extended to cover yet is a
|
|
known gap, not a defect -- same non-flag as the near-square case."""
|
|
panel = _panel_with_fallback(NOT_HORIZONTAL_WHY)
|
|
block = report._up_end_block(panel)
|
|
assert block
|
|
assert 'tot-flag' not in block
|
|
assert 'TOT' not in block
|
|
|
|
|
|
def test_no_block_at_all_when_the_ruled_mechanism_answered():
|
|
"""`up_end_basis` == the ruled mechanism (`RULED_UP_END_BASIS`) means
|
|
there is nothing to disclose -- and therefore nothing to flag."""
|
|
panel = _panel_with_fallback('assembly origin should never get here',
|
|
basis=report.RULED_UP_END_BASIS)
|
|
assert report._up_end_block(panel) == ''
|
|
|
|
|
|
def test_no_block_at_all_when_the_panel_has_no_source_disclosure():
|
|
panel = make_panel()
|
|
panel.source = {}
|
|
assert report._up_end_block(panel) == ''
|
|
|
|
|
|
def test_the_flag_uses_the_same_red_every_fail_on_the_page_already_uses():
|
|
"""Match the report's existing severity styling rather than inventing a
|
|
new colour -- var(--fail), the same variable `.FAIL` uses."""
|
|
assert '.tot-flag{color:var(--fail)' in report.CSS
|
|
|
|
|
|
def test_the_flag_is_small_text_not_a_banner():
|
|
"""It sits inside the existing warn-styled .upend box; it must not carry
|
|
its own background, border or oversized font that would read as a
|
|
second banner on the page."""
|
|
css = report.CSS
|
|
start = css.index('.tot-flag{')
|
|
end = css.index('}', start)
|
|
rule = css[start:end]
|
|
assert 'font-size:12px' in rule
|
|
assert 'background' not in rule
|
|
assert 'border' not in rule
|