Safety class — drawing vs BOM#

Safety class classifies a part for downstream QA: Structural (ST, traceability + NDT) or Non Structural (NS, lighter QA). It appears in two places that must agree: the drawing’s title block (a cell like SAFETY CLASS ST ACC TO EP04-06) and the BOM metadata (a Safety Class column). This demo extracts both, normalizes abbreviations (STStructural), and flags a mismatch — a wrong safety class routes the part through the wrong inspection workflow.

Input drawing#

Sheet 1 of support_assembly.json — note the title block (cartouche).

How the check works#

Detect the title block with SheetTableDetector, pull the safety-class token from the SAFETY CLASS cell, read the BOM Safety Class value, normalize both, and compare.

src/drawing_tools/demo_scenarios/safety_class/minimal_script.py#
"""Demo: Safety Class — Drawing title block vs BOM metadata.

Extracts the safety-class token from the title block (`SheetTableDetector`),
reads the `Safety Class` value from the BOM metadata, normalizes both and
compares, then writes (into the current working directory):
  - ``safety_class_report.md``   — Markdown verification report
  - ``safety_class_check.html``  — annotated drawing (frame + arrow)
"""

from pathlib import Path

from drawing_tools.sheet.featured_sheet import FeaturedSheet

from ..shared import read_bom_safety_class, write_report
from ..viz_helpers import load_sheet, render_html
from .build_visualization import DATA_DIR, DRAWING_JSON, OUT_HTML, build_extras, locate_safety_class

BOM_XLSX = DATA_DIR / "bom_support_assembly_v8.xlsx"
#: relative → written to the current working directory.
REPORT_MD = Path("safety_class_report.md")


def normalize(value: str) -> str:
    """Normalize a safety-class value to ``STRUCTURAL`` / ``NON STRUCTURAL``."""
    normalized = value.strip().upper().replace("-", " ").replace("_", " ")
    if normalized in {"ST", "STRUCTURAL"}:
        return "STRUCTURAL"
    if normalized in {"NS", "NON STRUCTURAL"}:
        return "NON STRUCTURAL"
    return normalized


def drawing_safety_class(sheet: FeaturedSheet) -> str:
    """Return the safety-class token (``ST``/``NS``) from the drawing's title block.

    Delegates to :func:`build_visualization.locate_safety_class` — the single
    detection + matching strategy shared with the visualization — so report and
    drawing can never disagree on which token was read.
    """
    token, _text_piece, _title_block = locate_safety_class(sheet)
    return token


def build_report_md(drawing_raw: str, bom_raw: str, status: str) -> str:
    """Render the Markdown source-values + check-result tables."""
    sources = (
        "### Source values\n\n"
        "| Source | Raw value | Normalized |\n|--------|-----------|------------|\n"
        f"| Drawing title block | {drawing_raw} | {normalize(drawing_raw)} |\n"
        f"| BOM metadata | {bom_raw} | {normalize(bom_raw)} |"
    )
    check = (
        "### Check result\n\n"
        "| Check | Status |\n|-------|--------|\n"
        f"| safety_class | {'🟢 OK' if status == 'OK' else '🔴 MISMATCH'} |"
    )
    ok = 1 if status == "OK" else 0
    summary = f"**Summary.** 1 check · {ok} OK · {1 - ok} MISMATCH · pass rate {100 * ok:.0f}%."
    return "\n\n".join([sources, check, summary])


def main() -> None:
    """Run the safety-class check, write the report and the visualization."""
    sheet = load_sheet(DRAWING_JSON, sheet_index=1)

    drawing_raw = drawing_safety_class(sheet)
    bom_raw = read_bom_safety_class(BOM_XLSX)
    status = "OK" if normalize(drawing_raw) == normalize(bom_raw) else "MISMATCH"

    print(f"Drawing safety class : {drawing_raw} ({normalize(drawing_raw)})")
    print(f"BOM safety class     : {bom_raw} ({normalize(bom_raw)})")
    print(f"Status               : {status}")

    write_report(REPORT_MD, "Safety Class Verification Report", [build_report_md(drawing_raw, bom_raw, status)])

    render_html(sheet, build_extras(sheet, bom_value=bom_raw, status=status), OUT_HTML)


if __name__ == "__main__":
    main()

Result#

Source

Raw value

Normalized

Drawing title block

ST

STRUCTURAL

BOM metadata

Non Structural

NON STRUCTURAL

1 check · 0 OK · 1 MISMATCH · pass rate 0%. The drawing says ST (Structural) but the BOM says Non Structural. The visualization frames the ST token red and points an arrow from the explanatory labels to the cell.