-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch_results.py
138 lines (110 loc) · 5.45 KB
/
search_results.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# search_results.py
import os
from PIL import Image, ImageDraw, ImageFont
from render_tree import RenderNode
class SearchGroup(object):
def __init__(self):
super().__init__()
self.relationship_list = []
def is_match(self, relationship):
raise Exception('Pure virtual call.')
class BaptismNeededGroup(SearchGroup):
def __init__(self):
super().__init__()
def is_match(self, relationship):
person = relationship.person
return person.baptism_date is None and not person.died_before_eight
class EndownmentNeededGroup(SearchGroup):
def __init__(self):
super().__init__()
def is_match(self, relationship):
person = relationship.person
return person.endownment_date is None
class SealingToParentsNeededGroup(SearchGroup):
def __init__(self):
super().__init__()
def is_match(self, relationship):
person = relationship.person
return person.sealing_to_parents_date is None and not person.born_in_the_covenant
class SealingToSpouseNeededGroup(SearchGroup):
def __init__(self):
super().__init__()
def is_match(self, relationship):
person = relationship.person
# Try to weed out cases where the individual most likely was
# not sealed to a spouse because they just died too young.
if not person.had_any_children():
life_span = person.calc_life_span()
if life_span is not None and life_span.days < 365 * 13:
return False
return person.sealing_to_spouse_date is None
class SearchResults(object):
def __init__(self):
super().__init__()
self.search_group_list = [
BaptismNeededGroup(),
EndownmentNeededGroup(),
SealingToParentsNeededGroup(),
SealingToSpouseNeededGroup()
]
self.max_results = 15
def max_results_reached(self):
return all([len(search_group.relationship_list) >= self.max_results for search_group in self.search_group_list])
def conditionally_accumulate(self, relationship):
if relationship.person.deathday is not None:
for search_group in self.search_group_list:
if search_group.is_match(relationship) and len(search_group.relationship_list) < self.max_results:
search_group.relationship_list.append(relationship)
def web_scrape(self, driver, scrape_cache):
all_people = set()
for search_group in self.search_group_list:
all_people = all_people.union({relationship.person for relationship in search_group.relationship_list})
total_people = len(all_people)
print('Total people in search results: %d' % total_people)
i = 0
for person in all_people:
i += 1
print('(%d/%d) Scraping additional info for: %s' % (i, total_people, person.name))
person.web_scrape(driver, scrape_cache)
def generate_text_file(self, out_file):
raise Exception('Not yet implimented')
def generate_csv_file(self, out_file):
raise Exception('Not yet implimented')
def generate_png_files(self, out_file, root_person, use_optimal_paths):
font = ImageFont.truetype('JetBrainsFonts/static/JetBrainsMono-Regular.ttf')
for search_group in self.search_group_list:
path, ext = os.path.splitext(out_file)
image_file_path = path + '_' + search_group.__class__.__name__ + ext
image = Image.new('RGBA', (4096, 4096), (255, 255, 255, 0))
draw = ImageDraw.Draw(image)
print('Generating render tree...')
person_subset = {root_person}
person_subset = person_subset.union({relationship.person for relationship in search_group.relationship_list})
if use_optimal_paths:
# The relationship paths should be of optimal (minimal) length, because
# they were built using a breadth-first search of the family tree.
# This is an additive method.
root_node = RenderNode(person=root_person)
for relationship in search_group.relationship_list:
root_node.construct_using_path(relationship.path)
size = root_node.calculate_size()
print('Render tree size: %d' % size)
else:
# This is one way to generate the render tree, but it does not necessarily result
# in optimal paths. I'm keeping it around, because it is interesting (and perhaps
# unsettling) to see how I can be related to people on both my mother and father's
# side of the family tree. This is a subtractive method.
visitation_set = set()
root_node = root_person.generate_render_tree(visitation_set)
size_before = root_node.calculate_size()
print('Pruning render tree...')
root_node.prune_tree(person_subset)
size_after = root_node.calculate_size()
percentage = 100.0 * size_after / size_before
print('Tree reduced to %2.2f%% of its former size.' % percentage)
print('Calculating tree layout...')
root_node.calculate_graph_layout(draw, font)
root_node.calculate_bounding_box()
print('Rendering tree to image file "%s"...' % image_file_path)
root_node.render_graph(draw, image, font, person_subset)
image.save(image_file_path)