# -*- 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. # `
` 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: ticket_created: ticket_detailed: sheet: total_required: sheet_count: # either list the control-number slots explicitly ... control_numbers: [, , ...] nomenclature: # ... or, for numbered slots, match them with one expression: control_slot_re: '^_\\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()