Coordinate > Clash Reports

Snapshot

Records the current results of the chosen clash tests as a snapshot that Clash Delta and Clash Trends compare with. Shift+Click to snapshot automatically on save, or to delete old snapshots.

Tab and panelCoordinate > Clash Reports
KindButton
Live whenThe model has clash tests

Status

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

The public ask

No public source is on record for this tool.

Installs with

Snapshot sits in a stack, so installing it also installs:

Events it listens to

It needs these Navisworks event hooks, so installing it also copies the shared hooks folder:

  • doc-saved

Files

Paths are relative to the Playground.extension folder.

  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/bundle.yaml
  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/config.py
  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/icon.dark.png
  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/icon.png
  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/icon.small.dark.png
  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/icon.small.png
  • Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/script.py

Install

  1. Only this tool. In Navisworks, click pyNavis > Playground, search for Snapshot, 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. Snapshot is on the Coordinate tab, Clash Reports 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_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/script.py View on GitHub
# -*- coding: utf-8 -*-
"""Records the current results of the chosen clash tests as a snapshot for Clash Delta and Clash Trends."""
from pynavis import forms, script, toast

from namo import clashrep_forms as cf
from namo import clashrep_history as hist
from namo import clashreport as cr
from namo import progress

TITLE = 'Clash Snapshot'
DEFAULTS = {'tests': []}


def run():
    values = script.get_config(DEFAULTS)
    doc = cr.active_doc()
    problem = cf.start(doc)
    if problem:
        toast.show(*problem)
        return
    if not cr.file_info(doc)[0]:
        toast.info('Save the model first', 'Clash snapshots are kept per model file.')
        return
    if cf.runner_active():
        tests, _missing = cr.resolve_tests(values['tests'], doc)
        label = ''
    else:
        tests = cf.pick_tests(TITLE, 'Tick the tests to record.', values['tests'], doc)
        if tests is None:
            return
        if not tests:
            toast.info('No clash results yet', 'Run your clash tests in Clash Detective first.')
            return
        label = forms.ask_string('Label for this snapshot (optional, for example "Before the 12 Oct meeting"):',
                                 '', TITLE)
        if label is None:
            return
        values['tests'] = cf.test_paths(tests)
        script.save_config(values)
    if not tests:
        toast.info('No clash results yet', 'Run your clash tests in Clash Detective first.')
        return
    try:
        outcome = progress.run('Recording clashes', lambda report: cr.record_snapshot(tests, label, doc, report))
    except RuntimeError as error:
        toast.error('Could not read the clashes', str(error))
        return
    except (IOError, OSError) as error:
        toast.error('Could not save the snapshot', str(error))
        return
    if outcome is progress.CANCELLED:
        return
    entry, where = outcome
    toast.success(*hist.snapshot_message(entry, where))


run()

config.py

The Shift+Click action.

Coordinate.tab/Clash_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/config.py View on GitHub
# -*- coding: utf-8 -*-
"""Options for clash snapshots: take one automatically on save, or delete old ones."""
from pynavis import forms, settings, toast

from namo import clashrep_forms as cf
from namo import clashrep_history as hist
from namo import clashreport as cr
from namo import config as namo_config
from namo import hk_clashhistory as auto
from namo import ui

TITLE = 'Clash snapshots'
AUTOMATIC = 'Snapshot automatically when the model is saved'
REVIEW = 'Review and delete snapshots of this model'


def automatic():
    values = auto.load_config()
    names = []
    try:
        from namo import clashdata
        names = clashdata.unique_names([t.DisplayName for t in cr.tests_with_results()])
    except Exception:
        names = []
    fields = cf.snapshot_settings_fields(values, names, namo_config.get('data_mode'))
    answers = ui.form(TITLE, fields, ok='Save', width=540)
    if answers is None:
        return
    values['auto_on_save'] = bool(answers['auto_on_save'])
    if answers.get('min_hours') is not None:
        values['min_hours'] = float(answers['min_hours'])
    if answers.get('max_results') is not None:
        values['max_results'] = int(answers['max_results'])
    if 'tests' in answers:
        values['tests'] = list(answers['tests'] or [])
    try:
        settings.save(auto.TOOL, values)
    except (IOError, OSError) as error:
        toast.error('Could not save the snapshot options', str(error))
        return
    if values['auto_on_save']:
        toast.success('Automatic clash snapshots are on',
                      'At most one every %s hours, when the clash counts changed, up to %s results.'
                      % ('%g' % values['min_hours'], '{:,}'.format(values['max_results'])))
    else:
        toast.success('Automatic clash snapshots are off', 'Use Snapshot to record one by hand.')


def review():
    from namo import clashdata
    doc_path = cr.file_info()[0]
    if not doc_path:
        toast.info('Save the model first', 'Clash snapshots are kept per model file.')
        return
    entries = sorted(clashdata.list_history(doc_path), key=lambda e: (e.get('time') or '', e.get('id') or ''),
                     reverse=True)
    if not entries:
        toast.info('No clash snapshots yet', 'Use Snapshot to record one.')
        return
    rows = []
    for entry in entries:
        when = hist.entry_time(entry)
        totals = hist.entry_totals(entry)
        rows.append({'value': entry['id'], 'cells': [when.strftime('%Y-%m-%d %H:%M') if when else '',
                                                     entry.get('label') or '', totals['tests'], totals['total'],
                                                     totals['open']]})
    picked = ui.choose(TITLE, rows, ['Taken', 'Label', 'Tests', 'Clashes', 'Open'], multiselect=True,
                       prompt='Tick the snapshots to delete. Clash Delta and Clash Trends will no longer see them.',
                       ok='Delete', width=720)
    if picked is None:
        return
    if not picked:
        toast.info('No snapshots ticked', 'Nothing was deleted.')
        return
    if not forms.confirm('Delete %s? This cannot be undone.' % cr.plural(len(picked), 'snapshot'), TITLE):
        return
    deleted = 0
    try:
        for history_id in picked:
            if clashdata.delete_history(doc_path, history_id):
                deleted += 1
    except (IOError, OSError) as error:
        toast.error('Could not delete every snapshot', str(error))
        return
    toast.success('Deleted %s' % cr.plural(deleted, 'snapshot'),
                  '%s left for this model.' % '{:,}'.format(len(entries) - deleted))


if cf.runner_active():
    toast.warning('These options need a window, so they cannot run under pynavis run',
                  'Shift+Click Snapshot on the ribbon instead.')
else:
    choice = forms.ask_options('What do you want to change?', [AUTOMATIC, REVIEW], TITLE)
    if choice == AUTOMATIC:
        automatic()
    elif choice == REVIEW:
        review()

Library modules this tool uses

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

  • namo.clashdataOne reader for every clash tool: snapshots, groups, run-to-run comparison, history, images, and the per-result edits that are safe at scale.
  • namo.clashrep_formsThe questions the Clash Reports buttons ask, in one place.
  • namo.clashrep_historyClash history maths for Clash Delta, Clash Trends and the automatic snapshot: which snapshot to compare with, what changed since, and the series over time. Pure.
  • namo.clashreportClash reports: the row shaping, summaries and tallies every Clash Reports tool shares.
  • namo.configPlayground-wide settings: one JSON file shared by every Playground tool.
  • namo.hk_clashhistoryAutomatic clash snapshot on save (registered for 'doc-saved' in namo.hooks).
  • 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_Reports.panel/History.stack/Snapshot_Clashes.pushbutton/bundle.yaml View on GitHub
title: Snapshot
tooltip: Records the current results of the chosen clash tests as a snapshot that Clash Delta and Clash Trends compare with. Shift+Click to snapshot automatically on save, or to delete old snapshots.
context: clash-tests