Files
willtools-demo/WillTools.extension/lib/vibe/extractrules/mc.py
T
will 52d94303bc
ci / test (push) Has been cancelled
WillTools: precast shop-ticket QC and panel handling tools
Public demo snapshot of an in-house pyRevit extension for precast concrete
shop-ticket QC (37 checks / 46 checklist rows) and a standalone panel
handling solver (PCI Chapter 8).

- No client, job, plant or product data: knowledge layers ship as
  structural templates only.
- Ticket QC's model-read escalation targets a LAN model server endpoint
  (configurable), not a cloud provider.
- Built from the private development repo via tools/make_dist.py.
2026-09-03 16:33:47 -05:00

119 lines
4.9 KiB
Python

# -*- 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