Section lines — cross-reference with views#
A section-line indicator (A2, B2 …) defines a cutting plane and
promises a matching section view titled COUPE A2-A2 / SECTION A-A on
the same sheet. This demo builds an index of every view-title identifier
(identifiers_by_language, FR/EN, full id preferred over letter), then
checks that each indicator resolves to a view — a dangling indicator is a
manufacturing ambiguity, and a titled view nobody points at is an orphan.
Input drawing#
Sheet 1 of support_assembly_wrong_section_line.json — a wrong-case variant
where COUPE A2-A2 was renamed COUPE X2-X2, so the A2 indicator no
longer resolves and X2 becomes an orphan.
How the check works#
analyze() resolves each indicator against the view-title index and flags
orphan views; it is shared between the report and the visualization so they
never disagree.
"""Demo: Section lines — cross-reference with section views.
For each section-line indicator, checks that a section view with the same
identifier exists on the same sheet, and flags orphan section views, then
writes:
- ``section_lines_report.md`` — Markdown cross-reference report
- ``section_lines_check.html`` — annotated drawing (green=OK, red=DANGLING/ORPHAN)
Both are written into the current working directory.
"""
from pathlib import Path
from ..shared import write_report
from ..viz_helpers import load_sheet, render_html
from . import build_visualization
from .build_visualization import DRAWING_JSON, OUT_HTML
#: relative → written to the current working directory.
REPORT_MD = Path("section_lines_report.md")
def build_report_md(
indicator_rows: list[build_visualization.IndicatorRow],
orphan_rows: list[build_visualization.OrphanRow],
) -> str:
"""Render the Markdown indicators + orphan-views tables and the summary."""
ok = sum(row.matched_view is not None for row in indicator_rows)
total = len(indicator_rows)
rate = 100 * ok / total if total else 0.0
ind_header = (
"Indicators:\n\n"
"| Type | Identifier | Source view | Matched view | Status |\n"
"|------|------------|-------------|--------------|--------|"
)
ind_body = "\n".join(
f"| section_line | {row.identifier} | {row.source_view} | "
f"{row.matched_view or '-'} | {'🟢 OK' if row.matched_view else '🔴 DANGLING'} |"
for row in indicator_rows
)
orphan_header = "Orphan views:\n\n| View | Identifier | Status |\n|------|------------|--------|"
orphan_body = (
"\n".join(f"| {row.view} | {row.identifier} | 🔴 ORPHAN |" for row in orphan_rows) or "| _(none)_ | | |"
)
summary = (
f"**Summary.** {total} indicators checked · {ok} OK · "
f"{total - ok} DANGLING · {len(orphan_rows)} orphan view"
f"{'s' if len(orphan_rows) != 1 else ''} · pass rate {rate:.1f}%."
)
return "\n\n".join([ind_header + "\n" + ind_body, orphan_header + "\n" + orphan_body, summary])
def main() -> None:
"""Run the section-line cross-reference, write the report and visualization."""
sheet = load_sheet(DRAWING_JSON, sheet_index=1)
indicator_rows, orphan_rows = build_visualization.analyze(sheet)
for row in indicator_rows:
status = "OK" if row.matched_view else "DANGLING"
print(f"{row.identifier:<4} source={row.source_view:<14} -> {row.matched_view or '-':<25} {status}")
for row in orphan_rows:
print(f"ORPHAN view {row.view} (identifier {row.identifier})")
ok = sum(row.matched_view is not None for row in indicator_rows)
total = len(indicator_rows)
rate = 100 * ok / total if total else 0.0
print(
f"\n{total} indicators checked - {ok} OK - {total - ok} dangling - "
f"{len(orphan_rows)} orphan - pass rate {rate:.1f}%"
)
write_report(REPORT_MD, "Section Line Cross-Reference Report", [build_report_md(indicator_rows, orphan_rows)])
render_html(sheet, build_visualization.build_extras(sheet), OUT_HTML)
if __name__ == "__main__":
main()
Result#
Identifier |
Source view |
Matched view |
Status |
|---|---|---|---|
A2 |
VUE DE FACE |
— |
🔴 DANGLING |
B2 |
VUE DE FACE |
COUPE B2-B2 |
🟢 OK |
C2 |
COUPE X2-X2 |
COUPE C2-C2 REDRESSEE |
🟢 OK |
Plus one orphan view (COUPE X2-X2, identifier X2). 3 indicators · 2
OK · 1 DANGLING · 1 orphan · pass rate 66.7%. The visualization draws matched
pairs in green (indicator + title + arrow + ✓) and the dangling indicator /
orphan title in red.