Skip to content

Commit

Permalink
Log why entities are privatized. (#1116)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartfeenstra authored Dec 28, 2023
1 parent fda0a3b commit a3fd5a7
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 39 deletions.
5 changes: 4 additions & 1 deletion betty/assets/betty.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-12-19 18:46+0000\n"
"POT-Creation-Date: 2023-12-28 23:21+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -602,6 +602,9 @@ msgstr ""
msgid "Privatized {count} {entity_type}, because they are associated with private people."
msgstr ""

msgid "Privatized {privatized_entity_type} {privatized_entity_id} ({privatized_entity}) because of {reason_entity_type} {reason_entity_id} ({reason_entity})."
msgstr ""

msgid "Privatizer"
msgstr ""

Expand Down
8 changes: 7 additions & 1 deletion betty/assets/locale/fr-FR/betty.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-12-19 18:46+0000\n"
"POT-Creation-Date: 2023-12-28 23:21+0000\n"
"PO-Revision-Date: 2020-11-27 19:49+0100\n"
"Last-Translator: \n"
"Language: fr\n"
Expand Down Expand Up @@ -685,6 +685,12 @@ msgid ""
"private people."
msgstr ""

msgid ""
"Privatized {privatized_entity_type} {privatized_entity_id} "
"({privatized_entity}) because of {reason_entity_type} {reason_entity_id} "
"({reason_entity})."
msgstr ""

msgid "Privatizer"
msgstr ""

Expand Down
16 changes: 14 additions & 2 deletions betty/assets/locale/nl-NL/betty.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-12-19 18:46+0000\n"
"POT-Creation-Date: 2023-12-28 23:21+0000\n"
"PO-Revision-Date: 2022-04-08 01:58+0100\n"
"Last-Translator: \n"
"Language: nl\n"
Expand Down Expand Up @@ -508,7 +508,10 @@ msgid ""
"Generate <a href=\"\">nginx</a> configuration for your site, as well as a"
" <code>Dockerfile</code> to build a <a "
"href=\"https://www.docker.com/\">Docker</a> container around it."
msgstr "Genereer <a href=\"\">nginx</a>-configuratie voor je site, evenals een <code>Dockerfile</code> om er een <a href=\"https://www.docker.com/\">Docker</a>-container omheen te bouwen."
msgstr ""
"Genereer <a href=\"\">nginx</a>-configuratie voor je site, evenals een "
"<code>Dockerfile</code> om er een <a "
"href=\"https://www.docker.com/\">Docker</a>-container omheen te bouwen."

msgid "Generate entity listing pages"
msgstr "Genereer pagina's met entiteitsoverzichten"
Expand Down Expand Up @@ -717,6 +720,15 @@ msgid ""
"private people."
msgstr ""

msgid ""
"Privatized {privatized_entity_type} {privatized_entity_id} "
"({privatized_entity}) because of {reason_entity_type} {reason_entity_id} "
"({reason_entity})."
msgstr ""
"{privatized_entity_type} {privatized_entity_id} "
"({privatized_entity}) geprivatiseerd vanwege {reason_entity_type} {reason_entity_id} "
"({reason_entity})."

msgid "Privatizer"
msgstr "Privatiseerder"

Expand Down
8 changes: 7 additions & 1 deletion betty/assets/locale/uk/betty.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-12-19 18:46+0000\n"
"POT-Creation-Date: 2023-12-28 23:21+0000\n"
"PO-Revision-Date: 2020-05-02 22:29+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: uk\n"
Expand Down Expand Up @@ -685,6 +685,12 @@ msgid ""
"private people."
msgstr ""

msgid ""
"Privatized {privatized_entity_type} {privatized_entity_id} "
"({privatized_entity}) because of {reason_entity_type} {reason_entity_id} "
"({reason_entity})."
msgstr ""

msgid "Privatizer"
msgstr ""

Expand Down
5 changes: 4 additions & 1 deletion betty/extension/privatizer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ def privatize(self) -> None:
logger = getLogger()
logger.info(self._app.localizer._('Privatizing...'))

privatizer = PrivatizerApi(self._app.project.configuration.lifetime_threshold)
privatizer = PrivatizerApi(
self._app.project.configuration.lifetime_threshold,
localizer=self._app.localizer,
)

newly_privatized: dict[type[HasMutablePrivacy & Entity], int] = defaultdict(lambda: 0)
entities: list[HasMutablePrivacy & Entity] = []
Expand Down
9 changes: 8 additions & 1 deletion betty/model/ancestry.py
Original file line number Diff line number Diff line change
Expand Up @@ -1074,8 +1074,15 @@ def affiliation(self) -> str | None:

@property
def label(self) -> Str:
# @todo Rendering a different label when the name is private
# @todo means that the original data becomes somewhat unavailable
# @todo We do need to be able to communicate ABOUT OUR PRIVATE DATA to the USER (not the public)
# @todo
if self.private:
return Str._('private')
# @todo Temporarily check where this errors so we can add privacy checks there, then remove this.
raise RuntimeError
# if self.private:
# return Str._('private')
return Str._(
'{individual_name} {affiliation_name}',
individual_name='…' if not self.individual else self.individual,
Expand Down
4 changes: 2 additions & 2 deletions betty/model/event_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import TYPE_CHECKING

from betty.locale import Str
from betty.locale import Str, DEFAULT_LOCALIZER

if TYPE_CHECKING:
from betty.model.ancestry import Person
Expand Down Expand Up @@ -136,7 +136,7 @@ def comes_after(cls) -> set[type[EventType]]:
def may_create(cls, person: Person, lifetime_threshold: int) -> bool:
from betty.privatizer import Privatizer

return Privatizer(lifetime_threshold).has_expired(person, 1)
return Privatizer(lifetime_threshold, localizer=DEFAULT_LOCALIZER).has_expired(person, 1)


class FinalDispositionEventType(PostDeathEventType, DerivableEventType, EndOfLifeEventType):
Expand Down
41 changes: 29 additions & 12 deletions betty/privatizer.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
from __future__ import annotations

import logging
from datetime import datetime
from typing import Iterator, TypeAlias
from typing import Iterator, TypeAlias, Any

from betty.functools import walk
from betty.locale import DateRange, Date
from betty.locale import DateRange, Date, Localizer
from betty.model import Entity
from betty.model.ancestry import Person, Event, Citation, HasFiles, HasCitations, HasNotes, Source, \
Presence, Privacy, HasMutablePrivacy, HasPrivacy

Expirable: TypeAlias = Person | Event | Date | None


class Privatizer:
def __init__(self, lifetime_threshold: int):
def __init__(
self,
lifetime_threshold: int,
*,
localizer: Localizer,
):
self._lifetime_threshold = lifetime_threshold
self._localizer = localizer
self._seen: list[HasPrivacy] = []

def privatize(self, subject: HasPrivacy) -> None:
Expand Down Expand Up @@ -61,7 +69,7 @@ def _privatize_person(self, person: Person) -> None:
return

for name in person.names:
self._mark_private(name)
self._mark_private(name, person)
self._privatize(name)

for presence in person.presences:
Expand All @@ -71,7 +79,7 @@ def _privatize_presence(self, presence: Presence) -> None:
if not presence.private:
return
if presence.event is not None:
self._mark_private(presence.event)
self._mark_private(presence.event, presence)
self._privatize(presence.event)

def _privatize_event(self, event: Event) -> None:
Expand All @@ -84,38 +92,38 @@ def _privatize_has_citations(self, has_citations: HasCitations & HasPrivacy) ->
if not has_citations.private:
return
for citation in has_citations.citations:
self._mark_private(citation)
self._mark_private(citation, has_citations)
self._privatize(citation)

def _privatize_citation(self, citation: Citation) -> None:
if not citation.private:
return
if citation.source is not None:
self._mark_private(citation.source)
self._mark_private(citation.source, citation)
self._privatize(citation.source)

def _privatize_source(self, source: Source) -> None:
if not source.private:
return
for contained_source in source.contains:
self._mark_private(contained_source)
self._mark_private(contained_source, source)
self._privatize(contained_source)
for citation in source.citations:
self._mark_private(citation)
self._mark_private(citation, source)
self._privatize(citation)

def _privatize_has_files(self, has_files: HasFiles & HasPrivacy) -> None:
if not has_files.private:
return
for file in has_files.files:
self._mark_private(file)
self._mark_private(file, has_files.private)
self._privatize(file)

def _privatize_has_notes(self, has_notes: HasNotes & HasPrivacy) -> None:
if not has_notes.private:
return
for note in has_notes.notes:
self._mark_private(note)
self._mark_private(note, has_notes.private)
self._privatize(note)

def _ancestors_by_generation(self, person: Person, generations_ago: int = 1) -> Iterator[tuple[Person, int]]:
Expand Down Expand Up @@ -188,7 +196,16 @@ def _date_has_expired(

return date <= Date(datetime.now().year - self._lifetime_threshold * generations_ago, datetime.now().month, datetime.now().day)

def _mark_private(self, target: HasMutablePrivacy) -> None:
def _mark_private(self, target: HasMutablePrivacy, reason: Any) -> None:
# Do not change existing explicit privacy declarations.
if target.privacy is not Privacy.PUBLIC:
if isinstance(target, Entity) and isinstance(reason, Entity):
logging.getLogger().debug(self._localizer._('Privatized {privatized_entity_type} {privatized_entity_id} ({privatized_entity}) because of {reason_entity_type} {reason_entity_id} ({reason_entity}).').format(
privatized_entity_type=target.entity_type_label().localize(self._localizer),
privatized_entity_id=target.id,
privatized_entity=target.label.localize(self._localizer),
reason_entity_type=reason.entity_type_label().localize(self._localizer),
reason_entity_id=reason.id,
reason_entity=reason.label.localize(self._localizer),
))
target.privacy = Privacy.PRIVATE
Loading

0 comments on commit a3fd5a7

Please sign in to comment.