Balloon addresses — BOM ZONE check#
Balloons are the numbered circles that call out parts on a drawing; each
carries a tag (001, 100, …) and sits in a grid address (zone) such
as 2C4 (ISO 5457). The BOM ZONE column declares where each part’s
balloon is supposed to be. This demo verifies that every BOM row’s declared
zone matches the actual balloon.drawing_address on the sheet — if the BOM
says part 400 is at 1B2 but its balloon is drawn at 2D1, a reader
wastes time hunting for it.
Input drawing#
Sheet 1 of support_assembly.json (with its grid reference).
How the check works#
Load the drawing, build a {TAG: ZONE} lookup from the BOM, then compare
each balloon’s drawing_address against the zone declared for its tag.
"""Demo: BOM vs Balloon Address verification.
Compares each balloon's grid address (``balloon.drawing_address``) against the
ZONE declared for its tag in the BOM, then writes (into the current working
directory):
- ``balloon_addresses_report.md`` — Markdown verification report
- ``bom_vs_balloons.html`` — annotated drawing (green=OK, red=MISMATCH)
"""
from pathlib import Path
from drawing_tools.view.annotations.symbol.balloons import Balloon
from ..shared import read_bom_zone_by_tag, write_report
from ..viz_helpers import load_sheet, render_html
from .build_visualization import BOM_XLSX, DRAWING_JSON, OUT_HTML, build_extras, compare
#: relative → written to the current working directory.
REPORT_MD = Path("balloon_addresses_report.md")
def build_report_md(rows: list[tuple[Balloon, str, str, str, str]]) -> str:
"""Render the Markdown metrics + per-tag table for the address report."""
total = len(rows)
ok = sum(row[4] == "OK" for row in rows)
mismatch = total - ok
rate = 100 * ok / total if total else 0.0
metrics = (
"| Metric | Value |\n|--------|-------|\n"
f"| Total checked | {total} |\n| OK | {ok} |\n| MISMATCH | {mismatch} |\n"
f"| **Pass rate** | **{rate:.1f}%** |"
)
header = "| | TAG | Expected (BOM) | Actual (balloon) | Status |\n|---|-----|----------|--------|--------|"
body = "\n".join(
f"| {'🟢' if status == 'OK' else '🔴'} | {tag} | {expected} | {actual} | {status} |"
for _balloon, tag, expected, actual, status in rows
)
return "\n\n".join([metrics, header + "\n" + body])
def main() -> None:
"""Run the address check, write the report and the visualization."""
sheet = load_sheet(DRAWING_JSON, sheet_index=1)
zone_by_tag = read_bom_zone_by_tag(BOM_XLSX)
rows = compare(sheet, zone_by_tag)
for _balloon, tag, expected, actual, status in rows:
print(f"TAG {tag}: expected(BOM)={expected} actual(balloon)={actual} -> {status}")
ok = sum(row[4] == "OK" for row in rows)
rate = 100 * ok / len(rows) if rows else 0.0
print(f"\n{len(rows)} checked | {ok} OK | {len(rows) - ok} MISMATCH | pass rate {rate:.1f}%")
write_report(REPORT_MD, "Address Verification Report", [build_report_md(rows)])
render_html(sheet, build_extras(sheet, zone_by_tag), OUT_HTML)
if __name__ == "__main__":
main()
Result#
Metric |
Value |
|---|---|
Total checked |
9 |
OK |
6 |
MISMATCH |
3 |
Pass rate |
66.7% |
Mismatches: 102 (BOM 2C3 vs drawing 2B3), 400 and 402
(BOM zones on sheet 1, balloons drawn on sheet 2). The annotated visualization
boxes each balloon green (OK) or red (MISMATCH) and labels the BOM-expected
zone under each mismatch.