diff --git a/betty/assets/betty.pot b/betty/assets/betty.pot index 00fc86b95..9390a9d31 100644 --- a/betty/assets/betty.pot +++ b/betty/assets/betty.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-11 21:30+0100\n" +"POT-Creation-Date: 2024-07-12 00:13+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -208,6 +208,9 @@ msgstr "" msgid "Could not extract {file_path} as a tar (*.tar) file after extracting the outer gzip (*.gz) file." msgstr "" +msgid "Could not find a plugin \"{plugin_id}\"." +msgstr "" + msgid "Could not find any of the following configuration files in {project_directory_path}: {configuration_file_names}." msgstr "" diff --git a/betty/assets/locale/de-DE/betty.po b/betty/assets/locale/de-DE/betty.po index bff593802..9719279ff 100644 --- a/betty/assets/locale/de-DE/betty.po +++ b/betty/assets/locale/de-DE/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-11 21:30+0100\n" +"POT-Creation-Date: 2024-07-12 00:13+0100\n" "PO-Revision-Date: 2024-02-08 13:24+0000\n" "Last-Translator: Bart Feenstra \n" "Language: de\n" @@ -290,6 +290,9 @@ msgstr "" "Konnte {file_path} nicht als tar-Datei (*.tar) extrahieren, nachdem die " "äußere gzip-Datei (*.gz) extrahiert wurde." +msgid "Could not find a plugin \"{plugin_id}\"." +msgstr "" + msgid "" "Could not find any of the following configuration files in " "{project_directory_path}: {configuration_file_names}." @@ -713,8 +716,8 @@ msgid "" " at all." msgstr "" "Die Entitätsreferenz muss für eine Entität des Typs " -"{expected_entity_type_label} sein, gibt " -"aber stattdessen überhaupt keinen Entitätstyp an." +"{expected_entity_type_label} sein, gibt aber stattdessen überhaupt keinen" +" Entitätstyp an." msgid "" "The entity reference must be for an entity of type " @@ -722,8 +725,8 @@ msgid "" "{actual_entity_type_label}." msgstr "" "Die Entitätsreferenz muss für eine Entität vom Typ " -"{expected_entity_type_label} sein, ist aber" -" stattdessen für eine Entität vom Typ {actual_entity_type_label}" +"{expected_entity_type_label} sein, ist aber stattdessen für eine Entität " +"vom Typ {actual_entity_type_label}" msgid "The following errors occurred" msgstr "Die folgenden Fehler sind aufgetreten" diff --git a/betty/assets/locale/fr-FR/betty.po b/betty/assets/locale/fr-FR/betty.po index 5ab51b72f..45ab1bf67 100644 --- a/betty/assets/locale/fr-FR/betty.po +++ b/betty/assets/locale/fr-FR/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-11 21:30+0100\n" +"POT-Creation-Date: 2024-07-12 00:13+0100\n" "PO-Revision-Date: 2024-02-08 13:24+0000\n" "Last-Translator: Bart Feenstra \n" "Language: fr\n" @@ -249,6 +249,9 @@ msgid "" "outer gzip (*.gz) file." msgstr "" +msgid "Could not find a plugin \"{plugin_id}\"." +msgstr "" + msgid "" "Could not find any of the following configuration files in " "{project_directory_path}: {configuration_file_names}." diff --git a/betty/assets/locale/nl-NL/betty.po b/betty/assets/locale/nl-NL/betty.po index 29e529c93..c0ceba9e7 100644 --- a/betty/assets/locale/nl-NL/betty.po +++ b/betty/assets/locale/nl-NL/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-11 21:30+0100\n" +"POT-Creation-Date: 2024-07-12 00:13+0100\n" "PO-Revision-Date: 2024-02-11 15:31+0000\n" "Last-Translator: Bart Feenstra \n" "Language: nl\n" @@ -289,6 +289,9 @@ msgstr "" "Kan het bestand {file_path} niet als een tar-bestand (*.tar) uitpakken, " "na het uitpakken van het buitenste gzip-bestand (*.gz)." +msgid "Could not find a plugin \"{plugin_id}\"." +msgstr "Kan de plugin \"{plugin_id}\" niet vinden." + msgid "" "Could not find any of the following configuration files in " "{project_directory_path}: {configuration_file_names}." @@ -720,8 +723,7 @@ msgid "" " at all." msgstr "" "De entiteitsverwijzing moet verwijzen naar een entiteit van het type " -"{expected_entity_type_label}, maar geen " -"entiteitstype is ingesteld." +"{expected_entity_type_label}, maar geen entiteitstype is ingesteld." msgid "" "The entity reference must be for an entity of type " @@ -729,8 +731,8 @@ msgid "" "{actual_entity_type_label}." msgstr "" "De entiteitsverwijzing moet verwijzen naar een entiteit van het type " -"{expected_entity_type_label}, maar verwijst" -" echter naar een entiteit van het type {actual_entity_type_label}" +"{expected_entity_type_label}, maar verwijst echter naar een entiteit van " +"het type {actual_entity_type_label}" msgid "The following errors occurred" msgstr "De volgende problemen zijn voorgekomen" diff --git a/betty/assets/locale/uk/betty.po b/betty/assets/locale/uk/betty.po index a987891f7..3c183f368 100644 --- a/betty/assets/locale/uk/betty.po +++ b/betty/assets/locale/uk/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-11 21:30+0100\n" +"POT-Creation-Date: 2024-07-12 00:13+0100\n" "PO-Revision-Date: 2024-02-08 13:08+0000\n" "Last-Translator: Rainer Thieringer \n" "Language: uk\n" @@ -249,6 +249,9 @@ msgid "" "outer gzip (*.gz) file." msgstr "" +msgid "Could not find a plugin \"{plugin_id}\"." +msgstr "" + msgid "" "Could not find any of the following configuration files in " "{project_directory_path}: {configuration_file_names}." diff --git a/betty/deriver.py b/betty/deriver.py index d28915b3e..e2ee2e1b9 100644 --- a/betty/deriver.py +++ b/betty/deriver.py @@ -73,7 +73,7 @@ async def derive(self) -> None: "Updated {updated_derivations} {event_type} events based on existing information." ).format( updated_derivations=str(updated_derivations), - event_type=derivable_event_type.label().localize( + event_type=derivable_event_type.plugin_label().localize( self._localizer ), ) @@ -84,7 +84,7 @@ async def derive(self) -> None: "Created {created_derivations} additional {event_type} events based on existing information." ).format( created_derivations=str(created_derivations), - event_type=derivable_event_type.label().localize( + event_type=derivable_event_type.plugin_label().localize( self._localizer ), ) diff --git a/betty/extension/cotton_candy/assets/templates/entity/label--event.html.j2 b/betty/extension/cotton_candy/assets/templates/entity/label--event.html.j2 index cf8ba19c3..28512c076 100644 --- a/betty/extension/cotton_candy/assets/templates/entity/label--event.html.j2 +++ b/betty/extension/cotton_candy/assets/templates/entity/label--event.html.j2 @@ -4,7 +4,7 @@ {%- macro person_label(person) -%} {% include 'entity/label--person.html.j2' %} {%- endmacro -%} -{%- set formatted_event = event.event_type.label() | localize -%} +{%- set formatted_event = event.event_type.plugin_label() | localize -%} {%- if event is not has_generated_entity_id and not embedded -%} {% set formatted_event = ('' + formatted_event + '') | safe %} {%- endif -%} diff --git a/betty/extension/cotton_candy/assets/templates/entity/meta--person.html.j2 b/betty/extension/cotton_candy/assets/templates/entity/meta--person.html.j2 index 0c1792c85..b4d06c4eb 100644 --- a/betty/extension/cotton_candy/assets/templates/entity/meta--person.html.j2 +++ b/betty/extension/cotton_candy/assets/templates/entity/meta--person.html.j2 @@ -40,10 +40,10 @@ {%- if formatted_start is defined or formatted_end is defined -%}
{%- if formatted_start is defined -%} -
{{ start_of_life_event.event_type.label() | localize }}
{{ formatted_start }}
+
{{ start_of_life_event.event_type.plugin_label() | localize }}
{{ formatted_start }}
{%- endif -%} {%- if formatted_end is defined -%} -
{{ end_of_life_event.event_type.label() | localize }}
{{ formatted_end }}
+
{{ end_of_life_event.event_type.plugin_label() | localize }}
{{ formatted_end }}
{%- endif -%}
{%- endif -%} diff --git a/betty/extension/deriver/__init__.py b/betty/extension/deriver/__init__.py index fd165e79e..60a0ab7d1 100644 --- a/betty/extension/deriver/__init__.py +++ b/betty/extension/deriver/__init__.py @@ -13,6 +13,7 @@ from betty.extension.privatizer import Privatizer from betty.load import PostLoader from betty.locale.localizable import _, Localizable +from betty.model import event_type from betty.model.event_type import DerivableEventType from betty.project.extension import Extension from typing import TYPE_CHECKING @@ -40,11 +41,7 @@ async def post_load(self) -> None: deriver = DeriverApi( self.project.ancestry, self.project.configuration.lifetime_threshold, - { - event_type - for event_type in self.project.event_types - if issubclass(event_type, DerivableEventType) - }, + set(await event_type.ENTITY_TYPE_REPOSITORY.select(DerivableEventType)), localizer=self.project.app.localizer, ) await deriver.derive() diff --git a/betty/extension/gramps/config.py b/betty/extension/gramps/config.py index f491248f1..e19b6af72 100644 --- a/betty/extension/gramps/config.py +++ b/betty/extension/gramps/config.py @@ -9,9 +9,6 @@ from typing_extensions import override -from betty.config import Configuration -from betty.config.collections.sequence import ConfigurationSequence -from betty.serde.dump import minimize, Dump, VoidableDump from betty.assertion import ( RequiredField, OptionalField, @@ -19,6 +16,9 @@ assert_path, assert_setattr, ) +from betty.config import Configuration +from betty.config.collections.sequence import ConfigurationSequence +from betty.serde.dump import minimize, Dump, VoidableDump class FamilyTreeConfiguration(Configuration): diff --git a/betty/gramps/loader.py b/betty/gramps/loader.py index ed75db3dd..eafbbb44a 100644 --- a/betty/gramps/loader.py +++ b/betty/gramps/loader.py @@ -787,7 +787,9 @@ def _load_event(self, element: ElementTree.Element) -> None: ).format( event_id=event_id, gramps_event_type=gramps_type, - betty_event_type=event_type.label().localize(self._localizer), + betty_event_type=event_type.plugin_label().localize( + self._localizer + ), ) ) diff --git a/betty/model/ancestry.py b/betty/model/ancestry.py index 6562fc2e1..e762b6f29 100644 --- a/betty/model/ancestry.py +++ b/betty/model/ancestry.py @@ -1643,7 +1643,7 @@ def __init__( @property def label(self) -> Localizable: format_kwargs: dict[str, str | Localizable] = { - "event_type": self._event_type.label(), + "event_type": self._event_type.plugin_label(), } subjects = [ presence.person @@ -1721,7 +1721,7 @@ async def dump_linked_data(self, project: Project) -> DictDump[Dump]: dump = await super().dump_linked_data(project) dump_context(dump, presences="performer") dump["@type"] = "https://schema.org/Event" - dump["type"] = self.event_type.name() + dump["type"] = self.event_type.plugin_id() dump["eventAttendanceMode"] = "https://schema.org/OfflineEventAttendanceMode" dump["eventStatus"] = "https://schema.org/EventScheduled" dump["presences"] = presences = [] diff --git a/betty/model/event_type.py b/betty/model/event_type.py index a5a2926d8..b7415777e 100644 --- a/betty/model/event_type.py +++ b/betty/model/event_type.py @@ -4,53 +4,24 @@ from __future__ import annotations -from abc import ABC, abstractmethod from typing import TYPE_CHECKING, final from typing_extensions import override from betty.locale import DEFAULT_LOCALIZER from betty.locale.localizable import _, Localizable +from betty.plugin import Plugin, PluginRepository +from betty.plugin.entry_point import EntryPointPluginRepository if TYPE_CHECKING: from betty.model.ancestry import Person -class EventTypeProvider(ABC): - """ - Provide additional event types. - """ - - @property - @abstractmethod - def entity_types(self) -> set[type[EventType]]: - """ - The event types. - """ - pass - - -class EventType(ABC): +class EventType(Plugin): """ Define an :py:class:`betty.model.ancestry.Event` type. """ - @classmethod - @abstractmethod - def name(cls) -> str: - """ - Get the machine name. - """ - pass - - @classmethod - @abstractmethod - def label(cls) -> Localizable: - """ - Get the human-readable label. - """ - pass - @classmethod def comes_before(cls) -> set[type[EventType]]: """ @@ -70,24 +41,44 @@ def comes_after(cls) -> set[type[EventType]]: return set() # pragma: no cover -@final -class UnknownEventType(EventType): +ENTITY_TYPE_REPOSITORY: PluginRepository[EventType] = EntryPointPluginRepository( + "betty.event_type" +) +""" +The event type plugin repository. +""" + + +class _EventTypeShorthandBase(EventType): """ - Described an event for which no more specific type is known. + Provide helpers for deprecated methods. """ + _plugin_id: str + _plugin_label: Localizable + @override @classmethod - def name(cls) -> str: - return "unknown" # pragma: no cover + def plugin_id(cls) -> str: + return cls._plugin_id @override @classmethod - def label(cls) -> Localizable: - return _("Unknown") # pragma: no cover + def plugin_label(cls) -> Localizable: + return cls._plugin_label -class DerivableEventType(EventType): +@final +class UnknownEventType(_EventTypeShorthandBase): + """ + Described an event for which no more specific type is known. + """ + + _plugin_id = "unknown" + _plugin_label = _("Unknown") + + +class DerivableEventType(_EventTypeShorthandBase): """ Any event that that may be updated by the deriver API. """ @@ -171,20 +162,13 @@ def comes_after(cls) -> set[type[EventType]]: @final -class Birth(CreatableDerivableEventType, StartOfLifeEventType): +class Birth(CreatableDerivableEventType, StartOfLifeEventType, _EventTypeShorthandBase): """ Someone was born. """ - @override - @classmethod - def name(cls) -> str: - return "birth" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Birth") # pragma: no cover + _plugin_id = "birth" + _plugin_label = _("Birth") @override @classmethod @@ -193,54 +177,33 @@ def comes_before(cls) -> set[type[EventType]]: @final -class Baptism(DuringLifeEventType, StartOfLifeEventType): +class Baptism(DuringLifeEventType, StartOfLifeEventType, _EventTypeShorthandBase): """ Someone was `baptized `_. """ - @override - @classmethod - def name(cls) -> str: - return "baptism" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Baptism") # pragma: no cover + _plugin_id = "baptism" + _plugin_label = _("Baptism") @final -class Adoption(DuringLifeEventType): +class Adoption(DuringLifeEventType, _EventTypeShorthandBase): """ Someone was adopted. """ - @override - @classmethod - def name(cls) -> str: - return "adoption" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Adoption") # pragma: no cover + _plugin_id = "adoption" + _plugin_label = _("Adoption") @final -class Death(CreatableDerivableEventType, EndOfLifeEventType): +class Death(CreatableDerivableEventType, EndOfLifeEventType, _EventTypeShorthandBase): """ Someone died. """ - @override - @classmethod - def name(cls) -> str: - return "death" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Death") # pragma: no cover + _plugin_id = "death" + _plugin_label = _("Death") @override @classmethod @@ -268,88 +231,53 @@ class FinalDispositionEventType( @final -class Funeral(FinalDispositionEventType): +class Funeral(FinalDispositionEventType, _EventTypeShorthandBase): """ Someone's funeral took place. """ - @override - @classmethod - def name(cls) -> str: - return "funeral" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Funeral") # pragma: no cover + _plugin_id = "funeral" + _plugin_label = _("Funeral") @final -class Cremation(FinalDispositionEventType): +class Cremation(FinalDispositionEventType, _EventTypeShorthandBase): """ Someone was cremated. """ - @override - @classmethod - def name(cls) -> str: - return "cremation" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Cremation") # pragma: no cover + _plugin_id = "cremation" + _plugin_label = _("Cremation") @final -class Burial(FinalDispositionEventType): +class Burial(FinalDispositionEventType, _EventTypeShorthandBase): """ Someone was buried. """ - @override - @classmethod - def name(cls) -> str: - return "burial" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Burial") # pragma: no cover + _plugin_id = "burial" + _plugin_label = _("Burial") @final -class Will(PostDeathEventType): +class Will(PostDeathEventType, _EventTypeShorthandBase): """ Someone's `will and testament `_ came into effect. """ - @override - @classmethod - def name(cls) -> str: - return "will" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Will") # pragma: no cover + _plugin_id = "will" + _plugin_label = _("Will") @final -class Engagement(DuringLifeEventType): +class Engagement(DuringLifeEventType, _EventTypeShorthandBase): """ People got engaged with the intent to marry. """ - @override - @classmethod - def name(cls) -> str: - return "engagement" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Engagement") # pragma: no cover + _plugin_id = "engagement" + _plugin_label = _("Engagement") @override @classmethod @@ -358,37 +286,23 @@ def comes_before(cls) -> set[type[EventType]]: @final -class Marriage(DuringLifeEventType): +class Marriage(DuringLifeEventType, _EventTypeShorthandBase): """ People were married. """ - @override - @classmethod - def name(cls) -> str: - return "marriage" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Marriage") # pragma: no cover + _plugin_id = "marriage" + _plugin_label = _("Marriage") @final -class MarriageAnnouncement(DuringLifeEventType): +class MarriageAnnouncement(DuringLifeEventType, _EventTypeShorthandBase): """ People's marriage was announced. """ - @override - @classmethod - def name(cls) -> str: - return "marriage-announcement" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Announcement of marriage") # pragma: no cover + _plugin_id = "marriage-announcement" + _plugin_label = _("Announcement of marriage") @override @classmethod @@ -397,20 +311,13 @@ def comes_before(cls) -> set[type[EventType]]: @final -class Divorce(DuringLifeEventType): +class Divorce(DuringLifeEventType, _EventTypeShorthandBase): """ People were divorced. """ - @override - @classmethod - def name(cls) -> str: - return "divorce" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Divorce") # pragma: no cover + _plugin_id = "divorce" + _plugin_label = _("Divorce") @override @classmethod @@ -419,20 +326,13 @@ def comes_after(cls) -> set[type[EventType]]: @final -class DivorceAnnouncement(DuringLifeEventType): +class DivorceAnnouncement(DuringLifeEventType, _EventTypeShorthandBase): """ People's divorce was announced. """ - @override - @classmethod - def name(cls) -> str: - return "divorce-announcement" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Announcement of divorce") # pragma: no cover + _plugin_id = "divorce-announcement" + _plugin_label = _("Announcement of divorce") @override @classmethod @@ -446,155 +346,92 @@ def comes_before(cls) -> set[type[EventType]]: @final -class Residence(DuringLifeEventType): +class Residence(DuringLifeEventType, _EventTypeShorthandBase): """ Someone resided/lived in a place. """ - @override - @classmethod - def name(cls) -> str: - return "residence" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Residence") # pragma: no cover + _plugin_id = "residence" + _plugin_label = _("Residence") @final -class Immigration(DuringLifeEventType): +class Immigration(DuringLifeEventType, _EventTypeShorthandBase): """ Someone immigrated to a place. """ - @override - @classmethod - def name(cls) -> str: - return "immigration" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Immigration") # pragma: no cover + _plugin_id = "immigration" + _plugin_label = _("Immigration") @final -class Emigration(DuringLifeEventType): +class Emigration(_EventTypeShorthandBase, DuringLifeEventType): """ Someone emigrated from a place. """ - @override - @classmethod - def name(cls) -> str: - return "emigration" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Emigration") # pragma: no cover + _plugin_id = "emigration" + _plugin_label = _("Emigration") @final -class Occupation(DuringLifeEventType): +class Occupation(_EventTypeShorthandBase, DuringLifeEventType): """ Someone's occupation, e.g. their main recurring activity. This may include employment, education, stay at home parent, etc. """ - @override - @classmethod - def name(cls) -> str: - return "occupation" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Occupation") # pragma: no cover + _plugin_id = "occupation" + _plugin_label = _("Occupation") @final -class Retirement(DuringLifeEventType): +class Retirement(_EventTypeShorthandBase, DuringLifeEventType): """ Someone `retired `_. """ - @override - @classmethod - def name(cls) -> str: - return "retirement" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Retirement") # pragma: no cover + _plugin_id = "retirement" + _plugin_label = _("Retirement") @final -class Correspondence(EventType): +class Correspondence(_EventTypeShorthandBase): """ People corresponded with each other. """ - @override - @classmethod - def name(cls) -> str: - return "correspondence" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Correspondence") # pragma: no cover + _plugin_id = "correspondence" + _plugin_label = _("Correspondence") @final -class Confirmation(DuringLifeEventType): +class Confirmation(_EventTypeShorthandBase, DuringLifeEventType): """ Someone's `confirmation `_ took place. """ - @override - @classmethod - def name(cls) -> str: - return "confirmation" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Confirmation") # pragma: no cover + _plugin_id = "confirmation" + _plugin_label = _("Confirmation") @final -class Missing(DuringLifeEventType): +class Missing(_EventTypeShorthandBase, DuringLifeEventType): """ Someone went missing. """ - @override - @classmethod - def name(cls) -> str: - return "missing" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Missing") # pragma: no cover + _plugin_id = "missing" + _plugin_label = _("Missing") @final -class Conference(DuringLifeEventType): +class Conference(_EventTypeShorthandBase, DuringLifeEventType): """ A conference between people took place. """ - @override - @classmethod - def name(cls) -> str: - return "conference" # pragma: no cover - - @override - @classmethod - def label(cls) -> Localizable: - return _("Conference") # pragma: no cover + _plugin_id = "conference" + _plugin_label = _("Conference") diff --git a/betty/plugin/__init__.py b/betty/plugin/__init__.py index cfd82c0df..a619a1497 100644 --- a/betty/plugin/__init__.py +++ b/betty/plugin/__init__.py @@ -13,11 +13,22 @@ from abc import ABC, abstractmethod from typing import TypeVar, Generic, Self, overload, TYPE_CHECKING, TypeAlias, TypeGuard +from betty.error import UserFacingError +from betty.locale.localizable import _ + if TYPE_CHECKING: from betty.locale.localizable import Localizable from collections.abc import AsyncIterator, Sequence +class PluginError(UserFacingError): + """ + Any error originating from the Plugin API. + """ + + pass + + PluginId: TypeAlias = str """ A plugin ID is a string that meets these criteria: @@ -74,7 +85,7 @@ def plugin_description(cls) -> Localizable | None: _PluginT = TypeVar("_PluginT", bound=Plugin) -class PluginNotFound(ValueError): +class PluginNotFound(PluginError): """ Raised when a plugin cannot be found. """ @@ -84,7 +95,9 @@ def new(cls, plugin_id: PluginId) -> Self: """ Create a new instance. """ - return cls(f'Could not find a plugin "{plugin_id}".') + return cls( + _('Could not find a plugin "{plugin_id}".').format(plugin_id=plugin_id) + ) _PluginMixinOneT = TypeVar("_PluginMixinOneT") diff --git a/betty/project/__init__.py b/betty/project/__init__.py index b5b9507c1..a1be75c5e 100644 --- a/betty/project/__init__.py +++ b/betty/project/__init__.py @@ -19,6 +19,7 @@ from typing_extensions import override from betty import fs +from betty import model from betty.assertion import ( Assertion, RequiredField, @@ -47,33 +48,8 @@ from betty.hashid import hashid from betty.locale import DEFAULT_LOCALE, LocalizerRepository from betty.locale.localizable import _ -from betty import model from betty.model import Entity, UserFacingEntity from betty.model.ancestry import Ancestry, Person, Event, Place, Source -from betty.model.event_type import ( - EventType, - EventTypeProvider, - Birth, - Baptism, - Adoption, - Death, - Funeral, - Cremation, - Burial, - Will, - Engagement, - Marriage, - MarriageAnnouncement, - Divorce, - DivorceAnnouncement, - Residence, - Immigration, - Emigration, - Occupation, - Retirement, - Correspondence, - Confirmation, -) from betty.plugin.assertion import assert_plugin from betty.project import extension from betty.project.extension import ( @@ -103,7 +79,6 @@ from betty.url import LocalizedUrlGenerator, StaticUrlGenerator from betty.jinja2 import Environment - _EntityT = TypeVar("_EntityT", bound=Entity) @@ -1078,7 +1053,6 @@ def __init__( self._extensions: Extensions | None = None self._dispatcher: ExtensionDispatcher | None = None self._entity_types: set[type[Entity]] | None = None - self._event_types: set[type[EventType]] | None = None @classmethod @asynccontextmanager @@ -1276,36 +1250,3 @@ def dispatcher(self) -> Dispatcher: self._dispatcher = ExtensionDispatcher(self.extensions) return self._dispatcher - - @property - def event_types(self) -> set[type[EventType]]: - """ - The available event types. - """ - if self._event_types is None: - self._assert_bootstrapped() - self._event_types = set( - wait_to_thread(self.dispatcher.dispatch(EventTypeProvider)()) - ) | { - Birth, - Baptism, - Adoption, - Death, - Funeral, - Cremation, - Burial, - Will, - Engagement, - Marriage, - MarriageAnnouncement, - Divorce, - DivorceAnnouncement, - Residence, - Immigration, - Emigration, - Occupation, - Retirement, - Correspondence, - Confirmation, - } - return self._event_types diff --git a/betty/tests/coverage/test_coverage.py b/betty/tests/coverage/test_coverage.py index 90b32b953..a30229d43 100644 --- a/betty/tests/coverage/test_coverage.py +++ b/betty/tests/coverage/test_coverage.py @@ -540,9 +540,7 @@ class TestKnownToBeMissing: "Emigration": TestKnownToBeMissing, "EndOfLifeEventType": TestKnownToBeMissing, "Engagement": TestKnownToBeMissing, - # This is an abstract class. "EventType": TestKnownToBeMissing, - "EventTypeProvider": TestKnownToBeMissing, "FinalDispositionEventType": TestKnownToBeMissing, "Funeral": TestKnownToBeMissing, "Immigration": TestKnownToBeMissing, @@ -584,7 +582,8 @@ class TestKnownToBeMissing: # This is an interface method. "plugin_label": TestKnownToBeMissing, }, - # This is an interface. + # This is a base/sentinel class. + "PluginError": TestKnownToBeMissing, "PluginRepository": { # This is an interface method. "__aiter__": TestKnownToBeMissing, diff --git a/betty/tests/extension/deriver/test___init__.py b/betty/tests/extension/deriver/test___init__.py index abd94e1ab..3f87644ff 100644 --- a/betty/tests/extension/deriver/test___init__.py +++ b/betty/tests/extension/deriver/test___init__.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing_extensions import override + from betty.extension.deriver import Deriver from betty.load import load from betty.locale import DateRange, Date @@ -19,28 +21,31 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: + from betty.plugin import PluginId from betty.app import App -class _DummyEventTypeBase(EventType): +class DummyEventType(EventType): + @override @classmethod - def name(cls) -> str: + def plugin_id(cls) -> PluginId: return cls.__name__ + @override @classmethod - def label(cls) -> Localizable: + def plugin_label(cls) -> Localizable: return plain("") -class Ignored(_DummyEventTypeBase): +class Ignored(DummyEventType): pass -class ComesBeforeReference(_DummyEventTypeBase): +class ComesBeforeReference(DummyEventType): pass -class ComesAfterReference(_DummyEventTypeBase): +class ComesAfterReference(DummyEventType): pass @@ -54,7 +59,7 @@ class ComesBeforeCreatableDerivable(CreatableDerivableEventType, ComesBeforeDeri pass -class ComesAfterDerivable(DerivableEventType): +class ComesAfterDerivable(DerivableEventType, DummyEventType): @classmethod def comes_after(cls) -> set[type[EventType]]: return {ComesAfterReference} @@ -64,7 +69,7 @@ class ComesAfterCreatableDerivable(CreatableDerivableEventType, ComesAfterDeriva pass -class ComesBeforeAndAfterDerivable(DerivableEventType): +class ComesBeforeAndAfterDerivable(DerivableEventType, DummyEventType): @classmethod def comes_before(cls) -> set[type[EventType]]: return {Ignored} diff --git a/betty/tests/locale/__init__.py b/betty/tests/locale/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/betty/tests/project/test___init__.py b/betty/tests/project/test___init__.py index 15466d94e..e435dec87 100644 --- a/betty/tests/project/test___init__.py +++ b/betty/tests/project/test___init__.py @@ -1511,10 +1511,6 @@ async def test_dispatcher(self, new_temporary_app: App) -> None: async with Project.new_temporary(new_temporary_app) as sut, sut: sut.dispatcher # noqa B018 - async def test_event_types(self, new_temporary_app: App) -> None: - async with Project.new_temporary(new_temporary_app) as sut, sut: - assert len(sut.event_types) > 0 - async def test_jinja2_environment(self, new_temporary_app: App) -> None: async with Project.new_temporary(new_temporary_app) as sut, sut: sut.jinja2_environment # noqa B018 diff --git a/betty/tests/test_deriver.py b/betty/tests/test_deriver.py index e7f047db3..ca3770afb 100644 --- a/betty/tests/test_deriver.py +++ b/betty/tests/test_deriver.py @@ -4,7 +4,6 @@ from betty.deriver import Deriver from betty.locale import DateRange, Date, Datey, DEFAULT_LOCALIZER -from betty.locale.localizable import plain, Localizable from betty.model import record_added from betty.model.ancestry import Person, Presence, Event, Ancestry from betty.model.presence_role import Subject @@ -14,31 +13,22 @@ EventType, ) from betty.project import DEFAULT_LIFETIME_THRESHOLD +from betty.tests.extension.deriver.test___init__ import DummyEventType -class DeriverTestEventType(EventType): - @classmethod - def name(cls) -> str: - return repr(cls) - - @classmethod - def label(cls) -> Localizable: - return plain(repr(cls)) - - -class Ignored(DeriverTestEventType): +class Ignored(DummyEventType): pass -class ComesBeforeReference(DeriverTestEventType): +class ComesBeforeReference(DummyEventType): pass -class ComesAfterReference(DeriverTestEventType): +class ComesAfterReference(DummyEventType): pass -class ComesBeforeDerivable(DeriverTestEventType, DerivableEventType): +class ComesBeforeDerivable(DummyEventType, DerivableEventType): @classmethod def comes_before(cls) -> set[type[EventType]]: return {ComesBeforeReference} @@ -48,7 +38,7 @@ class ComesBeforeCreatableDerivable(ComesBeforeDerivable, CreatableDerivableEven pass -class ComesAfterDerivable(DeriverTestEventType, DerivableEventType): +class ComesAfterDerivable(DummyEventType, DerivableEventType): @classmethod def comes_after(cls) -> set[type[EventType]]: return {ComesAfterReference} @@ -58,7 +48,7 @@ class ComesAfterCreatableDerivable(ComesAfterDerivable, CreatableDerivableEventT pass -class ComesBeforeAndAfterDerivable(DeriverTestEventType, DerivableEventType): +class ComesBeforeAndAfterDerivable(DummyEventType, DerivableEventType): @classmethod def comes_before(cls) -> set[type[EventType]]: return {Ignored} @@ -69,7 +59,7 @@ def comes_after(cls) -> set[type[EventType]]: class ComesBeforeAndAfterCreatableDerivable( - DeriverTestEventType, CreatableDerivableEventType + DummyEventType, CreatableDerivableEventType ): pass diff --git a/pyproject.toml b/pyproject.toml index 61bab0b80..8f8106636 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,31 @@ betty = 'betty.cli:main' 'place' = 'betty.model.ancestry:Place' 'source' = 'betty.model.ancestry:Source' +[project.entry-points.'betty.event_type'] +'adoption' = 'betty.model.event_type:Adoption' +'baptism' = 'betty.model.event_type:Baptism' +'birth' = 'betty.model.event_type:Birth' +'burial' = 'betty.model.event_type:Burial' +'conference' = 'betty.model.event_type:Conference' +'confirmation' = 'betty.model.event_type:Confirmation' +'correspondence' = 'betty.model.event_type:Correspondence' +'cremation' = 'betty.model.event_type:Cremation' +'death' = 'betty.model.event_type:Death' +'divorce' = 'betty.model.event_type:Divorce' +'divorce-announcement' = 'betty.model.event_type:DivorceAnnouncement' +'emigration' = 'betty.model.event_type:Emigration' +'engagement' = 'betty.model.event_type:Engagement' +'funeral' = 'betty.model.event_type:Funeral' +'immigration' = 'betty.model.event_type:Immigration' +'marriage' = 'betty.model.event_type:Marriage' +'marriage-announcement' = 'betty.model.event_type:MarriageAnnouncement' +'missing' = 'betty.model.event_type:Missing' +'occupation' = 'betty.model.event_type:Occupation' +'residence' = 'betty.model.event_type:Residence' +'retirement' = 'betty.model.event_type:Retirement' +'unknown' = 'betty.model.event_type:UnknownEventType' +'will' = 'betty.model.event_type:Will' + [project.entry-points.'betty.extension'] 'cotton-candy' = 'betty.extension.cotton_candy:CottonCandy' 'demo' = 'betty.extension.demo:Demo'