Skip to content

Commit d97292c

Browse files
authored
Add event role to timeline. (#284)
* Type hint improvements * Add event role to timeline * Add tests * Fix test
1 parent 633c5a0 commit d97292c

File tree

2 files changed

+58
-55
lines changed

2 files changed

+58
-55
lines changed

gramps_webapi/api/resources/timeline.py

Lines changed: 57 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
#
1919

2020
"""Timeline API resources."""
21-
from typing import Dict, List, Tuple, Union
21+
from typing import Dict, List, Optional, Set, Tuple, Union
2222

2323
from flask import abort
2424
from gramps.gen.const import GRAMPS_LOCALE as glocale
2525
from gramps.gen.db.base import DbReadBase
2626
from gramps.gen.display.place import PlaceDisplay
2727
from gramps.gen.errors import HandleError
28-
from gramps.gen.lib import Date, Event, EventType, Person, Span
28+
from gramps.gen.lib import Date, Event, EventRef, EventType, Person, Span
2929
from gramps.gen.relationship import get_relationship_calculator
3030
from gramps.gen.utils.alive import probably_alive_range
3131
from gramps.gen.utils.db import (
@@ -99,19 +99,19 @@ class Timeline:
9999
def __init__(
100100
self,
101101
db_handle: DbReadBase,
102-
dates: Union[str, None] = None,
103-
events: Union[List, None] = None,
102+
dates: Optional[str] = None,
103+
events: Optional[List[str]] = None,
104104
ratings: bool = False,
105-
relatives: Union[List, None] = None,
106-
relative_events: Union[List, None] = None,
105+
relatives: Optional[List[str]] = None,
106+
relative_events: Optional[List[str]] = None,
107107
discard_empty: bool = True,
108108
omit_anchor: bool = True,
109109
precision: int = 1,
110110
locale: GrampsLocale = glocale,
111111
):
112112
"""Initialize timeline."""
113113
self.db_handle = db_handle
114-
self.timeline = []
114+
self.timeline: List[Tuple[Event, Person, str, str]] = []
115115
self.dates = dates
116116
self.start_date = None
117117
self.end_date = None
@@ -122,14 +122,14 @@ def __init__(
122122
self.anchor_person = None
123123
self.omit_anchor = omit_anchor
124124
self.depth = 1
125-
self.eligible_events = set([])
126-
self.event_filters = events or []
127-
self.eligible_relative_events = set([])
128-
self.relative_event_filters = relative_events or []
129-
self.relative_filters = relatives or []
125+
self.eligible_events: Set[str] = set()
126+
self.event_filters: List[str] = events or []
127+
self.eligible_relative_events: Set[str] = set()
128+
self.relative_event_filters: List[str] = relative_events or []
129+
self.relative_filters: List[str] = relatives or []
130130
self.set_event_filters(self.event_filters)
131131
self.set_relative_event_filters(self.relative_event_filters)
132-
self.birth_dates = {}
132+
self.birth_dates: Dict[str, Date] = {}
133133

134134
if dates and "-" in dates:
135135
start, end = dates.split("-")
@@ -172,19 +172,19 @@ def set_locale(self, locale: str):
172172
"""Set optional locale for span."""
173173
self.locale = get_locale_for_language(locale, default=True)
174174

175-
def set_event_filters(self, filters: Union[List, None] = None):
175+
def set_event_filters(self, filters: Optional[List[str]] = None):
176176
"""Prepare the event filter table."""
177177
self.event_filters = filters or []
178178
self.eligible_events = self._prepare_eligible_events(self.event_filters)
179179

180-
def set_relative_event_filters(self, filters: Union[List, None] = None):
180+
def set_relative_event_filters(self, filters: Optional[List[str]] = None):
181181
"""Prepare the relative event filter table."""
182182
self.relative_event_filters = filters or []
183183
self.eligible_relative_events = self._prepare_eligible_events(
184184
self.relative_event_filters
185185
)
186186

187-
def _prepare_eligible_events(self, event_filters: List):
187+
def _prepare_eligible_events(self, event_filters: List[str]):
188188
"""Prepare an event filter list."""
189189
eligible_events = {"Birth", "Death"}
190190
event_type = EventType()
@@ -199,9 +199,7 @@ def _prepare_eligible_events(self, event_filters: List):
199199
eligible_events.add(key)
200200
continue
201201
if key not in EVENT_CATEGORIES:
202-
raise ValueError(
203-
"{} is not a valid event or event category".format(key)
204-
)
202+
raise ValueError(f"{key} is not a valid event or event category")
205203
for entry in event_type.get_menu_standard_xml():
206204
event_key = entry[0].lower().replace("life events", "vital")
207205
if event_key in event_filters:
@@ -214,7 +212,7 @@ def _prepare_eligible_events(self, event_filters: List):
214212
eligible_events.add(event_name)
215213
return eligible_events
216214

217-
def get_age(self, start_date, date):
215+
def get_age(self, start_date: str, date: str):
218216
"""Return calculated age or empty string otherwise."""
219217
age = ""
220218
if start_date:
@@ -255,7 +253,7 @@ def is_eligible(self, event: Event, relative: bool):
255253
return True
256254
return str(event.get_type()) in self.eligible_events
257255

258-
def add_event(self, event: Tuple, relative=False):
256+
def add_event(self, event: Tuple[Event, Person, str, str], relative: bool = False):
259257
"""Add event to timeline if needed."""
260258
if self.discard_empty:
261259
if event[0].date.sortval == 0:
@@ -295,7 +293,8 @@ def add_person(
295293
self.birth_dates.update({person.handle: event.date})
296294
for event_ref in person.event_ref_list:
297295
event = self.db_handle.get_event_from_handle(event_ref.ref)
298-
self.add_event((event, person, "self"))
296+
role = event_ref.get_role().xml_str()
297+
self.add_event((event, person, "self", role))
299298
if anchor and not self.anchor_person:
300299
self.anchor_person = person
301300
self.depth = max(ancestors, offspring) + 1
@@ -345,28 +344,29 @@ def add_relative(self, handle: Handle, ancestors: int = 1, offspring: int = 1):
345344
if self.relative_event_filters:
346345
for event_ref in person.event_ref_list:
347346
event = self.db_handle.get_event_from_handle(event_ref.ref)
348-
self.add_event((event, person, relationship), relative=True)
347+
role = event_ref.get_role().xml_str()
348+
self.add_event((event, person, relationship, role), relative=True)
349349

350350
event = get_birth_or_fallback(self.db_handle, person)
351351
if event:
352-
self.add_event((event, person, relationship), relative=True)
352+
self.add_event((event, person, relationship, "Primary"), relative=True)
353353
if person.handle not in self.birth_dates:
354354
self.birth_dates.update({person.handle: event.date})
355355

356356
event = get_death_or_fallback(self.db_handle, person)
357357
if event:
358-
self.add_event((event, person, relationship), relative=True)
358+
self.add_event((event, person, relationship, "Primary"), relative=True)
359359

360360
for family_handle in person.family_list:
361361
family = self.db_handle.get_family_from_handle(family_handle)
362362

363363
event = get_marriage_or_fallback(self.db_handle, family)
364364
if event:
365-
self.add_event((event, person, relationship), relative=True)
365+
self.add_event((event, person, relationship, "Family"), relative=True)
366366

367367
event = get_divorce_or_fallback(self.db_handle, family)
368368
if event:
369-
self.add_event((event, person, relationship), relative=True)
369+
self.add_event((event, person, relationship, "Family"), relative=True)
370370

371371
if offspring > 1:
372372
for child_ref in family.child_ref_list:
@@ -382,7 +382,7 @@ def add_relative(self, handle: Handle, ancestors: int = 1, offspring: int = 1):
382382
def add_family(
383383
self,
384384
handle: Handle,
385-
anchor: Union[Person, None] = None,
385+
anchor: Optional[Person] = None,
386386
include_children: bool = True,
387387
ancestors: int = 1,
388388
offspring: int = 1,
@@ -393,7 +393,8 @@ def add_family(
393393
if anchor:
394394
for event_ref in family.event_ref_list:
395395
event = self.db_handle.get_event_from_handle(event_ref.ref)
396-
self.add_event((event, anchor, "self"))
396+
role = event_ref.get_role().xml_str()
397+
self.add_event((event, anchor, "self", role))
397398
if events_only:
398399
return
399400
if self.anchor_person:
@@ -427,68 +428,69 @@ def profile(self, page=0, pagesize=20):
427428
if page > 0:
428429
offset = (page - 1) * pagesize
429430
events = events[offset : offset + pagesize]
430-
for event in events:
431-
label = self.locale.translation.sgettext(str(event[0].type))
431+
for (event, person_object, relationship, role) in events:
432+
label = self.locale.translation.sgettext(str(event.type))
432433
if (
433-
event[1]
434+
person_object
434435
and self.anchor_person
435-
and self.anchor_person.handle != event[1].handle
436-
and event[2] not in ["self", "", None]
436+
and self.anchor_person.handle != person_object.handle
437+
and relationship not in ["self", "", None]
437438
):
438-
label = "{} ({})".format(label, event[2].title())
439+
label = f"{label} ({relationship.title()})"
439440

440441
try:
441-
obj = self.db_handle.get_place_from_handle(event[0].place)
442+
obj = self.db_handle.get_place_from_handle(event.place)
442443
place = get_place_profile_for_object(
443444
self.db_handle, obj, locale=self.locale
444445
)
445-
place["display_name"] = pd.display_event(self.db_handle, event[0])
446-
place["handle"] = event[0].place
446+
place["display_name"] = pd.display_event(self.db_handle, event)
447+
place["handle"] = event.place
447448
except HandleError:
448449
place = {}
449450

450451
age = ""
451452
person = {}
452-
if event[1] is not None:
453+
if person_object is not None:
453454
person_age = ""
454455
get_person = True
455456
if self.anchor_person:
456457
if self.anchor_person.handle in self.birth_dates:
457458
age = self.get_age(
458-
self.birth_dates[self.anchor_person.handle], event[0].date
459+
self.birth_dates[self.anchor_person.handle], event.date
459460
)
460-
if self.anchor_person.handle == event[1].handle:
461+
if self.anchor_person.handle == person_object.handle:
461462
person_age = age
462463
if self.omit_anchor:
463464
get_person = False
464465
if get_person:
465466
person = get_person_profile_for_object(
466-
self.db_handle, event[1], {}, locale=self.locale
467+
self.db_handle, person_object, {}, locale=self.locale
467468
)
468-
if not person_age and event[1].handle in self.birth_dates:
469+
if not person_age and person_object.handle in self.birth_dates:
469470
person_age = self.get_age(
470-
self.birth_dates[event[1].handle], event[0].date
471+
self.birth_dates[person_object.handle], event.date
471472
)
472473
if not age:
473474
age = person_age
474475
person["age"] = person_age
475476

476477
profile = {
477-
"date": self.locale.date_displayer.display(event[0].date),
478-
"description": event[0].description,
479-
"gramps_id": event[0].gramps_id,
480-
"handle": event[0].handle,
478+
"date": self.locale.date_displayer.display(event.date),
479+
"description": event.description,
480+
"gramps_id": event.gramps_id,
481+
"handle": event.handle,
481482
"label": self.locale.translation.sgettext(label),
482-
"media": [x.ref for x in event[0].media_list],
483+
"media": [x.ref for x in event.media_list],
483484
"person": person,
484485
"place": place,
485486
"age": age,
486-
"type": event[0].type,
487+
"type": event.type,
488+
"role": self.locale.translation.gettext(role),
487489
}
488-
profile["person"]["relationship"] = str(event[2])
490+
profile["person"]["relationship"] = str(relationship)
489491
if self.ratings:
490-
profile["citations"] = event[0].citations
491-
profile["confidence"] = event[0].confidence
492+
profile["citations"] = event.citations
493+
profile["confidence"] = event.confidence
492494
profiles.append(profile)
493495
return profiles
494496

@@ -584,7 +586,7 @@ def get(self, args: Dict, handle: str):
584586
locale=locale,
585587
)
586588
timeline.add_person(
587-
handle,
589+
Handle(handle),
588590
anchor=True,
589591
start=args["first"],
590592
end=args["last"],
@@ -645,7 +647,7 @@ def get(self, args: Dict, handle: str):
645647
discard_empty=args["discard_empty"],
646648
locale=locale,
647649
)
648-
timeline.add_family(handle)
650+
timeline.add_family(Handle(handle))
649651
except ValueError:
650652
abort(422)
651653
except HandleError:

tests/test_endpoints/test_timelines.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def test_get_timelines_people_parameter_locale_expected_result(self):
167167
"""Test expected profile response for a locale."""
168168
rv = check_success(self, TEST_URL + "people/?page=1&locale=de")
169169
self.assertEqual(rv[0]["label"], "Heirat")
170+
self.assertEqual(rv[0]["role"], "Familie")
170171
self.assertEqual(rv[0]["person"]["birth"]["type"], "Geburt")
171172
self.assertEqual(rv[0]["person"]["death"]["type"], "Tod")
172173

0 commit comments

Comments
 (0)