Skip to content

Commit

Permalink
Refactor event types into plugins (#1699)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartfeenstra authored Sep 14, 2024
1 parent 607eba6 commit 8d59618
Show file tree
Hide file tree
Showing 22 changed files with 953 additions and 127 deletions.
1 change: 1 addition & 0 deletions betty/ancestry/event_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from betty.locale.localizable import _, Localizable
from betty.locale.localizer import DEFAULT_LOCALIZER
from betty.machine_name import MachineName
from betty.plugin import Plugin, PluginRepository
from betty.plugin.entry_point import EntryPointPluginRepository

Expand Down
3 changes: 3 additions & 0 deletions betty/assertion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ def __call__(self, value: _AssertionValueT) -> _AssertionReturnT:
Invoke the chain with a value.
This method may be called more than once.
:raises betty.assertion.error.AssertionFailed: Raised if any part of the
assertion chain fails.
"""
return self._assertion(value)

Expand Down
6 changes: 1 addition & 5 deletions betty/config/collections/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,7 @@ class ConfigurationMapping(
"""

@abstractmethod
def _load_key(
self,
item_dump: Dump,
key_dump: str,
) -> Dump:
def _load_key(self, item_dump: Dump, key_dump: str) -> Dump:
pass

@abstractmethod
Expand Down
10 changes: 8 additions & 2 deletions betty/extension/gramps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@ async def _load_ancestry(event: LoadAncestryEvent) -> None:
Gramps
].extension_configuration
assert isinstance(gramps_configuration, GrampsConfiguration)
for family_tree in gramps_configuration.family_trees:
file_path = family_tree.file_path
for family_tree_configuration in gramps_configuration.family_trees:
file_path = family_tree_configuration.file_path
if file_path:
await GrampsLoader(
project.ancestry,
attribute_prefix_key=project.configuration.name,
factory=project.new,
localizer=project.app.localizer,
event_type_map={
event_type_configuration.gramps_event_type: await project.event_types.get(
event_type_configuration.event_type_id
)
for event_type_configuration in family_tree_configuration.event_types.values()
},
).load_file(file_path)


Expand Down
159 changes: 149 additions & 10 deletions betty/extension/gramps/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,159 @@
assert_record,
assert_path,
assert_setattr,
assert_mapping,
assert_len,
assert_str,
)
from betty.config import Configuration
from betty.config.collections.mapping import ConfigurationMapping
from betty.config.collections.sequence import ConfigurationSequence
from betty.machine_name import assert_machine_name, MachineName
from betty.serde.dump import minimize, Dump, VoidableDump


def _assert_gramps_event_type(value: Any) -> str:
event_type = assert_str()(value)
assert_len(minimum=1)(event_type)
return event_type


class FamilyTreeEventTypeConfiguration(Configuration):
"""
Configure for loading Gramps events.
"""

_gramps_event_type: str
_event_type_id: MachineName

def __init__(self, gramps_event_type: str, event_type_id: MachineName):
super().__init__()
self.gramps_event_type = gramps_event_type
self.event_type_id = event_type_id

@property
def gramps_event_type(self) -> str:
"""
The Gramps event type this configuration applies to.
"""
return self._gramps_event_type

@gramps_event_type.setter
def gramps_event_type(self, event_type: str) -> None:
self._gramps_event_type = _assert_gramps_event_type(event_type)

@property
def event_type_id(self) -> MachineName:
"""
The ID of the Betty event type to load Gramps events of type :py:attr:`betty.extension.gramps.config.FamilyTreeEventTypeConfiguration.gramps_event_type` as.
"""
return self._event_type_id

@event_type_id.setter
def event_type_id(self, event_type_id: MachineName) -> None:
self._event_type_id = assert_machine_name()(event_type_id)

@override
def load(self, dump: Dump) -> None:
assert_record(
RequiredField(
"gramps_event_type", assert_setattr(self, "gramps_event_type")
),
RequiredField("event_type", assert_setattr(self, "event_type_id")),
)(dump)

@override
def dump(self) -> VoidableDump:
return {
"gramps_event_type": self.gramps_event_type,
"event_type": self.event_type_id,
}

@override
def update(self, other: Self) -> None:
self.gramps_event_type = other.gramps_event_type
self.event_type_id = other.event_type_id


class FamilyTreeEventTypeConfigurationMapping(
ConfigurationMapping[str, FamilyTreeEventTypeConfiguration]
):
"""
Configure how to map Gramps events to Betty events.
"""

def __init__(
self, configurations: Iterable[FamilyTreeEventTypeConfiguration] | None = None
):
if configurations is None:
configurations = [
FamilyTreeEventTypeConfiguration("Adopted", "adoption"),
FamilyTreeEventTypeConfiguration("Baptism", "baptism"),
FamilyTreeEventTypeConfiguration("Birth", "birth"),
FamilyTreeEventTypeConfiguration("Burial", "burial"),
FamilyTreeEventTypeConfiguration("Confirmation", "confirmation"),
FamilyTreeEventTypeConfiguration("Cremation", "cremation"),
FamilyTreeEventTypeConfiguration("Death", "death"),
FamilyTreeEventTypeConfiguration("Divorce", "divorce"),
FamilyTreeEventTypeConfiguration(
"Divorce Filing", "divorce-announcement"
),
FamilyTreeEventTypeConfiguration("Emigration", "emigration"),
FamilyTreeEventTypeConfiguration("Engagement", "engagement"),
FamilyTreeEventTypeConfiguration("Immigration", "immigration"),
FamilyTreeEventTypeConfiguration("Marriage", "marriage"),
FamilyTreeEventTypeConfiguration(
"Marriage Banns", "marriage-announcement"
),
FamilyTreeEventTypeConfiguration("Occupation", "occupation"),
FamilyTreeEventTypeConfiguration("Residence", "residence"),
FamilyTreeEventTypeConfiguration("Retirement", "retirement"),
FamilyTreeEventTypeConfiguration("Will", "will"),
]
super().__init__(configurations)

@override
def _minimize_item_dump(self) -> bool:
return True

@override
def _get_key(self, configuration: FamilyTreeEventTypeConfiguration) -> str:
return configuration.gramps_event_type

@override
def _load_key(self, item_dump: Dump, key_dump: str) -> Dump:
mapping_dump = assert_mapping()(item_dump)
mapping_dump["gramps_event_type"] = _assert_gramps_event_type(key_dump)
return mapping_dump

@override
def _dump_key(self, item_dump: VoidableDump) -> tuple[VoidableDump, str]:
mapping_dump = assert_mapping()(item_dump)
return mapping_dump, mapping_dump.pop("entity_type")

@override
def load_item(self, dump: Dump) -> FamilyTreeEventTypeConfiguration:
# Use dummy configuration for now to satisfy the initializer.
# It will be overridden when loading the dump.
configuration = FamilyTreeEventTypeConfiguration("-", "-")
configuration.load(dump)
return configuration


class FamilyTreeConfiguration(Configuration):
"""
Configure a single Gramps family tree.
"""

def __init__(self, file_path: Path):
def __init__(
self,
file_path: Path,
*,
event_types: Iterable[FamilyTreeEventTypeConfiguration] | None = None,
):
super().__init__()
self.file_path = file_path
self._event_types = FamilyTreeEventTypeConfigurationMapping(event_types)

@override
def __eq__(self, other: Any) -> bool:
Expand All @@ -47,6 +186,13 @@ def file_path(self) -> Path | None:
def file_path(self, file_path: Path | None) -> None:
self._file_path = file_path

@property
def event_types(self) -> FamilyTreeEventTypeConfigurationMapping:
"""
How to map event types.
"""
return self._event_types

@override
def load(self, dump: Dump) -> None:
assert_record(
Expand Down Expand Up @@ -82,9 +228,7 @@ class GrampsConfiguration(Configuration):
"""

def __init__(
self,
*,
family_trees: Iterable[FamilyTreeConfiguration] | None = None,
self, *, family_trees: Iterable[FamilyTreeConfiguration] | None = None
):
super().__init__()
self._family_trees = FamilyTreeConfigurationSequence(family_trees)
Expand All @@ -106,9 +250,4 @@ def load(self, dump: Dump) -> None:

@override
def dump(self) -> VoidableDump:
return minimize(
{
"family_trees": self.family_trees.dump(),
},
True,
)
return minimize({"family_trees": self.family_trees.dump()}, True)
51 changes: 3 additions & 48 deletions betty/gramps/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,8 @@
Ancestry,
)
from betty.ancestry.event_type import (
Birth,
Baptism,
Adoption,
Cremation,
Death,
Funeral,
Burial,
Will,
Engagement,
Marriage,
MarriageAnnouncement,
Divorce,
DivorceAnnouncement,
Residence,
Immigration,
Emigration,
Occupation,
Retirement,
Correspondence,
Confirmation,
Missing,
UnknownEventType,
EventType,
Conference,
)
from betty.ancestry.presence_role import (
Subject,
Expand Down Expand Up @@ -156,6 +134,7 @@ def __init__(
factory: Factory[Any],
localizer: Localizer,
attribute_prefix_key: str | None = None,
event_type_map: Mapping[str, type[EventType]] | None = None,
):
super().__init__()
self._ancestry = ancestry
Expand All @@ -169,6 +148,7 @@ def __init__(
self._gramps_tree_directory_path: Path | None = None
self._loaded = False
self._localizer = localizer
self._event_type_map = event_type_map or {}

async def load_file(self, file_path: Path) -> None:
"""
Expand Down Expand Up @@ -759,31 +739,6 @@ def _load_events(self, database: ElementTree.Element) -> None:
for element in self._xpath(database, "./ns:events/ns:event"):
self._load_event(element)

_EVENT_TYPE_MAP = {
"Birth": Birth,
"Baptism": Baptism,
"Adopted": Adoption,
"Cremation": Cremation,
"Death": Death,
"Funeral": Funeral,
"Burial": Burial,
"Will": Will,
"Engagement": Engagement,
"Marriage": Marriage,
"Marriage Banns": MarriageAnnouncement,
"Divorce": Divorce,
"Divorce Filing": DivorceAnnouncement,
"Residence": Residence,
"Immigration": Immigration,
"Emigration": Emigration,
"Occupation": Occupation,
"Retirement": Retirement,
"Correspondence": Correspondence,
"Confirmation": Confirmation,
"Missing": Missing,
"Conference": Conference,
}

def _load_event(self, element: ElementTree.Element) -> None:
event_handle = element.get("handle")
event_id = element.get("id")
Expand All @@ -792,7 +747,7 @@ def _load_event(self, element: ElementTree.Element) -> None:
assert gramps_type is not None

try:
event_type: EventType = self._EVENT_TYPE_MAP[gramps_type]()
event_type: EventType = self._event_type_map[gramps_type]()
except KeyError:
event_type = UnknownEventType()
getLogger(__name__).warning(
Expand Down
7 changes: 4 additions & 3 deletions betty/locale/localizable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
from betty.classtools import repr_instance
from betty.json.linked_data import LinkedDataDumpable
from betty.json.schema import Object
from betty.locale import negotiate_locale, to_locale, UNDETERMINED_LOCALE
from betty.locale import UNDETERMINED_LOCALE
from betty.locale.localized import LocalizedStr
from betty.locale.localizer import DEFAULT_LOCALIZER
from betty.locale.localizer import Localizer

if TYPE_CHECKING:
from betty.serde.dump import DumpMapping, Dump
from betty.project import Project
from betty.locale import negotiate_locale, to_locale
from betty.locale.localizer import DEFAULT_LOCALIZER
from betty.locale.localizer import Localizer


class Localizable(ABC):
Expand Down
Loading

0 comments on commit 8d59618

Please sign in to comment.