Exemplo n.º 1
0
 def test_event_should_encode_full(self):
     event = IdentifiableEvent('the_event', Birth())
     event.date = DateRange(Date(2000, 1, 1), Date(2019, 12, 31))
     event.place = Place('the_place', [PlaceName('The Place')])
     Presence(Person('the_person'), Subject(), event)
     event.citations.append(
         IdentifiableCitation('the_citation', Source('The Source')))
     expected = {
         '$schema': '/schema.json#/definitions/event',
         '@context': {
             'place': 'https://schema.org/location',
         },
         '@type': 'https://schema.org/Event',
         'id': 'the_event',
         'type': 'birth',
         'presences': [
             {
                 '@context': {
                     'person': 'https://schema.org/actor',
                 },
                 'role': 'subject',
                 'person': '/en/person/the_person/index.json',
             },
         ],
         'citations': [
             '/en/citation/the_citation/index.json',
         ],
         'date': {
             'start': {
                 'year': 2000,
                 'month': 1,
                 'day': 1,
             },
             'end': {
                 'year': 2019,
                 'month': 12,
                 'day': 31,
             },
         },
         'place': '/en/place/the_place/index.json',
         'links': [
             {
                 'url': '/en/event/the_event/index.json',
                 'relationship': 'canonical',
                 'mediaType': 'application/json',
             },
             {
                 'url': '/nl/event/the_event/index.json',
                 'relationship': 'alternate',
                 'locale': 'nl-NL',
             },
             {
                 'url': '/en/event/the_event/index.html',
                 'relationship': 'alternate',
                 'mediaType': 'text/html',
             },
         ],
     }
     self.assert_encodes(expected, event, 'event')
Exemplo n.º 2
0
def _load_dateval(element: ElementTree.Element, value_attribute_name: str) -> Optional[Date]:
    dateval = str(element.get(value_attribute_name))
    if _DATE_PATTERN.fullmatch(dateval):
        date_parts = [int(part) if _DATE_PART_PATTERN.fullmatch(
            part) and int(part) > 0 else None for part in dateval.split('-')]
        date = Date(*date_parts)
        dateval_quality = element.get('quality')
        if dateval_quality == 'estimated':
            date.fuzzy = True
        return date
    return None
Exemplo n.º 3
0
 def test_post_parse(self):
     with TemporaryDirectory() as output_directory_path:
         configuration = Configuration(
             output_directory_path, 'https://example.com')
         configuration.plugins[Deriver] = {}
         site = Site(configuration)
         person = Person('P0')
         other_presence = Presence(person, Presence.Role.SUBJECT, IdentifiableEvent('E0', Event.Type.MARRIAGE))
         other_presence.event.date = Date(1970, 1, 1)
         site.ancestry.people[person.id] = person
         parse(site)
         self.assertEquals(3, len(person.presences))
         self.assertEquals(DateRange(None, Date(1970, 1, 1)), person.start.date)
         self.assertEquals(DateRange(Date(1970, 1, 1)), person.end.date)
Exemplo n.º 4
0
 def test_derive_birth_with_existing_event(self, other_date: Datey):
     with TemporaryDirectory() as output_directory_path:
         configuration = Configuration(
             output_directory_path, 'https://example.com')
         configuration.plugins[Deriver] = {}
         site = Site(configuration)
         person = Person('P0')
         other_presence = Presence(person, Presence.Role.SUBJECT, IdentifiableEvent('E0', Event.Type.MARRIAGE))
         other_presence.event.date = other_date
         irrelevant_presence = Presence(person, Presence.Role.SUBJECT, IdentifiableEvent('E1', Event.Type.DIVORCE))
         irrelevant_presence.event.date = Date(1972, 1, 1)
         site.ancestry.people[person.id] = person
         parse(site)
         self.assertEquals(4, len(person.presences))
         self.assertEquals(DateRange(None, Date(1971, 1, 1)), person.start.date)
Exemplo n.º 5
0
 async def test(self):
     template = '{{ date | format_date }}'
     date = Date(1970, 1, 1)
     async with self._render(template_string=template, data={
         'date': date,
     }) as (actual, _):
         self.assertEquals('January 1, 1970', actual)
Exemplo n.º 6
0
 def test_derive_birth_with_existing_birth_with_date(self):
     with TemporaryDirectory() as output_directory_path:
         configuration = Configuration(
             output_directory_path, 'https://example.com')
         configuration.plugins[Deriver] = {}
         site = Site(configuration)
         person = Person('P0')
         birth_presence = Presence(person, Presence.Role.SUBJECT, IdentifiableEvent('E0', Event.Type.BIRTH))
         birth_presence.event.date = Date(1970, 2, 1)
         other_presence = Presence(person, Presence.Role.SUBJECT, IdentifiableEvent('E0', Event.Type.MARRIAGE))
         other_presence.event.date = Date(1970, 1, 1)
         site.ancestry.people[person.id] = person
         parse(site)
         self.assertEquals(3, len(person.presences))
         self.assertIsNotNone(birth_presence.event.date)
         self.assertEquals(Date(1970, 2, 1), birth_presence.event.date)
Exemplo n.º 7
0
    def _event_has_expired(self, event: Event, multiplier: int) -> bool:
        assert multiplier >= 0

        if event.date is None:
            return False

        date = event.date

        if isinstance(date, DateRange):
            if date.end is not None:
                date = date.end
            # A multiplier of 0 is only used for generation 0's end-of-life events. If those only have start dates, they
            # do not contain any information about by which date the event definitely has taken place, and therefore
            # they MUST be checked using another method call with a multiplier of 1 to verify they lie far enough in the
            # past.
            elif multiplier != 0:
                date = date.start
            else:
                return False

        if date is None:
            return False

        if not date.comparable:
            return False

        return date <= Date(
            datetime.now().year - self._lifetime_threshold * multiplier,
            datetime.now().month,
            datetime.now().day)
Exemplo n.º 8
0
class Test(TemplateTestCase):
    template_file = 'label/place.html.j2'

    @parameterized.expand([
        ('<address><a href="/place/P0/index.html"><span>The Place</span></a></address>',
         {
             'place': Place('P0', [PlaceName('The Place')]),
         }),
        ('<address><a href="/place/P0/index.html"><span lang="en">The Place</span></a></address>',
         {
             'place': Place('P0', [PlaceName('The Place', 'en')]),
         }),
        ('<address><a href="/place/P0/index.html"><span lang="nl">De Plaats</span></a></address>',
         {
             'place': Place('P0', [PlaceName('The Place', 'en'), PlaceName('De Plaats', 'nl')]),
             'locale': 'nl',
         }),
        ('<address><span>The Place</span></address>',
         {
             'place': Place('P0', [PlaceName('The Place')]),
             'embedded': True,
         }),
        ('<address><a href="/place/P0/index.html"><span lang="nl">De Nieuwe Plaats</span></a></address>',
         {
             'place': Place('P0', [PlaceName('The Old Place', 'en', date=DateRange(None, Date(1969, 12, 31))),
                                   PlaceName('De Nieuwe Plaats', 'nl', date=DateRange(Date(1970, 1, 1)))]),
             'locale': 'nl',
             'date_context': Date(1970, 1, 1),
         })
    ])
    @sync
    async def test(self, expected, data):
        async with self._render(data=data) as (actual, _):
            self.assertEqual(expected, actual)
Exemplo n.º 9
0
 def test_event_should_encode_full(self):
     event = IdentifiableEvent('the_event', Event.Type.BIRTH)
     event.date = DateRange(Date(2000, 1, 1), Date(2019, 12, 31))
     event.place = Place('the_place', [LocalizedName('The Place')])
     Presence(Person('the_person'), Presence.Role.SUBJECT, event)
     event.citations.append(
         Citation('the_citation', Source('the_source', 'The Source')))
     expected = {
         '$schema':
         '/schema.json#/definitions/event',
         '@context': {
             'place': 'https://schema.org/location',
         },
         '@type':
         'https://schema.org/Event',
         'id':
         'the_event',
         'type':
         Event.Type.BIRTH.value,
         'presences': [
             {
                 '@context': {
                     'person': 'https://schema.org/actor',
                 },
                 'role': Presence.Role.SUBJECT.value,
                 'person': '/person/the_person/index.json',
             },
         ],
         'citations': [
             '/citation/the_citation/index.json',
         ],
         'date': {
             'start': {
                 'year': 2000,
                 'month': 1,
                 'day': 1,
             },
             'end': {
                 'year': 2019,
                 'month': 12,
                 'day': 31,
             },
         },
         'place':
         '/place/the_place/index.json',
     }
     self.assert_encodes(expected, event, 'event')
Exemplo n.º 10
0
 async def test_with_end(self):
     person = Person('P0')
     Presence(person, Subject(), Event(Death(), Date(1970)))
     expected = '<div class="meta"><dl><dt>Death</dt><dd>1970</dd></dl></div>'
     async with self._render(data={
             'person': person,
     }) as (actual, _):
         self.assertEqual(expected, actual)
 async def test_with_date(self):
     event = Event(Birth())
     event.date = Date(1970)
     expected = '1970'
     async with self._render(data={
         'event': event,
     }) as (actual, _):
         self.assertEqual(expected, actual)
Exemplo n.º 12
0
 def test_date(self) -> None:
     encloses = Mock(Place)
     enclosed_by = Mock(Place)
     sut = Enclosure(encloses, enclosed_by)
     date = Date()
     self.assertIsNone(sut.date)
     sut.date = date
     self.assertEquals(date, sut.date)
Exemplo n.º 13
0
 def test_source_should_encode_full(self):
     source = IdentifiableSource('the_source', 'The Source')
     source.author = 'The Author'
     source.publisher = 'The Publisher'
     source.date = Date(2000, 1, 1)
     source.contained_by = IdentifiableSource(
         'the_containing_source', 'The Containing Source')
     link = Link('https://example.com/the-source')
     link.label = 'The Source Online'
     source.links.add(link)
     source.contains.append(
         IdentifiableSource('the_contained_source', 'The Contained Source'))
     IdentifiableCitation('the_citation', source)
     expected = {
         '$schema': '/schema.json#/definitions/source',
         '@context': {
             'name': 'https://schema.org/name',
         },
         '@type': 'https://schema.org/Thing',
         'id': 'the_source',
         'name': 'The Source',
         'author': 'The Author',
         'publisher': 'The Publisher',
         'contains': [
             '/en/source/the_contained_source/index.json',
         ],
         'citations': [
             '/en/citation/the_citation/index.json',
         ],
         'containedBy': '/en/source/the_containing_source/index.json',
         'date': {
             'year': 2000,
             'month': 1,
             'day': 1,
         },
         'links': [
             {
                 'url': '/en/source/the_source/index.json',
                 'relationship': 'canonical',
                 'mediaType': 'application/json',
             },
             {
                 'url': '/nl/source/the_source/index.json',
                 'relationship': 'alternate',
                 'locale': 'nl-NL',
             },
             {
                 'url': '/en/source/the_source/index.html',
                 'relationship': 'alternate',
                 'mediaType': 'text/html',
             },
             {
                 'url': 'https://example.com/the-source',
                 'label': 'The Source Online',
             },
         ],
     }
     self.assert_encodes(expected, source, 'source')
 async def test_with_date_and_place(self):
     event = Event(Birth())
     event.date = Date(1970)
     event.place = Place('P0', [PlaceName('The Place')])
     expected = '1970 in <address><a href="/place/P0/index.html"><span>The Place</span></a></address>'
     async with self._render(data={
         'event': event,
     }) as (actual, _):
         self.assertEqual(expected, actual)
Exemplo n.º 15
0
def _date_has_expired(date: Optional[Date], lifetime_threshold: int, multiplier: int) -> bool:
    assert multiplier >= 0

    if date is None:
        return False

    if not date.comparable:
        return False

    return date <= Date(datetime.now().year - lifetime_threshold * multiplier, datetime.now().month, datetime.now().day)
Exemplo n.º 16
0
 def test(self):
     with TemporaryDirectory() as www_directory_path:
         configuration = Configuration(www_directory_path,
                                       'https://example.com')
         environment = create_environment(Site(configuration))
         template = '{{ date | format_date }}'
         date = Date(1970, 1, 1)
         self.assertEquals(
             'January 1, 1970',
             environment.from_string(template).render(date=date))
Exemplo n.º 17
0
    async def test_post_parse(self):
        person = Person('P0')
        reference_presence = Presence(person, Subject(), Event(Residence()))
        reference_presence.event.date = Date(1970, 1, 1)

        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            configuration.plugins[Deriver] = None
            async with Site(configuration) as site:
                site.ancestry.people[person.id] = person
                await parse(site)

        self.assertEquals(3, len(person.presences))
        self.assertEquals(
            DateRange(None, Date(1970, 1, 1), end_is_boundary=True),
            person.start.date)
        self.assertEquals(DateRange(Date(1970, 1, 1), start_is_boundary=True),
                          person.end.date)
Exemplo n.º 18
0
def _expand(generation: int):
    multiplier = abs(generation) + 1 if generation < 0 else 1
    threshold_year = datetime.now().year - 100 * multiplier
    date_under_threshold = Date(threshold_year + 1, 1, 1)
    date_range_start_under_threshold = DateRange(date_under_threshold)
    date_range_end_under_threshold = DateRange(None, date_under_threshold)
    date_over_threshold = Date(threshold_year - 1, 1, 1)
    date_range_start_over_threshold = DateRange(date_over_threshold)
    date_range_end_over_threshold = DateRange(None, date_over_threshold)
    return parameterized.expand([
        # If there are no events for a person, their privacy does not change.
        (True, None, None),
        (True, True, None),
        (False, False, None),
        # Deaths and burials are special, and their existence prevents generation 0 from being private even without
        # having passed the usual threshold.
        (generation != 0, None, IdentifiableEvent('E0', Event.Type.DEATH, date=Date(datetime.now().year, datetime.now().month, datetime.now().day))),
        (generation != 0, None, IdentifiableEvent('E0', Event.Type.DEATH, date=date_under_threshold)),
        (True, None, IdentifiableEvent('E0', Event.Type.DEATH, date=date_range_start_under_threshold)),
        (generation != 0, None, IdentifiableEvent('E0', Event.Type.DEATH, date=date_range_end_under_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.DEATH)),
        (False, False, IdentifiableEvent('E0', Event.Type.DEATH)),
        (generation != 0, None, IdentifiableEvent('E0', Event.Type.BURIAL, date=Date(datetime.now().year, datetime.now().month, datetime.now().day))),
        (generation != 0, None, IdentifiableEvent('E0', Event.Type.BURIAL, date=date_under_threshold)),
        (True, None, IdentifiableEvent('E0', Event.Type.BURIAL, date=date_range_start_under_threshold)),
        (generation != 0, None, IdentifiableEvent('E0', Event.Type.BURIAL, date=date_range_end_under_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BURIAL)),
        (False, False, IdentifiableEvent('E0', Event.Type.BURIAL)),
        # Regular events without dates do not affect privacy.
        (True, None, IdentifiableEvent('E0', Event.Type.BIRTH)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH)),
        # Regular events with incomplete dates do not affect privacy.
        (True, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=Date())),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=Date())),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=Date())),
        # Regular events under the lifetime threshold do not affect privacy.
        (True, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_under_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_under_threshold)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_under_threshold)),
        (True, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_start_under_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_start_under_threshold)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_start_under_threshold)),
        (True, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_end_under_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_end_under_threshold)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_end_under_threshold)),
        # Regular events over the lifetime threshold affect privacy.
        (False, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_over_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_over_threshold)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_over_threshold)),
        (False, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_start_over_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_start_over_threshold)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_start_over_threshold)),
        (False, None, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_end_over_threshold)),
        (True, True, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_end_over_threshold)),
        (False, False, IdentifiableEvent('E0', Event.Type.BIRTH, date=date_range_end_over_threshold)),
    ])
 async def test_embedded(self):
     event = Event(Birth())
     event.date = Date(1970)
     event.place = Place('P0', [PlaceName('The Place')])
     event.citations.append(Citation(Source('The Source')))
     expected = '1970 in <address><span>The Place</span></address>'
     async with self._render(data={
         'event': event,
         'embedded': True,
     }) as (actual, _):
         self.assertEqual(expected, actual)
Exemplo n.º 20
0
    async def test_post_parse(self):
        person = Person('P0')
        reference_presence = Presence(person, Subject(),
                                      Event(None, Residence()))
        reference_presence.event.date = Date(1970, 1, 1)

        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            configuration.extensions.add(ExtensionConfiguration(Deriver))
            async with App(configuration) as app:
                app.ancestry.entities.append(person)
                await load(app)

        self.assertEquals(3, len(person.presences))
        self.assertEquals(
            DateRange(None, Date(1970, 1, 1), end_is_boundary=True),
            person.start.date)
        self.assertEquals(DateRange(Date(1970, 1, 1), start_is_boundary=True),
                          person.end.date)
Exemplo n.º 21
0
 async def test_embedded(self):
     person = Person('P0')
     Presence(person, Subject(), Event(None, Birth(), Date(1970)))
     PersonName(person, 'Jane', 'Dough')
     name = PersonName(person, 'Janet', 'Doughnut')
     name.citations.append(Citation(None, Source(None, 'The Source')))
     expected = '<div class="meta person-meta"><span class="aka">Also known as <span class="person-label" typeof="foaf:Person"><span property="foaf:individualName">Janet</span> <span property="foaf:familyName">Doughnut</span></span></span><dl><div><dt>Birth</dt><dd>1970</dd></div></dl></div>'
     async with self._render(data={
             'person': person,
             'embedded': True,
     }) as (actual, _):
         self.assertEqual(expected, actual)
Exemplo n.º 22
0
 def _init_globals(self) -> None:
     self.globals['site'] = self.site
     self.globals['locale'] = self.site.locale
     today = datetime.date.today()
     self.globals['today'] = Date(today.year, today.month, today.day)
     self.globals['plugins'] = _Plugins(self.site.plugins)
     self.globals['citer'] = _Citer()
     self.globals['search_index'] = lambda: Index(self.site).build()
     self.globals['html_providers'] = list([
         plugin for plugin in self.site.plugins.values()
         if isinstance(plugin, HtmlProvider)
     ])
     self.globals['path'] = os.path
Exemplo n.º 23
0
 def _init_globals(self) -> None:
     self.globals['app'] = self.app
     self.globals['locale'] = self.app.locale
     today = datetime.date.today()
     self.globals['today'] = Date(today.year, today.month, today.day)
     self.globals['extensions'] = _Extensions(self.app.extensions)
     self.globals['citer'] = _Citer()
     self.globals['search_index'] = lambda: Index(self.app).build()
     self.globals['html_providers'] = list([
         extension for extension in self.app.extensions.values()
         if isinstance(extension, HtmlProvider)
     ])
     self.globals['path'] = os.path
Exemplo n.º 24
0
 def test_source_should_encode_full(self):
     source = Source('the_source', 'The Source')
     source.author = 'The Author'
     source.publisher = 'The Publisher'
     source.date = Date(2000, 1, 1)
     source.contained_by = Source('the_containing_source',
                                  'The Containing Source')
     source.links.add(
         Link('https://example.com/the-person', 'The Person Online'))
     source.contains.append(
         Source('the_contained_source', 'The Contained Source'))
     source.citations.append(
         Citation('the_citation', Source('the_source', 'The Source')))
     expected = {
         '$schema':
         '/schema.json#/definitions/source',
         '@context': {
             'name': 'https://schema.org/name',
         },
         '@type':
         'https://schema.org/Thing',
         'id':
         'the_source',
         'name':
         'The Source',
         'author':
         'The Author',
         'publisher':
         'The Publisher',
         'contains': [
             '/source/the_contained_source/index.json',
         ],
         'citations': [
             '/citation/the_citation/index.json',
         ],
         'containedBy':
         '/source/the_containing_source/index.json',
         'date': {
             'year': 2000,
             'month': 1,
             'day': 1,
         },
         'links': [
             {
                 'url': 'https://example.com/the-person',
                 'label': 'The Person Online',
             },
         ],
     }
     self.assert_encodes(expected, source, 'source')
Exemplo n.º 25
0
    async def test_derive_update_comes_after_derivable_event(
            self, expected_datey: Optional[Datey],
            after_datey: Optional[Datey], derivable_datey: Optional[Datey]):
        expected_updates = 0 if expected_datey == derivable_datey else 1
        person = Person('P0')
        Presence(person, Subject(), Event(Ignored(), Date(0, 0, 0)))
        Presence(person, Subject(), Event(ComesAfterReference(), after_datey))
        derivable_event = Event(ComesAfterDerivable(), derivable_datey)
        Presence(person, Subject(), derivable_event)

        created, updated = derive(person, ComesAfterDerivable)

        self.assertEquals(expected_datey, derivable_event.date)
        self.assertEquals(0, created)
        self.assertEquals(expected_updates, updated)
        self.assertEquals(3, len(person.presences))
Exemplo n.º 26
0
class FormatDateTest(TestCase):
    @parameterized.expand([
        ('unknown date', Date()),
        ('unknown date', Date(None, None, 1)),
        ('January', Date(None, 1, None)),
        ('1970', Date(1970, None, None)),
        ('January, 1970', Date(1970, 1, None)),
        ('January 1, 1970', Date(1970, 1, 1)),
        ('January 1', Date(None, 1, 1)),
    ])
    def test(self, expected: str, date: Date):
        locale = 'en'
        with Translations(gettext.NullTranslations()):
            self.assertEquals(expected, format_datey(date, locale))
Exemplo n.º 27
0
    async def test_derive_create_comes_after_derivable_event(
            self, expected_datey: Optional[Datey],
            after_datey: Optional[Datey]):
        expected_creations = 0 if expected_datey is None else 1
        person = Person('P0')
        Presence(person, Subject(), Event(Ignored(), Date(0, 0, 0)))
        Presence(person, Subject(), Event(ComesAfterReference(), after_datey))

        created, updated = derive(person, ComesAfterCreatableDerivable)

        derived_presences = [
            presence for presence in person.presences
            if isinstance(presence.event.type, ComesAfterCreatableDerivable)
        ]
        self.assertEquals(expected_creations, len(derived_presences))
        if expected_creations:
            derived_presence = derived_presences[0]
            self.assertIsInstance(derived_presence.role, Subject)
            self.assertEquals(expected_datey, derived_presence.event.date)
        self.assertEquals(expected_creations, created)
        self.assertEquals(0, updated)
        self.assertEquals(2 + expected_creations, len(person.presences))
Exemplo n.º 28
0
 def test_date(self) -> None:
     date = Date()
     sut = PlaceName('Ikke', date=date)
     self.assertEquals(date, sut.date)
Exemplo n.º 29
0
def _expand_person(generation: int):
    lifetime_threshold = 125
    multiplier = abs(generation) + 1 if generation < 0 else 1
    lifetime_threshold_year = datetime.now(
    ).year - lifetime_threshold * multiplier
    date_under_lifetime_threshold = Date(lifetime_threshold_year + 1, 1, 1)
    date_range_start_under_lifetime_threshold = DateRange(
        date_under_lifetime_threshold)
    date_range_end_under_lifetime_threshold = DateRange(
        None, date_under_lifetime_threshold)
    date_over_lifetime_threshold = Date(lifetime_threshold_year - 1, 1, 1)
    date_range_start_over_lifetime_threshold = DateRange(
        date_over_lifetime_threshold)
    date_range_end_over_lifetime_threshold = DateRange(
        None, date_over_lifetime_threshold)
    return parameterized.expand([
        # If there are no events for a person, they are private.
        (True, None, None),
        (True, True, None),
        (False, False, None),

        # Deaths and other end-of-life events are special, but only for the person whose privacy is being checked:
        # - If they're present without dates, the person isn't private.
        # - If they're present and their dates or date ranges' end dates are in the past, the person isn't private.
        (generation != 0, None,
         Event(Death(),
               date=Date(datetime.now().year,
                         datetime.now().month,
                         datetime.now().day))),
        (generation != 0, None,
         Event(Death(), date=date_under_lifetime_threshold)),
        (True, None,
         Event(Death(), date=date_range_start_under_lifetime_threshold)),
        (generation != 0, None,
         Event(Death(), date=date_range_end_under_lifetime_threshold)),
        (False, None, Event(Death(), date=date_over_lifetime_threshold)),
        (True, None,
         Event(Death(), date=date_range_start_over_lifetime_threshold)),
        (False, None,
         Event(Death(), date=date_range_end_over_lifetime_threshold)),
        (True, True, Event(Death())),
        (False, False, Event(Death())),
        (generation != 0, None, Event(Death())),

        # Regular events without dates do not affect privacy.
        (True, None, Event(Birth())),
        (True, True, Event(Birth())),
        (False, False, Event(Birth())),

        # Regular events with incomplete dates do not affect privacy.
        (True, None, Event(Birth(), date=Date())),
        (True, True, Event(Birth(), date=Date())),
        (False, False, Event(Birth(), date=Date())),

        # Regular events under the lifetime threshold do not affect privacy.
        (True, None, Event(Birth(), date=date_under_lifetime_threshold)),
        (True, True, Event(Birth(), date=date_under_lifetime_threshold)),
        (False, False, Event(Birth(), date=date_under_lifetime_threshold)),
        (True, None,
         Event(Birth(), date=date_range_start_under_lifetime_threshold)),
        (True, True,
         Event(Birth(), date=date_range_start_under_lifetime_threshold)),
        (False, False,
         Event(Birth(), date=date_range_start_under_lifetime_threshold)),
        (True, None,
         Event(Birth(), date=date_range_end_under_lifetime_threshold)),
        (True, True,
         Event(Birth(), date=date_range_end_under_lifetime_threshold)),
        (False, False,
         Event(Birth(), date=date_range_end_under_lifetime_threshold)),

        # Regular events over the lifetime threshold affect privacy.
        (False, None, Event(Birth(), date=date_over_lifetime_threshold)),
        (True, True, Event(Birth(), date=date_over_lifetime_threshold)),
        (False, False, Event(Birth(), date=date_over_lifetime_threshold)),
        (True, None,
         Event(Birth(), date=date_range_start_over_lifetime_threshold)),
        (True, True,
         Event(Birth(), date=date_range_start_over_lifetime_threshold)),
        (False, False,
         Event(Birth(), date=date_range_start_over_lifetime_threshold)),
        (False, None,
         Event(Birth(), date=date_range_end_over_lifetime_threshold)),
        (True, True, Event(Birth(),
                           date=date_range_end_over_lifetime_threshold)),
        (False, False,
         Event(Birth(), date=date_range_end_over_lifetime_threshold)),
    ])
Exemplo n.º 30
0
class LoadXmlTest(TestCase):
    @classmethod
    @sync
    async def setUpClass(cls) -> None:
        TestCase.setUpClass()
        # @todo Convert each test method to use self._load(), so we can remove this shared XML file.
        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            async with App(configuration) as app:
                cls.ancestry = app.ancestry
                xml_file_path = Path(__file__).parent / 'assets' / 'data.xml'
                with open(xml_file_path) as f:
                    load_xml(app.ancestry, f.read(), rootname(xml_file_path))

    @sync
    async def load(self, xml: str) -> Ancestry:
        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            async with App(configuration) as app:
                with TemporaryDirectory() as tree_directory_path:
                    load_xml(app.ancestry, xml.strip(),
                             Path(tree_directory_path))
                    return app.ancestry

    def _load_partial(self, xml: str) -> Ancestry:
        return self.load("""
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE database PUBLIC "-//Gramps//DTD Gramps XML 1.7.1//EN"
"http://gramps-project.org/xml/1.7.1/grampsxml.dtd">
<database xmlns="http://gramps-project.org/xml/1.7.1/">
  <header>
    <created date="2019-03-09" version="4.2.8"/>
    <researcher>
    </researcher>
  </header>
  %s
</database>
""" % xml)

    @sync
    async def test_load_xml_with_string(self):
        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            async with App(configuration) as app:
                gramps_file_path = Path(
                    __file__).parent / 'assets' / 'minimal.xml'
                with open(gramps_file_path) as f:
                    load_xml(app.ancestry, f.read(),
                             rootname(gramps_file_path))

    @sync
    async def test_load_xml_with_file_path(self):
        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            async with App(configuration) as app:
                gramps_file_path = Path(
                    __file__).parent / 'assets' / 'minimal.xml'
                load_xml(app.ancestry, gramps_file_path,
                         rootname(gramps_file_path))

    @sync
    async def test_load_gramps(self):
        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            async with App(configuration) as app:
                gramps_file_path = Path(
                    __file__).parent / 'assets' / 'minimal.gramps'
                load_gramps(app.ancestry, gramps_file_path)

    @sync
    async def test_load_gpkg(self):
        with TemporaryDirectory() as output_directory_path:
            configuration = Configuration(output_directory_path,
                                          'https://example.com')
            async with App(configuration) as app:
                gramps_file_path = Path(
                    __file__).parent / 'assets' / 'minimal.gpkg'
                load_gpkg(app.ancestry, gramps_file_path)

    def test_place_should_include_name(self):
        place = self.ancestry.entities[Place]['P0000']
        names = place.names
        self.assertEquals(1, len(names))
        name = names[0]
        self.assertEquals('Amsterdam', name.name)

    def test_place_should_include_coordinates(self):
        place = self.ancestry.entities[Place]['P0000']
        self.assertAlmostEquals(52.366667, place.coordinates.latitude)
        self.assertAlmostEquals(4.9, place.coordinates.longitude)

    def test_place_should_include_events(self):
        place = self.ancestry.entities[Place]['P0000']
        event = self.ancestry.entities[Event]['E0000']
        self.assertIn(event, place.events)

    def test_person_should_include_name(self):
        person = self.ancestry.entities[Person]['I0000']
        expected = PersonName(person, 'Jane', 'Doe')
        self.assertEquals(expected, person.name)

    def test_person_should_include_alternative_names(self):
        person = self.ancestry.entities[Person]['I0000']
        self.assertEqual(2, len(person.alternative_names))
        self.assertIs(person, person.alternative_names[0].person)
        self.assertEquals('Jane', person.alternative_names[0].individual)
        self.assertEquals('Doh', person.alternative_names[0].affiliation)
        self.assertIs(person, person.alternative_names[1].person)
        self.assertEquals('Jen', person.alternative_names[1].individual)
        self.assertEquals('Van Doughie',
                          person.alternative_names[1].affiliation)

    def test_person_should_include_birth(self):
        person = self.ancestry.entities[Person]['I0000']
        self.assertEquals('E0000', person.start.id)

    def test_person_should_include_death(self):
        person = self.ancestry.entities[Person]['I0003']
        self.assertEquals('E0002', person.end.id)

    def test_person_should_be_private(self):
        person = self.ancestry.entities[Person]['I0003']
        self.assertTrue(person.private)

    def test_person_should_not_be_private(self):
        person = self.ancestry.entities[Person]['I0002']
        self.assertFalse(person.private)

    def test_person_should_include_citation(self):
        person = self.ancestry.entities[Person]['I0000']
        citation = self.ancestry.entities[Citation]['C0000']
        self.assertIn(citation, person.citations)

    def test_family_should_set_parents(self):
        expected_parents = [
            self.ancestry.entities[Person]['I0002'],
            self.ancestry.entities[Person]['I0003']
        ]
        children = [
            self.ancestry.entities[Person]['I0000'],
            self.ancestry.entities[Person]['I0001']
        ]
        for child in children:
            self.assertCountEqual(expected_parents, child.parents)

    def test_family_should_set_children(self):
        parents = [
            self.ancestry.entities[Person]['I0002'],
            self.ancestry.entities[Person]['I0003']
        ]
        expected_children = [
            self.ancestry.entities[Person]['I0000'],
            self.ancestry.entities[Person]['I0001']
        ]
        for parent in parents:
            self.assertCountEqual(expected_children, parent.children)

    def test_event_should_be_birth(self):
        self.assertIsInstance(self.ancestry.entities[Event]['E0000'].type,
                              Birth)

    def test_event_should_be_death(self):
        self.assertIsInstance(self.ancestry.entities[Event]['E0002'].type,
                              Death)

    def test_event_should_load_unknown(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>SomeEventThatIUsedToKnow</type>
        <dateval val="0000-00-00" quality="calculated"/>
    </event>
</events>
""")
        self.assertIsInstance(ancestry.entities[Event]['E0000'].type,
                              UnknownEventType)

    def test_event_should_include_place(self):
        event = self.ancestry.entities[Event]['E0000']
        place = self.ancestry.entities[Place]['P0000']
        self.assertEquals(place, event.place)

    def test_event_should_include_date(self):
        event = self.ancestry.entities[Event]['E0000']
        self.assertEquals(1970, event.date.year)
        self.assertEquals(1, event.date.month)
        self.assertEquals(1, event.date.day)

    def test_event_should_include_people(self):
        event = self.ancestry.entities[Event]['E0000']
        expected_people = [self.ancestry.entities[Person]['I0000']]
        self.assertCountEqual(
            expected_people, [presence.person for presence in event.presences])

    def test_event_should_include_description(self):
        event = self.ancestry.entities[Event]['E0008']
        self.assertEquals('Something happened!', event.description)

    @parameterized.expand([
        (Date(), '0000-00-00'),
        (Date(None, None, 1), '0000-00-01'),
        (Date(None, 1), '0000-01-00'),
        (Date(None, 1, 1), '0000-01-01'),
        (Date(1970), '1970-00-00'),
        (Date(1970, None, 1), '1970-00-01'),
        (Date(1970, 1), '1970-01-00'),
        (Date(1970, 1, 1), '1970-01-01'),
    ])
    def test_date_should_load_parts(self, expected: Date, dateval_val: str):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <dateval val="%s" quality="calculated"/>
    </event>
</events>
""" % dateval_val)
        self.assertEquals(expected, ancestry.entities[Event]['E0000'].date)

    def test_date_should_ignore_calendar_format(self):
        self.assertIsNone(self.ancestry.entities[Event]['E0005'].date)

    def test_date_should_load_before(self):
        date = self.ancestry.entities[Event]['E0003'].date
        self.assertIsNone(date.start)
        self.assertEquals(1970, date.end.year)
        self.assertEquals(1, date.end.month)
        self.assertEquals(1, date.end.day)
        self.assertTrue(date.end_is_boundary)
        self.assertFalse(date.end.fuzzy)

    def test_date_should_load_after(self):
        date = self.ancestry.entities[Event]['E0004'].date
        self.assertIsNone(date.end)
        self.assertEquals(1970, date.start.year)
        self.assertEquals(1, date.start.month)
        self.assertEquals(1, date.start.day)
        self.assertTrue(date.start_is_boundary)
        self.assertFalse(date.start.fuzzy)

    def test_date_should_load_calculated(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <dateval val="1970-01-01" quality="calculated"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertEquals(1970, date.year)
        self.assertEquals(1, date.month)
        self.assertEquals(1, date.day)
        self.assertFalse(date.fuzzy)

    def test_date_should_load_estimated(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <dateval val="1970-01-01" quality="estimated"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertEquals(1970, date.year)
        self.assertEquals(1, date.month)
        self.assertEquals(1, date.day)
        self.assertTrue(date.fuzzy)

    def test_date_should_load_about(self):
        date = self.ancestry.entities[Event]['E0007'].date
        self.assertEquals(1970, date.year)
        self.assertEquals(1, date.month)
        self.assertEquals(1, date.day)
        self.assertTrue(date.fuzzy)

    def test_daterange_should_load(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <daterange start="1970-01-01" stop="1999-12-31"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertEquals(1970, date.start.year)
        self.assertEquals(1, date.start.month)
        self.assertEquals(1, date.start.day)
        self.assertFalse(date.start.fuzzy)
        self.assertTrue(date.start_is_boundary)
        self.assertEquals(1999, date.end.year)
        self.assertEquals(12, date.end.month)
        self.assertEquals(31, date.end.day)
        self.assertTrue(date.end_is_boundary)
        self.assertFalse(date.end.fuzzy)

    def test_daterange_should_load_calculated(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <daterange start="1970-01-01" stop="1999-12-31" quality="calculated"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertFalse(date.start.fuzzy)
        self.assertFalse(date.end.fuzzy)

    def test_daterange_should_load_estimated(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <daterange start="1970-01-01" stop="1999-12-31" quality="estimated"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertTrue(date.start.fuzzy)
        self.assertTrue(date.end.fuzzy)

    def test_datespan_should_load(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <datespan start="1970-01-01" stop="1999-12-31"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertEquals(1970, date.start.year)
        self.assertEquals(1, date.start.month)
        self.assertEquals(1, date.start.day)
        self.assertFalse(date.start.fuzzy)
        self.assertEquals(1999, date.end.year)
        self.assertEquals(12, date.end.month)
        self.assertEquals(31, date.end.day)
        self.assertFalse(date.end.fuzzy)

    def test_datespan_should_load_calculated(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <datespan start="1970-01-01" stop="1999-12-31" quality="calculated"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertFalse(date.start.fuzzy)
        self.assertFalse(date.end.fuzzy)

    def test_datespan_should_load_estimated(self):
        ancestry = self._load_partial("""
<events>
    <event handle="_e7692ea23775e80643fe4fcf91" change="1590243374" id="E0000">
        <type>Birth</type>
        <datespan start="1970-01-01" stop="1999-12-31" quality="estimated"/>
    </event>
</events>
""")
        date = ancestry.entities[Event]['E0000'].date
        self.assertTrue(date.start.fuzzy)
        self.assertTrue(date.end.fuzzy)

    def test_source_from_repository_should_include_name(self):
        source = self.ancestry.entities[Source]['R0000']
        self.assertEquals('Library of Alexandria', source.name)

    def test_source_from_repository_should_include_link(self):
        links = self.ancestry.entities[Source]['R0000'].links
        self.assertEquals(1, len(links))
        link = list(links)[0]
        self.assertEquals('https://alexandria.example.com', link.url)
        self.assertEquals('Library of Alexandria Catalogue', link.label)

    def test_source_from_source_should_include_title(self):
        source = self.ancestry.entities[Source]['S0000']
        self.assertEquals('A Whisper', source.name)

    def test_source_from_source_should_include_author(self):
        source = self.ancestry.entities[Source]['S0000']
        self.assertEquals('A Little Birdie', source.author)

    def test_source_from_source_should_include_publisher(self):
        source = self.ancestry.entities[Source]['S0000']
        self.assertEquals('Somewhere over the rainbow', source.publisher)

    def test_source_from_source_should_include_repository(self):
        source = self.ancestry.entities[Source]['S0000']
        containing_source = self.ancestry.entities[Source]['R0000']
        self.assertEquals(containing_source, source.contained_by)

    @parameterized.expand([
        (True, 'private'),
        (False, 'public'),
        (None, 'publi'),
        (None, 'privat'),
    ])
    def test_person_should_include_privacy_from_attribute(
            self, expected: Optional[bool], attribute_value: str) -> None:
        ancestry = self._load_partial("""
<people>
    <person handle="_e1dd3ac2fa22e6fefa18f738bdd" change="1552126811" id="I0000">
        <gender>U</gender>
        <attribute type="betty:privacy" value="%s"/>
    </person>
</people>
""" % attribute_value)
        person = ancestry.entities[Person]['I0000']
        self.assertEquals(expected, person.private)

    @parameterized.expand([
        (True, 'private'),
        (False, 'public'),
        (None, 'publi'),
        (None, 'privat'),
    ])
    def test_event_should_include_privacy_from_attribute(
            self, expected: Optional[bool], attribute_value: str) -> None:
        ancestry = self._load_partial("""
<events>
    <event handle="_e1dd3ac2fa22e6fefa18f738bdd" change="1552126811" id="E0000">
        <type>Birth</type>
        <attribute type="betty:privacy" value="%s"/>
    </event>
</events>
""" % attribute_value)
        event = ancestry.entities[Event]['E0000']
        self.assertEquals(expected, event.private)

    @parameterized.expand([
        (True, 'private'),
        (False, 'public'),
        (None, 'publi'),
        (None, 'privat'),
    ])
    def test_file_should_include_privacy_from_attribute(
            self, expected: Optional[bool], attribute_value: str) -> None:
        ancestry = self._load_partial("""
<objects>
    <object handle="_e66f421249f3e9ebf6744d3b11d" change="1583534526" id="O0000">
        <file src="/tmp/file.txt" mime="text/plain" checksum="d41d8cd98f00b204e9800998ecf8427e" description="file"/>
        <attribute type="betty:privacy" value="%s"/>
    </object>
</objects>
""" % attribute_value)
        file = ancestry.entities[File]['O0000']
        self.assertEquals(expected, file.private)

    @parameterized.expand([
        (True, 'private'),
        (False, 'public'),
        (None, 'publi'),
        (None, 'privat'),
    ])
    def test_source_from_source_should_include_privacy_from_attribute(
            self, expected: Optional[bool], attribute_value: str) -> None:
        ancestry = self._load_partial("""
<sources>
    <source handle="_e1dd686b04813540eb3503a342b" change="1558277217" id="S0000">
        <stitle>A Whisper</stitle>
        <srcattribute type="betty:privacy" value="%s"/>
    </source>
</sources>
""" % attribute_value)
        source = ancestry.entities[Source]['S0000']
        self.assertEquals(expected, source.private)

    @parameterized.expand([
        (True, 'private'),
        (False, 'public'),
        (None, 'publi'),
        (None, 'privat'),
    ])
    def test_citation_should_include_privacy_from_attribute(
            self, expected: Optional[bool], attribute_value: str) -> None:
        ancestry = self._load_partial("""
<citations>
    <citation handle="_e2c25a12a097a0b24bd9eae5090" change="1558277266" id="C0000">
        <confidence>2</confidence>
        <sourceref hlink="_e1dd686b04813540eb3503a342b"/>
        <srcattribute type="betty:privacy" value="%s"/>
    </citation>
</citations>
<sources>
    <source handle="_e1dd686b04813540eb3503a342b" change="1558277217" id="S0000">
        <stitle>A Whisper</stitle>
    </source>
</sources>
""" % attribute_value)
        citation = ancestry.entities[Citation]['C0000']
        self.assertEquals(expected, citation.private)

    def test_note_should_include_text(self) -> None:
        ancestry = self._load_partial("""
<notes>
    <note handle="_e1cb35d7e6c1984b0e8361e1aee" change="1551643112" id="N0000" type="Transcript">
        <text>I left this for you.</text>
    </note>
</notes>
""")
        note = ancestry.entities[Note]['N0000']
        self.assertEquals('I left this for you.', note.text)