# -*- coding: utf-8 -*- """The `mc` pack: one producer's design-summary habits. Loaded only when the plant layer declares it (`extract.rule_packs: [mc]`) -- see `vibe/extractrules/__init__.py` for why that is declared and not inferred from `plant.id`. WHERE THESE RULES COME FROM --------------------------- Every rule below was written against text observed on a real sheet, and each one says which. They are not a model of how this producer draws; they are a record of lines that actually landed in `unresolved` and what they turned out to be. A rule with no observation behind it does not belong here -- this file is evidence, and a guess mixed into evidence is indistinguishable from it afterwards. Observed on `WPA1.0` (sample_job, 8" horizontal wall, read 2026-09-01): all seven notes the generic reader could not place, listed in the order they sit on the sheet. WHAT IS DELIBERATELY NOT HERE ------------------------------ `STRIPPING & BLOCKING DETAIL < 25'` is NOT treated as the same thing as the bare `STRIPPING & BLOCKING DETAIL` beside it. The bare one is the title of a detail drawn on the sheet and carries no data. The other one carries a CONDITION -- which detail applies below twenty-five feet -- and a condition is the part that could change what gets built. So the bare title is furniture and the conditional one is a note, and the rule that separates them tests for anything trailing the title rather than matching both with one loose pattern. That asymmetry is the module's standing rule (`__init__`: when unsure, note). Nothing here encodes a NUMBER. `*USE MC STANDARD REINFORCEMENT ...` names a standard this office keeps; what that standard says is plant knowledge and belongs in the plant knowledge file, looked up by name, never frozen into a regex in this file. The rule's whole job is to carry the sentence to a field a person reads. IronPython 2.7: no f-strings, no pathlib, no typing, no dataclasses. """ import re from vibe.extractrules import Placement, NOTE, FURNITURE NAME = 'mc' def _rule(rule_id, pattern, kind, reason): return (rule_id, re.compile(pattern, re.IGNORECASE), kind, reason) # Order matters and is not alphabetical: a more specific rule must come before # the looser one it would otherwise be swallowed by. The conditional detail # title is tested before the bare one for exactly that reason. RULES = ( # ---------------------------------------------------------- instructions _rule('mc/standard-reinforcement', r"^\*?\s*USE\s+MC\s+STANDARD\s+REINFORCEMENT\b", NOTE, 'a standing instruction naming this plant\'s standard reinforcement ' 'for cut-down panels and openings -- prose, not a number; what the ' 'standard says is plant knowledge, looked up by name'), _rule('mc/conditional-bar', r"^#\s*\d+\s+BAR\b.*\bONLY\s+NEEDED\s+WHEN\b", NOTE, 'a conditional reinforcing instruction -- the bar is required only ' 'when the condition in the sentence holds, so the condition is the ' 'value and it stays as prose'), _rule('mc/pick-instruction', r"^USE\s+FOR\b.*\bPICK\b", NOTE, 'a handling instruction saying which pick this detail is for -- ' 'prose, and it qualifies a detail rather than stating a number'), _rule('mc/detail-title-conditional', r"^STRIPPING\s*&\s*BLOCKING\s+DETAIL\b\s*\S+", NOTE, 'a standard-detail title carrying a CONDITION (which detail applies, ' 'and when) -- the title alone would be furniture, the condition is ' 'not'), # ------------------------------------------------------------- furniture _rule('mc/detail-title', r"^STRIPPING\s*&\s*BLOCKING\s+DETAIL\s*$", FURNITURE, 'the bare title of a standard detail drawn on the sheet -- names ' 'the detail, states nothing about this design'), _rule('mc/legend-title', r"^STRAND\s+PATTERN\s*$", FURNITURE, 'the title above the strand-pattern legend -- the legend\'s DATA is ' 'the dimension chain under it, which is read as geometry, not as ' 'this text'), _rule('mc/legend-label', r"^STRAND\s*$", FURNITURE, 'a bare column label in the strand legend -- a heading over values ' 'read elsewhere, with no value of its own'), ) def classify(text): """A Placement for `text`, or None when this pack does not recognise it. `text` arrives already whitespace-normalised by the caller (one space between words, no leading or trailing run), so every pattern here can assume single spaces and anchor at both ends where it means to. """ probe = (text or '').strip() if not probe: return None for rule_id, pattern, kind, reason in RULES: if pattern.search(probe): return Placement(kind, rule_id, reason) return None