async def test_post_parse(self, m_aioresponses) -> None: resource = IdentifiableSource('the_source', 'The Source') link = Link('https://en.wikipedia.org/wiki/Amsterdam') resource.links.add(link) entry_title = 'Amstelredam' entry_extract = 'Capitol of the Netherlands' entry_api_response_body = { 'query': { 'pages': [ { 'title': entry_title, 'extract': entry_extract, }, ], } } entry_api_url = 'https://en.wikipedia.org/w/api.php?action=query&titles=Amsterdam&prop=extracts&exintro&format=json&formatversion=2' m_aioresponses.get(entry_api_url, payload=entry_api_response_body) translations_api_response_body = { 'query': { 'pages': [ { 'langlinks': [], }, ], }, } translations_api_url = 'https://en.wikipedia.org/w/api.php?action=query&titles=Amsterdam&prop=langlinks&lllimit=500&format=json&formatversion=2' m_aioresponses.get(translations_api_url, payload=translations_api_response_body) with TemporaryDirectory() as output_directory_path: with TemporaryDirectory() as cache_directory_path: configuration = Configuration(output_directory_path, 'https://example.com') configuration.cache_directory_path = cache_directory_path configuration.plugins[Wikipedia] = None async with Site(configuration) as site: site.ancestry.sources[resource.id] = resource await parse(site) self.assertEqual(1, len(resource.links)) self.assertEqual(entry_title, link.label) self.assertEqual('en', link.locale) self.assertEqual('text/html', link.media_type) self.assertIsNotNone(link.description) self.assertEqual('external', link.relationship)
async def test_populate_should_ignore_non_wikipedia_links( self, m_retriever) -> None: sut = Populator(m_retriever) link = Link('https://example.com') resource = IdentifiableSource('the_source', 'The Source') resource.links.add(link) ancestry = Ancestry() ancestry.sources[resource.id] = resource with TemporaryDirectory() as output_directory_path: with TemporaryDirectory() as cache_directory_path: configuration = Configuration(output_directory_path, 'https://example.com') configuration.cache_directory_path = cache_directory_path async with Site(configuration) as site: await sut.populate(ancestry, site) self.assertSetEqual({link}, resource.links)
def test_one_should_return_translated_entry(self, m_requests): language = 'nl' page_uri = 'https://en.wikipedia.org/wiki/Amsterdam' link = Link(page_uri) translations_api_uri = 'https://en.wikipedia.org/w/api.php?action=query&titles=Amsterdam&prop=langlinks&lllimit=500&format=json&formatversion=2' page_api_uri = 'https://nl.wikipedia.org/w/api.php?action=query&titles=Amsterdam&prop=extracts&exintro&format=json&formatversion=2' title = 'Amsterdam' extract_nl = 'De hoofdstad van Nederland.' api_translations_response_body_nl = { 'query': { 'pages': [ { 'langlinks': [ { 'lang': 'nl', 'title': title, }, ], }, ], }, } api_page_response_body_nl = { 'query': { 'pages': [ { 'title': title, 'extract': extract_nl, }, ], } } m_requests.register_uri('GET', translations_api_uri, json=api_translations_response_body_nl) m_requests.register_uri('GET', page_api_uri, json=api_page_response_body_nl) with TemporaryDirectory() as cache_directory_path: retriever = Retriever(cache_directory_path, 1) entry = retriever.one(language, link) self.assertEquals(2, m_requests.call_count) self.assertEquals(page_uri, entry.uri) self.assertEquals(title, entry.title) self.assertEquals(extract_nl, entry.content)
async def _populate_resource(self, resource: Resource, locales: Set[str]) -> None: if not isinstance(resource, HasLinks): return entry_links = set() for link in resource.links: try: entry_language, entry_name = _parse_url(link.url) entry_links.add((entry_language, entry_name)) except NotAnEntryError: continue entry = None if link.label is None: with suppress(RetrievalError): entry = await self._retriever.get_entry( entry_language, entry_name) await self.populate_link(link, entry_language, entry) for entry_language, entry_name in list(entry_links): entry_translations = await self._retriever.get_translations( entry_language, entry_name) if len(entry_translations) == 0: continue entry_languages = list(entry_translations.keys()) for locale in locales.difference({entry_language}): added_entry_language = negotiate_locale( locale, entry_languages) if added_entry_language is None: continue added_entry_name = entry_translations[added_entry_language] if (added_entry_language, added_entry_name) in entry_links: continue try: added_entry = await self._retriever.get_entry( added_entry_language, added_entry_name) except RetrievalError: continue added_link = Link(added_entry.url) await self.populate_link(added_link, added_entry_language, added_entry) resource.links.add(added_link) entry_links.add((added_entry_language, added_entry_name))
def test_link_should_encode_full(self) -> None: link = Link('https://example.com') link.label = 'The Link' link.relationship = 'external' link.locale = 'nl-NL' link.media_type = MediaType('text/html') expected = { 'url': 'https://example.com', 'relationship': 'external', 'label': 'The Link', 'locale': 'nl-NL', 'mediaType': 'text/html', } self.assert_encodes(expected, link, 'link')
async def populate_link(self, link: Link, site: Site, entry_language: str, entry: Optional[Entry] = None) -> None: if link.url.startswith('http:'): link.url = 'https:' + link.url[5:] if link.media_type is None: link.media_type = 'text/html' if link.relationship is None: link.relationship = 'external' if link.locale is None: link.locale = entry_language if link.description is None: # There are valid reasons for links in locales that aren't supported. with suppress(ValueError): async with site.with_locale(link.locale): link.description = _('Read more on Wikipedia.') if entry is not None and link.label is None: link.label = entry.title
def test_all_should_return_entry(self, page_uri: str, m_requests): link = Link(page_uri) api_uri = 'https://en.wikipedia.org/w/api.php?action=query&titles=Amsterdam&prop=extracts&exintro&format=json&formatversion=2' title = 'Amstelredam' extract = 'De hoofdstad van Nederland.' api_response_body = { 'query': { 'pages': [ { 'title': title, 'extract': extract, }, ], } } m_requests.register_uri('GET', api_uri, json=api_response_body) with TemporaryDirectory() as cache_directory_path: entries = list(Retriever(cache_directory_path).all([link])) self.assertEquals(1, len(entries)) entry = entries[0] self.assertEquals(page_uri, entry.uri) self.assertEquals(title, entry.title) self.assertEquals(extract, entry.content)
def test_one_should_return_none_if_no_translation_exists( self, langlinks: List[Dict], m_requests): language = 'nl' page_uri = 'https://en.wikipedia.org/wiki/Amsterdam' link = Link(page_uri) translations_api_uri = 'https://en.wikipedia.org/w/api.php?action=query&titles=Amsterdam&prop=langlinks&lllimit=500&format=json&formatversion=2' api_translations_response_body_nl = { 'query': { 'pages': [ { 'langlinks': langlinks, }, ], }, } m_requests.register_uri('GET', translations_api_uri, json=api_translations_response_body_nl) with TemporaryDirectory() as cache_directory_path: retriever = Retriever(cache_directory_path, 1) entry = retriever.one(language, link) self.assertEquals(1, m_requests.call_count) self.assertIsNone(entry)
def test_person_should_encode_full(self): parent_id = 'the_parent' parent = Person(parent_id) child_id = 'the_child' child = Person(child_id) sibling_id = 'the_sibling' sibling = Person(sibling_id) sibling.parents.append(parent) person_id = 'the_person' person_affiliation_name = 'Person' person_individual_name = 'The' person = Person(person_id) person.names.append( PersonName(person_individual_name, person_affiliation_name)) person.parents.append(parent) person.children.append(child) person.private = False person.links.add( Link('https://example.com/the-person', 'The Person Online')) person.citations.append( Citation('the_citation', Source('the_source', 'The Source'))) Presence(person, Presence.Role.SUBJECT, IdentifiableEvent('the_event', Event.Type.BIRTH)) expected = { '$schema': '/schema.json#/definitions/person', '@context': { 'parents': 'https://schema.org/parent', 'children': 'https://schema.org/child', 'siblings': 'https://schema.org/sibling', }, '@type': 'https://schema.org/Person', 'id': person_id, 'names': [ { '@context': { 'individual': 'https://schema.org/givenName', 'affiliation': 'https://schema.org/familyName', }, 'individual': person_individual_name, 'affiliation': person_affiliation_name, }, ], 'parents': [ '/person/the_parent/index.json', ], 'children': [ '/person/the_child/index.json', ], 'siblings': [ '/person/the_sibling/index.json', ], 'private': False, 'presences': [ { '@context': { 'event': 'https://schema.org/performerIn', }, 'role': Presence.Role.SUBJECT.value, 'event': '/event/the_event/index.json', }, ], 'citations': [ '/citation/the_citation/index.json', ], 'links': [ { 'url': 'https://example.com/the-person', 'label': 'The Person Online', }, ], } self.assert_encodes(expected, person, 'person')
def test_label(self) -> None: url = 'https://example.com' sut = Link(url) self.assertIsNone(sut.label)
def test_relationship(self) -> None: url = 'https://example.com' sut = Link(url) self.assertIsNone(sut.relationship)
def test_description(self) -> None: url = 'https://example.com' sut = Link(url) self.assertIsNone(sut.description)
def test_media_type(self) -> None: url = 'https://example.com' sut = Link(url) self.assertIsNone(sut.media_type)
def test_url(self) -> None: url = 'https://example.com' sut = Link(url) self.assertEquals(url, sut.url)
def _encode_identifiable_resource( self, encoded: Dict, resource: Union[Identifiable, Resource]) -> None: if 'links' not in encoded: encoded['links'] = [] canonical = Link(self._generate_url(resource)) canonical.relationship = 'canonical' canonical.media_type = 'application/json' encoded['links'].append(canonical) for locale in self._site.configuration.locales: if locale == self._locale: continue translation = Link(self._generate_url(resource, locale=locale)) translation.relationship = 'alternate' translation.locale = locale encoded['links'].append(translation) html = Link(self._generate_url(resource, media_type='text/html')) html.relationship = 'alternate' html.media_type = 'text/html' encoded['links'].append(html)
async def load(self) -> None: amsterdam = Place('betty-demo-amsterdam', [PlaceName('Amsterdam')]) amsterdam.coordinates = Point(52.366667, 4.9) amsterdam.links.add(Link('https://nl.wikipedia.org/wiki/Amsterdam')) self._ancestry.places[amsterdam.id] = amsterdam ilpendam = Place('betty-demo-ilpendam', [PlaceName('Ilpendam')]) ilpendam.coordinates = Point(52.465556, 4.951111) ilpendam.links.add(Link('https://nl.wikipedia.org/wiki/Ilpendam')) self._ancestry.places[ilpendam.id] = ilpendam personal_accounts = IdentifiableSource('betty-demo-personal-accounts', 'Personal accounts') self._ancestry.sources[personal_accounts.id] = personal_accounts cite_first_person_account = IdentifiableCitation( 'betty-demo-first-person-account', personal_accounts) self._ancestry.citations[ cite_first_person_account.id] = cite_first_person_account bevolkingsregister_amsterdam = IdentifiableSource( 'betty-demo-bevolkingsregister-amsterdam', 'Bevolkingsregister Amsterdam') bevolkingsregister_amsterdam.author = 'Gemeente Amsterdam' bevolkingsregister_amsterdam.publisher = 'Gemeente Amsterdam' self._ancestry.sources[ bevolkingsregister_amsterdam.id] = bevolkingsregister_amsterdam david_marinus_lankester = Person('betty-demo-david-marinus-lankester') david_marinus_lankester.names.append( PersonName('David Marinus', 'Lankester')) self._ancestry.people[ david_marinus_lankester.id] = david_marinus_lankester geertruida_van_ling = Person('betty-demo-geertruida-van-ling') geertruida_van_ling.names.append(PersonName('Geertruida', 'Van Ling')) self._ancestry.people[geertruida_van_ling.id] = geertruida_van_ling marriage_of_dirk_jacobus_lankester_and_jannigje_palsen = IdentifiableEvent( 'betty-demo-marriage-of-dirk-jacobus-lankester-and-jannigje-palsen', Marriage(), Date(1922, 7, 4)) marriage_of_dirk_jacobus_lankester_and_jannigje_palsen.place = ilpendam self._ancestry.events[ marriage_of_dirk_jacobus_lankester_and_jannigje_palsen. id] = marriage_of_dirk_jacobus_lankester_and_jannigje_palsen birth_of_dirk_jacobus_lankester = IdentifiableEvent( 'betty-demo-birth-of-dirk-jacobus-lankester', Birth(), Date(1897, 8, 25)) birth_of_dirk_jacobus_lankester.place = amsterdam self._ancestry.events[birth_of_dirk_jacobus_lankester. id] = birth_of_dirk_jacobus_lankester death_of_dirk_jacobus_lankester = IdentifiableEvent( 'betty-demo-death-of-dirk-jacobus-lankester', Death(), Date(1986, 8, 18)) death_of_dirk_jacobus_lankester.place = amsterdam self._ancestry.events[death_of_dirk_jacobus_lankester. id] = death_of_dirk_jacobus_lankester dirk_jacobus_lankester = Person('betty-demo-dirk-jacobus-lankester') dirk_jacobus_lankester.names.append( PersonName('Dirk Jacobus', 'Lankester')) Presence(dirk_jacobus_lankester, Subject(), birth_of_dirk_jacobus_lankester) Presence(dirk_jacobus_lankester, Subject(), death_of_dirk_jacobus_lankester) Presence(dirk_jacobus_lankester, Subject(), marriage_of_dirk_jacobus_lankester_and_jannigje_palsen) dirk_jacobus_lankester.parents.append(david_marinus_lankester, geertruida_van_ling) self._ancestry.people[ dirk_jacobus_lankester.id] = dirk_jacobus_lankester birth_of_marinus_david_lankester = IdentifiableEvent( 'betty-demo-birth-of-marinus-david', Birth(), DateRange(Date(1874, 1, 15), Date(1874, 3, 21), start_is_boundary=True, end_is_boundary=True)) birth_of_marinus_david_lankester.place = amsterdam self._ancestry.events[birth_of_marinus_david_lankester. id] = birth_of_marinus_david_lankester death_of_marinus_david_lankester = IdentifiableEvent( 'betty-demo-death-of-marinus-david', Death(), Date(1971)) death_of_marinus_david_lankester.place = amsterdam self._ancestry.events[death_of_marinus_david_lankester. id] = death_of_marinus_david_lankester marinus_david_lankester = Person('betty-demo-marinus-david-lankester') marinus_david_lankester.names.append( PersonName('Marinus David', 'Lankester')) Presence(marinus_david_lankester, Subject(), birth_of_marinus_david_lankester) Presence(marinus_david_lankester, Subject(), death_of_marinus_david_lankester) marinus_david_lankester.parents.append(david_marinus_lankester, geertruida_van_ling) self._ancestry.people[ marinus_david_lankester.id] = marinus_david_lankester birth_of_jacoba_gesina_lankester = IdentifiableEvent( 'betty-demo-birth-of-jacoba-gesina', Birth(), Date(1900, 3, 14)) birth_of_jacoba_gesina_lankester.place = amsterdam self._ancestry.events[birth_of_jacoba_gesina_lankester. id] = birth_of_jacoba_gesina_lankester jacoba_gesina_lankester = Person('betty-demo-jacoba-gesina-lankester') jacoba_gesina_lankester.names.append( PersonName('Jacoba Gesina', 'Lankester')) Presence(jacoba_gesina_lankester, Subject(), birth_of_jacoba_gesina_lankester) jacoba_gesina_lankester.parents.append(david_marinus_lankester, geertruida_van_ling) self._ancestry.people[ jacoba_gesina_lankester.id] = jacoba_gesina_lankester jannigje_palsen = Person('betty-demo-jannigje-palsen') jannigje_palsen.names.append(PersonName('Jannigje', 'Palsen')) Presence(jannigje_palsen, Subject(), marriage_of_dirk_jacobus_lankester_and_jannigje_palsen) self._ancestry.people[jannigje_palsen.id] = jannigje_palsen marriage_of_johan_de_boer_and_liberta_lankester = IdentifiableEvent( 'betty-demo-marriage-of-johan-de-boer-and-liberta-lankester', Marriage(), Date(1953, 6, 19)) marriage_of_johan_de_boer_and_liberta_lankester.place = amsterdam self._ancestry.events[ marriage_of_johan_de_boer_and_liberta_lankester. id] = marriage_of_johan_de_boer_and_liberta_lankester cite_birth_of_liberta_lankester_from_bevolkingsregister_amsterdam = IdentifiableCitation( 'betty-demo-birth-of-liberta-lankester-from-bevolkingsregister-amsterdam', bevolkingsregister_amsterdam) cite_birth_of_liberta_lankester_from_bevolkingsregister_amsterdam.location = 'Amsterdam' self._ancestry.citations[ cite_birth_of_liberta_lankester_from_bevolkingsregister_amsterdam. id] = cite_birth_of_liberta_lankester_from_bevolkingsregister_amsterdam birth_of_liberta_lankester = IdentifiableEvent( 'betty-demo-birth-of-liberta-lankester', Birth(), Date(1929, 12, 22)) birth_of_liberta_lankester.place = amsterdam birth_of_liberta_lankester.citations.append( cite_birth_of_liberta_lankester_from_bevolkingsregister_amsterdam) self._ancestry.events[ birth_of_liberta_lankester.id] = birth_of_liberta_lankester death_of_liberta_lankester = IdentifiableEvent( 'betty-demo-death-of-liberta-lankester', Death(), Date(2015, 1, 17)) death_of_liberta_lankester.place = amsterdam death_of_liberta_lankester.citations.append(cite_first_person_account) self._ancestry.events[ death_of_liberta_lankester.id] = death_of_liberta_lankester liberta_lankester = Person('betty-demo-liberta-lankester') liberta_lankester.names.append(PersonName('Liberta', 'Lankester')) liberta_lankester.names.append(PersonName('Betty')) Presence(liberta_lankester, Subject(), birth_of_liberta_lankester) Presence(liberta_lankester, Subject(), death_of_liberta_lankester) Presence(liberta_lankester, Subject(), marriage_of_johan_de_boer_and_liberta_lankester) liberta_lankester.parents.append(dirk_jacobus_lankester, jannigje_palsen) self._ancestry.people[liberta_lankester.id] = liberta_lankester birth_of_johan_de_boer = IdentifiableEvent( 'betty-demo-birth-of-johan-de-boer', Birth(), Date(1930, 6, 20)) birth_of_johan_de_boer.place = amsterdam self._ancestry.events[ birth_of_johan_de_boer.id] = birth_of_johan_de_boer death_of_johan_de_boer = IdentifiableEvent( 'betty-demo-death-of-johan-de-boer', Death(), Date(1999, 3, 10)) death_of_johan_de_boer.place = amsterdam death_of_johan_de_boer.citations.append(cite_first_person_account) self._ancestry.events[ death_of_johan_de_boer.id] = death_of_johan_de_boer johan_de_boer = Person('betty-demo-johan-de-boer') johan_de_boer.names.append(PersonName('Johan', 'De Boer')) johan_de_boer.names.append(PersonName('Hans')) Presence(johan_de_boer, Subject(), birth_of_johan_de_boer) Presence(johan_de_boer, Subject(), death_of_johan_de_boer) Presence(johan_de_boer, Subject(), marriage_of_johan_de_boer_and_liberta_lankester) self._ancestry.people[johan_de_boer.id] = johan_de_boer parent_of_bart_feenstra_child_of_liberta_lankester = Person( 'betty-demo-parent-of-bart-feenstra-child-of-liberta-lankester') parent_of_bart_feenstra_child_of_liberta_lankester.names.append( PersonName('Bart\'s parent')) parent_of_bart_feenstra_child_of_liberta_lankester.parents.append( johan_de_boer, liberta_lankester) self._ancestry.people[ parent_of_bart_feenstra_child_of_liberta_lankester. id] = parent_of_bart_feenstra_child_of_liberta_lankester bart_feenstra = Person('betty-demo-bart-feenstra') bart_feenstra.names.append(PersonName('Bart', 'Feenstra')) bart_feenstra.parents.append( parent_of_bart_feenstra_child_of_liberta_lankester) self._ancestry.people[bart_feenstra.id] = bart_feenstra
def test_place_should_encode_full(self): place_id = 'the_place' name = 'The Place' locale = 'nl-NL' latitude = 12.345 longitude = -54.321 coordinates = Point(latitude, longitude) place = Place(place_id, [PlaceName(name, locale)]) place.coordinates = coordinates Enclosure(place, Place('the_enclosing_place', [])) Enclosure(Place('the_enclosed_place', []), place) link = Link('https://example.com/the-place') link.label = 'The Place Online' place.links.add(link) place.events.append(IdentifiableEvent('E1', Birth())) expected = { '$schema': '/schema.json#/definitions/place', '@context': { 'enclosedBy': 'https://schema.org/containedInPlace', 'encloses': 'https://schema.org/containsPlace', 'events': 'https://schema.org/event', 'coordinates': 'https://schema.org/geo', }, '@type': 'https://schema.org/Place', 'id': place_id, 'names': [ { 'name': name, 'locale': 'nl-NL', }, ], 'events': [ '/en/event/E1/index.json', ], 'links': [ { 'url': '/en/place/the_place/index.json', 'relationship': 'canonical', 'mediaType': 'application/json', }, { 'url': '/nl/place/the_place/index.json', 'relationship': 'alternate', 'locale': 'nl-NL', }, { 'url': '/en/place/the_place/index.html', 'relationship': 'alternate', 'mediaType': 'text/html', }, { 'url': 'https://example.com/the-place', 'label': 'The Place Online', }, ], 'coordinates': { '@context': { 'latitude': 'https://schema.org/latitude', 'longitude': 'https://schema.org/longitude', }, '@type': 'https://schema.org/GeoCoordinates', 'latitude': latitude, 'longitude': longitude, }, 'encloses': [ '/en/place/the_enclosed_place/index.json', ], 'enclosedBy': [ '/en/place/the_enclosing_place/index.json', ], } self.assert_encodes(expected, place, 'place')
def _parse_urls(owner: HasLinks, element: Element): url_elements = _xpath(element, './ns:url') for url_element in url_elements: uri = str(_xpath1(url_element, './@href')) label = str(_xpath1(url_element, './@description')) owner.links.add(Link(uri, label))
def test_link_should_encode_minimal(self) -> None: link = Link('https://example.com') expected = { 'url': 'https://example.com', } self.assert_encodes(expected, link, 'link')
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')
def test_person_should_encode_full(self): parent_id = 'the_parent' parent = Person(parent_id) child_id = 'the_child' child = Person(child_id) sibling_id = 'the_sibling' sibling = Person(sibling_id) sibling.parents.append(parent) person_id = 'the_person' person_affiliation_name = 'Person' person_individual_name = 'The' person = Person(person_id) person.names.append( PersonName(person_individual_name, person_affiliation_name)) person.parents.append(parent) person.children.append(child) person.private = False link = Link('https://example.com/the-person') link.label = 'The Person Online' person.links.add(link) person.citations.append( IdentifiableCitation('the_citation', Source('The Source'))) Presence(person, Subject(), IdentifiableEvent('the_event', Birth())) expected = { '$schema': '/schema.json#/definitions/person', '@context': { 'parents': 'https://schema.org/parent', 'children': 'https://schema.org/child', 'siblings': 'https://schema.org/sibling', }, '@type': 'https://schema.org/Person', 'id': person_id, 'names': [ { '@context': { 'individual': 'https://schema.org/givenName', 'affiliation': 'https://schema.org/familyName', }, 'individual': person_individual_name, 'affiliation': person_affiliation_name, }, ], 'parents': [ '/en/person/the_parent/index.json', ], 'children': [ '/en/person/the_child/index.json', ], 'siblings': [ '/en/person/the_sibling/index.json', ], 'private': False, 'presences': [ { '@context': { 'event': 'https://schema.org/performerIn', }, 'role': 'subject', 'event': '/en/event/the_event/index.json', }, ], 'citations': [ '/en/citation/the_citation/index.json', ], 'links': [ { 'url': '/en/person/the_person/index.json', 'relationship': 'canonical', 'mediaType': 'application/json', }, { 'url': '/nl/person/the_person/index.json', 'relationship': 'alternate', 'locale': 'nl-NL', }, { 'url': '/en/person/the_person/index.html', 'relationship': 'alternate', 'mediaType': 'text/html', }, { 'url': 'https://example.com/the-person', 'label': 'The Person Online', }, ], } self.assert_encodes(expected, person, 'person')