Provided documentation, an example and made some changes in the interface

This commit is contained in:
Alkis Plaskovitis 2016-06-07 17:03:43 +03:00
parent ffba2e2f55
commit c9a956a0ac
4 changed files with 172 additions and 76 deletions

BIN
doc/recruitment.pdf Normal file

Binary file not shown.

23
doc/recruitment.txt Normal file
View File

@ -0,0 +1,23 @@
initial()
compound_state(Application)
state(A, Non Hire)
styles(node, [fontname=Helvetica, fontcolor=red])
state(B, New Hire,[entry/Candidate Hire, do/Add Employee])
compound_end()
state(C, Employee, [entry/Intake New Employee])
state(D, Retiree, [do/Employee Retirement])
state(E, Former Employee, [do/Terminate, exit/Archive])
final()
transition(initial, B, Accepts offer)
transition(initial, A, Rejects offer)
transition(B, C, Begins work)
transition(C, C, Boring Reality)
transition(C, D, Employee Retires, [color=blue])
transition(C, E, Employee Terminates, [color=red])
transition(D, final)

View File

@ -0,0 +1,90 @@
stategraph
========
Stategraph is a python script which allows the declarative specification and drawing of UML state diagrams.
How to use it:
--------
1) Use the commands stated below to declare a state diagram and save it in a text file.
2) Change your directory to the place where both the script and the text file are located
3) Run the script as stated below
4) At the test-output directory you will find the diagram in the format you provided.
State diagram commands
--------
state(id, name, events[])
-description:Creates a state
-arguments: id: Unique identifier for the state inside the source (mandatory).
name: Caption to be displayed (defaults to the state id).
events[]: Events of the state (optional).
transition(source, target, label, styling[])
-description:Creates a transition
-arguments: source: Source state identifier (mandatory).
target: Target state identifier (mandatory).
label: Caption to be displayed near the transition (optional).
styling[]: Array of styling attributes to be applied (optional).
initial()
-description:The initial state. To be referred as initial when referenced in a transition.
final()
-description:The final state. To be referred as final when referenced in a transition.
compound_state(name)
-description: Creates a subgraph which includes states and transitions
-arguments: name (optional)
compound_end()
-description: Ends the compound state
styles(element, styling[])
-description: Applies styling to the element specified
-arguments: element (mandatory), styling[] (mandatory)
note(id, note)
-description: Applies a note to the state specified
-arguments: node_id (mandatory), note (mandatory)
Running the script
------------
Run the script as stategraph.py [-o output_file] [-v] [input file]
1) If an output file is not specified a pdf is generated using
the name of the input file.
2) The -v parameter will open the diagram for you to view
example: Running the script as stategraph.py -recruitment.txt -v -o recruitment.png
will create a png file called recruitment in the test-output directory as well as
open the file right away.
Example
-------
A full example containing both the declaration and the resulting diagram can be found
under the stategraph example directory.
The example is called recruitment.txt and the generated pdf is called
recruitment.pdf
Support
-------
If you are having issues, please let us know.
Contact at: alkisplas@gmail.com

View File

@ -1,53 +1,13 @@
"""
description:
This script allows the declarative specification of uml state diagrams.
1) use the commands stated below to form a state diagram and save it in a text file
2) change your directory to the place where both the script and the text file are located
3) provide the arguments as stated below
4) at the test-output directory you will find the diagram both in dot format and in pdf.
cmd args:
argv[1]: input file name. (Mandatory)
argv[2]: output file name. (Optional). Defaults to the input file's name
argv[3]: view. (Optional). If true the generated pdf opens immediately. Defaults to False
commands:
state: creates a state
arguments: id (mandatory), name (mandatory), events[](optional), style[](optional
transition: creates a transition
arguments: source (mandatory), target (mandatory), label (optional), styling[] (optional)
initial: the initial node. To be referred as initial
final: the final node. To be referred as final
compound_state: creates a subgraph which includes states and transitions
arguments: name (optional)
compound_end: ends the subgraph
styles: applies styling to the element specified
arguments: element (mandatory), styling[] (mandatory)
note: applies a note to the state specified
arguments: node_id (mandatory), note (mandatory)
"""
#!/usr/bin/env python
from graphviz import Digraph
from pyparsing import *
import sys
import argparse
import os
# -----Parser-----
LP, RP, LB, RB = map(Suppress, "()[]") # those will be skipped when iterating
tokens = "+" + "'" + "=" + "/" + "_"
@ -65,10 +25,11 @@ rule = OneOrMore(Group(sentence)) # rule: sentence, sentence, ...
def add_state(graph, args_list):
"""
Creates the state based on the arguments given
Args of args_list:
""" Creates the state based on the arguments given
:param args_list:
args_list[0]: Unique identifier for the state inside the source(MANDATORY).
args_list[1]: Caption to be displayed (defaults to the state id).
args_list[2]: Events of the state(OPTIONAL).
@ -88,13 +49,14 @@ def add_state(graph, args_list):
def add_transition(graph, args_list):
"""
Creates the transition based on the arguments given
Args of args_list:
args_list[0]: Start state identifier(MANDATORY).
args_list[1]: End state identifier(MANDATORY).
args_list[2]: Caption to be displayed near the edge(OPTIONAL).
""" Creates the transition based on the arguments given
:param args_list :
args_list[0]: Source state identifier(MANDATORY).
args_list[1]: Target state identifier(MANDATORY).
args_list[2]: Caption to be displayed near the transition(OPTIONAL).
args_list[3]: Any styling to be applied(OPTIONAL).
"""
@ -159,8 +121,8 @@ def compound_state(args_list):
def list_to_dict(alist):
"""
takes the list that contains the styling arguments to be applied
""" Takes the list that contains the styling arguments to be applied
and turns it to a dictionary which graphviz understands
"""
@ -172,9 +134,16 @@ def list_to_dict(alist):
return changed_dict
# Parses every line of the script and calls an add method depending on that line's action command
def parse_and_draw(graph, script):
""" Parses every line of the script and calls
an add method depending on that line's action command
:param graph: current graph being used
:param script: txt file being parsed
"""
entry = graph
try:
@ -214,31 +183,45 @@ def parse_and_draw(graph, script):
entry.subgraph(graph)
graph = entry
# checks if user wants to view the graph right away
if len(sys.argv) == 4:
accepted_values = ['true', 'TRUE', 'True']
if sys.argv[3] in accepted_values:
view = True
else:
raise Exception("no suitable value for view")
else:
view = False
if sys.argv[2]: # if the user has specified an output file
graph.render('test-output/'+str(sys.argv[2])+'.gv', view=view)
'test-output/'+str(sys.argv[2])+'.pdf'
else: # else use the input file's name
graph.render('test-output/'+str(sys.argv[1])+'.gv', view=view)
'test-output/'+str(sys.argv[1])+'.pdf'
return graph
except Exception as e:
print "could not draw diagram because: " + str(e)
# Reads data from the input file
def render_graph(cmd_args, graph):
with open(str(sys.argv[1])) as f:
""" Renders the graph and generates the output files as specified by the user
If an output file is not specified. A pdf is generated using
the name of the input file.
The extension dot is erased because the format function can't handle it.
:param cmd_args: contains the input file, the viewing preference and the output file
:param graph: graph to render
"""
if cmd_args.output is not None:
filename, file_extension = os.path.splitext(cmd_args.output)
else:
filename = os.path.splitext(cmd_args.filename)[0]
file_extension = 'pdf'
graph.format = file_extension.replace('.', '')
graph.render('test-output/' + filename, view=cmd_args.view)
# parse cmd arguments
parser = argparse.ArgumentParser()
parser.add_argument("filename")
parser.add_argument("-v", "--view", action="store_true", default=False)
parser.add_argument("-o", "--output", type=str)
args = parser.parse_args()
with open(args.filename) as f:
content = f.read().splitlines()
script = "\n".join(content)
@ -246,4 +229,4 @@ script = "\n".join(content)
g1 = Digraph('g1', node_attr={'shape': 'box', 'style': 'rounded'})
g1.body.extend(['rankdir=LR']) # graph's direction left-->right
parse_and_draw(g1, script)
render_graph(args, parse_and_draw(g1, script))