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.
This commit is contained in:
+174
@@ -0,0 +1,174 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Reconcile the issued ticket set against the whole job.
|
||||
#
|
||||
# Every other check on this panel judges one ticket at a time, and no amount of
|
||||
# per-ticket checking can find a ticket that is not there. This button asks the
|
||||
# question about the SET: every piece printed on something, everything printed
|
||||
# about a piece that exists, and the counts reconciling mark by mark.
|
||||
#
|
||||
# It reads. It never writes, and it opens no transaction.
|
||||
#
|
||||
# THE SHAPE OF THE PAGE, AND WHOSE IT IS. Owner, 2026-08-30, now standing
|
||||
# across this tab: the answer is the headline and is never folded; everything
|
||||
# else follows it, collapsed; a section that carries a problem opens itself.
|
||||
# The reader's contract is that if nothing below the headline is open, nothing
|
||||
# needs him.
|
||||
#
|
||||
# Here the answer is the VERDICT IN NUMBERS -- pieces and tickets accounted
|
||||
# for, and a count per finding -- not the findings themselves. On a job with
|
||||
# four hundred uncovered pieces the lists are thousands of lines and the
|
||||
# verdict is invisible inside them. So:
|
||||
#
|
||||
# * the headline is the totals and the finding counts, printed through
|
||||
# pyRevit's own markdown and tables;
|
||||
# * every non-empty finding is a section below it, OPEN, because there is no
|
||||
# finding in this reconciliation that is merely interesting;
|
||||
# * coverage by design, the page-set inventory, the categories swept, the
|
||||
# checks that found nothing and the timings are folded;
|
||||
# * and the two sections that say what this run could NOT see -- out of
|
||||
# scope, and blind spots -- are separate, always, and carry their counts
|
||||
# on their headers with the fold shut. A blind spot that is a REFUSAL
|
||||
# (nothing reconciled) opens itself and is repeated in the headline,
|
||||
# because a refusal reported quietly reads as a clean job.
|
||||
#
|
||||
# WHY THE HTML IS WRITTEN BY HAND AND LOOKS DATED. This output panel is a
|
||||
# `System.Windows.Forms.WebBrowser` -- the IE engine, in IE7 document mode.
|
||||
# `<details>` does not exist there and fails SILENTLY. The fold is an inline
|
||||
# `onclick` plus `style.display`, which is IE5-era DOM; a section with a
|
||||
# problem in it renders open as plain HTML and needs no script at all; and the
|
||||
# whole report, nothing folded and nothing capped, is written to
|
||||
# `last-report.txt` beside the model's other job data on every press, with the
|
||||
# page saying where. A fold that will not open therefore costs a file open,
|
||||
# never a lost sentence. All of that lives in `vibe.foldreport`.
|
||||
|
||||
# pylint: disable=import-error,invalid-name,broad-except
|
||||
# `__title__` mirrors bundle.yaml's `title:` and is otherwise dead --
|
||||
# pyRevit reads it only `if not self.meta`. It is kept in step so that
|
||||
# trimming a bundle file cannot bring an old name back. One line, no
|
||||
# `\n`: this button is a row in a `.pulldown`, and a row has no second
|
||||
# line to put one on.
|
||||
__title__ = 'Job Recon'
|
||||
|
||||
import os
|
||||
|
||||
from pyrevit import revit, script
|
||||
|
||||
from vibe import compat, designspec, knowledge, jobrecon, jobreconscan
|
||||
from vibe import foldreport as fold
|
||||
|
||||
doc = revit.doc
|
||||
output = script.get_output()
|
||||
output.close_others()
|
||||
|
||||
REQUIRED = ('member.control_number', 'sheet.control_numbers')
|
||||
|
||||
# The cap on each list ON THE PAGE. The file written beside the model has no
|
||||
# cap at all -- a page is read, a file is searched.
|
||||
PAGE_LIMIT = 60
|
||||
|
||||
HOW_TO_MAP = """
|
||||
This reconciliation needs roles the older checks never used. Add them to the
|
||||
plant knowledge file (the layer that describes the office TEMPLATE), naming
|
||||
whatever your title block and your pieces actually call these things:
|
||||
|
||||
parameters:
|
||||
member:
|
||||
control_number: <the piece's control number>
|
||||
ticket_created: <optional -- the model's own 'a ticket exists' flag>
|
||||
ticket_detailed: <optional>
|
||||
sheet:
|
||||
total_required: <the quantity the title block states>
|
||||
sheet_count: <how many pages the ticket says it has>
|
||||
# either list the control-number slots explicitly ...
|
||||
control_numbers: [<slot 1>, <slot 2>, ...]
|
||||
|
||||
nomenclature:
|
||||
# ... or, for numbered slots, match them with one expression:
|
||||
control_slot_re: '^<PREFIX>_\\d+$'
|
||||
|
||||
Until then this button refuses rather than reporting a job it cannot see.
|
||||
"""
|
||||
|
||||
|
||||
def write_full(recon, full_path):
|
||||
"""The whole report, unfolded, beside the model. Returns True if written.
|
||||
|
||||
Written BEFORE anything is printed, so the file exists even if the
|
||||
printing throws. `as_text` first and encode second: under IronPython 2.7
|
||||
the report is a byte string and a mark carrying a non-ASCII character
|
||||
would make a bare `.encode('utf-8')` raise on the implicit ASCII decode.
|
||||
"""
|
||||
payload = compat.as_text(
|
||||
'\n'.join(jobrecon.format_report(recon, full_path=full_path)))
|
||||
handle = open(full_path, 'wb')
|
||||
try:
|
||||
handle.write(payload.encode('utf-8'))
|
||||
finally:
|
||||
handle.close()
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
know = knowledge.load(doc)
|
||||
problems = knowledge.config_problems(doc, know)
|
||||
if problems:
|
||||
for line in problems:
|
||||
output.print_md('**' + line + '**')
|
||||
return
|
||||
|
||||
names = knowledge.param_names(know, 'member.control_number')
|
||||
slots = (knowledge.param_names(know, 'sheet.control_numbers')
|
||||
or knowledge.get(know, 'nomenclature.control_slot_re', None))
|
||||
if not names or not slots:
|
||||
output.print_md('## Job Recon cannot run')
|
||||
output.print_md('Nothing was reconciled, and that is **not** a clean '
|
||||
'result -- it is a refusal.')
|
||||
print(HOW_TO_MAP)
|
||||
return
|
||||
|
||||
recon = jobreconscan.run(doc, know=know)
|
||||
|
||||
full_path = os.path.join(designspec.sub_dir(doc, 'jobrecon'),
|
||||
'last-report.txt')
|
||||
written = False
|
||||
try:
|
||||
written = write_full(recon, full_path)
|
||||
except Exception as exc:
|
||||
output.print_md('> The full report could not be written to disk ({0}). '
|
||||
'Everything is still on this page; use the fold '
|
||||
'headers.'.format(exc))
|
||||
|
||||
sections = jobrecon.render(recon, limit=PAGE_LIMIT,
|
||||
full_path=full_path if written else None)
|
||||
|
||||
# ---- the answer, through pyRevit, big and first.
|
||||
for block in sections[0].blocks:
|
||||
if block[0] == fold.TABLE:
|
||||
output.print_table(block[2], columns=block[1])
|
||||
else:
|
||||
output.print_md(block[1])
|
||||
|
||||
# ---- everything else, folded.
|
||||
tail = sections[1:]
|
||||
if not tail:
|
||||
return
|
||||
output.add_style(fold.STYLE)
|
||||
output.inject_script(fold.SCRIPT)
|
||||
opened = fold.sections_with_problems(tail)
|
||||
output.print_md('---')
|
||||
if opened:
|
||||
output.print_md('**{0} of the {1} section(s) below opened themselves '
|
||||
'— they have something in them that needs you.** '
|
||||
'The rest are folded; click a header to open one.'
|
||||
.format(len(opened), len(tail)))
|
||||
else:
|
||||
output.print_md('**Nothing below needs you.** Every section is folded; '
|
||||
'click a header to open one.')
|
||||
if written:
|
||||
output.print_md('The whole report, nothing folded and nothing capped, '
|
||||
'is in `{0}`.'.format(full_path))
|
||||
output.print_html(fold.html(tail))
|
||||
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user