Skip to content

Commit 007627e

Browse files
Formatted and cleaned up naming
1 parent 610419f commit 007627e

File tree

2 files changed

+93
-55
lines changed

2 files changed

+93
-55
lines changed

README.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ NOTE: This project is a work in progress. While functional, it is not optimized.
1616

1717
from django.db import models
1818
from django_postgresql_dag.models import node_factory, edge_factory
19-
19+
2020
class NetworkEdge(edge_factory("NetworkNode", concrete=False)):
2121
name = models.CharField(max_length=100)
22-
22+
2323
def __str__(self):
2424
return self.name
25-
25+
2626
def save(self, *args, **kwargs):
2727
self.name = f"{self.parent.name} {self.child.name}"
2828
super().save(*args, **kwargs)
29-
30-
29+
30+
3131
class NetworkNode(node_factory(NetworkEdge)):
3232
name = models.CharField(max_length=100)
33-
33+
3434
def __str__(self):
3535
return self.name
3636

@@ -111,9 +111,9 @@ NOTE: This project is a work in progress. While functional, it is not optimized.
111111

112112
# Descendant methods which return ids
113113

114-
>>> root.descendant_ids()
114+
>>> root.descendants_ids()
115115
[2, 3, 4, 5, 6, 7, 8, 9, 10]
116-
>>> root.self_and_descendant_ids()
116+
>>> root.self_and_descendants_ids()
117117
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
118118
>>> root.descendants_and_self_ids()
119119
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
@@ -129,11 +129,11 @@ NOTE: This project is a work in progress. While functional, it is not optimized.
129129

130130
# Ancestor methods which return ids
131131

132-
>>> c1.ancestor_ids()
132+
>>> c1.ancestors_ids()
133133
[1, 4, 7, 8]
134-
>>> c1.ancestor_and_self_ids()
134+
>>> c1.ancestors_and_self_ids()
135135
[1, 4, 7, 8, 9]
136-
>>> c1.self_and_ancestor_ids()
136+
>>> c1.self_and_ancestors_ids()
137137
[9, 8, 7, 4, 1]
138138

139139
# Ancestor methods which return a queryset
@@ -187,6 +187,15 @@ NOTE: This project is a work in progress. While functional, it is not optimized.
187187
pg.models.NodeNotReachableException
188188
>>> c1.shortest_path(root, directional=False)
189189
<QuerySet [<NetworkNode: root>, <NetworkNode: a3>, <NetworkNode: b4>, <NetworkNode: c1>]>
190+
191+
# Get a queryset of edges relatd to a particular node
192+
193+
>>> a1.ancestors_edges()
194+
<QuerySet [<NetworkEdge: root a1>]>
195+
>>> b4.descendants_edges()
196+
<QuerySet [<NetworkEdge: b4 c1>]>
197+
>>> b4.clan_edges()
198+
{<NetworkEdge: b4 c1>, <NetworkEdge: root a3>, <NetworkEdge: a3 b4>}
190199

191200
# Get the nodes at the start or end of an edge
192201

tests/test.py

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.test import TestCase
66
from django.core.exceptions import ValidationError
77
from django_postgresql_dag.models import NodeNotReachableException
8+
89
# from .dag_output import expected_dag_output
910
from .models import NetworkNode, NetworkEdge
1011

@@ -14,12 +15,11 @@
1415
node_name_list = ["root", "a1", "a2", "a3", "b1", "b2", "b3", "b4", "c1", "c2"]
1516

1617

17-
class DagTestCase(TestCase):
18-
18+
class DagTestCase(TestCase):
1919
def setUp(self):
2020
for node in node_name_list:
2121
NetworkNode.objects.create(name=node)
22-
22+
2323
def test_01_objects_were_created(self):
2424
for node in node_name_list:
2525
self.assertEqual(NetworkNode.objects.get(name=f"{node}").name, f"{node}")
@@ -61,19 +61,19 @@ def test_02_dag(self):
6161
try:
6262
b3.add_parent(c1)
6363
except ValidationError as e:
64-
self.assertEqual(e.message, 'The object is an ancestor.')
64+
self.assertEqual(e.message, "The object is an ancestor.")
6565

6666
# Try to add a node that is already an ancestor (alternate method)
6767
try:
6868
c1.add_child(b3)
6969
except ValidationError as e:
70-
self.assertEqual(e.message, 'The object is an ancestor.')
70+
self.assertEqual(e.message, "The object is an ancestor.")
7171

7272
# Try to add a node as it's own child
7373
try:
7474
b3.add_child(b3)
7575
except ValidationError as e:
76-
self.assertEqual(e.message, 'The object is an ancestor.')
76+
self.assertEqual(e.message, "The object is an ancestor.")
7777

7878
# Verify that the tree methods work
7979
tree_from_root = root.descendants_tree()
@@ -116,31 +116,44 @@ def test_02_dag(self):
116116

117117
# Check other descendant methods
118118
self.assertEqual(b4.descendants_ids(), [c1.id])
119-
self.assertEqual(b4.descendants_and_self_ids(), [b4.id, c1.id])
120-
self.assertEqual(b4.self_and_descendants_ids(), [c1.id, b4.id])
121-
self.assertEqual(b4.descendants_and_self()[0], b4)
122-
self.assertEqual(b4.descendants_and_self()[1], c1)
123-
self.assertEqual(b4.self_and_descendants()[0], c1)
124-
self.assertEqual(b4.self_and_descendants()[1], b4)
125-
119+
self.assertEqual(b4.descendants_and_self_ids(), [c1.id, b4.id])
120+
self.assertEqual(b4.self_and_descendants_ids(), [b4.id, c1.id])
121+
self.assertEqual(b4.descendants_and_self()[0], c1)
122+
self.assertEqual(b4.descendants_and_self()[1], b4)
123+
self.assertEqual(b4.self_and_descendants()[0], b4)
124+
self.assertEqual(b4.self_and_descendants()[1], c1)
125+
126+
# Check clan methods
127+
self.assertEqual(a1.clan_ids(), [root.id, a1.id, b1.id, b2.id])
128+
self.assertEqual(a1.clan()[0], root)
129+
self.assertEqual(a1.clan()[3], b2)
126130

127131
# Check distance between nodes
128132
self.assertEqual(root.distance(c1), 3)
129133

130134
# Test additional fields for edge
131-
self.assertEqual(b3.children.through.objects.filter(child=c1)[0].name, 'b3 c1')
135+
self.assertEqual(b3.children.through.objects.filter(child=c1)[0].name, "b3 c1")
132136

133-
self.assertTrue([p.name for p in root.shortest_path(c1)] == ['root', 'a3', 'b3', 'c1'] or [p.name for p in c1.shortest_path(root, directional=False)] == ['root', 'a3', 'b4', 'c1'])
137+
self.assertTrue(
138+
[p.name for p in root.shortest_path(c1)] == ["root", "a3", "b3", "c1"]
139+
or [p.name for p in c1.shortest_path(root, directional=False)]
140+
== ["root", "a3", "b4", "c1"]
141+
)
134142

135143
try:
136144
[p.name for p in c1.shortest_path(root)]
137145
except Exception as e:
138146
self.assertRaises(NodeNotReachableException)
139147

140-
self.assertTrue([p.name for p in c1.shortest_path(root, directional=False)] == ['root', 'a3', 'b3', 'c1'] or [p.name for p in c1.shortest_path(root, directional=False)] == ['root', 'a3', 'b4', 'c1'])
148+
self.assertTrue(
149+
[p.name for p in c1.shortest_path(root, directional=False)]
150+
== ["root", "a3", "b3", "c1"]
151+
or [p.name for p in c1.shortest_path(root, directional=False)]
152+
== ["root", "a3", "b4", "c1"]
153+
)
141154

142-
self.assertEqual([p.name for p in root.get_leaves()], ['b2', 'c1', 'c2', 'b1'])
143-
self.assertEqual([p.name for p in c2.get_roots()], ['root'])
155+
self.assertEqual([p.name for p in root.get_leaves()], ["b2", "c1", "c2", "b1"])
156+
self.assertEqual([p.name for p in c2.get_roots()], ["root"])
144157

145158
self.assertTrue(root.is_root())
146159
self.assertTrue(c1.is_leaf())
@@ -151,26 +164,26 @@ def test_02_dag(self):
151164

152165
# Remove a node and test island
153166
self.assertTrue(c2 in b3.descendants())
154-
self.assertEqual([p.name for p in c2.ancestors()], ['root', 'a3', 'b3'])
167+
self.assertEqual([p.name for p in c2.ancestors()], ["root", "a3", "b3"])
155168
c2.remove_parent(b3)
156169
self.assertFalse(c2 in b3.descendants())
157170
self.assertEqual([p.name for p in c2.ancestors()], [])
158171
self.assertTrue(c2.is_island())
159172

160173
# Remove a node and test that it is still connected elsewhere
161174
self.assertTrue(c1 in b3.descendants())
162-
self.assertEqual([p.name for p in c1.ancestors()], ['root', 'a3', 'b3', 'b4'])
175+
self.assertEqual([p.name for p in c1.ancestors()], ["root", "a3", "b3", "b4"])
163176
b3.remove_child(c1)
164177
self.assertFalse(c1 in b3.descendants())
165-
self.assertEqual([p.name for p in c1.ancestors()], ['root', 'a3', 'b4'])
178+
self.assertEqual([p.name for p in c1.ancestors()], ["root", "a3", "b4"])
166179
self.assertFalse(c1.is_island())
167180

168181
"""
169182
Simulate a basic irrigation canal network
170183
"""
171-
log = logging.getLogger('test_2_canal')
184+
log = logging.getLogger("test_2_canal")
172185

173-
node_name_list2 = [x for x in range(0,201)]
186+
node_name_list2 = [x for x in range(0, 201)]
174187
adjacency_list = [
175188
["0", "1"],
176189
["1", "2"],
@@ -374,7 +387,7 @@ def test_02_dag(self):
374387
["199", "200"],
375388
]
376389

377-
for n in range(1,200):
390+
for n in range(1, 200):
378391
if n % 5 != 0:
379392
node_name_list2.append(f"SA{n}")
380393
node_name_list2.append(f"SB{n}")
@@ -395,27 +408,35 @@ def test_02_dag(self):
395408
canal_root = NetworkNode.objects.get(name="0")
396409
start_time = time.time()
397410
log.debug("Descendants: %s" % str(len(canal_root.descendants())))
398-
execution_time = (time.time() - start_time)
411+
execution_time = time.time() - start_time
399412
log.debug("Execution time in seconds: %s" % str(execution_time))
400413

401414
# Compute descendants of a leaf node
402415
canal_leaf = NetworkNode.objects.get(name="200")
403416
start_time = time.time()
404417
log.debug("Ancestors: %s" % str(len(canal_leaf.ancestors())))
405-
execution_time = (time.time() - start_time)
418+
execution_time = time.time() - start_time
406419
log.debug("Execution time in seconds: %s" % str(execution_time))
407420

408421
# Count number of paths from start to end of graph
409422
start_time = time.time()
410-
log.debug("Paths through graph: : %s" % str(len(canal_root.path_ids_list(canal_leaf, max_depth=n + 1, max_paths=500000000))))
411-
execution_time = (time.time() - start_time)
423+
log.debug(
424+
"Paths through graph: : %s"
425+
% str(
426+
len(
427+
canal_root.path_ids_list(
428+
canal_leaf, max_depth=n + 1, max_paths=500000000
429+
)
430+
)
431+
)
432+
)
433+
execution_time = time.time() - start_time
412434
log.debug("Execution time in seconds: %s" % str(execution_time))
413435

414436
# Find distance from root to leaf
415437
log.debug("Distance: %s" % str(canal_root.distance(canal_leaf, max_depth=100)))
416438
self.assertEqual(canal_root.distance(canal_leaf, max_depth=100), 60)
417439

418-
419440
log.debug("Node count: %s" % str(NetworkNode.objects.count()))
420441
log.debug("Edge count: %s" % str(NetworkEdge.objects.count()))
421442

@@ -425,26 +446,27 @@ def test_03_deep_dag(self):
425446
reasonable amount of time (linear in size of graph, not
426447
exponential).
427448
"""
449+
428450
def run_test():
429451
# Using the graph generation algorithm below, the number of potential
430452
# paths from node 0 doubles for each increase in n.
431453
# number_of_paths = 2^(n-1) WRONG!!!
432454
# When n=22, there are on the order of 1 million paths through the graph
433455
# from node 0, so results for intermediate nodes need to be cached
434456

435-
log = logging.getLogger('test_3')
457+
log = logging.getLogger("test_3")
436458

437459
n = 22 # Keep it an even number
438460

439-
for i in range(2*n):
461+
for i in range(2 * n):
440462
NetworkNode(pk=i, name=str(i)).save()
441463

442464
# Create edges
443-
for i in range(0, 2*n - 2, 2):
465+
for i in range(0, 2 * n - 2, 2):
444466
p1 = NetworkNode.objects.get(pk=i)
445-
p2 = NetworkNode.objects.get(pk=i+1)
446-
p3 = NetworkNode.objects.get(pk=i+2)
447-
p4 = NetworkNode.objects.get(pk=i+3)
467+
p2 = NetworkNode.objects.get(pk=i + 1)
468+
p3 = NetworkNode.objects.get(pk=i + 2)
469+
p4 = NetworkNode.objects.get(pk=i + 3)
448470

449471
p1.add_child(p3)
450472
p1.add_child(p4)
@@ -455,23 +477,28 @@ def run_test():
455477
root_node = NetworkNode.objects.get(pk=0)
456478
start_time = time.time()
457479
log.debug("Descendants: %s" % str(len(root_node.ancestors())))
458-
execution_time = (time.time() - start_time)
480+
execution_time = time.time() - start_time
459481
log.debug("Execution time in seconds: %s" % str(execution_time))
460482

461483
# Compute ancestors of a leaf node
462-
leaf_node = NetworkNode.objects.get(pk=2*n - 1)
484+
leaf_node = NetworkNode.objects.get(pk=2 * n - 1)
463485
start_time = time.time()
464486
log.debug("Ancestors: %s" % str(len(leaf_node.ancestors())))
465-
execution_time = (time.time() - start_time)
487+
execution_time = time.time() - start_time
466488
log.debug("Execution time in seconds: %s" % str(execution_time))
467489

468490
first = NetworkNode.objects.get(name="0")
469-
last = NetworkNode.objects.get(pk=2*n - 1)
491+
last = NetworkNode.objects.get(pk=2 * n - 1)
470492

471493
# Count number of paths from start to end of graph
472494
start_time = time.time()
473-
log.debug("Paths through graph: %s" % str(len(first.path_ids_list(last, max_depth=n + 1, max_paths=500000000))))
474-
execution_time = (time.time() - start_time)
495+
log.debug(
496+
"Paths through graph: %s"
497+
% str(
498+
len(first.path_ids_list(last, max_depth=n + 1, max_paths=500000000))
499+
)
500+
)
501+
execution_time = time.time() - start_time
475502
log.debug("Execution time in seconds: %s" % str(execution_time))
476503

477504
self.assertEqual(first.distance(last, max_depth=n), n - 1)
@@ -480,10 +507,12 @@ def run_test():
480507
log.debug("Edge count: %s" % str(NetworkEdge.objects.count()))
481508

482509
# Connect the first-created node to the last-created node
483-
NetworkNode.objects.get(pk=0).add_child(NetworkNode.objects.get(pk=2*n - 1))
510+
NetworkNode.objects.get(pk=0).add_child(
511+
NetworkNode.objects.get(pk=2 * n - 1)
512+
)
484513

485514
middle = NetworkNode.objects.get(pk=n - 1)
486-
self.assertEqual(first.distance(middle, max_depth=n), n/2 - 1)
515+
self.assertEqual(first.distance(middle, max_depth=n), n / 2 - 1)
487516

488517
# Run the test, raising an error if the code times out
489518
p = multiprocessing.Process(target=run_test)
@@ -492,4 +521,4 @@ def run_test():
492521
if p.is_alive():
493522
p.terminate()
494523
p.join()
495-
raise RuntimeError('Graph operations take too long!')
524+
raise RuntimeError("Graph operations take too long!")

0 commit comments

Comments
 (0)