300 lines
11 KiB
Python
300 lines
11 KiB
Python
# 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 <http://www.gnu.org/licenses/>.
|
|
|
|
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)
|