def __call__(self, table: Table, root: Person, x0: int = 0, y0: int = 0 ) -> List[Union[PersonBox, SpouseLink, ParentChildLink]]: # handle single person spouse = table.get_spouse(root) if spouse is None: x = x0 + self.x_margin y = y0 + self.y_margin elements = [self.person_box_class(root, x, y)] return elements # get family members from the table if root.gender == 'M': parents = [root, spouse] else: parents = [spouse, root] children = table.find_children(*parents) children = sorted(children, key=lambda p: (p.birth_order, p.id_)) elements = [] x = x0 # elements for children for child in children: elems = self(table, child, x0=x, y0=y0 + self.unit_h) x = max([e.xmax for e in _get_person_boxes(elems)]) x += self.x_margin elements += elems child_elements = [e for e in _get_person_boxes(elements) if e.person in children] # elements for parents center = (x0 + x) // 2 x = max(x0, center - self.unit_w) x1 = x + self.x_margin x2 = x + self.x_margin + self.unit_w y = y0 + self.y_margin elements.append(self.person_box_class(parents[0], x1, y)) elements.append(self.person_box_class(parents[1], x2, y)) # parent and child link ym = y0 + self.person_box_class.height // 2 for child_elem in child_elements: x1 = x + self.unit_w y1 = ym x2 = (child_elem.xmin + child_elem.xmax) // 2 y2 = child_elem.ymin elements.append(self.parent_child_link_class(x1, y1, x2, y2)) # spouse link elements.append(self.spouse_link_class( x + self.unit_w - self.x_margin, ym, x + self.unit_w + self.x_margin, ym)) return elements
def test_get_parents_no_result(self): child = Person(1, "Name") table = Table([child]) parents = table.get_parents(child) assert len(parents) == 2 assert parents[0] == None assert parents[1] == None
def test(self): table = Table([ Person(1, "Name", "M", spouse_id=2), Person(2, "Name", "F", spouse_id=1) ]) html = render_detail_page(table, table.get(1)) assert 'svg' in html assert '2.html' in html
def test_get_parents(self): father = Person(1, "Father") mother = Person(2, "Mother") child = Person(3, "Child", father_id=1, mother_id=2) table = Table([father, mother, child]) parents = table.get_parents(child) assert len(parents) == 2 assert parents[0] == father assert parents[1] == mother
def test_find_children(self): father = Person(1, "Father") mother = Person(2, "Mother") child1 = Person(3, "Child1", father_id=1, mother_id=2) child2 = Person(4, "Child2", father_id=1, mother_id=2) table = Table([father, mother, child1, child2]) children = table.find_children(father, mother) assert len(children) == 2 assert set(children) == {child1, child2}
def test_single_no_parent_info(self): table = Table([ Person(1, "target"), Person(2, "other"), Person(3, "other"), ]) layout_func = ButterflyLayout() elements = layout_func(table, table.get(1)) assert len(elements) == 1
def test_couple_no_parent_info(self): table = Table([ Person(1, "husband", "M", spouse_id=2), Person(2, "wife", "F", spouse_id=1), ]) layout_func = ButterflyLayout() elements = layout_func(table, table.get(1)) assert len(elements) == 2 + 1 box_p1 = _get_box(elements, table.get(1)) box_p2 = _get_box(elements, table.get(2)) assert box_p1.xmin < box_p2.xmin assert box_p1.ymin == box_p2.ymin
def parse_csv(csv_file: str) -> Table: """Parse family tree data CSV.""" with open(csv_file) as f: lines = f.readlines() lines = [l.strip() for l in lines if l.strip()] # parse header row headers = [s.strip() for s in lines[0].split(',')] headers = [_convert_key(s) for s in headers] # parse content row persons = [] for line in lines[1:]: if len(line.strip()) == 0: continue values = [s.strip() for s in line.split(',')] assert len(headers) == len(values) data = {k: _convert_value(k, v) for k, v in zip(headers, values)} if 'family_name' in data and 'first_name' in data: data['name'] = '{} {}'.format(data['family_name'], data['first_name']) del data['family_name'] del data['first_name'] persons.append(Person(**data)) return Table(persons)
def __call__(self, table: Table, target: Person) -> List[Element]: # a) single # - with parent info -> tree(father) # - no parent info -> tree(target) spouse = table.get_spouse(target) if spouse is None: parents = table.get_parents(target) if parents[0] and parents[1]: return self.tree_layout_func(table, parents[0]) else: return self.tree_layout_func(table, target) if target.gender == 'M': husband = target wife = spouse else: husband = spouse wife = target parents1 = table.get_parents(husband) parents2 = table.get_parents(wife) # b) couple, no parents info -> tree(target) if ((parents1[0] is None or parents1[1] is None) and (parents2[0] is None or parents2[1] is None)): return self.tree_layout_func(table, target) # c) couple, with parents info -> butterfly layout children1 = _get_brothers_and_sisters(table, husband) children2 = _get_brothers_and_sisters(table, wife) elements = [] # layout children x = 0 elements, x = self._layout_children(elements, x, table, children1) elements, x = self._layout_children(elements, x, table, [husband]) elements, x = self._layout_children(elements, x, table, children2) # layout parents x = 0 elements, x = self._layout_parents(elements, x, table, parents1, children1 + [husband]) elements, x = self._layout_parents(elements, x, table, parents2, [wife] + children2) return elements
def test_single(self): persons = [ Person(1, "Name1"), Person(2, "Name2") ] layout_func = TreeLayout() elements = layout_func(Table(persons), persons[0]) assert len(elements) == 1
def test(self): table = Table([ Person(1, "Name", "M", spouse_id=2), Person(2, "Name", "F", spouse_id=1) ]) html = render_index_page(table) assert './details/1.html' in html assert './details/2.html' in html
def test_single_with_parents(self): table = Table([ Person(1, "father", "M", spouse_id=2), Person(2, "mother", "F", spouse_id=1), Person(3, "target", "M", father_id=1, mother_id=2, birth_order=1), Person(4, "brother", "M", father_id=1, mother_id=2, birth_order=2), ]) layout_func = ButterflyLayout() elements = layout_func(table, table.get(3)) assert len(elements) == 4 + 1 + 2 box_p1 = _get_box(elements, table.get(1)) box_p2 = _get_box(elements, table.get(2)) box_p3 = _get_box(elements, table.get(3)) box_p4 = _get_box(elements, table.get(4)) assert box_p1.xmin < box_p2.xmin assert box_p1.ymin == box_p2.ymin assert box_p3.xmin < box_p4.xmin assert box_p3.ymin == box_p4.ymin
def test_couple_partial_parent_info(self): table = Table([ Person(1, "father1", "M", spouse_id=2), Person(2, "mother1", "F", spouse_id=1), Person(3, "brother1", "M", father_id=1, mother_id=2, birth_order=1), Person(4, "brother2", "M", father_id=1, mother_id=2, birth_order=3), Person(5, "husband", "M", spouse_id=6, father_id=1, mother_id=2, birth_order=2), Person(6, "wife", "F", spouse_id=5), ]) layout_func = ButterflyLayout() elements = layout_func(table, table.get(5)) assert len(elements) == 6 + 2 + 3 box_p1 = _get_box(elements, table.get(1)) box_p2 = _get_box(elements, table.get(2)) box_p3 = _get_box(elements, table.get(3)) box_p4 = _get_box(elements, table.get(4)) box_p5 = _get_box(elements, table.get(5)) box_p6 = _get_box(elements, table.get(6)) assert box_p1.xmin < box_p2.xmin assert box_p1.ymin == box_p2.ymin assert box_p3.xmin < box_p4.xmin < box_p5.xmin < box_p6.xmin assert box_p3.ymin == box_p4.ymin == box_p5.ymin == box_p6.ymin
def test_butterfly(self): table = Table([ Person(1, "father1", "M", spouse_id=2), Person(2, "mother1", "F", spouse_id=1), Person(3, "brother1", "M", father_id=1, mother_id=2, birth_order=1), Person(4, "brother2", "M", father_id=1, mother_id=2, birth_order=3), Person(5, "husband", "M", spouse_id=6, father_id=1, mother_id=2, birth_order=2), Person(6, "wife", "F", spouse_id=5, father_id=7, mother_id=8, birth_order=1), Person(7, "father2", "M", spouse_id=8), Person(8, "mother2", "F", spouse_id=7), Person(9, "sister1", "F", father_id=7, mother_id=8, birth_order=2) ]) layout_func = ButterflyLayout() elements = layout_func(table, table.get(5)) assert len(elements) == 9 + 3 + 5 box_p1 = _get_box(elements, table.get(1)) box_p2 = _get_box(elements, table.get(2)) box_p3 = _get_box(elements, table.get(3)) box_p4 = _get_box(elements, table.get(4)) box_p5 = _get_box(elements, table.get(5)) box_p6 = _get_box(elements, table.get(6)) box_p7 = _get_box(elements, table.get(7)) box_p8 = _get_box(elements, table.get(8)) box_p9 = _get_box(elements, table.get(9)) assert box_p1.xmin < box_p2.xmin < box_p7.xmin < box_p8.xmin assert box_p1.ymin == box_p2.ymin == box_p7.ymin == box_p8.ymin assert box_p3.xmin < box_p4.xmin < box_p5.xmin < box_p6.xmin < box_p9.xmin assert box_p3.ymin == box_p4.ymin == box_p5.ymin == box_p6.ymin == box_p9.ymin
def test_tree(self): persons = [ Person(1, "Father", gender="M", spouse_id=2), Person(2, "Mother", gender="F", spouse_id=1), Person(3, "Child1", father_id=1, mother_id=2, birth_order=1), Person(4, "Child2", father_id=1, mother_id=2, birth_order=2), Person(5, "Child3", father_id=1, mother_id=2, birth_order=3), ] layout_func = TreeLayout() elements = layout_func(Table(persons), persons[0]) assert len(elements) == 5 + 1 + 3 box_p1 = _get_box(elements, persons[0]) box_p2 = _get_box(elements, persons[1]) box_p3 = _get_box(elements, persons[2]) box_p4 = _get_box(elements, persons[3]) box_p5 = _get_box(elements, persons[4]) assert box_p1.xmin < box_p2.xmin assert box_p1.ymin == box_p2.ymin assert box_p3.xmin < box_p4.xmin < box_p5.xmin assert box_p3.ymin == box_p4.ymin == box_p5.ymin
def test_get_spouse(self): husband = Person(1, "Husband", spouse_id=2) wife = Person(2, "Wife", spouse_id=1) table = Table([husband, wife]) assert table.get_spouse(husband) == wife assert table.get_spouse(wife) == husband
def test_iter(self): persons = [Person(1, "Name"), Person(2, "Name")] table = Table(persons) assert list(table) == persons
def test_find_children_no_result(self): person1 = Person(1, "Person1") person2 = Person(2, "Person2") table = Table([person1, person2]) children = table.find_children(person1, person2) assert len(children) == 0
def test_len(self): table = Table([Person(1, "Name"), Person(2, "Name")]) assert len(table) == 2
def test_get(self): table = Table([Person(1, "Name1"), Person(2, "Name2")]) p = table.get(2) assert p.id_ == 2 assert p.name == "Name2"
def test_get_spouse_key_error(self): person = Person(1, "Name", spouse_id=999) table = Table([person]) with pytest.raises(KeyError): table.get_spouse(person)
def test_get_spouse_no_result(self): person = Person(1, "Name") table = Table([person]) assert table.get_spouse(person) == None
def test_get_key_error(self): table = Table([Person(1, "Name")]) with pytest.raises(KeyError): table.get(2)
def test_get_parents_key_error(self): child = Person(1, "Child", father_id=999) table = Table([child]) with pytest.raises(KeyError): table.get_parents(child)