diff --git a/rero_ils/config.py b/rero_ils/config.py index 4380073bc3..d87294a272 100644 --- a/rero_ils/config.py +++ b/rero_ils/config.py @@ -3037,15 +3037,20 @@ def _(x): 'base_url': os.environ.get('RERO_ILS_MEF_CONCEPTS_URL', 'https://mef.rero.ch/api/concepts'), 'sources': ['idref'], 'filters': [ - {'idref.bnf_type': 'genre/forme Rameau'} + {'idref.bnf_type': 'genre/forme Rameau'} ] - } + }, + 'places': { + 'base_url': os.environ.get('RERO_ILS_MEF_PLACES_URL', 'https://mef.rero.ch/api/places'), + 'sources': ['idref'] + }, } RERO_ILS_ENTITY_TYPES = { 'bf:Person': 'agents', 'bf:Organisation': 'agents', 'bf:Topic': 'concepts', - 'bf:Temporal': 'concepts' + 'bf:Temporal': 'concepts', + 'bf:Place': 'places' } # The absolute path to put the agent synchronization logs, default is the diff --git a/rero_ils/modules/documents/jsonschemas/documents/document_subjects_entity_link-v0.0.1.json b/rero_ils/modules/documents/jsonschemas/documents/document_subjects_entity_link-v0.0.1.json index 7454bddfc7..bddefa3638 100644 --- a/rero_ils/modules/documents/jsonschemas/documents/document_subjects_entity_link-v0.0.1.json +++ b/rero_ils/modules/documents/jsonschemas/documents/document_subjects_entity_link-v0.0.1.json @@ -12,7 +12,7 @@ "$ref": { "title": "Subject", "type": "string", - "pattern": "^(https://mef.rero.ch/api/(agents|concepts)/(gnd|idref|rero)/.*|https://bib.rero.ch/api/local_entities/.*?)$", + "pattern": "^(https://mef.rero.ch/api/(agents|concepts|places)/(gnd|idref|rero)/.*|https://bib.rero.ch/api/local_entities/.*?)$", "widget": { "formlyConfig": { "type": "entityTypeahead", diff --git a/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json b/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json index 8c04f52dc6..d657798a45 100644 --- a/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json +++ b/rero_ils/modules/entities/remote_entities/jsonschemas/remote_entities/remote_entity-v0.0.1.json @@ -32,7 +32,8 @@ "bf:Organisation", "bf:Person", "bf:Topic", - "bf:Temporal" + "bf:Temporal", + "bf:Place" ] }, "viaf_pid": { diff --git a/rero_ils/modules/entities/remote_entities/proxy.py b/rero_ils/modules/entities/remote_entities/proxy.py index 5517157da9..d7d3cf8b00 100644 --- a/rero_ils/modules/entities/remote_entities/proxy.py +++ b/rero_ils/modules/entities/remote_entities/proxy.py @@ -83,13 +83,18 @@ def create_proxy(category): 'concepts-genreForm': { 'class': MefConceptsGenreFormProxy, 'entities': (EntityType.TOPIC,) - } + }, + 'places': { + 'class': MefPlacesProxy, + 'entities': (EntityType.PLACE, ) + }, } # Create proxy configuration aliases proxy_config[EntityType.PERSON] = proxy_config['person'] proxy_config[EntityType.ORGANISATION] = proxy_config['organisation'] proxy_config[EntityType.TOPIC] = proxy_config['topics'] proxy_config[EntityType.TEMPORAL] = proxy_config['temporals'] + proxy_config[EntityType.PLACE] = proxy_config['places'] # Try to create the proxy, otherwise raise a ValueError if data := proxy_config.get(category): @@ -322,3 +327,9 @@ class MefConceptsGenreFormProxy(MefConceptsProxy): """Proxy on RERO-MEF authority system for specific `genreForm` concepts.""" mef_entrypoint = 'concepts-genreForm' + + +class MefPlacesProxy(MEFProxyMixin): + """Proxy on RERO-MEF authority system when searching for `places`.""" + + mef_entrypoint = 'places' diff --git a/tests/api/entities/remote_entities/test_remote_entities_rest.py b/tests/api/entities/remote_entities/test_remote_entities_rest.py index 5ee9d43608..066d72ed64 100644 --- a/tests/api/entities/remote_entities/test_remote_entities_rest.py +++ b/tests/api/entities/remote_entities/test_remote_entities_rest.py @@ -84,7 +84,7 @@ def test_remote_entities_get(client, entity_person): @mock.patch('requests.request') def test_remote_search_proxy( mock_es_concept_get, app, client, - mef_concept2_es_response, mef_agents1_es_response + mef_concept2_es_response, mef_agents1_es_response, mef_places1_es_response ): """Test entities search on remote servers.""" # TEST#1 :: Concepts @@ -120,7 +120,21 @@ def test_remote_search_proxy( assert identifier == response.json['hits']['hits'][0][ 'metadata']['idref']['identifiedBy'][0]['value'] - # TEST#3 :: Unknown MEF search type + # TEST#3 :: Places + # All result must include a `identifiedBy` object if a root + mock_es_concept_get.return_value = mock_response( + json_data=mef_places1_es_response) + response = client.get(url_for( + 'api_remote_entities.remote_search_proxy', + entity_type='places', + term='Rouen' + )) + authorized_access_point = mef_places1_es_response['hits']['hits'][0][ + 'metadata']['idref']['authorized_access_point'] + assert authorized_access_point == response.json['hits']['hits'][0][ + 'metadata']['idref']['authorized_access_point'] + + # TEST#4 :: Unknown MEF search type # Try to execute a search on a not-configured MEF category. It should be # raised a `ValueError` caught by flask to return an HTTP 400 response category = 'unknown_category' diff --git a/tests/conftest.py b/tests/conftest.py index 8646d3ae9f..f6c4f255da 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -220,13 +220,17 @@ def app_config(app_config): 'base_url': 'https://mef.rero.ch/api/concepts', 'sources': ['idref'] }, - 'concepts-rameau': { + 'concepts-genreForm': { 'base_url': 'https://mef.rero.ch/api/concepts', 'sources': ['idref'], 'filters': [ {'idref.bnf_type': 'sujet Rameau'} ] - } + }, + 'places': { + 'base_url': 'https://mef.rero.ch/api/places', + 'sources': ['idref'] + }, } return app_config diff --git a/tests/data/data.json b/tests/data/data.json index 09a6c7ddfb..bcac27ce9f 100644 --- a/tests/data/data.json +++ b/tests/data/data.json @@ -1849,6 +1849,69 @@ ], "type": "bf:Temporal" }, + "ent_place": { + "$schema": "https://mef.rero.ch/schemas/places_mef/mef-place-v0.0.1.json", + "idref": { + "$schema": "https://mef.rero.ch/schemas/places_idref/idref-place-v0.0.1.json", + "authorized_access_point": "Suisse", + "bnf_type": "sujet Rameau", + "broader": [ + { + "authorized_access_point": "Europe centrale" + } + ], + "identifiedBy": [ + { + "source": "IDREF", + "type": "uri", + "value": "http://www.idref.fr/027249654" + }, + { + "source": "BNF", + "type": "uri", + "value": "http://catalogue.bnf.fr/ark:/12148/cb15241146v" + }, + { + "source": "BNF", + "type": "uri", + "value": "http://catalogue.bnf.fr/ark:/12148/cb11933332n" + } + ], + "md5": "87df11402bcbdfd147ee23a612e5ed69", + "note": [ + { + "label": [ + "IFLA, Names of states, 1981. GLU, 1991", + "Laval RVM, 1997-02" + ], + "noteType": "dataSource" + }, + { + "label": [ + "Voir aussi la subdivision Et la Suisse aux personnes et collectivit\u00e9s" + ], + "noteType": "seeReference" + } + ], + "pid": "027249654", + "type": "bf:Place", + "variant_access_point": [ + "Conf\u00e9d\u00e9ration helv\u00e9tique", + "Confederazione svizzera", + "Helvetia", + "Helv\u00e9tie", + "Schweiz", + "Schweizerische Eidgenossenschaft", + "Svizra", + "Svizzera" + ] + }, + "pid": "16788252", + "sources": [ + "idref" + ], + "type": "bf:Place" + }, "ent_pers_all": { "$schema": "https://bib.rero.ch/schemas/remote_entities/remote_entity-v0.0.1.json", "type": "bf:Person", @@ -2150,6 +2213,16 @@ "value": "033472890" } } + }, + { + "entity": { + "authorized_access_point": "Suisse", + "type": "bf:Place", + "identifiedBy": { + "type": "IdRef", + "value": "027249654" + } + } } ], "dimensions": [ diff --git a/tests/data/mef.json b/tests/data/mef.json index 2ce4925224..4d5b2f46a1 100644 --- a/tests/data/mef.json +++ b/tests/data/mef.json @@ -295,5 +295,62 @@ ], "total": 1 } + }, + "es_places_1": { + "hits": { + "hits": [ + { + "created": "2023-09-06T08:24:41.125695+00:00", + "id": "16684460", + "links": { + "self": "https://mef.rero.ch/api/places/mef/16684460" + }, + "metadata": { + "$schema": "https://mef.rero.ch/schemas/places_mef/mef-place-v0.0.1.json", + "idref": { + "$schema": "https://mef.rero.ch/schemas/places_idref/idref-place-v0.0.1.json", + "authorized_access_point": "Rouen (Seine-Maritime) - Tour Jeanne d'Arc", + "bnf_type": "sujet Rameau", + "broader": [ + { + "authorized_access_point": "Rouen(Seine-Maritime) - Ch\u00e2teau" + } + ], + "identifiedBy": [ + { + "source": "IDREF", + "type": "uri", + "value": "http://www.idref.fr/085544434" + } + ], + "md5": "18a0a5d11d1a149449544d2f258690d5", + "note": [ + { + "label": [ + "Le ch\u00e2teau de Rouen et son donjon dit la Tour Jeanne d'Arc / [texte de Dominique L\u00e9ost], 2004" + ], + "noteType": "dataSource" + }, + { + "label": [ + "Donjon constituant le dernier vestige du ch\u00e2teau construit par Philippe Auguste en 1204" + ], + "noteType": "general" + } + ], + "pid": "085544434", + "type": "bf:Place" + }, + "pid": "16684460", + "sources": [ + "idref" + ], + "type": "bf:Place" + }, + "updated": "2023-09-06T08:24:41.125700+00:00" + } + ], + "total": 1 + } } -} +} \ No newline at end of file diff --git a/tests/fixtures/mef.py b/tests/fixtures/mef.py index d713ddfef0..91e6e8b460 100644 --- a/tests/fixtures/mef.py +++ b/tests/fixtures/mef.py @@ -73,5 +73,11 @@ def mef_concept2_es_response(mef_entities): @pytest.fixture(scope="module") def mef_agents1_es_response(mef_entities): - """Load MEF es_concept_1 data.""" + """Load MEF es_agents_1 data.""" return deepcopy(mef_entities.get('es_agents_1')) + + +@pytest.fixture(scope="module") +def mef_places1_es_response(mef_entities): + """Load MEF es_places_1 data.""" + return deepcopy(mef_entities.get('es_places_1')) diff --git a/tests/fixtures/metadata.py b/tests/fixtures/metadata.py index 64de652d75..36f7afe3b1 100644 --- a/tests/fixtures/metadata.py +++ b/tests/fixtures/metadata.py @@ -281,6 +281,12 @@ def entity_topic_data_temporal(data): return deepcopy(data.get('ent_topic_temporal')) +@pytest.fixture(scope="module") +def entity_place_data(data): + """Load mef place data.""" + return deepcopy(data.get('ent_place')) + + @pytest.fixture(scope="module") def entity_person_response_data(entity_topic_data): """Load mef concept topic response data.""" diff --git a/tests/ui/entities/remote_entities/test_remote_entities_api.py b/tests/ui/entities/remote_entities/test_remote_entities_api.py index 1c22b1cce7..1e1f44111e 100644 --- a/tests/ui/entities/remote_entities/test_remote_entities_api.py +++ b/tests/ui/entities/remote_entities/test_remote_entities_api.py @@ -355,6 +355,7 @@ def test_remote_entity_properties( def test_replace_identified_by( app, entity_organisation, entity_person_rero, person2_data, entity_person_all, entity_topic_data_2, entity_topic_data_temporal, + entity_place_data, document, document_sion_items, export_document ): """Test replace identified by with $ref.""" @@ -419,18 +420,24 @@ def test_replace_identified_by( side_effect=[ mock_response(json_data=entity_person_all), mock_response(json_data=entity_topic_data_temporal), + mock_response(json_data=entity_place_data), mock_response(json_data=person2_data), + mock_response(json_data={'rero': { + 'authorized_access_point': 'Europe occidentale', + 'type': 'bf:Place' + }}), mock_response(json_data=entity_topic_data_2) ] ): changed, not_found, rero_only = replace_identified_by.run() assert changed == 1 assert not_found == 0 - assert rero_only == 2 + assert rero_only == 3 assert dict(sorted(replace_identified_by.rero_only.items())) == { 'rero:A009963344': 'bf:Person: Athenagoras (patriarche oecuménique ; 1)', - 'rero:A021039750': 'bf:Topic: Bases de données déductives' + 'rero:A021039750': 'bf:Topic: Bases de données déductives', + 'rero:A009975209': 'bf:Place: Europe occidentale' }