Files
willtools-demo/WillTools.extension/tests/test_weight_gate.py
T
will 52d94303bc
ci / test (push) Has been cancelled
WillTools: precast shop-ticket QC and panel handling tools
Public demo snapshot of an in-house pyRevit extension for precast concrete
shop-ticket QC (37 checks / 46 checklist rows) and a standalone panel
handling solver (PCI Chapter 8).

- No client, job, plant or product data: knowledge layers ship as
  structural templates only.
- Ticket QC's model-read escalation targets a LAN model server endpoint
  (configurable), not a cloud provider.
- Built from the private development repo via tools/make_dist.py.
2026-09-03 16:33:47 -05:00

145 lines
5.8 KiB
Python

# -*- coding: utf-8 -*-
"""The weight gate must count cast concrete that is not the panel rectangle.
A slab-seat ledge is a separate family in the assembly and real concrete:
WP371's carried 8.99 ft3 = 1,349 lb, 7.5% of the piece. Before
`extra_cast_ft3` existed, `weight_check()` reconstructed the piece without it
and refused panels whose model -- whose own cast-weight parameter, which does
count the ledge -- was right.
"""
import pytest
from vibe import knowledge, liftexport
def make_know():
return knowledge._deep_copy(knowledge.DEFAULTS)
def make_job(extra_cast_ft3=None):
"""A 20 x 10 ft x 8 in panel with one blockout, WP371-shaped."""
gross_ft3 = 20.0 * 10.0 * 8.0 / 12.0 # 133.333
hole_ft3 = 4.0 * 5.0 * 8.0 / 12.0 # 13.333
pcf = 150.0
job = {
'panel': {
'mark': 'TEST-1',
'length_ft': 20.0,
'width_ft': 10.0,
'thickness_in': 8.0,
'unit_weight_pcf': pcf,
'weight_lb': (gross_ft3 - hole_ft3) * pcf,
'blockouts': [{'name': 'OPENING 1', 'x0': 8.0, 'y0': 2.5,
'length': 4.0, 'width': 5.0}],
},
'source': {'solid_ft3': gross_ft3 - hole_ft3},
}
if extra_cast_ft3 is not None:
job['panel']['extra_cast_ft3'] = extra_cast_ft3
job['source']['solid_ft3'] += extra_cast_ft3
job['panel']['weight_lb'] += extra_cast_ft3 * pcf
return job
def test_no_extra_cast_still_reconciles():
"""A plain rectangle behaves exactly as before the field existed."""
ok, message, numbers = liftexport.weight_check(make_job(), make_know())
assert ok
assert message == ''
assert numbers['reconstructed_lb'] == pytest.approx(numbers['solid_lb'])
def test_ledge_is_added_to_the_reconstruction():
"""With the ledge stated on both sides, the gate passes."""
ok, _message, numbers = liftexport.weight_check(
make_job(extra_cast_ft3=8.992), make_know())
assert ok
assert numbers['extra_cast_lb'] == pytest.approx(8.992 * 150.0)
assert numbers['reconstructed_lb'] == pytest.approx(
(133.333 - 13.333 + 8.992) * 150.0, rel=0.001)
def test_a_missing_ledge_is_the_refusal_it_just_was():
"""Stated includes the ledge; our reconstruction without it must fail.
This is WP371 as the button found it: +7.5%, refused rather than
calculated -- correct behaviour then, and pinned so removing the field
by accident fails here instead of at a plant.
"""
job = make_job()
job['panel']['weight_lb'] += 8.992 * 150.0 # model counts its ledge
ok, message, _numbers = liftexport.weight_check(job, make_know())
assert not ok
assert 'disagree' in message
# --------------------------------------------------------------------------
# WPA545E, measured live 2026-09-02: a custom cast-weight shared parameter's
# AsDouble() returned 560537.22 while AsValueString() -- what Revit shows
# the engineer -- read "38,409" lb, a 14.5939029x units artifact (this
# shared parameter's raw stored value is not pounds; verified 451/451
# against AsValueString in `_scratch/match_weights.py`). The parameter's
# actual name is plant knowledge and does not belong in this repository.
# The production read path (`ticketqc.rstr` -> `pstr` -> `AsValueString()`,
# never `AsDouble()`, for the `panel.weight` / `panel.unit_weight` roles)
# never produces the bad number -- but this pins that IF a raw AsDouble
# value ever reached `weight_lb`, `weight_check` refuses rather than
# calculates on it. 256.06 ft3 x 150 pcf = 38,409 lb is the true weight
# (9.48376 cubicYards -- consistent with the companion cast-volume
# parameter's own AsValueString); 560,537 lb over the same volume implies
# ~2189 pcf, which is not concrete.
def make_wpa545e_job(stated_lb):
return {
'panel': {
'mark': 'WPA545E',
'length_ft': 25.606,
'width_ft': 10.0,
'thickness_in': 12.0, # 25.606 x 10 x 1 ft = 256.06 ft3
'unit_weight_pcf': 150.0,
'weight_lb': stated_lb,
'blockouts': [],
},
'source': {'solid_ft3': 256.06},
}
def test_true_display_weight_reconciles_at_normal_weight_concrete():
"""The engineer's number (AsValueString, 38,409 lb) passes clean at
exactly 150 pcf -- the free sanity net this guard is built on."""
job = make_wpa545e_job(38409.0)
ok, message, numbers = liftexport.weight_check(job, make_know())
assert ok, message
assert numbers['solid_lb'] == pytest.approx(38409.0, rel=1e-4)
def test_raw_asdouble_style_weight_is_refused_not_trusted():
"""The 14.5939x units-artifact value (560,537.22, the live AsDouble()
read on WPA545E) is never silently accepted: it implies ~2189 pcf over
the piece's own 256.06 ft3 solid, which weight_check refuses rather
than calculating on."""
job = make_wpa545e_job(560537.22)
ok, message, numbers = liftexport.weight_check(job, make_know())
assert not ok
assert 'disagree' in message
implied_pcf = numbers['stated_lb'] / (numbers['solid_lb'] / 150.0)
assert implied_pcf > 500 # nowhere near a real concrete density
def test_implausible_density_is_refused_4050pcf():
"""A stated weight implying ~4050 pcf (over 25x normal-weight concrete)
on the same 256.06 ft3 solid must refuse, never pass as a two-way
'agreement' -- the 150 pcf cross-check is the point of this guard."""
job = make_wpa545e_job(256.06 * 4050.0)
ok, message, _numbers = liftexport.weight_check(job, make_know())
assert not ok
assert 'disagree' in message
def test_normal_weight_concrete_density_passes():
"""The companion pass case: a stated weight at ordinary normal-weight
concrete density (150 pcf) reconciles cleanly on the same solid."""
job = make_wpa545e_job(256.06 * 150.0)
ok, message, _numbers = liftexport.weight_check(job, make_know())
assert ok, message