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:
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"];
+}"""
+ )