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.

src/drawing_tools/demo_scenarios/section_lines/minimal_script.py#
"""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.