Coordinate > Clash Results
Clash Assigner
Assigns clash groups and ungrouped clashes to the trade that moves, keeps the ball-in-court history in the model, and shows who holds what and for how long. Shift+Click for settings.
Status
Untested Never executed inside Navisworks. Passed the headless checks only.
Related asks to confirm
No quote is on record yet. These threads look related, but a person still has to open and read them.
- https://forums.autodesk.com/t5/navisworks-forum/design-request-for-clash-detective/td-p/13166238
- https://forums.autodesk.com/t5/navisworks-forum/clash-detective-redesign-ideas/td-p/13660891
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/Clash_Assigner.pushbutton/bundle.yamlCoordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/config.pyCoordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/icon.dark.pngCoordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/icon.pngCoordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/icon.small.dark.pngCoordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/icon.small.pngCoordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/script.py
Install
- Only this tool. In Navisworks, click pyNavis > Playground, search for Clash Assigner, tick it and click Install.
- The whole extension. Download
Playground.extension.zip, unzip it, put thePlayground.extensionfolder in%APPDATA%\pyNavis\extensions\, and click pyNavis > Reload. Clash Assigner 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.
ReportSource
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/Clash_Assigner.pushbutton/script.py View on GitHub"""Assigns clash groups and ungrouped clashes to the trade that moves, and tracks ball in court."""
import datetime
from pynavis import forms, script, toast
from namo import clashassign, config, progress, ui
from namo import clashset_common as common
DEFAULTS = {'tests': [], 'priority': list(clashassign.DEFAULT_PRIORITY), 'names': '',
'reassign': False, 'mode': 'auto'}
TITLE = 'Clash Assigner'
MODES = [('Auto: the lower-priority trade of each clash moves', 'auto'),
('Manual: pick a trade or person for chosen items', 'manual'),
('Show the ball-in-court dashboard', 'dashboard')]
SOMEONE = '\x00someone'
def pick_assignee(values, units):
"""A trade or person for manual mode, or None."""
names = clashassign.names_map(values['names'])
choices = []
for trade in config.get('trades') or clashassign.DEFAULT_PRIORITY:
person = names.get(trade.lower())
choices.append((trade if not person else person, trade))
for unit in units:
current = unit.get('assigned')
if current and current not in [c[0] for c in choices]:
choices.append((current, 'already used'))
rows = [{'value': name, 'cells': [name, note if note != name else '']} for name, note in choices]
rows.append({'value': SOMEONE, 'cells': ['Someone else...', 'type a name']})
picked = ui.choose(TITLE, rows, ['Assign to', 'Trade'], multiselect=False, ok='Assign',
prompt='Who has the ball for these items?', width=520, height=480)
if picked is None:
return None
if picked[0] != SOMEONE:
return picked[0]
typed = forms.ask_string('Assign to:', '', TITLE)
if typed is None or not typed.strip():
return None
return typed.strip()
def write(pairs, mode, doc):
changes = common.write_progress('Assigning clashes', lambda report_to: clashassign.assign(pairs, doc))
try:
note = clashassign.save_history(changes, mode, doc)
except (IOError, OSError) as error:
return 'The ball-in-court history could not be saved (%s).' % error
if note:
return note
return 'Ball-in-court history saved in the model; save the file to keep it.'
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 to assign.', 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
labels = [label for label, _value in MODES]
picked = forms.ask_options('How do you want to assign?', labels, TITLE)
if picked is None:
return
mode = dict(MODES)[picked]
values['mode'] = mode
values['tests'] = [t.DisplayName for t in tests]
script.save_config(values)
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
units = []
for entry in entries:
units.extend(clashassign.units_of(entry))
title = 'Ball in court: %s' % (pdoc.get_title(doc) or 'this model')
today = datetime.datetime.now()
if mode == 'dashboard':
board = clashassign.show_dashboard(units, clashassign.load_owners(doc), today, title)
toast.info('%s open across %s' % (common.plural(board['open'], 'item'),
common.plural(len(board['by_assignee']), 'assignee')))
return
if mode == 'auto':
planned = clashassign.plan_auto(units, clashassign.priority_list(values['priority']),
clashassign.names_map(values['names']), values['reassign'])
if not planned['assign']:
reason = 'Every open item already has an assignee.' if planned['kept'] else \
'No trade could be decided; add trade rules in Playground settings.'
toast.info('Nothing to assign', reason)
return
if not forms.confirm(clashassign.confirm_text(planned), TITLE):
return
pairs = planned['assign']
else:
rows = [{'value': i, 'cells': clashassign.unit_cells(unit)} for i, unit in enumerate(units)
if unit['open']]
if not rows:
toast.info('Nothing to assign', 'Every item in these tests is resolved or approved.')
return
chosen = ui.choose(TITLE, rows, ['Test', 'Item', 'Kind', 'Clashes', 'Status', 'Assigned to',
'Trades'], multiselect=True, ok='Next', width=980,
prompt='Tick the groups and clashes to assign.')
if chosen is None:
return
who = pick_assignee(values, units)
if who is None:
return
pairs = [(units[i], who) for i in chosen]
try:
note = write(pairs, mode, doc)
except ValueError as error:
toast.error('Could not assign the clashes', str(error))
return
clashassign.show_dashboard(units, clashassign.load_owners(doc), today, title)
tally = clashassign.tally_names(pairs)
detail = '%s. %s' % (', '.join('%s %s' % (name, '{:,}'.format(n)) for name, n in tally[:4]), note)
toast.success('Assigned %s' % common.plural(len(pairs), 'item'), detail)
if globals().get('__runner__'):
toast.warning(*common.RUNNER_MESSAGE)
else:
run()
config.py
The Shift+Click action.
Coordinate.tab/Clash_Results.panel/Clash_Assigner.pushbutton/config.py View on GitHub"""Options for Clash Assigner: who moves first, who stands for each trade, and reassignment."""
from pynavis import script, toast
from namo import clashassign, ui
DEFAULTS = {'tests': [], 'priority': list(clashassign.DEFAULT_PRIORITY), 'names': '',
'reassign': False, 'mode': 'auto'}
values = script.get_config(DEFAULTS)
fields = [
{'kind': 'textarea', 'key': 'priority', 'label': 'Who moves (highest priority first)',
'default': '\n'.join(clashassign.priority_list(values['priority'])), 'rows': 7,
'hint': 'One trade per line. In a clash, the trade lower in this list moves; a trade not '
'listed moves before every listed one.'},
{'kind': 'textarea', 'key': 'names', 'label': 'Assign trades to people', 'rows': 4,
'default': values['names'] if isinstance(values['names'], str) else '',
'hint': 'Optional, one per line: Mechanical = Jane Smith. Without a line the trade name is used.'},
{'kind': 'check', 'key': 'reassign', 'label': 'Auto mode also reassigns items that already have an assignee',
'default': values['reassign']},
]
def validate(answers):
if not clashassign.priority_list(answers['priority']):
return {'priority': 'List at least one trade.'}
return None
answers = ui.form('Clash Assigner options', fields, ok='Save', width=520, validate=validate)
if answers is not None:
values['priority'] = clashassign.priority_list(answers['priority'])
values['names'] = answers['names']
values['reassign'] = answers['reassign']
script.save_config(values)
toast.success('Saved Clash Assigner options', 'Who moves: %s.' % ' > '.join(values['priority']))
Library modules this tool uses
From the shared namo library, which installs with every tool. Each name opens the module on GitHub.
namo.clashassignClash Assigner: ball in court for clash groups and ungrouped clashes.namo.clashset_commonSmall things every Clash Setup and Clash Results tool shares.namo.configPlayground-wide settings: one JSON file shared by every Playground tool.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/Clash_Assigner.pushbutton/bundle.yaml View on GitHubtitle: Clash\nAssigner
tooltip: Assigns clash groups and ungrouped clashes to the trade that moves, keeps the ball-in-court history in the model, and shows who holds what and for how long. Shift+Click for settings.
context: clash-results