__init__.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import collections
  2. import datetime
  3. import typing
  4. class Person:
  5. """
  6. >>> alice = Person('alice')
  7. >>> alice.name = 'Alice Test'
  8. >>> alice.birth_date = datetime.date(2019, 12, 23)
  9. >>> alice
  10. Person(alice, Alice Test, *2019-12-23)
  11. >>> str(alice)
  12. 'Alice Test (*2019-12-23)'
  13. >>> bob = Person('bob')
  14. >>> bob.name = 'Bob Test'
  15. >>> alice.father = bob
  16. """
  17. def __init__(self, identifier: str):
  18. self.__identifier: str = identifier
  19. self.name: typing.Optional[str] = None
  20. self.birth_date: typing.Optional[datetime.date] = None
  21. self.mother: typing.Optional["Person"] = None
  22. self.father: typing.Optional["Person"] = None
  23. @property
  24. def identifier(self) -> str:
  25. return self.__identifier
  26. def __hash__(self) -> int:
  27. return hash(self.__identifier)
  28. def __repr__(self) -> str:
  29. """
  30. >>> p = Person("max-mustermann")
  31. >>> repr(p)
  32. 'Person(max-mustermann)'
  33. >>> p.name = "Hr. Mustermann"
  34. >>> repr(p)
  35. 'Person(max-mustermann, Hr. Mustermann)'
  36. >>> p.name = "Max Mustermann"
  37. >>> repr(p)
  38. 'Person(max-mustermann, Max Mustermann)'
  39. >>> p.birth_date = datetime.date(1976, 2, 1)
  40. >>> repr(p)
  41. 'Person(max-mustermann, Max Mustermann, *1976-02-01)'
  42. """
  43. return "{}({})".format(
  44. type(self).__name__,
  45. ", ".join(
  46. filter(
  47. None,
  48. (
  49. self.identifier,
  50. self.name,
  51. "*" + self.birth_date.isoformat()
  52. if self.birth_date is not None
  53. else None,
  54. ),
  55. )
  56. ),
  57. )
  58. def __str__(self) -> str:
  59. """
  60. >>> p = Person("max-mustermann")
  61. >>> p.name = "Max Mustermann"
  62. >>> str(p)
  63. 'Max Mustermann'
  64. >>> p.birth_date = datetime.date(1976, 2, 1)
  65. >>> str(p)
  66. 'Max Mustermann (*1976-02-01)'
  67. """
  68. return (self.name or "unnamed") + (
  69. " (*{})".format(self.birth_date.isoformat()) if self.birth_date else ""
  70. )
  71. def __eq__(self, other: "Person") -> bool:
  72. """
  73. >>> maxl = Person("max")
  74. >>> maxl.name = "Max Mustermann"
  75. >>> maxl == Person("max")
  76. True
  77. >>> erika = Person("erika")
  78. >>> erika.name = "Max Mustermann"
  79. >>> maxl == erika
  80. False
  81. """
  82. return self.identifier == other.identifier
  83. def merge(self, person: "Person") -> None:
  84. """
  85. >>> p1 = Person("max")
  86. >>> p1.name = "Max Mustermann"
  87. >>> str(p1)
  88. 'Max Mustermann'
  89. >>> p2 = Person("max2")
  90. >>> p2.birth_date = datetime.date(1976, 2, 1)
  91. >>> p2.mother = Person("mother")
  92. >>> p2.father = Person("father")
  93. >>> str(p2)
  94. 'unnamed (*1976-02-01)'
  95. add attributes of p2 to p1:
  96. >>> p1.merge(p2)
  97. >>> str(p1)
  98. 'Max Mustermann (*1976-02-01)'
  99. >>> p1.mother, p1.father
  100. (Person(mother), Person(father))
  101. p2 is unchanged:
  102. >>> str(p2)
  103. 'unnamed (*1976-02-01)'
  104. """
  105. for attr in ["name", "birth_date", "mother", "father"]:
  106. if getattr(person, attr) is not None:
  107. setattr(self, attr, getattr(person, attr))
  108. class PersonCollection:
  109. """
  110. >>> bob = Person('bob')
  111. >>> bob.name = 'Bob Test'
  112. >>> alice = Person('alice')
  113. >>> alice.name = 'Alice Test'
  114. >>> alice.father = bob
  115. >>> collection = PersonCollection()
  116. >>> collection.add_person(alice)
  117. Person(alice, Alice Test)
  118. >>> for person in collection:
  119. ... print(person.name)
  120. Bob Test
  121. Alice Test
  122. """
  123. def __init__(self):
  124. self._persons = {}
  125. self.__children = collections.defaultdict(set)
  126. self.__it = None
  127. def __getitem__(self, identifier: str) -> Person:
  128. """
  129. >>> c = PersonCollection()
  130. >>> c.add_person(Person("alice"))
  131. Person(alice)
  132. >>> c["alice"]
  133. Person(alice)
  134. """
  135. return self._persons[identifier]
  136. def add_person(self, person: Person) -> Person:
  137. """
  138. >>> c = PersonCollection()
  139. >>> c.add_person(Person("alice"))
  140. Person(alice)
  141. >>> c.add_person(Person("bob"))
  142. Person(bob)
  143. >>> c["bob"]
  144. Person(bob)
  145. >>> bob = Person("bob")
  146. >>> bob.birth_date = datetime.date(2010, 2, 3)
  147. >>> bob.mother = Person("bob-mum")
  148. >>> bob.father = Person("bob-dad")
  149. >>> c.add_person(bob)
  150. Person(bob, *2010-02-03)
  151. >>> list(c)
  152. [Person(alice), Person(bob, *2010-02-03), Person(bob-mum), Person(bob-dad)]
  153. """
  154. if person.mother is not None:
  155. person.mother = self.add_person(person.mother)
  156. self.__children[person.mother.identifier].add(person)
  157. if person.father is not None:
  158. person.father = self.add_person(person.father)
  159. self.__children[person.father.identifier].add(person)
  160. if person.identifier not in self._persons:
  161. self._persons[person.identifier] = person
  162. return person
  163. existing_person = self._persons[person.identifier]
  164. existing_person.merge(person)
  165. return existing_person
  166. def get_children(self, parent: typing.Union[Person, str]) -> typing.Set[Person]:
  167. """
  168. >>> c = PersonCollection()
  169. >>> alice = Person('alice')
  170. >>> alice.father = Person('bob')
  171. >>> c.add_person(alice)
  172. Person(alice)
  173. >>> c.get_children(alice)
  174. set()
  175. >>> c.get_children('bob')
  176. {Person(alice)}
  177. >>> carol = Person('carol')
  178. >>> carol.father = c['bob']
  179. >>> c.add_person(carol)
  180. Person(carol)
  181. >>> sorted(c.get_children('bob'), key=lambda p: p.identifier)
  182. [Person(alice), Person(carol)]
  183. does not support change / removal of parents:
  184. >>> carol.father = Person('other-bob')
  185. >>> c.add_person(carol)
  186. Person(carol)
  187. >>> c.get_children('other-bob')
  188. {Person(carol)}
  189. >>> sorted(c.get_children('bob'), key=lambda p: p.identifier)
  190. [Person(alice), Person(carol)]
  191. """
  192. if isinstance(parent, str):
  193. return self.__children[parent]
  194. return self.get_children(parent.identifier)
  195. def __iter__(self) -> "PersonCollection":
  196. """
  197. >>> c = PersonCollection()
  198. >>> for identifier in ['alice', 'bob', 'charlie']:
  199. ... c.add_person(Person(identifier))
  200. Person(alice)
  201. Person(bob)
  202. Person(charlie)
  203. >>> list(c)
  204. [Person(alice), Person(bob), Person(charlie)]
  205. """
  206. self.__it = iter(self._persons.values())
  207. return self
  208. def __next__(self) -> Person:
  209. return next(self.__it)