예제 #1
0
    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
예제 #2
0
 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
예제 #3
0
 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
예제 #4
0
 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
예제 #5
0
 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}
예제 #6
0
 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
예제 #7
0
    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
예제 #8
0
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)
예제 #9
0
    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
예제 #10
0
 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
예제 #11
0
 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
예제 #12
0
    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
예제 #13
0
    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
예제 #14
0
    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
예제 #15
0
    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
예제 #16
0
 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
예제 #17
0
 def test_iter(self):
     persons = [Person(1, "Name"), Person(2, "Name")]
     table = Table(persons)
     assert list(table) == persons
예제 #18
0
 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
예제 #19
0
 def test_len(self):
     table = Table([Person(1, "Name"), Person(2, "Name")])
     assert len(table) == 2
예제 #20
0
 def test_get(self):
     table = Table([Person(1, "Name1"), Person(2, "Name2")])
     p = table.get(2)
     assert p.id_ == 2
     assert p.name == "Name2"
예제 #21
0
 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)
예제 #22
0
 def test_get_spouse_no_result(self):
     person = Person(1, "Name")
     table = Table([person])
     assert table.get_spouse(person) == None
예제 #23
0
 def test_get_key_error(self):
     table = Table([Person(1, "Name")])
     with pytest.raises(KeyError):
         table.get(2)
예제 #24
0
 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)