# -*- coding: utf-8 -*- """The numbers behind the curves, station by station. Eriksson Wall prints, after each stage's plots, a summary table of the same quantities at engineering stations with the governing row bolded. This is our version of it, and these are the properties that make it worth printing: 1. It carries the columns we actually compute, in the units the engineer states them in -- ft, kip, kip-ft, psi -- and does NOT carry the ones we don't. A zero in a capacity column is worse than no column at all, because somebody would divide by it. Six capacity columns landed 2026-08-31 (M_cr, phi, phi-Mn, M/phi-Mn, phi-Vn, V/phi-Vn); the rest are still named as absent in the footnote, and a station whose capacity cannot be computed prints a dash and a REASON -- never a zero, never the neighbouring station's number. 2. Its rows are the analysis's own stations. Thinning may drop a station that is only there because the grid is uniform; it may never drop an opening edge, a pick line, a peak, or the governing station. 3. The governing row is marked once. Not zero times, and not twice -- "which row is the answer" has exactly one answer. 4. It is closed on load like every other section, and it survives the print overrides, because a printed report that hides its numbers is a report that says nothing. """ import re from pathlib import Path import pytest from conftest import aspire_pockets, make_panel from panellift import checks, layout, report, statics from panellift.model import Blockout, Stage from panellift.units import parse_length def _analyse(panel, stage, rigging): return statics.analyse(panel, stage, rigging) def _table_of(page, after='Station table'): """The one that follows this heading.""" i = page.index(after) j = page.index('
', j) + 8] def _rows(table): return re.findall(r']*>(.*?)', table[table.index(''):]) def _cells(row): return [re.sub(r'<[^>]+>', '', c) for c in re.findall(r']*>(.*?)', row)] # ------------------------------------------------ 1. the columns, converted def test_the_columns_are_the_ones_we_compute_in_display_units(stripping, rolling): a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) table = _table_of(report.station_table(a), ']+>', '|', table[:table.index('')]) for column in ('x, ft', 'V, kip', 'M, kip-ft', 'S, in', 'allow, psi', '/allow', 'note'): assert column in head, column assert head.count(', psi') == 3 # f_t, f_c, allow def test_the_capacity_columns_are_carried_and_the_rest_are_named_absent( stripping, rolling): """The load-bearing honesty of the whole table, in its 2026-08-31 form. Six capacity columns are computed and carried. The ones that are still not computed are NAMED in the footnote rather than being quietly missing, and the sentence that forbids zero-filling them survives. """ a = _analyse(make_panel(), stripping, rolling) html = report.station_table(a) table = _table_of(html, '')] for carried in ('Mcr', 'φMn', 'M/φMn', 'φVn', 'V/φVn', 'φ'): assert carried in head, carried footnote = html[html.index('
    '):] for still_absent in ('Vci', 'Vcw', 'deflection', 'Av'): assert still_absent in footnote, still_absent assert 'left out rather than filled with zeros' in footnote # and the footnote does not claim the new ones are missing any more assert 'This revision computes none of those' not in footnote def test_every_value_is_the_engines_value_converted_once(stripping, rolling): """A row's V and M are diagram.shear_lb / moment_lbft at that station over LB_PER_KIP -- not a re-solve, not an interpolation.""" a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) points = dict((p.x, p) for p in checks.stress_profile(a)) table = _table_of(report.station_table(a), '= 2 table = _table_of(report.station_table(a), '') == 1 assert table.count('governing') == 1 gov = [r for r in _rows(table) if 'gov' in r][0] assert 'governing' in gov def test_the_governing_row_is_the_worst_ratio_on_the_page(stripping, rolling): """It has to be the same station the verdict, the red dot on the stress plot and the CRACK finding all point at.""" a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) points = checks.stress_profile(a) worst = max(points, key=lambda p: p.stress_psi) table = _table_of(report.station_table(a, points), '') == 2 assert page.count('>Station table') == 2 # ------------------------------------------- 4. collapsed, and it prints def test_the_section_is_closed_on_load_like_every_other(stripping, rolling): a = _analyse(make_panel(), stripping, rolling) page = report.build(a, rolling, checks.run_all(a, rolling), recommendation=False) assert ('
    Station table' '') in page assert '
    is hidden on screen by two different mechanisms in two generations of engine, and the print block overrides both. The table must be inside that, and its scroll box must open up on paper.""" a = _analyse(make_panel(), stripping, rolling) page = report.build(a, rolling, checks.run_all(a, rolling), recommendation=False) printed = page[page.index('@media print'):] printed = printed[:printed.index('')] assert 'details>*:not(summary){display:block !important}' in printed assert 'details::details-content' in printed assert '.tablewrap{overflow:visible !important}' in printed assert 'table.stations thead{display:table-header-group}' in printed assert 'details.stationsec{break-inside:auto}' in printed # and the table is inside a details, so those overrides reach it assert '
    ' in page without = page[:page.index('