_graphviz.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import typing
  2. import graphviz
  3. from yamily import Person, PersonCollection
  4. def digraph(collection: PersonCollection) -> graphviz.dot.Digraph:
  5. """
  6. >>> bob = Person('bob')
  7. >>> bob.father = Person('frank')
  8. >>> carol = Person('carol')
  9. >>> carol.mother = Person('grace')
  10. >>> alice = Person('alice')
  11. >>> alice.mother = carol
  12. >>> alice.father = bob
  13. >>> david = Person('david')
  14. >>> david.mother = carol
  15. >>> david.father = bob
  16. >>> collection = PersonCollection()
  17. >>> collection.add_person(alice)
  18. Person(alice)
  19. >>> collection.add_person(david)
  20. Person(david)
  21. >>> graph = digraph(collection)
  22. >>> print(graph.source)
  23. digraph yamily {
  24. {
  25. rank=same
  26. grace [shape=box]
  27. }
  28. {
  29. rank=same
  30. carol [shape=box]
  31. bob [shape=box]
  32. "relation-bob-carol" [shape=point width=0]
  33. carol -> "relation-bob-carol" [arrowhead=none constraint=False]
  34. bob -> "relation-bob-carol" [arrowhead=none constraint=False]
  35. }
  36. {
  37. rank=same
  38. frank [shape=box]
  39. }
  40. {
  41. rank=same
  42. alice [shape=box]
  43. }
  44. {
  45. rank=same
  46. david [shape=box]
  47. }
  48. grace -> carol
  49. frank -> bob
  50. "relation-bob-carol" -> alice
  51. "relation-bob-carol" -> david
  52. }
  53. >>> graph.render('/tmp/yamily.gv')
  54. '/tmp/yamily.gv.pdf'
  55. """
  56. graph = graphviz.Digraph("yamily")
  57. nodes: typing.Set[Person] = set()
  58. parent_node_names: typing.Dict[typing.Tuple[Person, Person], str] = dict()
  59. for person in collection:
  60. if person in nodes:
  61. continue
  62. with graph.subgraph() as subgraph:
  63. subgraph.attr(rank="same")
  64. subgraph.node(person.identifier, shape="box") # , str(person))
  65. nodes.add(person)
  66. for coparent in collection.get_coparents(person):
  67. subgraph.node(coparent.identifier, shape="box")
  68. nodes.add(coparent)
  69. parents = tuple(sorted((person, coparent), key=lambda p: p.identifier))
  70. parent_node_name = "relation-{}-{}".format(
  71. parents[0].identifier, parents[1].identifier
  72. )
  73. subgraph.node(parent_node_name, shape="point", width="0")
  74. subgraph.edge(
  75. person.identifier,
  76. parent_node_name,
  77. constraint="False",
  78. arrowhead="none",
  79. )
  80. subgraph.edge(
  81. coparent.identifier,
  82. parent_node_name,
  83. constraint="False",
  84. arrowhead="none",
  85. )
  86. parent_node_names[parents] = parent_node_name
  87. parent_node_names[tuple(parents[::-1])] = parent_node_name
  88. for child in collection:
  89. if child.mother is not None and child.father is not None:
  90. parents = sorted(child.parents, key=lambda p: p.identifier)
  91. parent_node_name = parent_node_names[tuple(parents)]
  92. graph.edge(parent_node_name, child.identifier)
  93. elif child.mother is not None:
  94. graph.edge(child.mother.identifier, child.identifier)
  95. elif child.father is not None:
  96. graph.edge(child.father.identifier, child.identifier)
  97. return graph