Coordinate > Clash Results

Status Rules

Applies ordered rules (depth, trades, age, element properties) that set clash statuses, assignees and comments, with a count of what each rule would change before anything is written.

Tab and panelCoordinate > Clash Results
KindButton
Live whenThe model has clash results

Status

Untested Never executed inside Navisworks. Passed the headless checks only.

The public ask

No public source is on record for this tool.

Events it listens to

None. It runs only when you click it.

Files

Paths are relative to the Playground.extension folder.

  • Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/bundle.yaml
  • Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/icon.dark.png
  • Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/icon.png
  • Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/icon.small.dark.png
  • Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/icon.small.png
  • Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/script.py

Install

  1. Only this tool. In Navisworks, click pyNavis > Playground, search for Status Rules, tick it and click Install.
  2. The whole extension. Download Playground.extension.zip, unzip it, put the Playground.extension folder in %APPDATA%\pyNavis\extensions\, and click pyNavis > Reload. Status Rules is on the Coordinate tab, Clash Results panel.

You need pyNavis 1.3.0 or later.

Tried it? Tell us whether it worked or not. That report is what moves a tool up the status ladder.

Report

Source

The tool's own files, word for word as published in pynavis/Playground. Paths are relative to the Playground.extension folder.

script.py

The script that runs on a click.

Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/script.py View on GitHub
"""Sets clash statuses, assignees and notes with ordered rules that a team keeps and shares."""
import datetime

from pynavis import forms, script, toast

from namo import clashrules, progress, ui
from namo import clashset_common as common
from namo import clashset_tests as defs

DEFAULTS = {'tests': [], 'set': clashrules.EXAMPLES_NAME}
TITLE = 'Status Rules'
NEW_SET = '\x00new'


def run():
    from pynavis import doc as pdoc
    doc = pdoc.get_doc()
    problem = common.clash_problem(doc, need_results=True)
    if problem:
        toast.show(*problem)
        return
    values = script.get_config(DEFAULTS)
    tests = common.choose_tests(TITLE, 'Tick the tests the rules should look at.', values['tests'],
                                doc=doc)
    if tests is None:
        return
    if not tests:
        toast.info('No clash results yet', 'Run your clash tests in Clash Detective first.')
        return
    try:
        entries = common.read_entries(tests, doc)
    except RuntimeError as error:
        toast.error('Could not read the clashes', str(error))
        return
    if entries is progress.CANCELLED:
        return
    values['tests'] = [t.DisplayName for t in tests]
    units = defs.doc_units(doc)
    today = datetime.datetime.now()
    rows_by_test = dict((entry['name'], entry['rows']) for entry in entries)
    readers = dict((entry['name'], clashrules.element_reader(entry['leaves'])) for entry in entries)
    store = clashrules.store()
    names = clashrules.ensure_examples(store)
    set_name = values['set'] if values['set'] in names else (names[0] if names else 'My rules')
    rules = clashrules.rules_of(store.get(set_name)) if set_name in names else []

    def preview(rule_list):
        if not rule_list:
            return None
        compiled = [clashrules.compile_rule(rule, units)[0] for rule in rule_list]
        return clashrules.plan(rows_by_test, compiled, units, today, lambda name: readers[name])

    def edit_rule(rule):
        start = rule or {'name': 'New rule', 'conditions': [], 'status': 'Reviewed'}
        answers = ui.form('Status rule', clashrules.rule_form_fields(start), ok='OK', width=560,
                          validate=lambda answered: clashrules.validate_form(answered, units))
        return None if answers is None else clashrules.normalise_rule(answers)

    def save_set(state):
        try:
            store.save(state.set_name, clashrules.rule_set_config(state.rules))
        except (IOError, OSError, ValueError) as error:
            toast.error('Could not save the rule set', str(error))
            return False
        state.dirty = False
        state.refresh()
        return True

    def on_save(state):
        if save_set(state):
            toast.success('Saved the rule set "%s"' % state.set_name)

    def on_switch(state):
        if state.dirty and not forms.confirm('Discard your changes to "%s"?' % state.set_name, TITLE):
            return
        rows = [{'value': name, 'cells': [name, common.plural(len(clashrules.rules_of(store.get(name))),
                                                             'rule')]} for name in store.names()]
        rows.append({'value': NEW_SET, 'cells': ['New rule set', '']})
        picked = ui.choose('Rule sets', rows, ['Rule set', 'Rules'], multiselect=False,
                           checked=[state.set_name], ok='Open', width=480, height=420)
        if picked is None:
            return
        if picked[0] == NEW_SET:
            name = forms.ask_string('Name of the new rule set:', '', TITLE)
            if name is None or not name.strip():
                return
            state.load(name.strip(), [])
            state.dirty = True
            state.refresh()
        else:
            state.load(picked[0], clashrules.rules_of(store.get(picked[0])))

    win = clashrules.build_window(set_name, rules, preview, units, edit_rule, on_save, on_switch, TITLE)
    ui.show_modal(win)
    state = win.Tag
    values['set'] = state.set_name
    script.save_config(values)
    if not state.accepted:
        if state.dirty and forms.confirm('Save your changes to the rule set "%s"?' % state.set_name, TITLE):
            if save_set(state):
                toast.success('Saved the rule set "%s"' % state.set_name)
        return
    if state.dirty:
        save_set(state)
    planned = preview(state.result['rules'])
    if not planned or not planned['changed']:
        toast.info('No clash needs a change', 'Every matching clash already has what its rule sets.')
        return
    entries_by_name = dict((entry['name'], entry) for entry in entries)
    try:
        totals = common.write_progress('Applying status rules', lambda report_to: clashrules.apply(
            entries_by_name, planned, doc, report_to))
    except ValueError as error:
        toast.error('Could not update the clashes', str(error))
        return
    parts = []
    if totals['status']:
        parts.append('%s changed' % common.plural(totals['status'], 'status', 'statuses'))
    if totals['assign']:
        parts.append('%s assigned' % '{:,}'.format(totals['assign']))
    if totals['notes']:
        parts.append('%s added' % common.plural(totals['notes'], 'comment'))
    toast.success('Updated %s in %s' % (common.plural(planned['changed'], 'clash', 'clashes'),
                                        common.plural(totals['tests'], 'test')),
                  ', '.join(parts) + '. One undo step.')


if globals().get('__runner__'):
    toast.warning(*common.RUNNER_MESSAGE)
else:
    run()

Library modules this tool uses

From the shared namo library, which installs with every tool. Each name opens the module on GitHub.

  • namo.clashrulesStatus Rules: ordered rules that set clash statuses (and assignees, and a note).
  • namo.clashset_commonSmall things every Clash Setup and Clash Results tool shares.
  • namo.clashset_testsClash test definitions as plain data, and the one place Playground builds tests from them.
  • namo.progressGlue between pyNavis' cancellable progress window and Playground's progress callbacks.
  • namo.uiWPF building blocks for Playground tools, themed from the pyNavis design system.

bundle.yaml

The bundle contract: the keys pyNavis reads for this tool.

Coordinate.tab/Clash_Results.panel/Status_Rules.pushbutton/bundle.yaml View on GitHub
title: Status\nRules
tooltip: Applies ordered rules (depth, trades, age, element properties) that set clash statuses, assignees and comments, with a count of what each rule would change before anything is written.
context: clash-results