packages = ["numpy", "matplotlib", "graphviz", "pandas"] use_case_svg = """ G Course Portal Course Portal Student 🧍 Post Discussion Post Discussion Student->Post Discussion Take Quiz Take Quiz Student->Take Quiz Student_name Student Instructor 🧍 Instructor->Post Discussion Create Quiz Create Quiz Instructor->Create Quiz Instructor_name Instructor """ context_svg = """ G User 🧍 User    Python Interpreter with PyML Python Interpreter with PyML Python Interpreter with PyML--User OS OS Python Interpreter with PyML--OS Graphviz Graphviz Python Interpreter with PyML--Graphviz """ sequence_diagram_svg = """ G Battle Planner 🧍 Battle Planner_name Battle Planner Battle Planner 14 Battle Planner_name->Battle Planner 14 main main main 14 main->main 14 Battle Planner 1 main 1 Battle Planner 1->main 1 run() Battle Planner 2 main 2 main 2->Battle Planner 2 request for side 1 name Battle Planner 3 main 3 Battle Planner 3->main 3 side 1 name Battle Planner 4 main 4 main 4->Battle Planner 4 request for side 2 name Battle Planner 5 main 5 Battle Planner 5->main 5 side 2 name Battle Planner 6 main 6 main 6->Battle Planner 6 request for side 1 starting level Battle Planner 7 main 7 Battle Planner 7->main 7 side 1 starting level Battle Planner 8 main 8 main 8->Battle Planner 8 request for side 1 lethality coefficient Battle Planner 9 main 9 Battle Planner 9->main 9 side 1 lethality coefficient Battle Planner 10 main 10 main 10->Battle Planner 10 request for side 2 starting level Battle Planner 11 main 11 Battle Planner 11->main 11 side 2 starting level Battle Planner 12 main 12 main 12->Battle Planner 12 request for side 2 lethality coefficient Battle Planner 13 main 13 Battle Planner 13->main 13 side 2 lethality coefficient main 14->Battle Planner 14 time history of troops and victor """ # Import and use JS functions and variables in Python from js import default_graph, svg_from_dot, return_svg_from_dot, console, dot_from_output from js import document from pyodide.ffi import create_proxy count = 0 def button_click(event): global count count += 1 dot_from_output(new_graph) #document.getElementById("msg").innerHTML = 'Button Clicked ' + str(count) #document.getElementById("test").innerHTML = 'new graph coming' def render_string(event): svg_from_dot("digraph G {Hello->World}") document.getElementById("msg").innerHTML = 'render from string' #document.getElementById("test").innerHTML = 'new graph coming' def setup(): # The page is ready, clear the "page loading" #document.getElementById("context").innerHTML = context_svg document.getElementById("use-case").innerHTML = use_case_svg document.getElementById("sequence").innerHTML = sequence_diagram_svg # Create a JsProxy for the callback function click_proxy = create_proxy(button_click) click_proxy2 = create_proxy(render_string) # Set the listener to the callback e = document.getElementById("Render") e.addEventListener("click", click_proxy) f = document.getElementById("Render_string") f.addEventListener("click", click_proxy2) setup() #console.log("Hello " + default_graph) #dot5(default_graph) """ PyML Version .20 Copyright (c) 2022 Ray Madachy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import csv import itertools import graphviz import textwrap import os import sys from os.path import exists import pandas as pd from copy import deepcopy online = True # text for SVG included files actor_svg = """ image/svg+xml """ def context_diagram(system, external_systems, filename=None, format='svg', engine='neato'): wrap_width = 20 def wrap(text): return textwrap.fill( text, width=wrap_width, break_long_words=False) node_attr = {'color': 'black', 'fontsize': '11', 'fontname': 'arial', 'shape': 'box'} # 'fontname': 'arial', c = graphviz.Graph('G', node_attr=node_attr, filename=filename, format=format, engine=engine) human_actor_keywords = ["user", "customer", "Patient", "Doctor", "operator", 'Citizen', 'Criminal', 'Enforcer', 'legislator'] for external_system in external_systems: if (type(external_system) is not tuple and external_system.casefold() in human_actor_keywords): c.node(wrap(external_system), label=f'''<🧍{'
' if online == False else '<br/>'}{external_system} >''', labelloc="b", shape='plain') c.edge(wrap(system), wrap(external_system), len="1.2") # len="1.2" elif (type(external_system) is tuple): c.node(wrap(external_system[0]), label=f'''<{external_system[1]}{'
' if online == False else '<br/>'}{external_system[0]} >''', labelloc="b", shape='plain') c.edge(wrap(system), wrap(external_system[0]), len="1.2") # len="1.2" else: c.edge(wrap(system), wrap(external_system), len="1.2") # len="1.2" if filename is not None: c.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" if online: svg = return_svg_from_dot(str(c), engine) print(svg) else: return c def context_diagram0(system, external_systems, filename=None, format='svg', engine='neato'): if exists("actor.svg") == False: a = open("actor.svg", "w") a.write(actor_svg) a.close() wrap_width = 20 def wrap(text): return textwrap.fill( text, width=wrap_width, break_long_words=False) node_attr = {'color': 'black', 'fontsize': '11', 'fontname': 'arial', 'shape': 'box'} # 'fontname': 'arial', c = graphviz.Graph('G', node_attr=node_attr, filename=filename, format=format, engine=engine) human_actor_keywords = ["User", "user", "Customer", "customer", "Operator", "Patient", "Doctor", "operator"] for external_system in external_systems: if (external_system in human_actor_keywords): c.node(wrap(external_system), label=external_system, labelloc="b", shape='plain') c.edge(wrap(system), wrap(external_system), len="1.2") # len="1.2" if filename is not None: c.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #return c if online: svg = return_svg_from_dot(str(c), engine) print(svg) else: return c def activity_diagram(element_dependencies, filename=None, format='svg'): node_attr = {'color': 'black', 'fontsize': '11', 'style': "rounded", 'shape': 'box'} # 'fontname': 'arial', edge_attr = {'arrowsize': '.5', 'fontname': 'arial', 'fontsize': '11', } activity = graphviz.Digraph('G', filename=filename, node_attr=node_attr, edge_attr=edge_attr, engine="dot", format=format) activity.attr(rankdir='LR',) activity.edges(element_dependencies) if filename != None: activity.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #os.remove(filename) also doesn't work on windows "permission denied" return activity def use_case_diagram(system_name, actors, use_cases, interactions, use_case_relationships, filename=None, format='svg'): wrap_width = 15 def wrap(text): return textwrap.fill( text, width=wrap_width, break_long_words=False) node_attr = {'color': 'black', 'fontname': 'arial', 'fontsize': '11', 'width': '0.'} edge_attr = {'arrowsize': '.5', 'fontname': 'arial', 'fontsize': '11', } u = graphviz.Digraph('G', filename=filename, node_attr=node_attr, edge_attr=edge_attr, engine="neato", format=format) system_height = len(use_cases) + .5 u.node(system_name, label=f'''<{system_name}>''', pos=f'0,{(system_height+.5)/2}!', shape="box", height=f"{system_height}", width="2", labelloc="t") if exists("actor.svg") == False: a = open("actor.svg", "w") a.write(actor_svg) a.close() column = -2 for number, actor in enumerate(actors): division = system_height / (len(actors) + 1) #print (division) u.node(wrap(actor), label=f'''<🧍<br/>{actor}>''', pos=f'{column}, {system_height - division*(number+1) + .25}!', width='.1', shape='none', labelloc='b') column = 0 for number, use_case in enumerate(use_cases): u.node( wrap(use_case), pos=f'{column}, {len(use_cases)-number}!', width='1.25', height=".7") for edge in (interactions): u.edge(wrap(edge[0]), wrap(edge[1])) # lhead='clusterx' not used if filename != None: u.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #os.remove(filename) also doesn't work on windows "permission denied" #return u if online: svg = return_svg_from_dot(str(u), "neato") print(svg) else: return u def sequence_diagram(system_name, actors, objects, actions, filename=None, format='svg'): verbose = False wrap_width = 40 def wrap(text): return textwrap.fill( text, width=wrap_width, break_long_words=False) graph_attr = {'rankdir': 'LR', 'color': 'black'} node_attr = {'shape': 'point', 'color': 'none', 'fontname': 'arial', 'fontsize': '11', 'width': '0.'} edge_attr = {'arrowsize': '.5', 'fontname': 'arial', 'fontsize': '11', } g = graphviz.Digraph('G', filename=filename, graph_attr=graph_attr, node_attr=node_attr, edge_attr=edge_attr, engine="neato", format=format) if exists("actor.svg") == False: a = open("actor.svg", "w") a.write(actor_svg) a.close() # spread actors/objects apart on x-axis, differentiate actors from objects for images used object_spacing = 3 object_x_coords = {} # dictionary holds x coordinates for action nodes and edges for actor_object_number, actor_object in enumerate(actors+objects, start=1): x = int(actor_object_number-1)*object_spacing if actor_object in actors: g.node(actor_object, pos=f"{x},.6!", shape="none", label=f'''<🧍>''', labelloc='b') g.node(actor_object+'_name', pos=f"{x},.3!", shape="none", label=f'''<{actor_object}>''', labelloc='b') if actor_object in objects: g.node(actor_object, pos=f"{x},.2!", shape="box", color='black') object_x_coords[actor_object] = x # create nodes for each action source and destination to draw arrows to/from # must capture last action # to draw lifelines to object_last_actions = {} # dictionary holds last action for objects and actors for action_number, action in enumerate(actions, start=1): for actor_object, x in object_x_coords.items(): y = int(action_number)*.4 if action[0] == actor_object: # arrow node names: actor/object action # g.node(f'{action[0]} {int(action_number)}', pos=f"{x},-{y}!") if action[1] == actor_object: g.node(f'{action[1]} {int(action_number)}', pos=f"{x},-{y}!") if actor_object == action[0] or actor_object == action[1]: object_last_actions[actor_object] = action_number if verbose: print( actor_object, f'{action[0]}->{action[1]} # {int(action_number)}', f"pos {x},-{y}!") # edges for each arrow going down for action_number, action in enumerate(actions, start=1): source_x, target_x = object_x_coords[action[0] ], object_x_coords[action[1]] g.edge(f'{action[0]} {int(action_number)}', f'{action[1]} {int(action_number)}', headlabel=f'{wrap(action[2]) if len(action[2]) > wrap_width else action[2]}', labeldistance=str( object_spacing * 3.6), labelangle=f'{-5 if source_x < target_x else 5}') # left to right -10 with x separation = 1 wrap = 25 .5 y, right to left 15 with x separation = 1 wrap = 25 .5y # lifelines for actor_object in actors+objects: if verbose: print(actor_object, f'{actor_object} {len(actions)}') if actor_object in actors: g.edge(actor_object+'_name', f'{actor_object} {object_last_actions[actor_object]}', style='dashed', arrowhead='none') if actor_object in objects: g.edge(actor_object, f'{actor_object} {object_last_actions[actor_object]}', style='dashed', arrowhead='none') if verbose: print(object_x_coords) if verbose: print(object_last_actions) if filename != None: g.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #os.remove(filename) also doesn't work on windows "permission denied" #return g if online: svg = return_svg_from_dot(str(g), "neato") print(svg) else: return g def design_structure_matrix(elements, element_dependencies, filename=None, format='svg'): cell_width = 20 # pixels dependency_elements = [dependency[0:2] for dependency in element_dependencies] node_string = '' for row, element in enumerate((['']+elements)): node_string += '' if row == 0: for column, element in enumerate((['']+elements)): node_string += f'' else: for column, element in enumerate((['']+elements)): if column > 0: if row == column: node_string += '' else: cell_dependency_tuple = ( elements[column-1], elements[row-1]) # e.g. ('B', 'A') if cell_dependency_tuple in dependency_elements: if len(element_dependencies[dependency_elements.index(cell_dependency_tuple)]) == 2: # default 'X' label node_string += f'' if len(element_dependencies[dependency_elements.index(cell_dependency_tuple)]) != 2: node_string += f'' # custom label # print('') else: node_string += f'' else: node_string += f'' node_string += '' node_string += '
' + \ element + '' + 'X' + '' + element_dependencies[dependency_elements.index( cell_dependency_tuple)][2] + '', row, column, '' + '' + '' + \ elements[row-1] + '
' dsm = graphviz.Digraph('dsm', filename=filename, format=format, node_attr={'shape': 'plaintext'}) dsm.node('dsm', f'''<{node_string}>''') #dsm.view() if filename != None: dsm.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #os.remove(filename) also doesn't work on windows "permission denied" return dsm def wbs_diagram(decompositions, filename=None, format='svg', rankdir='TB'): node_attr = {'color': 'black', 'fontsize': '11', 'shape': 'box'} # 'fontname': 'arial', edge_attr = {'arrowsize': '.5', 'fontname': 'arial', 'fontsize': '11', } activity = graphviz.Digraph('G', filename=filename, node_attr=node_attr, edge_attr=edge_attr, engine="dot", format='svg') activity.attr(rankdir=rankdir, splines='ortho') # rankdir='LR' activity.edges(decompositions) if filename != None: activity.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #os.remove(filename) also doesn't work on windows "permission denied" return activity def tree(element_dependencies, filename=None, format='svg'): node_attr = {'color': 'black', 'fontsize': '11', 'shape': 'box'} # 'fontname': 'arial', edge_attr = {'arrowsize': '.5', 'fontname': 'arial', 'fontsize': '11', } activity = graphviz.Digraph('G', filename=filename, node_attr=node_attr, edge_attr=edge_attr, engine="dot", format='svg') activity.attr(rankdir='TB', splines='ortho') # rankdir='LR' activity.edges(element_dependencies) if filename != None: activity.render() # render and save file, clean up temporary dot source file (no extension) after successful rendering with (cleanup=True) doesn't work on windows "permission denied" #os.remove(filename) also doesn't work on windows "permission denied" return activity

SysML Diagram Scratchpad

Enter Python statements in code cells and click the green run button or hit shift-enter to create diagrams. Additional diagrams can be created in the blank code cells at the end that are automatically generated after runs.

Use Case Diagram

# system model system_name = "Course Portal" actors = ['Student', 'Instructor'] use_cases = ['Post Discussion', 'Take Quiz', 'Create Quiz'] interactions = [('Student', 'Post Discussion'), ('Instructor', 'Post Discussion'), ('Student', 'Take Quiz'), ('Instructor', 'Create Quiz')] use_case_relationships = [] # create diagram use_case_diagram(system_name, actors, use_cases, interactions, use_case_relationships)
Please wait to load the interpreter ...

Sequence Diagram

# system model system_name = "Battle Simulator" actors = ['Battle Planner'] objects = ['main'] actions = [ ('Battle Planner', 'main', 'run()'), ('main', 'Battle Planner', 'request for side 1 name'), ('Battle Planner', 'main', 'side 1 name'), ('main', 'Battle Planner', 'request for side 2 name'), ('Battle Planner', 'main', 'side 2 name'), ('main', 'Battle Planner', 'request for side 1 starting level'), ('Battle Planner', 'main', 'side 1 starting level'), ('main', 'Battle Planner', 'request for side 1 lethality coefficient'), ('Battle Planner', 'main', 'side 1 lethality coefficient'), ('main', 'Battle Planner', 'request for side 2 starting level'), ('Battle Planner', 'main', 'side 2 starting level'), ('main', 'Battle Planner', 'request for side 2 lethality coefficient'), ('Battle Planner', 'main', 'side 2 lethality coefficient'), ('main', 'Battle Planner', 'time history of troops and victor'), ] # create diagram sequence_diagram(system_name, actors, objects, actions)