stoichiograph

Spell words with elemental symbols from the periodic table.
git clone git://git.amin.space/stoichiograph.git
Log | Files | Refs | LICENSE

commit 1af16660cb3e382227f3abcdec6796a0b9fe8bf4
parent f15b53fa13d5e8fad622308b9ec8dd25cbd268fd
Author: amin <dev@aminmesbah.com>
Date:   Sat, 25 Feb 2017 23:56:26 +0000

Fix graph export. Add Graph.nodes().

There is now a command line option, `--export-graph`, which prints to
stdout dot language code which can be used by Graphviz to generate a
graph diagram.

FossilOrigin-Name: 1aaa532395fdb540cb3664388a7d3b9557d81fc0c7f15deef954dc5c3116a8fe
Diffstat:
MREADME.rst | 9+++++----
Mspeller.py | 49+++++++++++++++++++++++++++++++++++++++++--------
Mstoichiograph.py | 10++++++++++
Mtests.py | 46+++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 101 insertions(+), 13 deletions(-)

diff --git a/README.rst b/README.rst @@ -1,5 +1,5 @@ -Stoichiograph -============= +Stoichiograph - The Elemental Speller +===================================== Spell words with elemental symbols from the periodic table ("He", "Cu", etc). @@ -15,7 +15,7 @@ Usage .. code-block:: usage: stoichiograph [-h] [-b BATCH_FILE] [-c] [--debug] [--list-elements] - [-o OUTPUT_FILE] [-s] [-t] [-v] [-V] + [--export-graph] [-o OUTPUT_FILE] [-s] [-t] [-v] [-V] [words [words ...]] Spell words with elemental symbols from the periodic table. @@ -30,7 +30,8 @@ Usage -c, --clobber overwrite output file if it exists --debug print debug log --list-elements print list of elemental symbols and exit - -o OUTPUT_FILE, --output_file OUTPUT_FILE + --export-graph export graph of first word as dot code + -o OUTPUT_FILE, --output-file OUTPUT_FILE path of output json file -s, --sort sort words by length -t, --tuples display spellings as tuples diff --git a/speller.py b/speller.py @@ -1,4 +1,5 @@ from collections import defaultdict, namedtuple +from io import StringIO import logging # TODO(amin): Convert symbol tuple to element name or atomic number tuple @@ -73,11 +74,30 @@ class Graph(): """Add a parent-child relatonship to the graph. None is ok as a key, but not a value. """ + log.debug('add_edge({}, {})'.format(parent, child)) if parent is not None: self._parents_of[child].add(parent) if child is not None: self._children_of[parent].add(child) + def nodes(self, connected_only=True): + """Return a list of all nodes.""" + if connected_only: + return set( + node for node in + set(self._children_of.keys()) | set(self._parents_of.keys()) + if node is not None + ) + else: + return set( + node for node in + set(self._children_of.keys()) + | set(self._parents_of.keys()) + | set(v for s in self._children_of.values() for v in s) + | set(v for s in self._parents_of.values() for v in s) + if node is not None + ) + def edges(self): """Return a list of all parent-child relationships.""" return [ @@ -86,15 +106,28 @@ class Graph(): for child in self._children_of[parent] ] - def export(self): - """Print a string to stdout that can be interpreted by Graphviz. + # TODO(amin): Eliminate dead-end nodes from exported graph. + def export(self, connected_only=True): + """Print a string to stdout that can be piped to Graphviz to + generate a graph diagram. """ - print('digraph G {') - for (parent, child) in self.edges(): - a = None if parent is None else parent.value - b = None if child is None else child.value - print('\t{} -> {}'.format(a, b)) - print('}') + export = StringIO() + export.write('digraph G {\n') + export.write('\tgraph [rankdir=LR];\n') + export.write('\tnode [width=0.75 shape=circle];\n') + + edges = [ + (p, c) + for p, c in self.edges() + if p is not None and c is not None + ] + for parent, child in sorted(edges): + export.write('\t"{}" -> "{}";\n'.format(parent, child)) + + for node in sorted(self.nodes(connected_only=connected_only)): + export.write('\t"{}" [label="{}"];\n'.format(node, node.value.capitalize())) + export.write('}') + return export.getvalue() # A single node of the graph. diff --git a/stoichiograph.py b/stoichiograph.py @@ -42,6 +42,10 @@ def get_args(): help='print list of elemental symbols and exit' ) parser.add_argument( + '--export-graph', action='store_true', + help='export graph of first word as dot code' + ) + parser.add_argument( '-o', '--output-file', help='path of output json file' ) @@ -112,6 +116,12 @@ def main(): if SORT_WORDS: words.sort(key=len, reverse=True) + if args.export_graph and words: + g = speller.Graph() + speller.build_spelling_graph(words[0], g) + print(g.export()) + raise SystemExit + spellable = collections.OrderedDict() for word in words: diff --git a/tests.py b/tests.py @@ -147,6 +147,28 @@ class TestGraph: assert None not in test_graph._children_of[parent] assert parent in test_graph._parents_of[child] + def test_nodes(self, test_graph): + """Assert that the graph properly lists its nodes.""" + assert set(test_graph.nodes(connected_only=True)) == set([ + Node(value='be', position=0), + Node(value='c', position=2), + Node(value='ca', position=2), + Node(value='au', position=3), + Node(value='u', position=4), + Node(value='s', position=5), + Node(value='se', position=5), + ]) + assert set(test_graph.nodes(connected_only=False)) == set([ + Node(value='b', position=0), + Node(value='be', position=0), + Node(value='c', position=2), + Node(value='ca', position=2), + Node(value='au', position=3), + Node(value='u', position=4), + Node(value='s', position=5), + Node(value='se', position=5), + ]) + def test_edges(self, test_graph): """Assert that the graph properly lists its edges.""" assert set(test_graph.edges()) == set([ @@ -162,4 +184,26 @@ class TestGraph: (Node(value='u', position=4), Node(value='se', position=5)) ]) - #TODO(amin): add a test for test_graph.export(). + def test_export(self, test_graph): + """Assert that the graph exports the proper dot code.""" + assert test_graph.export() == ( +"""digraph G { + graph [rankdir=LR]; + node [width=0.75 shape=circle]; + "Node(value='au', position=3)" -> "Node(value='s', position=5)"; + "Node(value='au', position=3)" -> "Node(value='se', position=5)"; + "Node(value='be', position=0)" -> "Node(value='c', position=2)"; + "Node(value='be', position=0)" -> "Node(value='ca', position=2)"; + "Node(value='c', position=2)" -> "Node(value='au', position=3)"; + "Node(value='ca', position=2)" -> "Node(value='u', position=4)"; + "Node(value='u', position=4)" -> "Node(value='s', position=5)"; + "Node(value='u', position=4)" -> "Node(value='se', position=5)"; + "Node(value='au', position=3)" [label="Au"]; + "Node(value='be', position=0)" [label="Be"]; + "Node(value='c', position=2)" [label="C"]; + "Node(value='ca', position=2)" [label="Ca"]; + "Node(value='s', position=5)" [label="S"]; + "Node(value='se', position=5)" [label="Se"]; + "Node(value='u', position=4)" [label="U"]; +}""" + )