Source code for betty.ancestry.person

"""
Data types describing persons.
"""

from __future__ import annotations

from typing import final, Iterable, MutableSequence, Iterator, TYPE_CHECKING
from urllib.parse import quote

from typing_extensions import override

from betty.ancestry.citation import Citation
from betty.ancestry.gender.genders import Unknown as UnknownGender
from betty.ancestry.has_citations import HasCitations
from betty.ancestry.has_file_references import HasFileReferences
from betty.ancestry.link import HasLinks, Link
from betty.ancestry.has_notes import HasNotes
from betty.ancestry.person_name import PersonName
from betty.ancestry.presence_role import PresenceRoleSchema
from betty.ancestry.privacy import HasPrivacy, Privacy
from betty.functools import Uniquifier
from betty.json.linked_data import dump_context, JsonLdObject
from betty.json.schema import Object, Array, Enum
from betty.locale.localizable import _, Localizable
from betty.model import (
    UserFacingEntity,
    Entity,
    GeneratedEntityId,
    EntityReferenceCollectionSchema,
    EntityReferenceSchema,
)
from betty.model.association import ManyToMany, OneToMany
from betty.plugin import ShorthandPluginBase

if TYPE_CHECKING:
    from betty.ancestry.note import Note
    from betty.ancestry.file_reference import FileReference
    from betty.ancestry.citation import Citation
    from betty.ancestry.presence import Presence
    from betty.ancestry.gender import Gender
    from betty.project import Project
    from betty.serde.dump import DumpMapping, Dump


[docs] @final class Person( ShorthandPluginBase, HasFileReferences, HasCitations, HasNotes, HasLinks, HasPrivacy, UserFacingEntity, Entity, ): """ A person. """ _plugin_id = "person" _plugin_label = _("Person") parents = ManyToMany["Person", "Person"]( "betty.ancestry.person:Person", "parents", "betty.ancestry.person:Person", "children", ) children = ManyToMany["Person", "Person"]( "betty.ancestry.person:Person", "children", "betty.ancestry.person:Person", "parents", ) presences = OneToMany["Person", "Presence"]( "betty.ancestry.person:Person", "presences", "betty.ancestry.presence:Presence", "person", ) names = OneToMany["Person", "PersonName"]( "betty.ancestry.person:Person", "names", "betty.ancestry.person_name:PersonName", "person", )
[docs] def __init__( self, *, id: str | None = None, # noqa A002 file_references: Iterable[FileReference] | None = None, citations: Iterable["Citation"] | None = None, links: MutableSequence[Link] | None = None, notes: Iterable[Note] | None = None, privacy: Privacy | None = None, public: bool | None = None, private: bool | None = None, parents: Iterable[Person] | None = None, children: Iterable[Person] | None = None, presences: Iterable["Presence"] | None = None, names: Iterable["PersonName"] | None = None, gender: Gender | None = None, ): super().__init__( id, file_references=file_references, citations=citations, links=links, notes=notes, privacy=privacy, public=public, private=private, ) if children is not None: self.children = children if parents is not None: self.parents = parents if presences is not None: self.presences = presences if names is not None: self.names = names self.gender = gender or UnknownGender()
[docs] @override @classmethod def plugin_label_plural(cls) -> Localizable: return _("People")
@property def ancestors(self) -> Iterator[Person]: """ All ancestors. """ for parent in self.parents: yield parent yield from parent.ancestors @property def siblings(self) -> Iterator[Person]: """ All siblings. """ yield from Uniquifier( sibling for parent in self.parents for sibling in parent.children if sibling != self ) @property def descendants(self) -> Iterator[Person]: """ All descendants. """ for child in self.children: yield child yield from child.descendants @override @property def label(self) -> Localizable: for name in self.names: if name.public: return name.label return super().label
[docs] @override async def dump_linked_data(self, project: Project) -> DumpMapping[Dump]: dump = await super().dump_linked_data(project) dump_context( dump, names="https://schema.org/name", parents="https://schema.org/parent", children="https://schema.org/child", siblings="https://schema.org/sibling", ) dump["@type"] = "https://schema.org/Person" dump["parents"] = [ project.static_url_generator.generate( f"/person/{quote(parent.id)}/index.json" ) for parent in self.parents if not isinstance(parent.id, GeneratedEntityId) ] dump["children"] = [ project.static_url_generator.generate( f"/person/{quote(child.id)}/index.json" ) for child in self.children if not isinstance(child.id, GeneratedEntityId) ] dump["siblings"] = [ project.static_url_generator.generate( f"/person/{quote(sibling.id)}/index.json" ) for sibling in self.siblings if not isinstance(sibling.id, GeneratedEntityId) ] dump["presences"] = [ self._dump_person_presence(presence, project) for presence in self.presences if presence.event is not None and not isinstance(presence.event.id, GeneratedEntityId) ] if self.public: dump["names"] = [ await name.dump_linked_data(project) for name in self.names if name.public ] dump["gender"] = self.gender.plugin_id() else: dump["names"] = [] return dump
def _dump_person_presence( self, presence: "Presence", project: Project ) -> DumpMapping[Dump]: assert presence.event dump: DumpMapping[Dump] = { "event": project.static_url_generator.generate( f"/event/{quote(presence.event.id)}/index.json" ), } dump_context(dump, event="https://schema.org/performerIn") if presence.public: dump["role"] = presence.role.plugin_id() return dump
[docs] @override @classmethod async def linked_data_schema(cls, project: Project) -> Object: schema = await super().linked_data_schema(project) schema.add_property( "names", Array(await PersonName.linked_data_schema(project), title="Names"), ) schema.add_property( "gender", Enum( *[gender.plugin_id() async for gender in project.genders], title="Gender", ), property_required=False, ) schema.add_property("parents", EntityReferenceCollectionSchema(Person)) schema.add_property("children", EntityReferenceCollectionSchema(Person)) schema.add_property("siblings", EntityReferenceCollectionSchema(Person)) schema.add_property( "presences", Array(_PersonPresenceSchema(), title="Presences") ) return schema
class _PersonPresenceSchema(JsonLdObject): """ A schema for the :py:class:`betty.ancestry.presence.Presence` associations on a :py:class:`betty.ancestry.person.Person`. """ def __init__(self): from betty.ancestry.event import Event super().__init__(title="Presence (person)") self.add_property("role", PresenceRoleSchema(), False) self.add_property("event", EntityReferenceSchema(Event))