# -*- coding: utf-8 -*- """The figures have to be the data, and the data has to be readable. Two complaints from the engineer using this, both about the same thing -- a picture that does not answer a question is decoration on a calculation: 1. The free body's load was the same drawing on every piece. It was: the tall stroke at each station came out of `beam_y - 30 - h + h`, so the load height cancelled itself out and the one stroke that DID scale was drawn inside the constant one. A panel with a run of openings looked exactly like a solid slab. `test_two_load_profiles_draw_differently` is the guard. 2. Shear, moment and stress were curves with a peak label and nothing else, so reading a value at a station meant measuring the page with your eye. They now carry their own samples and the cursor reads them off, in the units the engineer states them in: kip, kip-ft, psi. The rule underneath both: the report never shows a number the engine did not compute. The hover values are the plotted samples, converted once at the point of display, written onto the SVG -- not recalculated in the browser. """ import re from conftest import aspire_pockets, make_panel from panellift import checks, layout, report, statics from panellift.model import Blockout def _analyse(panel, stage, rigging): return statics.analyse(panel, stage, rigging) def _svg_of(page, title_fragment): """The one whose title contains this fragment.""" i = page.index(title_fragment) return page[page.rindex('', i)] # --------------------------------------------------- 1. the free body is data def test_the_load_band_steps_where_the_section_steps(stripping, rolling): """A blockout takes width out of the piece, so w drops over it. The drawing has to drop over it too, or it is not the free body of anything. """ a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) svg = _svg_of(report.free_body(a), 'Free body') band = re.search(r'= 3 # beam line, full load, reduced load def test_two_load_profiles_draw_differently(stripping, rolling): """The complaint, as a test. Same piece, same pick, same everything but the openings -- and before this the two free bodies were identical.""" solid = _analyse(make_panel(), stripping, rolling) pocketed = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) a = _svg_of(report.free_body(solid), 'Free body') b = _svg_of(report.free_body(pocketed), 'Free body') band_a = re.search(r']*>([^<]*)<', svg).group(1) def test_the_segments_carry_their_own_magnitudes(stripping, rolling): """Per-segment labels, in the unit the profile is in -- plf, factored.""" panel = make_panel(blockouts=[ Blockout(name='B1', x0=8.0, y0=0.0, length=10.0, width=4.0)]) a = _analyse(panel, stripping, rolling) svg = _svg_of(report.free_body(a), 'Free body') labels = re.findall(r']*>([^<]*)', svg) assert len(labels) >= 2 assert len(set(labels)) >= 2 # not the same number twice note = re.search(r']*>([^<]*)<', svg).group(1) assert 'plf factored' in note and ' to ' in note def test_the_load_segments_are_the_profile_not_an_average(stripping, rolling): """load_segments must return the real w in each run, and cover the piece end to end -- the free body's numbers are quoted, so they are exact.""" panel = make_panel(blockouts=[ Blockout(name='B1', x0=8.0, y0=0.0, length=10.0, width=4.0)]) a = _analyse(panel, stripping, rolling) segs = report.load_segments(a.profile) assert len(segs) == 3 assert segs[0][0] == 0.0 assert abs(segs[-1][1] - panel.length_ft) < 1e-9 for i in range(len(segs) - 1): assert segs[i][1] == segs[i + 1][0] assert segs[1][2] < segs[0][2] # the opening lightens the middle # every value is one the profile actually holds held = set(a.profile.w) assert all(w in held for _x0, _x1, w in segs) # ------------------------------------------------- 2 and 3. hover, and units def test_the_diagrams_carry_their_samples_in_display_units(stripping, rolling): """Shear in kip, moment in kip-ft, stress in psi, x in feet. Converted once, here, and written onto the plot -- nothing converts in the browser. """ a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) page = report.build(a, rolling, checks.run_all(a, rolling), recommendation=False) units = re.findall(r'data-yu="([^"]*)" data-yl="([^"]*)"', page) # 'fₜ' (real Unicode subscript), not 'f_t': this label lands in an # node and in the hover script's textContent, which parse a # tag as four literal characters rather than a subscript -- see # report.notate_plain(). assert units == [('kip', 'V'), ('kip-ft', 'M'), ('psi', 'fₜ')] assert page.count('data-xu="ft"') == 3 shear_svg = _svg_of(page, 'Shear V(x)') hy = re.search(r'data-hy="([^"]*)"', shear_svg).group(1).split() peak_kip = max(abs(float(v)) for v in hy) peak_lb = max(abs(v) for v in a.diagram.shear_lb) assert abs(peak_kip - peak_lb / report.LB_PER_KIP) < 0.02 assert peak_kip < peak_lb # it really was divided moment_svg = _svg_of(page, 'Moment M(x)') hy = re.search(r'data-hy="([^"]*)"', moment_svg).group(1).split() peak_kipft = max(abs(float(v)) for v in hy) assert abs(peak_kipft - max(abs(m) for m in a.diagram.moment_lbft) / report.LB_PER_KIP) < 0.2 assert all(len(v.split('.')[1]) == 1 for v in hy) # 1 dp, consistent def test_every_hover_sample_has_a_pixel_and_a_value(stripping, rolling): """Four arrays of the same length, or the lookup is off by one somewhere and the tooltip quietly reports the wrong station.""" a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) page = report.build(a, rolling, checks.run_all(a, rolling), recommendation=False) for svg in re.findall(r']*class="hoverplot"[^>]*>', page): got = [len(re.search(k + r'="([^"]*)"', svg).group(1).split()) for k in ('data-hx', 'data-hy', 'data-px', 'data-py')] assert len(set(got)) == 1 and got[0] > 10 assert re.search(r'data-t="([-0-9.]+)"', svg) assert re.search(r'data-b="([-0-9.]+)"', svg) def test_the_stations_are_feet_to_two_decimals(stripping, rolling): a = _analyse(make_panel(), stripping, rolling) svg = _svg_of(report.stress_plot(a), 'Tensile stress') hx = re.search(r'data-hx="([^"]*)"', svg).group(1).split() assert all(len(v.split('.')[1]) == 2 for v in hx) assert float(hx[0]) >= 0.0 assert float(hx[-1]) <= round(a.panel.length_ft, 2) + 0.01 def test_the_script_is_present_exactly_once(stripping, rolling): """One document, one listener. build() nested inside build_many() must not bring a second copy with it.""" a = _analyse(make_panel(), stripping, rolling) found = checks.run_all(a, rolling) one = report.build(a, rolling, found, recommendation=False) assert one.count('') == 1 nested = report.build(a, rolling, found, recommendation=False, standalone=False) assert '') == 1 assert many.count("querySelectorAll('svg.hoverplot')") == 1 def test_a_page_with_no_script_still_shows_every_curve(stripping, rolling): """Hover is additive. Strip the script out and the report is exactly the report it was -- same curves, same peak rules, same disclosures, and the print overrides untouched. That is the no-JS and the printed case.""" a = _analyse(make_panel(blockouts=aspire_pockets()), stripping, rolling) page = report.build(a, rolling, checks.run_all(a, rolling), recommendation=False) without = page[:page.index('