Files
willtools-demo/WillTools.extension/tests/test_compression_limit_setting.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

157 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
"""The handling compression limit, from the window down to the job file.
`handling.compression_limit_ratio` multiplies f'c (f'ci at stripping) to give
the compression allowable in psi. Unlike the tensile limit next to it, there
is no second school of thought to weigh -- the owner closed the question,
2026-09-01:
"0.6保持可调" ("0.6 stays adjustable")
-- so 0.6 remains the shipped default (ACI 318-19 Table 24.5.3.1 / Sec.
24.5.4.1) and becomes a per-job field the same way `tension_limit_sqrt_fc`
already is, in the same Project Config module, following the exact same
plumbing: `configmodel.SETTINGS` -> `configform.MODULE_OF` -> `liftexport`
writes it onto every stage -> panellift reads it into `Stage`.
Nothing here implements the higher 0.70*f'ci ACI permits at member ends --
the owner ruled that out the same night ("先不用实现0.7...别搞这么复杂") --
so there is exactly one ratio, and this file never tests a location split.
Mirrors `test_stress_limit_setting.py` structurally; see that file's own
docstring for why each half is tested where it is.
"""
from vibe import configform, configmodel, knowledge, liftexport
from test_liftexport import FACE_AND_EDGE_LIFTERS, make_job, make_know
KEY = 'handling.compression_limit_ratio'
# ------------------------------------------------------- it is in the window
def test_the_compression_limit_is_a_field_in_lifting_and_handling():
assert configform.MODULE_OF[KEY] == configform.LIFTING
assert KEY not in configform.NOT_SHOWN
def test_it_is_a_box_you_type_a_number_into():
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
assert field['kind'] == 'number'
ok, value, _why = configform.parse(field, '0.7')
assert ok and value == 0.7
def test_the_row_note_names_the_default_and_the_citation():
"""A box that says only 'Compression limit' asks a person to trust a
number with no way to check it. The note carries what it multiplies,
the default, and the ACI table it comes from."""
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
note = field['note']
assert '0.6' in note
assert "f'c" in note
assert '24.5' in note # the ACI section, either citation
def test_the_note_says_the_higher_member_end_value_is_not_implemented():
"""The owner's ruling against implementing ACI's 0.70*f'ci at member
ends has to be visible at the control, not only in a comment nobody
editing the job sees."""
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
assert '0.70' in field['note']
assert 'conservative' in field['note'] or 'flat' in field['note']
def test_the_full_view_says_where_it_is_edited_now():
assert configform.placement()[KEY] == 'Lifting and handling'
def test_nothing_fell_through_the_move():
"""The guard that makes the not-shown pool honest: every editable
setting is in a module or has a written reason for not being. Failing
this here would mean the new field landed with no home."""
assert configform.unmapped() == []
# ------------------------------------------------------------ what it refuses
def test_a_zero_or_negative_ratio_is_refused():
"""The exact mirror of the tensile limit's own refusal: a zero
allowable fails every piece in compression at any stress, and a
negative one inverts the comparison so every piece passes."""
assert configmodel.validate(KEY, 0)
assert configmodel.validate(KEY, -0.6)
assert 'greater than zero' in ' '.join(configmodel.validate(KEY, 0))
def test_a_garbage_value_is_refused_not_silently_reverted():
assert configmodel.validate(KEY, 'static_lift')
assert configmodel.validate(KEY, None) == [] # blank means "inherit"
def test_ordinary_values_are_accepted_without_argument():
"""0.6 is the shipped default, not a doctrine -- the validator does not
get to refuse a job that wants a different positive ratio."""
assert configmodel.validate(KEY, 0.6) == []
assert configmodel.validate(KEY, 0.5) == []
assert configmodel.validate(KEY, 0.7) == []
# ------------------------------------------------- it reaches the calculation
def test_the_default_is_still_point_six():
"""Byte-identity when nobody sets it. Every job configured today has no
override, and this is the number they have all been running."""
assert knowledge.get(knowledge.DEFAULTS, KEY) == 0.6
def test_the_job_layer_value_lands_on_every_stage_of_the_job_file():
know = make_know()
know['handling']['compression_limit_ratio'] = 0.7
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), know)
assert stages
assert all(s['compression_limit_ratio'] == 0.7 for s in stages)
def test_an_unset_ratio_writes_the_point_six_it_always_wrote():
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), make_know())
assert all(s['compression_limit_ratio'] == 0.6 for s in stages)
assert all(s['compression_limit_source'] == 'job' for s in stages)
def test_a_stage_that_states_its_own_ratio_is_marked_as_the_override():
know = make_know()
know['handling']['compression_limit_ratio'] = 0.6
know['handling']['stages'][0]['compression_limit_ratio'] = 0.7
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), know)
assert stages[0]['compression_limit_ratio'] == 0.7
assert stages[0]['compression_limit_source'] == 'stage'
assert stages[1]['compression_limit_ratio'] == 0.6
assert stages[1]['compression_limit_source'] == 'job'
def test_the_provenance_words_are_the_ones_panellift_reads():
"""Written on one side, refused on the other if they disagree -- see
`panellift.model.COMPRESSION_LIMIT_SOURCES`."""
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), make_know())
assert set(s['compression_limit_source'] for s in stages) <= \
set(['job', 'stage', 'default'])
def test_tension_and_compression_are_independent_fields():
"""Setting one must never move the other -- they are two separate
dotted keys, two separate stage entries, and this is the regression a
copy-paste of the tension plumbing could introduce silently."""
know = make_know()
know['handling']['tension_limit_sqrt_fc'] = 7.5
know['handling']['compression_limit_ratio'] = 0.7
stages, _rows, _problems = liftexport.default_stages(
make_job(FACE_AND_EDGE_LIFTERS), know)
assert all(s['tension_limit_sqrt_fc'] == 7.5 for s in stages)
assert all(s['compression_limit_ratio'] == 0.7 for s in stages)