"""
Data types to describe information sources.
"""
from __future__ import annotations
from typing import final, Iterable, MutableSequence, Iterator, TYPE_CHECKING
from typing_extensions import override
from betty.ancestry.date import HasDate
from betty.ancestry.has_file_references import HasFileReferences
from betty.ancestry.has_notes import HasNotes
from betty.ancestry.link import HasLinks, Link
from betty.json.linked_data import dump_context
from betty.locale.localizable import (
_,
OptionalStaticTranslationsLocalizableAttr,
ShorthandStaticTranslations,
Localizable,
)
from betty.model import UserFacingEntity, Entity
from betty.model.association import (
BidirectionalToZeroOrOne,
BidirectionalToMany,
ToOneResolver,
ToManyResolver,
ToZeroOrOneResolver,
)
from betty.plugin import ShorthandPluginBase
from betty.privacy import HasPrivacy, Privacy, merge_privacies
if TYPE_CHECKING:
from betty.ancestry.citation import Citation # noqa F401
from betty.ancestry.note import Note
from betty.ancestry.file_reference import FileReference
from betty.serde.dump import DumpMapping, Dump
from betty.project import Project
from betty.date import Datey
[docs]
@final
class Source(
ShorthandPluginBase,
HasDate,
HasFileReferences,
HasNotes,
HasLinks,
HasPrivacy,
UserFacingEntity,
Entity,
):
"""
A source of information.
"""
_plugin_id = "source"
_plugin_label = _("Source")
#: The source this one is directly contained by.
contained_by = BidirectionalToZeroOrOne["Source", "Source"](
"betty.ancestry.source:Source",
"contained_by",
"betty.ancestry.source:Source",
"contains",
title="Contained by",
description="Another source this source may be contained by",
)
contains = BidirectionalToMany["Source", "Source"](
"betty.ancestry.source:Source",
"contains",
"betty.ancestry.source:Source",
"contained_by",
title="Contains",
description="Other sources this source may contain",
)
citations = BidirectionalToMany["Source", "Citation"](
"betty.ancestry.source:Source",
"citations",
"betty.ancestry.citation:Citation",
"source",
title="Citations",
description="The citations referencing this source",
)
#: The human-readable source name.
name = OptionalStaticTranslationsLocalizableAttr("name", title="Name")
#: The human-readable author.
author = OptionalStaticTranslationsLocalizableAttr("author", title="Author")
#: The human-readable publisher.
publisher = OptionalStaticTranslationsLocalizableAttr(
"publisher", title="Publisher"
)
[docs]
def __init__(
self,
name: ShorthandStaticTranslations | None = None,
*,
id: str | None = None, # noqa A002 # noqa A002
author: ShorthandStaticTranslations | None = None,
publisher: ShorthandStaticTranslations | None = None,
contained_by: Source
| ToZeroOrOneResolver[Source]
| ToOneResolver[Source]
| None = None,
contains: Iterable[Source] | ToManyResolver[Source] | None = None,
notes: Iterable[Note] | ToManyResolver[Note] | None = None,
date: Datey | None = None,
file_references: Iterable["FileReference"]
| ToManyResolver["FileReference"]
| None = None,
links: MutableSequence[Link] | None = None,
privacy: Privacy | None = None,
public: bool | None = None,
private: bool | None = None,
):
super().__init__(
id,
notes=notes,
date=date,
file_references=file_references,
links=links,
privacy=privacy,
public=public,
private=private,
)
if name:
self.name = name
if author:
self.author = author
if publisher:
self.publisher = publisher
if contained_by is not None:
self.contained_by = contained_by
if contains is not None:
self.contains = contains
@override
def _get_effective_privacy(self) -> Privacy:
privacy = super()._get_effective_privacy()
if self.contained_by:
return merge_privacies(privacy, self.contained_by.privacy)
return privacy
@property
def walk_contains(self) -> Iterator[Source]:
"""
All directly and indirectly contained sources.
"""
for source in self.contains:
yield source
yield from source.contains
[docs]
@override
@classmethod
def plugin_label_plural(cls) -> Localizable:
return _("Sources")
@override
@property
def label(self) -> Localizable:
return self.name if self.name else super().label
[docs]
@override
async def dump_linked_data(self, project: Project) -> DumpMapping[Dump]:
dump = await super().dump_linked_data(project)
dump["@type"] = "https://schema.org/Thing"
dump_context(dump, name="https://schema.org/name")
return dump