def get_json(self, params, id): graph.update_if_needed() all_sources = {} p2e = {} p2c = {} p2g = {} p = extended_personas( nodes=set([graph.node_from_id(id)]), p2e=p2e, p2c=p2c, p2g=p2g, all_sources=all_sources, styles=None, as_css=True, graph=graph, schemes=None) query = models.P2P.objects.filter( type=models.P2P.sameAs) node = graph.node_from_id(id) assertions = list(models.P2P.objects.filter( type=models.P2P.sameAs, person1__in=node.ids.union(node.different))) decujus = p[node.main_id] return { "person": decujus, "sources": all_sources, "p2c": p2c.values(), "p2e": p2e.values(), "p2p": assertions, "p2g": p2g.values() }
def view(request, id): """Display all details known about persona ID""" id = int(id) graph.update_if_needed() #if len(graph) == 0: # return render_to_response( # 'geneaprove/firsttime.html', # context_instance=RequestContext(request)) styles = None p = extended_personas( nodes=set([graph.node_from_id(id)]), styles=styles, as_css=True, graph=graph, schemes=None) query = models.P2P.objects.filter( type=models.P2P.sameAs) node = graph.node_from_id(id) assertions = list(models.P2P.objects.filter( type=models.P2P.sameAs, person1__in=node.ids.union(node.different))) decujus = p[node.main_id] decujus.all_chars = decujus.all_chars.values() decujus.all_events = decujus.all_events.values() decujus.all_groups = decujus.all_groups.values() data = { "person": decujus, "p2p": assertions, } return HttpResponse(to_json(data), content_type='application/json')
def personaEvents(request, id): """All events for the person""" id = int(id) graph.update_if_needed() schemes = set() # The surety schemes that are needed styles = None p = extended_personas( nodes=set([graph.node_from_id(id)]), styles=styles, as_css=True, graph=graph, schemes=schemes) data = ["%s: %s (%s)%s" % (e.event.name, e.event.date, e.event.place, u"\u2713" if e.event.sources else u"\u2717") for i, e in p[id].all_events.items() if e.role == 'principal' and not e.assertion.disproved] data.extend("%s: %s%s%s" % ( c.char.name, " ".join("%s:%s" % (p.name, p.value) for p in c.parts), "(%s)" % c.char.date if c.char.date else "", u"\u2713" if c.char.sources else u"\u2717") for k, c in p[id].all_chars.items() if not c.assertion.disproved and c.char.name not in ("_UID", )) return HttpResponse( to_json(data, year_only=False), content_type="application/json")
def personaEvents(request, id): """All events for the person""" id = int(id) graph.update_if_needed() schemes = set() # The surety schemes that are needed styles = None p = extended_personas(nodes=set([graph.node_from_id(id)]), styles=styles, as_css=True, graph=graph, schemes=schemes) data = [ "%s: %s (%s)%s" % (e.event.name, e.event.date, e.event.place, u"\u2713" if e.event.sources else u"\u2717") for i, e in p[id].all_events.items() if e.role == 'principal' and not e.assertion.disproved ] data.extend("%s: %s%s%s" % (c.char.name, " ".join( "%s:%s" % (p.name, p.value) for p in c.parts), "(%s)" % c.char.date if c.char.date else "", u"\u2713" if c.char.sources else u"\u2717") for k, c in p[id].all_chars.items() if not c.assertion.disproved and c.char.name not in ("_UID", )) return HttpResponse(to_json(data, year_only=False), content_type="application/json")
def view(request, id): """Display all details known about persona ID""" id = int(id) graph.update_if_needed() if len(graph) == 0: return render_to_response( 'geneaprove/firsttime.html', context_instance=RequestContext(request)) schemes = set() # The surety schemes that are needed styles = None p = extended_personas( nodes=set([graph.node_from_id(id)]), styles=styles, as_css=True, graph=graph, schemes=schemes) surety_schemes = dict() for s in schemes: surety_schemes[s] = models.Surety_Scheme.objects.get(id=s).parts.all() query = models.P2P.objects.filter( type=models.P2P.sameAs) node = graph.node_from_id(id) assertions = list(models.P2P.objects.filter( type=models.P2P.sameAs, person1__in=node.ids.union(node.different))) decujus = p[node.main_id] return render_to_response( 'geneaprove/persona.html', {"decujus": id, "person": decujus, "decujus_name": "%s %s" % (decujus.given_name, decujus.surname), "chars": decujus.all_chars, "events": decujus.all_events, "groups": decujus.all_groups, "schemes": surety_schemes, "p2p": [(0, a) for a in assertions], }, context_instance=RequestContext(request))
def add_parents(p): p.generation = distance[graph.node_from_id(p.id)] if p.generation >= max_levels: return fathers = graph.fathers(p.id) mothers = graph.mothers(p.id) p.parents = [ None if not fathers else persons.get(fathers[0].main_id, None), None if not mothers else persons.get(mothers[0].main_id, None)] for pa in p.parents: if pa: add_parents(pa)
def build_sosa_tree(sosa_tree, marriage, sosa, id): # A person might not be in 'persons', and yet its parent be there, # in case we have filtered out earlier generations. if id in persons: sosa_tree[sosa] = id persons[id].generation = distance[graph.node_from_id(id)] if persons[id].marriage: marriage[sosa] = persons[id].marriage fathers = graph.fathers(id) if fathers: build_sosa_tree(sosa_tree, marriage, sosa * 2, fathers[0].main_id) mothers = graph.mothers(id) if mothers: build_sosa_tree( sosa_tree, marriage, sosa * 2 + 1, mothers[0].main_id)
def view(request, id): """Display all details known about persona ID""" id = int(id) graph.update_if_needed() #if len(graph) == 0: # return render_to_response( # 'geneaprove/firsttime.html', # context_instance=RequestContext(request)) styles = None p = extended_personas(nodes=set([graph.node_from_id(id)]), styles=styles, as_css=True, graph=graph, schemes=None) query = models.P2P.objects.filter(type=models.P2P.sameAs) node = graph.node_from_id(id) assertions = list( models.P2P.objects.filter(type=models.P2P.sameAs, person1__in=node.ids.union(node.different))) decujus = p[node.main_id] decujus.all_chars = decujus.all_chars.values() decujus.all_events = decujus.all_events.values() decujus.all_groups = decujus.all_groups.values() data = { "person": decujus, "p2p": assertions, } return HttpResponse(to_json(data), content_type='application/json')
def get_json(self, params, id): id = int(id); graph.update_if_needed() # ??? The stats includes persons "Unknown" that were created during a # gedcom import for the purpose of preserving families. Will be fixed # when we store children differently (for instance in a group) distance = dict() decujus = graph.node_from_id(id) allpeople = graph.people_in_tree(id=decujus.main_id, distance=distance) persons = extended_personas( nodes=allpeople, styles=None, event_types=event_types_for_pedigree, graph=graph) f = graph.fathers(decujus.main_id) fathers = graph.people_in_tree(id=f[0], maxdepthDescendants=0) if f else [] m = graph.mothers(decujus.main_id) mothers = graph.people_in_tree(id=m[0], maxdepthDescendants=0) if m else [] cal = CalendarGregorian() generations = dict() # list of persons for each generation for a in allpeople: d = distance[a] if d not in generations: generations[d] = [] generations[d].append(a) ranges = [] for index in sorted(generations.keys()): births = None deaths = None gen_range = [index + 1, "?", "?", ""] # gen, min, max, legend for p in generations[index]: p = persons[p.main_id] if p.birth and p.birth.Date: if births is None or p.birth.Date < births: births = p.birth.Date year = p.birth.Date.year(cal) if year is not None: gen_range[1] = year if p.death and p.death.Date: if deaths is None or p.death.Date > deaths: deaths = p.death.Date year = p.death.Date.year(cal) if year is not None: gen_range[2] = year if index >= 0: gen_range[3] = "Gen. %02d (%d / %d) %s - %s" \ % (index + 1, len(generations[index]), 2 ** (index + 1), gen_range[1], gen_range[2]) else: gen_range[3] = "Desc. %02d (%d) %s - %s" \ % (-index, len(generations[index]), gen_range[1], gen_range[2]) # Postprocess the ranges: # generation n's earliest date has to be at least 15 years before # its children's earliest date (can't have children before that) # generation n's latest date (death) has to be after the children's # generation earliest date (first birth) if len(ranges) > 0: if gen_range[1] == "?": gen_range[1] = ranges[-1][1] - 15 if gen_range[2] == "?" or gen_range[2] < ranges[-1][1]: gen_range[2] = ranges[-1][1] if gen_range[2] == '?': gen_range[2] = datetime.datetime.now().year ranges.append(gen_range) ages = [] for a in range(0, 120, 5): ages.append([a, 0, 0, 0]) # date_range, males, females, unknown for p in persons.itervalues(): if p.birth and p.birth.Date and p.death and p.death.Date: age = p.death.Date.years_since(p.birth.Date) if age is not None: if p.sex == "M": ages[int(age / 5)][1] += 1 elif p.sex == "F": ages[int(age / 5)][2] += 1 else: ages[int(age / 5)][3] += 1 return { "total_ancestors": len(allpeople), "total_father": len(fathers), "total_mother": len(mothers), "total_persons": len(graph), "ranges": ranges, "ages": ages, "decujus": decujus.main_id, "decujus_name": "%s %s" % ( persons[decujus.main_id].given_name, persons[decujus.main_id].surname) }
def __get_json_sosa_tree(graph, id, max_levels, style_rules, last_descendant_known=-1, maxdepthDescendants=1, last_gen_known=-1): """ :param last_gen_known: is the number of the last generation for which the client already has data, and thus do not need to be sent again. -1 to retrieve all. :param maxdepthDescendants: The number of generations for which we compute the children. """ decujus = graph.node_from_id(id) styles = Styles(style_rules, graph, decujus=decujus.main_id) distance = dict() ancestors = graph.people_in_tree( id=decujus.main_id, maxdepthAncestors=max_levels - 1, maxdepthDescendants=0, distance=distance) ancestors = [a for a in ancestors if distance[a] >= last_gen_known] descendants = graph.people_in_tree( id=decujus.main_id, maxdepthAncestors=0, distance=distance, maxdepthDescendants=maxdepthDescendants) descendants = [ a for a in descendants if a != decujus and distance[a] >= last_descendant_known] sosa_tree = dict() marriage = dict() children = {} persons = {} all_person_nodes = set(ancestors).union(descendants) if all_person_nodes: persons = extended_personas( all_person_nodes, styles, event_types=event_types_for_pedigree, graph=graph) def add_parents(p): p.generation = distance[graph.node_from_id(p.id)] if p.generation >= max_levels: return fathers = graph.fathers(p.id) mothers = graph.mothers(p.id) p.parents = [ None if not fathers else persons.get(fathers[0].main_id, None), None if not mothers else persons.get(mothers[0].main_id, None)] for pa in p.parents: if pa: add_parents(pa) def add_children(p, gen): p.children = [] sorted = [(persons[node.main_id] if node.main_id in persons else None, node) for node in graph.children(p.id)] sorted.sort( key=lambda c: c[0].birth.Date if c[0] and c[0].birth else None) for c in sorted: if c[0]: c[0].generation = -gen # distance[c[1]] p.children.append(c[0]) if gen < maxdepthDescendants: add_children(c[0], gen + 1) main = persons[decujus.main_id] add_parents(main) add_children(main, gen=1) # We will however return a simpler version of the information computed # above (which includes all known events for the persons) show_age = False def person_to_json_for_pedigree(obj): if isinstance(obj, models.Persona): d = obj.death if show_age and obj.birth: if d: if d.Date: d.Date += " (age %s)" % ( str(d.Date.years_since(obj.birth.Date)), ) else: d = {Date: " (age %s)" % ( str(DateRange.today().years_since(obj.birth.Date)), )} return { 'id': obj.id, 'givn': obj.given_name, 'surn': obj.surname, 'sex': obj.sex, 'generation': obj.generation, 'parents': obj.parents if hasattr(obj, 'parents') else None, 'children': obj.children if hasattr(obj, 'children') else None, 'style': obj.styles, 'birth': obj.birth, 'marriage': obj.marriage, 'death': d} return to_json( obj= {'generations': max_levels, 'descendants': maxdepthDescendants, 'decujus': main, 'styles': styles.all_styles()}, custom=person_to_json_for_pedigree)
def get_sosa_tree(graph, id, max_levels, style_rules, last_descendant_known=-1, maxdepthDescendants=1, last_gen_known=-1): """ :param last_gen_known: is the number of the last generation for which the client already has data, and thus do not need to be sent again. -1 to retrieve all. :param maxdepthDescendants: The number of generations for which we compute the children. """ decujus = graph.node_from_id(id) styles = Styles(style_rules, graph, decujus=decujus.main_id) distance = dict() ancestors = graph.people_in_tree( id=decujus.main_id, maxdepthAncestors=max_levels - 1, maxdepthDescendants=0, distance=distance) ancestors = [a for a in ancestors if distance[a] >= last_gen_known] descendants = graph.people_in_tree( id=decujus.main_id, maxdepthAncestors=0, distance=distance, maxdepthDescendants=maxdepthDescendants) descendants.remove(decujus) descendants = [a for a in descendants if distance[a] >= last_descendant_known] sosa_tree = dict() marriage = dict() children = {} persons = {} all_person_nodes = set(ancestors).union(descendants) if all_person_nodes: persons = extended_personas( all_person_nodes, styles, event_types=event_types_for_pedigree, graph=graph) def build_sosa_tree(sosa_tree, marriage, sosa, id): # A person might not be in 'persons', and yet its parent be there, # in case we have filtered out earlier generations. if id in persons: sosa_tree[sosa] = id persons[id].generation = distance[graph.node_from_id(id)] if persons[id].marriage: marriage[sosa] = persons[id].marriage fathers = graph.fathers(id) if fathers: build_sosa_tree(sosa_tree, marriage, sosa * 2, fathers[0].main_id) mothers = graph.mothers(id) if mothers: build_sosa_tree( sosa_tree, marriage, sosa * 2 + 1, mothers[0].main_id) def build_children_tree(children, id, gen): if id in persons: children[id] = [] sorted = [(persons[node.main_id] if node.main_id in persons else None, node) for node in graph.children(id)] sorted.sort(key=lambda p: p[0].birth.Date if p[0] and p[0].birth else None) for p in sorted: if p[0]: p[0].generation = -distance[p[1]] if id in persons: children[id].append(p[0].id) if gen < maxdepthDescendants: build_children_tree(children, id=p[0].id, gen=gen + 1) build_sosa_tree(sosa_tree, marriage, 1, decujus.main_id) build_children_tree(children, id=decujus.main_id, gen=1) return {'generations': max_levels, 'descendants': maxdepthDescendants, 'persons': persons, # All persons indexed by id 'sosa': sosa_tree, # sosa_number -> person_id 'children': children, # personId -> [children_id*] 'marriage': marriage, # sosa_number -> marriage info 'styles': styles.all_styles()}
def get_json(self, params, id): # ??? Should lock until the view has been generated graph.update_if_needed() max_levels = int(params.get("gens", 5)) last_descendant_known = int(params.get("desc_known", -1)) # The number of generations for which we compute the children. maxdepthDescendants = int(params.get("descendant_gens", 1)) # the number of the last generation for which the client already has # data, and thus do not need to be sent again. -1 to retrieve all. last_gen_known = int(params.get("gens_known", -1)) # Whether to show full dates or only the year self.year_only = params.get('year_only', '') == 'true' decujus = graph.node_from_id(id) styles = Styles(style_rules, graph, decujus=decujus.main_id) distance = dict() people = graph.people_in_tree( id=decujus.main_id, maxdepthAncestors=max_levels - 1, maxdepthDescendants=maxdepthDescendants, distance=distance) ancestors = [a for a in people if distance[a] >= 0 and distance[a] >= last_gen_known] descendants = [a for a in people if a != decujus and distance[a] < 0 and distance[a] <= -last_descendant_known] sosa_tree = dict() marriage = dict() children = {} persons = {} all_person_nodes = set(ancestors).union(descendants) if all_person_nodes: persons = extended_personas( all_person_nodes, styles, event_types=event_types_for_pedigree, graph=graph) def add_parents(p): p.generation = distance[graph.node_from_id(p.id)] if p.generation >= max_levels: return fathers = graph.fathers(p.id) mothers = graph.mothers(p.id) p.parents = [ None if not fathers else persons.get(fathers[0].main_id, None), None if not mothers else persons.get(mothers[0].main_id, None)] for pa in p.parents: if pa: add_parents(pa) def add_children(p, gen): p.children = [] sorted = [(persons[node.main_id] if node.main_id in persons else None, node) for node in graph.children(p.id)] sorted.sort( key=lambda c: c[0].birth.Date if c[0] and c[0].birth else None) for c in sorted: if c[0]: c[0].generation = -gen # distance[c[1]] p.children.append(c[0]) if gen < maxdepthDescendants: add_children(c[0], gen + 1) main = persons[decujus.main_id] add_parents(main) add_children(main, gen=1) return {'generations': max_levels, 'descendants': maxdepthDescendants, 'decujus': main, 'styles': styles.all_styles()}
def find_candidate(graph): """Find all candidate personas for a merge""" p1 = 7843 # Emmanuel Briot p2 = 1 # Emmanuel Briot p3 = 3052 # Marie HOUTEVILLE p4 = 3335 # Thomine Levesque p5 = 1311 p6 = 7842 persons = extended_personas( nodes=set([graph.node_from_id(p1), graph.node_from_id(p2), graph.node_from_id(p3), graph.node_from_id(p4), graph.node_from_id(p5), graph.node_from_id(p6)]), styles=None, same=same, query_groups=False) for p in [(p1, p2), (p2, p1), (p3, p4), (p5, p6), (p6, p5)]: print persons[p[0]].name, persons[p[1]].name score = compare(persons[p[0]], persons[p[1]]) print " => ", score # Get all persons from the database with a guess at their lifespan. # If we know the birth date, lifespan starts there, otherwise it starts # some years before the first event # Likewise for death date. # This results is potentially over-optimistic lifespans, but still reduces # the number of comparisons to do. # The following query (and its processing) might take a while on big # databases, but we'll need access to the whole information for persons # anyway, so we might as well query everything from the start) # number of persons: 9171 # number of queries:6 total queries time:0.26s total time:26.21s persons = extended_personas( nodes=None, styles=None, graph=graph, query_groups=False) # A temporary structure ordered by the first date in lifespan births = [] delta = datetime.timedelta(days=maximum_lifespan * 365) for p in persons.itervalues(): birth = death = None if p.birth is not None: birth = p.birth.date_sort if p.death is not None: death = p.death.date_sort if birth is None or death is None: h = [a.event.date_sort for a in p.all_events.itervalues() if a.event.date_sort is not None] if h: h.sort() birth = birth or h[0] - delta death = death or h[-1] + delta # If birth is None, that means there are no events, and we don't # really want to merge that person then. if birth: p.max_lifespan = death heapq.heappush(births, (birth, p)) # Now we traverse the list and only compare persons that were alive at # the same time (otherwise we assume they cannot be merged) # ??? We can save time by not comparing when we have already # decided in the past they can't be the same alive = [] # Each person alive at the given date comparisons = 0 same = 0 while births: date, person = heapq.heappop(births) for a in alive: if a.max_lifespan < date: alive.remove(a) elif date.year < 1970: continue else: # print "Compare %s and %s" % (person.name, a.name) comparisons += 1 score = compare(a, person) if score >= 150: print "%d Might be the same: %d %s and %d %s, score=%d %d" % ( date.year, person.id, person.name, a.id, a.name, score, compare(person, a)) same += 1 alive.append(person) # Maximum comparisons (n^2) would be: 83_302_129 # Actual comparisons with this algo: 14_635_289 print "Number of comparisons: ", comparisons print "Possible merges: ", same
def extended_personas( nodes, styles, graph, p2e=None, event_types=None, schemes=None, p2c=None, p2g=None, all_sources=None, as_css=False, query_groups=True): """ Compute the events for the various persons in `nodes` (all all persons in the database if None) Return a dict indexed on id containing extended instances of Persona, with additional fields for the birth, the death,... :param nodes: A set of graph.Persona_node, or None to get all persons from the database. :param graph: an instance of Graph, which is used to compute whether two ids represent the same person. :param dict all_sources: either a dictionary, or None. If specified, it will be filled with "sourceId -> models.Source" objects :param as_css: True to get the styles as a CSS string rather than a python dict :param event_types: restricts the types of events that are retrieved :param dict p2e: All person-to-event assertions :param dict p2c: All persona-to-characteristic assertions :param dict p2g: All persona-to-group assertions :return: a list of persons: * persons is a dictionary of Persona instances, indexed on persona_id SCHEMES is the list of ids of Surety_Scheme that are used. You should pass a set() if you are interested in this. Otherwise, it is just discarded. This sets persons[*].chars to a list of the characteristics. Only the events of type in TYPES are returned """ if nodes: ids = [a.main_id for a in nodes] else: ids = None compute_parts = styles and styles.need_place_parts() roles = dict() # role_id -> name places = dict() # place_id -> place assert(schemes is None or isinstance(schemes, set)) if styles: styles.start() # Get the role names for role in models.Event_Type_Role.objects.all(): roles[role.id] = role.name ############## # Create the personas that will be returned. ############## persons = dict() # id -> person if ids: for p in sql_in(models.Persona.objects, "id", ids): # p.id is always the main_id, since that's how ids was built persons[p.id] = p __add_default_person_attributes(p) else: for p in models.Persona.objects.all(): mid = graph.node_from_id(p.id).main_id if mid not in persons: persons[mid] = p __add_default_person_attributes(p) ################ # Check all events that the persons were involved in. ################ events = models.P2E.objects.select_related( 'event', 'event__place', 'event__type', 'surety') if event_types: events = events.filter(event__type__in=event_types) all_ids = None if nodes: all_ids = set() for p in nodes: all_ids.update(p.ids) # All query the 'principal' for each events, so that we can provide # that information graphically. for p in sql_in(events, "person", all_ids): e = p.event p_node = graph.node_from_id(p.person_id) person = persons[p_node.main_id] # ??? A person could be involved multiple times in the same # event, under multiple roles. Here we are only preserving the # last occurrence if p2e is not None: # ??? Should we reset p.p1, since this is always the same p2e[e.id] = p if all_sources is not None: all_sources.setdefault(p.source_id, {}) e.Date = e.date and DateRange(e.date) if schemes is not None: schemes.add(p.surety.scheme_id) if compute_parts and e.place: places[e.place_id] = e.place if styles: styles.process(person, p.role_id, e) if not p.disproved \ and p.role_id == models.Event_Type_Role.principal: if not e.Date: pass elif e.type_id == models.Event_Type.birth: if person.birth is None \ or person.birth.date_sort > e.date_sort: person.birth = e elif e.type_id == models.Event_Type.death: if person.death is None \ or person.death.date_sort < e.date_sort: person.death = e elif e.type_id == models.Event_Type.marriage: person.marriage = e ######### # Get all groups to which the personas belong ######### if query_groups: groups = models.P2G.objects.select_related('group') for gr in sql_in(groups, "person", all_ids): p_node = graph.node_from_id(gr.person_id) person = persons[p_node.main_id] if p2g is not None: p2g[gr.group_id] = gr if all_sources is not None: all_sources.setdefault(gr.source_id, {}) if schemes is not None: schemes.add(gr.surety.scheme_id) ######### # Get all characteristics of these personas ######### c2p = dict() # characteristic_id -> person all_p2c = models.P2C.objects.select_related( 'characteristic', 'characteristic__place') for p in sql_in(all_p2c, "person", all_ids): c = p.characteristic p_node = graph.node_from_id(p.person_id) person = persons[p_node.main_id] c2p[c.id] = person if all_sources is not None: all_sources.setdefault(p.source_id, {}) c.date = c.date and DateRange(c.date) if schemes is not None: schemes.add(p.surety.scheme_id) if p2c is not None: # ??? Should we reset p.person, since this is always the same # ??? but can't set it to None in the model p2c[c.id] = p if compute_parts and c.place: places[c.place_id] = c.place chars = models.Characteristic_Part.objects.select_related( 'type', 'characteristic', 'characteristic__place') for part in sql_in(chars, "characteristic", nodes and c2p.keys()): person = c2p[part.characteristic_id] if part.type_id == models.Characteristic_Part_Type.sex: person.sex = part.name elif part.type_id == models.Characteristic_Part_Type.given_name: person.given_name = part.name elif part.type_id == models.Characteristic_Part_Type.surname: person.surname = part.name ######## # Compute place parts once, to limit the number of queries # These are only used for styles, not for actual display, although we # could benefit from them. ######## if compute_parts: prev_place = None d = None for p in sql_in(models.Place_Part.objects .order_by('place').select_related('type'), "place", places.keys()): # ??? We should also check the parent place to gets its own parts if p.place_id != prev_place: prev_place = p.place_id d = dict() setattr(places[prev_place], "parts", d) d[p.type.name] = p.name ########## # Get the title for all sources that are mentioned ########## if all_sources is not None: for s in sql_in(models.Source.objects, "id", all_sources.keys()): all_sources[s.id] = s ########## # Compute the styles ########## if styles: for p in persons.itervalues(): styles.compute(p, as_css=as_css) return persons
def __get_events(nodes, styles, graph, types=None, schemes=None, query_groups=True): """Compute the events for the various persons in IDS (all all persons in the database if None) :param nodes: A set of graph.Persona_node, or None to get all persons from the database. :param graph: an instance of Graph, which is used to compute whether two ids represent the same person. :return: a list of persons: * persons is a dictionary of Persona instances, indexed on persona_id SCHEMES is the list of ids of Surety_Scheme that are used. You should pass a set() if you are interested in this. Otherwise, it is just discarded. This sets persons[*].chars to a list of the characteristics. Only the events of type in TYPES are returned """ if nodes: ids = [a.main_id for a in nodes] else: ids = None compute_parts = styles and styles.need_place_parts() roles = dict() # role_id -> name places = dict() # place_id -> place assert(schemes is None or isinstance(schemes, set)) # Get the role names for role in models.Event_Type_Role.objects.all(): roles[role.id] = role.name ############## # Create the personas that will be returned. ############## persons = dict() # id -> person if ids: for p in sql_in(models.Persona.objects, "id", ids): # p.id is always the main_id, since that's how ids was built persons[p.id] = p __add_default_person_attributes(p) else: for p in models.Persona.objects.all(): mid = graph.node_from_id(p.id).main_id if mid not in persons: persons[mid] = p __add_default_person_attributes(p) ################ # Check all events that the persons were involved in. ################ events = models.P2E.objects.select_related( 'event', 'event__place', 'event__type', 'surety') if types: events = events.filter(event__type__in=types) all_ids = None if nodes: all_ids = set() for p in nodes: all_ids.update(p.ids) all_events = dict() # All query the 'principal' for each events, so that we can provide # that information graphically. for p in sql_in(events, "person", all_ids): #or_q=Q(role=models.Event_Type_Role.principal)): e = p.event p_node = graph.node_from_id(p.person_id) person = persons[p_node.main_id] person.all_events[e.id] = EventInfo( event=e, role=roles[p.role_id], assertion=p) e.sources = getattr(e, "sources", set()) e.sources.add(p.source_id) e.Date = e.date and DateRange(e.date) if schemes is not None: schemes.add(p.surety.scheme_id) if compute_parts and e.place: places[e.place_id] = e.place if styles: styles.process(person, p.role_id, e) if not p.disproved \ and p.role_id == models.Event_Type_Role.principal: if not e.Date: pass elif e.type_id == models.Event_Type.birth: if person.birth is None \ or person.birth.date_sort > e.date_sort: person.birth = e elif e.type_id == models.Event_Type.death: if person.death is None \ or person.death.date_sort < e.date_sort: person.death = e elif e.type_id == models.Event_Type.marriage: person.marriage = e ######### # Get all groups to which the personas belong ######### if query_groups: groups = models.P2G.objects.select_related('group') for gr in sql_in(groups, "person", all_ids): p_node = graph.node_from_id(gr.person_id) person = persons[p_node.main_id] person.all_groups[gr.group_id] = GroupInfo( group=gr.group, assertion=gr) if gr.source_id: src = getattr(gr.group, "sources", []) src.append(gr.source_id) gr.group.sources = src gr.group.role = gr.role if schemes is not None: schemes.add(gr.surety.scheme_id) ######### # Get all characteristics of these personas ######### p2c = dict() # characteristic_id -> person all_p2c = models.P2C.objects.select_related( 'characteristic', 'characteristic__place') for p in sql_in(all_p2c, "person", all_ids): c = p.characteristic p_node = graph.node_from_id(p.person_id) person = persons[p_node.main_id] p2c[c.id] = person c.sources = getattr(c, "sources", set()) c.sources.add(p.source_id) c.date = c.date and DateRange(c.date) if schemes is not None: schemes.add(p.surety.scheme_id) person.all_chars[c.id] = CharInfo( char=c, assertion=p, parts=[]) if compute_parts and c.place: places[c.place_id] = c.place chars = models.Characteristic_Part.objects.select_related( 'type', 'characteristic', 'characteristic__place') for part in sql_in(chars, "characteristic", nodes and p2c.keys()): person = p2c[part.characteristic_id] ch = person.all_chars[part.characteristic_id] ch.parts.append(CharPartInfo(name=part.type.name, value=part.name)) if part.type_id == models.Characteristic_Part_Type.sex: person.sex = part.name elif part.type_id == models.Characteristic_Part_Type.given_name: person.given_name = part.name elif part.type_id == models.Characteristic_Part_Type.surname: person.surname = part.name ######## # Compute place parts once, to limit the number of queries # These are only used for styles, not for actual display, although we # could benefit from them. ######## if compute_parts: prev_place = None d = None for p in sql_in(models.Place_Part.objects .order_by('place').select_related('type'), "place", places.keys()): # ??? We should also check the parent place to gets its own parts if p.place_id != prev_place: prev_place = p.place_id d = dict() setattr(places[prev_place], "parts", d) d[p.type.name] = p.name return persons
def __get_events(nodes, styles, graph, types=None, schemes=None, query_groups=True): """Compute the events for the various persons in IDS (all all persons in the database if None) :param nodes: A set of graph.Persona_node, or None to get all persons from the database. :param graph: an instance of Graph, which is used to compute whether two ids represent the same person. :return: a list of persons: * persons is a dictionary of Persona instances, indexed on persona_id SCHEMES is the list of ids of Surety_Scheme that are used. You should pass a set() if you are interested in this. Otherwise, it is just discarded. This sets persons[*].chars to a list of the characteristics. Only the events of type in TYPES are returned """ if nodes: ids = [a.main_id for a in nodes] else: ids = None compute_parts = styles and styles.need_place_parts() roles = dict() # role_id -> name places = dict() # place_id -> place assert (schemes is None or isinstance(schemes, set)) # Get the role names for role in models.Event_Type_Role.objects.all(): roles[role.id] = role.name ############## # Create the personas that will be returned. ############## persons = dict() # id -> person if ids: for p in sql_in(models.Persona.objects, "id", ids): # p.id is always the main_id, since that's how ids was built persons[p.id] = p __add_default_person_attributes(p) else: for p in models.Persona.objects.all(): mid = graph.node_from_id(p.id).main_id if mid not in persons: persons[mid] = p __add_default_person_attributes(p) ################ # Check all events that the persons were involved in. ################ events = models.P2E.objects.select_related('event', 'event__place', 'event__type', 'surety') if types: events = events.filter(event__type__in=types) all_ids = None if nodes: all_ids = set() for p in nodes: all_ids.update(p.ids) all_events = dict() # All query the 'principal' for each events, so that we can provide # that information graphically. for p in sql_in(events, "person", all_ids): # or_q=Q(role=models.Event_Type_Role.principal)): e = p.event p_node = graph.node_from_id(p.person_id) person = persons[p_node.main_id] person.all_events[e.id] = EventInfo(event=e, role=roles[p.role_id], assertion=p) e.sources = getattr(e, "sources", set()) e.sources.add(p.source_id) e.Date = e.date and DateRange(e.date) if schemes is not None: schemes.add(p.surety.scheme_id) if compute_parts and e.place: places[e.place_id] = e.place if styles: styles.process(person, p.role_id, e) if not p.disproved \ and p.role_id == models.Event_Type_Role.principal: if not e.Date: pass elif e.type_id == models.Event_Type.birth: if person.birth is None \ or person.birth.date_sort > e.date_sort: person.birth = e elif e.type_id == models.Event_Type.death: if person.death is None \ or person.death.date_sort < e.date_sort: person.death = e elif e.type_id == models.Event_Type.marriage: person.marriage = e ######### # Get all groups to which the personas belong ######### if query_groups: groups = models.P2G.objects.select_related('group') for gr in sql_in(groups, "person", all_ids): p_node = graph.node_from_id(gr.person_id) person = persons[p_node.main_id] person.all_groups[gr.group_id] = GroupInfo(group=gr.group, assertion=gr) if gr.source_id: src = getattr(gr.group, "sources", []) src.append(gr.source_id) gr.group.sources = src gr.group.role = gr.role if schemes is not None: schemes.add(gr.surety.scheme_id) ######### # Get all characteristics of these personas ######### p2c = dict() # characteristic_id -> person all_p2c = models.P2C.objects.select_related('characteristic', 'characteristic__place') for p in sql_in(all_p2c, "person", all_ids): c = p.characteristic p_node = graph.node_from_id(p.person_id) person = persons[p_node.main_id] p2c[c.id] = person c.sources = getattr(c, "sources", set()) c.sources.add(p.source_id) c.date = c.date and DateRange(c.date) if schemes is not None: schemes.add(p.surety.scheme_id) person.all_chars[c.id] = CharInfo(char=c, assertion=p, parts=[]) if compute_parts and c.place: places[c.place_id] = c.place chars = models.Characteristic_Part.objects.select_related( 'type', 'characteristic', 'characteristic__place') for part in sql_in(chars, "characteristic", nodes and p2c.keys()): person = p2c[part.characteristic_id] ch = person.all_chars[part.characteristic_id] ch.parts.append(CharPartInfo(name=part.type.name, value=part.name)) if part.type_id == models.Characteristic_Part_Type.sex: person.sex = part.name elif part.type_id == models.Characteristic_Part_Type.given_name: person.given_name = part.name elif part.type_id == models.Characteristic_Part_Type.surname: person.surname = part.name ######## # Compute place parts once, to limit the number of queries # These are only used for styles, not for actual display, although we # could benefit from them. ######## if compute_parts: prev_place = None d = None for p in sql_in( models.Place_Part.objects.order_by('place').select_related( 'type'), "place", places.keys()): # ??? We should also check the parent place to gets its own parts if p.place_id != prev_place: prev_place = p.place_id d = dict() setattr(places[prev_place], "parts", d) d[p.type.name] = p.name return persons