# passerelle - uniform access to multiple data sources and services # Copyright (C) 2021 Entr'ouvert # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import json from io import StringIO from unittest import mock import pytest from django.core.files import File import tests.utils from passerelle.apps.base_adresse.models import BaseAdresse from passerelle.apps.sector.models import SectorResource from tests.test_manager import login BAN = { "attribution": "BAN", "features": [ { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "housenumber": "169", "id": "75114_1912_00169", "importance": 0.77751, "label": "169 Rue du Château 75014 Paris", "name": "169 Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "housenumber": "167", "id": "75114_1912_00167", "importance": 0.77751, "label": "167 Rue du Château 75014 Paris", "name": "167 Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "housenumber": "170", "id": "75114_1912_00169", "importance": 0.77751, "label": "170 Rue du Château 75014 Paris", "name": "170 Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, # no house number { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "id": "75114_1912_00169", "importance": 0.77751, "label": "XX Rue du Château 75014 Paris", "name": "Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "id": "75114_1913_00000", "importance": 0.77751, "label": "YY Rue du Château 75014 Paris", "name": "Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, # empty id (no street_id) { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "housenumber": "169", "id": "", # empty id => no street_id "importance": 0.77751, "label": "169 Rue du Château 75014 Paris", "name": "169 Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, # id is not a string => no street_id { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "housenumber": "169", "id": 42, # integer id => no street_id "importance": 0.77751, "label": "169 Rue du Château 75014 Paris", "name": "169 Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, # id without "_" => no street_id { "geometry": {"coordinates": [2.323365, 48.833702], "type": "Point"}, "properties": { "city": "Paris", "citycode": "75114", "context": "75, Paris, Île-de-France", "district": "Paris 14e Arrondissement", "housenumber": "169", "id": "123", # no "_" => no street_id "importance": 0.77751, "label": "169 Rue du Château 75014 Paris", "name": "169 Rue du Château", "postcode": "75014", "score": 0.9797736363636363, "street": "Rue du Château", "type": "housenumber", "x": 650331.55, "y": 6859506.98, }, "type": "Feature", }, ], "licence": "ETALAB-2.0", "limit": 5, "query": "169 rue du chateau, 75014 Paris", "type": "FeatureCollection", "version": "draft", } CSV = """street_id,street_name,parity,min_housenumber,max_housenumber,sector_id,sector_name 75114_1912,rue du Château,P,,,gs-moulin,Groupe Scolaire Moulin 75114_1912,rue du Château,I,0,167,gs-zola,Groupe Scolaire Zola 75114_1912,rue du Château,I,168,999999,ecole-hugo,École Hugo 75114_1913,rue 1913,,,,ecole-pascal,École Pascal """ @pytest.fixture def sector(db): return tests.utils.setup_access_rights( SectorResource.objects.create( slug='ecole', title='Secteur scolaire', description='desc', csv_file=File(StringIO(CSV), 'sectorization.csv'), ) ) @pytest.fixture def base_adresse(db): return tests.utils.setup_access_rights(BaseAdresse.objects.create(slug='base-adresse', zipcode='75114')) @mock.patch('passerelle.utils.Request.get') def test_sectorization(mocked_get, app, base_adresse, sector): assert base_adresse.sectors.count() == 0 mocked_get.return_value = tests.utils.FakedResponse(content=json.dumps(BAN), status_code=200) url = tests.utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug) addresses = app.get(url, params={'q': 'foo'}, status=200).json assert len(addresses) == 8 for address in addresses: assert 'sectors' not in address assert 'sectorization_error' not in address base_adresse.sectors.add(sector) # empty sector sector.sector_set.all().delete() addresses = app.get(url, params={'q': 'foo'}, status=200).json assert len(addresses) == 8 for address in addresses: assert address['sectors'] == {} # sector is empty sector.save() # reload CSV data in sector addresses = app.get(url, params={'q': 'foo'}, status=200).json assert addresses[0]['sectors'] == {'ecole': {'id': 'ecole-hugo', 'text': 'École Hugo'}} assert addresses[1]['sectors'] == {'ecole': {'id': 'gs-zola', 'text': 'Groupe Scolaire Zola'}} assert addresses[2]['sectors'] == {'ecole': {'id': 'gs-moulin', 'text': 'Groupe Scolaire Moulin'}} # addresses without housenumber assert addresses[3]['sectors'] == {} assert addresses[4]['sectors'] == {'ecole': {'id': 'ecole-pascal', 'text': 'École Pascal'}} # bad address format, no street_id assert addresses[5]['sectors'] == {} assert addresses[5]['sectorization_error'] == 'missing street_id in address' assert addresses[6]['sectors'] == {} assert addresses[6]['sectorization_error'] == 'missing street_id in address' assert addresses[7]['sectors'] == {} assert addresses[7]['sectorization_error'] == 'missing street_id in address' url = tests.utils.generic_endpoint_url('base-adresse', 'sectors', slug=base_adresse.slug) for params in (None, {'id': 'ecole'}, {'q': 'scolaire'}): sectors = app.get(url, params=params, status=200).json assert sectors['err'] == 0 assert sectors['data'] == [ { 'id': 'ecole', 'text': 'Secteur scolaire', 'sectors_url': 'http://localhost/sector/ecole/sectors/', 'description': 'desc', } ] for params in ({'id': 'foo'}, {'q': 'foo'}): sectors = app.get(url, params=params, status=200).json assert sectors['err'] == 0 assert sectors['data'] == [] def test_manager_with_or_without_sectors(db, app, admin_user, base_adresse, sector): app = login(app) path = '/manage/%s/add' % base_adresse.get_connector_slug() resp = app.get(path) assert 'Sectorizations' in str(resp.form.html) assert 'Secteur scolaire [ecole]' in str(resp.form.html) sector.delete() resp = app.get(path) assert 'Sectorizations' not in str(resp.form.html) assert 'Secteur scolaire [ecole]' not in str(resp.form.html)