[geneagrapher] 104/226: Added ability for user to specify the sort order of nodes in the graph. This fixes #10.

Doug Torrance dtorrance-guest at moszumanska.debian.org
Sat Jul 11 17:10:50 UTC 2015


This is an automated email from the git hooks/post-receive script.

dtorrance-guest pushed a commit to branch master
in repository geneagrapher.

commit 8cb28f159779cb64a0c7d5d1db30a5167d012737
Author: David Alber <alber.david at gmail.com>
Date:   Mon Dec 29 04:21:04 2008 +0000

    Added ability for user to specify the sort order of nodes in the graph. This fixes #10.
---
 Geneagrapher/geneagrapher/GGraph.py       |  50 ++++++++--
 Geneagrapher/geneagrapher/geneagrapher.py |  35 +++++--
 Geneagrapher/geneagrapher/ggrapher.py     |   5 +
 Geneagrapher/geneagrapher/grab.py         |   2 +-
 Geneagrapher/tests/tests.py               | 157 +++++++++++++++++++++++++++---
 5 files changed, 220 insertions(+), 29 deletions(-)

diff --git a/Geneagrapher/geneagrapher/GGraph.py b/Geneagrapher/geneagrapher/GGraph.py
index 04218db..f6c984d 100644
--- a/Geneagrapher/geneagrapher/GGraph.py
+++ b/Geneagrapher/geneagrapher/GGraph.py
@@ -10,7 +10,7 @@ class Record:
     """
     Container class storing record of a mathematician in the graph.
     """
-    def __init__(self, name, institution=None, year=None, id=None):
+    def __init__(self, name, institution=None, year=None, id=None, sort_order='year'):
         """
         Record class constructor.
         
@@ -20,11 +20,14 @@ class Record:
                 (empty if none)
             year: integer containing year degree was earned
             id: integer containing Math Genealogy Project id value
+            sort_order: 'year', 'surname', or 'id'; defines the order
+                to sort nodes
         """
         self.name = name
         self.institution = institution
         self.year = year
         self.id = id
+        self.sort_order = sort_order
         
         # Verify we got the types wanted.
         if not isinstance(self.name, basestring):
@@ -38,9 +41,34 @@ class Record:
 
     def __cmp__(self, r2):
         """
-        Compare a pair of mathematician records based on ids.
+        Compare a pair of mathematician records based on surnames,
+        ids, or graduation years.
         """
-        return self.id.__cmp__(r2.id)
+        if self.sort_order == "surname":
+            sname1 = self.name.split()[-1]
+            sname2 = r2.name.split()[-1]
+            if sname1 > sname2:
+                return 1
+            elif sname1 < sname2:
+                return -1
+            else:
+                return 0
+        elif self.sort_order == "id":
+            id1 = int(self.id)
+            id2 = int(r2.id)
+            return id1.__cmp__(id2)
+        else:
+            # Do the 'year' sort order.
+            if self.year is not None:
+                if r2.year is not None:
+                    return self.year.__cmp__(r2.year)
+                else:
+                    return -1
+            else:
+                if r2.year is not None:
+                    return 1
+                else:
+                    return 0
     
     def hasInstitution(self):
         """
@@ -173,12 +201,13 @@ class Graph:
         """
         return self.nodes.keys()
 
-    def addNode(self, name, institution, year, id, ancestors, descendants, isHead=False):
+    def addNode(self, name, institution, year, id, ancestors,
+                descendants, sort_order='year', isHead=False):
         """
         Add a new node to the graph if a matching node is not already
         present.
         """
-        record = Record(name, institution, year, id)
+        record = Record(name, institution, year, id, sort_order)
         node = Node(record, ancestors, descendants)
         self.addNodeObject(node, isHead)
 
@@ -212,6 +241,7 @@ class Graph:
         for head in self.heads:
             queue.append(head.id())
         edges = ""
+        nodes = []
         dotfile = ""
         
         dotfile += """digraph genealogy {
@@ -239,9 +269,8 @@ class Graph:
                 # Add this node's descendants to queue.
                 queue += node.descendants
         
-            # Print this node's information.
-            nodestr = "    %d [label=\"%s\"];" % (node_id, node)
-            dotfile += nodestr
+            # Add this node to the front of the node list.
+            nodes.insert(0, node)
 
             # Store the connection information for this node.
             for advisor in node.ancestors:
@@ -249,7 +278,10 @@ class Graph:
                     edgestr = "\n    %d -> %d;" % (advisor, node_id)
                     edges += edgestr
                 
-            dotfile += "\n"
+        # Generate nodes list text.
+        nodes.sort()
+        for node in nodes:
+            dotfile += "    %d [label=\"%s\"];\n" % (node.id(), node)
 
         # Now print the connections between the nodes.
         dotfile += edges
diff --git a/Geneagrapher/geneagrapher/geneagrapher.py b/Geneagrapher/geneagrapher/geneagrapher.py
index 60acc59..3db9f2c 100644
--- a/Geneagrapher/geneagrapher/geneagrapher.py
+++ b/Geneagrapher/geneagrapher/geneagrapher.py
@@ -1,7 +1,14 @@
 from optparse import OptionParser
+import sys
 import GGraph
 import grab
 
+class BadSortTypeError(ValueError):
+    def __init__(self, value):
+        self.value = value
+    def __str__(self):
+        return self.value
+
 class Geneagrapher:
 	"""
 	A class for building Graphviz "dot" files for math genealogies
@@ -37,6 +44,8 @@ class Geneagrapher:
 				       help="print version and exit")
 		self.parser.add_option("-n", "--attach-node-file", dest="supp_node_filename", metavar="FILE",
 				       help="attach supplementary nodes returned by function 'define_supp_nodes()' in FILE to the graph", default=None)
+		self.parser.add_option("-s", "--node-sort", dest="node_sort_order", metavar="SORT_TYPE",
+				       help="sort the nodes based on SORT_TYPE [defalut: 'year']; valid values: 'year' (sort by graduation year), 'surname' (sort by surname), 'id' (sort by node ID)", default="year")
 
 		(options, args) = self.parser.parse_args()
 		
@@ -51,9 +60,16 @@ class Geneagrapher:
 		self.get_descendants = options.get_descendants
 		self.verbose = options.verbose
 		self.supp_node_filename = options.supp_node_filename
+		self.node_sort_order = options.node_sort_order
 		self.write_filename = options.filename
 		for arg in args:
 			self.leaf_ids.append(int(arg))
+
+		# Verify the node sort order value is valid.
+		if self.node_sort_order != "year" and self.node_sort_order != "surname" and \
+			    self.node_sort_order != "id":
+			msg = "Node sort order type '%s' invalid" % (self.node_sort_order)
+			raise BadSortTypeError(msg)
 		
 	def buildGraph(self):
 		"""
@@ -86,10 +102,11 @@ class Geneagrapher:
 					print "Grabbing record #%d" % (id)
 				try:
 					[name, institution, year, advisors, descendants] = grabber.extractNodeInformation()
-				except ValueError:
+				except grab.BadIdError, msg:
 					# The given id does not exist in the Math Genealogy Project's database.
-					raise
-				self.graph.addNode(name, institution, year, id, advisors, descendants, True)
+					print msg
+					sys.exit()
+				self.graph.addNode(name, institution, year, id, advisors, descendants, self.node_sort_order, True)
 				if self.get_ancestors:
 					ancestor_grab_queue += advisors
 				if self.get_descendants:
@@ -109,7 +126,7 @@ class Geneagrapher:
 					except ValueError:
 						# The given id does not exist in the Math Genealogy Project's database.
 						raise
-					self.graph.addNode(name, institution, year, id, advisors, descendants)
+					self.graph.addNode(name, institution, year, id, advisors, descendants, self.node_sort_order)
 					ancestor_grab_queue += advisors
 						
 		# Grab descendants of leaf nodes.
@@ -126,7 +143,7 @@ class Geneagrapher:
 					except ValueError:
 						# The given id does not exist in the Math Genealogy Project's database.
 						raise
-					self.graph.addNode(name, institution, year, id, advisors, descendants)
+					self.graph.addNode(name, institution, year, id, advisors, descendants, self.node_sort_order)
 					descendant_grab_queue += descendants
 					
 	def generateDotFile(self):
@@ -142,8 +159,12 @@ if __name__ == "__main__":
 	geneagrapher = Geneagrapher()
 	try:
 		geneagrapher.parseInput()
-	except SyntaxError, e:
+	except SyntaxError, msg:
 		print geneagrapher.parser.get_usage()
-		print e
+		print msg
+		sys.exit()
+	except BadSortTypeError, msg:
+		print msg
+		sys.exit()
 	geneagrapher.buildGraph()
 	geneagrapher.generateDotFile()
diff --git a/Geneagrapher/geneagrapher/ggrapher.py b/Geneagrapher/geneagrapher/ggrapher.py
index d816605..d117a2e 100644
--- a/Geneagrapher/geneagrapher/ggrapher.py
+++ b/Geneagrapher/geneagrapher/ggrapher.py
@@ -1,3 +1,4 @@
+import sys
 import geneagrapher
 
 def ggrapher():
@@ -9,5 +10,9 @@ def ggrapher():
 	except SyntaxError, e:
 		print ggrapher.parser.get_usage()
 		print e
+		sys.exit()
+	except geneagrapher.BadSortTypeError, msg:
+		print msg
+		sys.exit()
 	ggrapher.buildGraph()
 	ggrapher.generateDotFile()
diff --git a/Geneagrapher/geneagrapher/grab.py b/Geneagrapher/geneagrapher/grab.py
index e190f3e..e227ca6 100644
--- a/Geneagrapher/geneagrapher/grab.py
+++ b/Geneagrapher/geneagrapher/grab.py
@@ -6,7 +6,7 @@ class BadIdError(ValueError):
     def __init__(self, value):
         self.value = value
     def __str__(self):
-        return repr(self.value)
+        return self.value
 
 class Grabber:
     """
diff --git a/Geneagrapher/tests/tests.py b/Geneagrapher/tests/tests.py
index 36b4b16..3fd2617 100644
--- a/Geneagrapher/tests/tests.py
+++ b/Geneagrapher/tests/tests.py
@@ -14,6 +14,7 @@ class TestRecordMethods(unittest.TestCase):
         self.assertEqual(record.institution, "Universitaet Helmstedt")
         self.assertEqual(record.year, 1799)
         self.assertEqual(record.id, 18231)
+        self.assertEqual(record.sort_order, "year")
         
     def test002_init_bad_name(self):
         # Test constructor with bad 'name' parameter.
@@ -39,28 +40,53 @@ class TestRecordMethods(unittest.TestCase):
         record2 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231)
         self.assert_(record1 == record2)
         
+        record1 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, "surname")
+        record2 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, "surname")
+        self.assert_(record1 == record2)
+        
+        record1 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, "id")
+        record2 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, "id")
+        self.assert_(record1 == record2)
+        
     def test007_cmp_unequal(self):
         # Verify two 'unequal' records are compared correctly.
         record1 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231)
         record2 = GGraph.Record("Leonhard Euler", "Universitaet Basel", 1726, 38586)
-        self.assert_(record1 < record2)
-
-    def test008_hasInstitution_yes(self):
+        self.assertTrue(record1 > record2)
+
+        record1 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, "surname")
+        record2 = GGraph.Record("Leonhard Euler", "Universitaet Basel", 1726, 38586, "surname")
+        self.assertTrue(record1 > record2)
+
+        record1 = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, "id")
+        record2 = GGraph.Record("Leonhard Euler", "Universitaet Basel", 1726, 38586, "id")
+        self.assertTrue(record1 < record2)
+
+    def test008_cmp_no_year(self):
+        # Verify year-based comparison acts as expected when one
+        # record is missing a year.
+        record1 = GGraph.Record(name="Record 1", institution="Universitaet Helmstedt",
+                                year=1999, id=18231)
+        record2 = GGraph.Record(name="Record 1", institution="Universitaet Helmstedt", id=18231)
+        self.assertTrue(record1 < record2)
+        self.assertTrue(record2 > record1)
+
+    def test009_hasInstitution_yes(self):
         # Verify hasInstitution() method returns True when the conditions are right.
         record = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231)
         self.assert_(record.hasInstitution())
 
-    def test009_hasInstitution_no(self):
+    def test010_hasInstitution_no(self):
         # Verify hasInstitution() method returns False when the conditions are right.
         record = GGraph.Record("Carl Friedrich Gauss", None, 1799, 18231)
         self.assert_(not record.hasInstitution())
 
-    def test010_hasYear_yes(self):
+    def test011_hasYear_yes(self):
         # Verify hasYear() method returns True when the conditions are right.
         record = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231)
         self.assert_(record.hasYear())
 
-    def test011_hasYear_no(self):
+    def test012_hasYear_no(self):
         # Verify hasYear() method returns False when the conditions are right.
         record = GGraph.Record("Carl Friedrich Gauss", "Universitaet Helmstedt", None, 18231)
         self.assert_(not record.hasYear())
@@ -136,7 +162,7 @@ class TestNodeMethods(unittest.TestCase):
         record2 = GGraph.Record("Leonhard Euler", "Universitaet Basel", 1726, 38586)
         node1 = GGraph.Node(self.record, [], [])
         node2 = GGraph.Node(record2, [], [])
-        self.assert_(node1 < node2)
+        self.assert_(node1 > node2)
 
     def test010_add_ancestor(self):
         # Test the addAncestor() method.
@@ -221,7 +247,7 @@ class TestGraphMethods(unittest.TestCase):
     def test010_add_second_node_head(self):
         # Test the addNode() method when adding a second node and
         # marking it as a head node.
-        self.graph1.addNode("Leonhard Euler", "Universitaet Basel", 1726, 38586, [], [], True)
+        self.graph1.addNode("Leonhard Euler", "Universitaet Basel", 1726, 38586, [], [], isHead=True)
         self.assertEquals([38586, 18231], self.graph1.getNodeList())
         self.assertEquals(self.graph1.heads, [self.node1, self.graph1.getNode(38586)])
 
@@ -259,7 +285,7 @@ class TestGraphMethods(unittest.TestCase):
         dotfile = self.graph1.generateDotFile(True, False)
         self.assertEquals(dotfile, dotfileexpt)
         
-    def test015_generate_dot_file(self):
+    def test015_generate_dot_file1(self):
         # Test the generateDotFile() method.
         graph = GGraph.Graph()
         graph.addNode("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, [18230], [])
@@ -274,12 +300,76 @@ class TestGraphMethods(unittest.TestCase):
     node [shape=plaintext];
     edge [style=bold];
 
-    18231 [label="Carl Friedrich Gauss \\nUniversitaet Helmstedt (1799)"];
-    18230 [label="Johann Friedrich Pfaff \\nGeorg-August-Universitaet Goettingen (1786)"];
+    21235 [label="Otto Mencke \\nUniversitaet Leipzig (1665)"];
+    72669 [label="Johann Christoph Wichmannshausen \\nUniversitaet Leipzig (1685)"];
+    57670 [label="Christian August Hausen \\nMartin-Luther-Universitaet Halle-Wittenberg (1713)"];
     66476 [label="Abraham Gotthelf Kaestner \\nUniversitaet Leipzig (1739)"];
+    18230 [label="Johann Friedrich Pfaff \\nGeorg-August-Universitaet Goettingen (1786)"];
+    18231 [label="Carl Friedrich Gauss \\nUniversitaet Helmstedt (1799)"];
+
+    18230 -> 18231;
+    66476 -> 18230;
+    57670 -> 66476;
+    72669 -> 57670;
+    21235 -> 72669;
+}
+"""
+        dotfile = graph.generateDotFile(True, False)
+        self.assertEquals(dotfile, dotfileexpt)
+
+    def test016_generate_dot_file2(self):
+        # Test the generateDotFile() method.
+        graph = GGraph.Graph()
+        graph.addNode("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, [18230], [], sort_order="surname")
+        graph.addNode("Johann Friedrich Pfaff", "Georg-August-Universitaet Goettingen", 1786, 18230, [66476], [], sort_order="surname")
+        graph.addNode("Abraham Gotthelf Kaestner", "Universitaet Leipzig", 1739, 66476, [57670], [], sort_order="surname")
+        graph.addNode("Christian August Hausen", "Martin-Luther-Universitaet Halle-Wittenberg", 1713, 57670, [72669], [], sort_order="surname")
+        graph.addNode("Johann Christoph Wichmannshausen", "Universitaet Leipzig", 1685, 72669, [21235], [], sort_order="surname")
+        graph.addNode("Otto Mencke", "Universitaet Leipzig", 1665, 21235, [], [], sort_order="surname")
+        
+        dotfileexpt = """digraph genealogy {
+    graph [charset="utf-8"];
+    node [shape=plaintext];
+    edge [style=bold];
+
+    18231 [label="Carl Friedrich Gauss \\nUniversitaet Helmstedt (1799)"];
     57670 [label="Christian August Hausen \\nMartin-Luther-Universitaet Halle-Wittenberg (1713)"];
+    66476 [label="Abraham Gotthelf Kaestner \\nUniversitaet Leipzig (1739)"];
+    21235 [label="Otto Mencke \\nUniversitaet Leipzig (1665)"];
+    18230 [label="Johann Friedrich Pfaff \\nGeorg-August-Universitaet Goettingen (1786)"];
     72669 [label="Johann Christoph Wichmannshausen \\nUniversitaet Leipzig (1685)"];
+
+    18230 -> 18231;
+    66476 -> 18230;
+    57670 -> 66476;
+    72669 -> 57670;
+    21235 -> 72669;
+}
+"""
+        dotfile = graph.generateDotFile(True, False)
+        self.assertEquals(dotfile, dotfileexpt)
+
+    def test017_generate_dot_file3(self):
+        # Test the generateDotFile() method.
+        graph = GGraph.Graph()
+        graph.addNode("Carl Friedrich Gauss", "Universitaet Helmstedt", 1799, 18231, [18230], [], sort_order="id")
+        graph.addNode("Johann Friedrich Pfaff", "Georg-August-Universitaet Goettingen", 1786, 18230, [66476], [], sort_order="id")
+        graph.addNode("Abraham Gotthelf Kaestner", "Universitaet Leipzig", 1739, 66476, [57670], [], sort_order="id")
+        graph.addNode("Christian August Hausen", "Martin-Luther-Universitaet Halle-Wittenberg", 1713, 57670, [72669], [], sort_order="id")
+        graph.addNode("Johann Christoph Wichmannshausen", "Universitaet Leipzig", 1685, 72669, [21235], [], sort_order="id")
+        graph.addNode("Otto Mencke", "Universitaet Leipzig", 1665, 21235, [], [], sort_order="id")
+        
+        dotfileexpt = """digraph genealogy {
+    graph [charset="utf-8"];
+    node [shape=plaintext];
+    edge [style=bold];
+
+    18230 [label="Johann Friedrich Pfaff \\nGeorg-August-Universitaet Goettingen (1786)"];
+    18231 [label="Carl Friedrich Gauss \\nUniversitaet Helmstedt (1799)"];
     21235 [label="Otto Mencke \\nUniversitaet Leipzig (1665)"];
+    57670 [label="Christian August Hausen \\nMartin-Luther-Universitaet Halle-Wittenberg (1713)"];
+    66476 [label="Abraham Gotthelf Kaestner \\nUniversitaet Leipzig (1739)"];
+    72669 [label="Johann Christoph Wichmannshausen \\nUniversitaet Leipzig (1685)"];
 
     18230 -> 18231;
     66476 -> 18230;
@@ -427,17 +517,19 @@ class TestGeneagrapherMethods(unittest.TestCase):
         self.assertEquals(self.ggrapher.get_descendants, False)
         self.assertEquals(self.ggrapher.verbose, False)
         self.assertEquals(self.ggrapher.supp_node_filename, None)
+        self.assertEquals(self.ggrapher.node_sort_order, "year")
         self.assertEquals(self.ggrapher.write_filename, None)
         self.assertEquals(self.ggrapher.leaf_ids, [3])
 
     def test004_parse_options(self):
         # Test parseInput() with options.
-        sys.argv = ['geneagrapher', '--with-ancestors', '--with-descendants', '--file=filler', '--verbose', '-n', 'suppfiller', '3', '43']
+        sys.argv = ['geneagrapher', '--with-ancestors', '--with-descendants', '--file=filler', '--verbose', '-n', 'suppfiller', '--node-sort=surname', '3', '43']
         self.ggrapher.parseInput()
         self.assertEquals(self.ggrapher.get_ancestors, True)
         self.assertEquals(self.ggrapher.get_descendants, True)
         self.assertEquals(self.ggrapher.verbose, True)
         self.assertEquals(self.ggrapher.supp_node_filename, "suppfiller")
+        self.assertEquals(self.ggrapher.node_sort_order, "surname")
         self.assertEquals(self.ggrapher.write_filename, "filler")
         self.assertEquals(self.ggrapher.leaf_ids, [3, 43])
 
@@ -448,6 +540,47 @@ class TestGeneagrapherMethods(unittest.TestCase):
         self.ggrapher.parseInput()
         self.assertRaises(AttributeError, self.ggrapher.buildGraph)
 
+    def test006_sort_order_type_test1(self):
+        # Test the parseInput() method with a valid sort order.
+        sys.argv = ['geneagrapher', '--node-sort=year', '3', '43']
+        self.ggrapher.parseInput()
+        self.assertEquals(self.ggrapher.get_ancestors, False)
+        self.assertEquals(self.ggrapher.get_descendants, False)
+        self.assertEquals(self.ggrapher.verbose, False)
+        self.assertEquals(self.ggrapher.supp_node_filename, None)
+        self.assertEquals(self.ggrapher.node_sort_order, "year")
+        self.assertEquals(self.ggrapher.write_filename, None)
+        self.assertEquals(self.ggrapher.leaf_ids, [3, 43])
+
+    def test007_sort_order_type_test2(self):
+        # Test the parseInput() method with a valid sort order.
+        sys.argv = ['geneagrapher', '--node-sort=surname', '3', '43']
+        self.ggrapher.parseInput()
+        self.assertEquals(self.ggrapher.get_ancestors, False)
+        self.assertEquals(self.ggrapher.get_descendants, False)
+        self.assertEquals(self.ggrapher.verbose, False)
+        self.assertEquals(self.ggrapher.supp_node_filename, None)
+        self.assertEquals(self.ggrapher.node_sort_order, "surname")
+        self.assertEquals(self.ggrapher.write_filename, None)
+        self.assertEquals(self.ggrapher.leaf_ids, [3, 43])
+
+    def test008_sort_order_type_test3(self):
+        # Test the parseInput() method with a valid sort order.
+        sys.argv = ['geneagrapher', '--node-sort=id', '3', '43']
+        self.ggrapher.parseInput()
+        self.assertEquals(self.ggrapher.get_ancestors, False)
+        self.assertEquals(self.ggrapher.get_descendants, False)
+        self.assertEquals(self.ggrapher.verbose, False)
+        self.assertEquals(self.ggrapher.supp_node_filename, None)
+        self.assertEquals(self.ggrapher.node_sort_order, "id")
+        self.assertEquals(self.ggrapher.write_filename, None)
+        self.assertEquals(self.ggrapher.leaf_ids, [3, 43])
+
+    def test009_sort_order_type_test4(self):
+        # Test the parseInput() method with an invalid sort order.
+        sys.argv = ['geneagrapher', '--node-sort=notreal', '3', '43']
+        self.assertRaises(geneagrapher.BadSortTypeError, self.ggrapher.parseInput)
+
 class GeneagrapherTestSuite(unittest.TestSuite):
     def __init__(self):
         unittest.TestSuite.__init__(self)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/geneagrapher.git



More information about the debian-science-commits mailing list