175 lines
6.7 KiB
Python
175 lines
6.7 KiB
Python
# passerelle - uniform access to multiple data sources and services
|
|
# Copyright (C) 2017 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
|
|
import re
|
|
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
from django.utils.text import slugify
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from passerelle.base.models import BaseResource
|
|
from passerelle.utils.api import endpoint
|
|
|
|
COMMUNE_EXTRA_MAPPING = {'Vaulx-en-Velin': 'VAULX'}
|
|
|
|
DEFAULT_MIN = 0
|
|
DEFAULT_MAX = 99999
|
|
|
|
|
|
def normalize_street(street):
|
|
return slugify(re.sub(r"[' ()-]", ' ', street))
|
|
|
|
|
|
def normalize_commune(commune):
|
|
if commune in COMMUNE_EXTRA_MAPPING:
|
|
return COMMUNE_EXTRA_MAPPING[commune]
|
|
return commune.upper()
|
|
|
|
|
|
class GrandLyonStreetSections(BaseResource):
|
|
category = _('Geographic information system')
|
|
|
|
class Meta:
|
|
verbose_name = _('Sections of Grand Lyon Streets')
|
|
|
|
@endpoint(
|
|
perm='can_access',
|
|
description=_('Get details on a section'),
|
|
parameters={
|
|
'streetname': {
|
|
'description': _('Street name'),
|
|
'example_value': 'Boulevard du Raquin',
|
|
},
|
|
'streetnumber': {
|
|
'description': _('Street number'),
|
|
'example_value': '12',
|
|
},
|
|
'commune': {
|
|
'description': _('Collectivity'),
|
|
'example_value': 'Chassieu',
|
|
},
|
|
'insee': {'description': _('INSEE Code'), 'example_value': '69271'},
|
|
},
|
|
)
|
|
def section_info(self, request, streetname, streetnumber, commune=None, insee=None):
|
|
sections = StreetSection.objects.filter(normalized_name=normalize_street(streetname))
|
|
if commune:
|
|
sections = sections.filter(nomcommune__startswith=normalize_commune(commune))
|
|
if insee:
|
|
sections = sections.filter(codeinsee=insee)
|
|
if streetnumber and re.findall(r'\d+', streetnumber):
|
|
streetnumber = int(re.findall(r'\d+', streetnumber)[0])
|
|
else:
|
|
# if no streetnumber, use the first section (it may happen street
|
|
# number 1 doesn't exist, ex: rue des cuirassiers).
|
|
streetnumber = sections[0].bornemindroite if len(sections) else 1
|
|
|
|
default_match = None
|
|
for section in sections:
|
|
if streetnumber < section.bornemindroite and streetnumber < section.bornemingauche:
|
|
continue
|
|
if streetnumber > section.bornemaxdroite and streetnumber > section.bornemaxgauche:
|
|
continue
|
|
|
|
nomcommune = section.nomcommune
|
|
if nomcommune.startswith('LYON '):
|
|
# remove districts from commune name
|
|
nomcommune = 'LYON'
|
|
|
|
match = {
|
|
'err': 0,
|
|
'data': {
|
|
'domanialite': section.domanialite,
|
|
'codefuv': section.codefuv,
|
|
'codetroncon': section.codetroncon,
|
|
'nom': section.nom,
|
|
'nomcommune': nomcommune,
|
|
'nomcommuneorigine': section.nomcommune, # with district
|
|
'codeinsee': section.codeinsee,
|
|
},
|
|
}
|
|
if DEFAULT_MIN in (section.bornemindroite, section.bornemingauche) or DEFAULT_MAX in (
|
|
section.bornemaxdroite,
|
|
section.bornemaxgauche,
|
|
):
|
|
default_match = match
|
|
continue
|
|
|
|
return match
|
|
|
|
if default_match:
|
|
return default_match
|
|
|
|
return {'err': 1}
|
|
|
|
def daily(self):
|
|
super().daily()
|
|
update_start = timezone.now()
|
|
sections = self.requests.get(
|
|
'https://download.data.grandlyon.com/ws/grandlyon/adr_voie_lieu.adraxevoie/all.json?maxfeatures=1000000'
|
|
).content
|
|
for value in json.loads(sections).get('values'):
|
|
if not value.get('codefuv') or not value.get('codetroncon'):
|
|
continue
|
|
section, dummy = StreetSection.objects.get_or_create(
|
|
codefuv=value.get('codefuv'), codetroncon=value.get('codetroncon')
|
|
)
|
|
for attribute in ('nom', 'nomcommune', 'domanialite', 'codeinsee'):
|
|
setattr(section, attribute, value.get(attribute) or '')
|
|
for attribute in ('bornemindroite', 'bornemingauche', 'bornemaxdroite', 'bornemaxgauche', 'gid'):
|
|
if value.get(attribute) in (None, 'None'):
|
|
# data.grandlyon returned 'None' as a string at a time
|
|
if 'min' in attribute:
|
|
attribute_value = DEFAULT_MIN
|
|
elif 'max' in attribute:
|
|
attribute_value = DEFAULT_MAX
|
|
else:
|
|
attribute_value = None
|
|
else:
|
|
attribute_value = int(value.get(attribute))
|
|
setattr(section, attribute, attribute_value)
|
|
section.save()
|
|
StreetSection.objects.filter(last_update__lt=update_start).delete()
|
|
|
|
|
|
class StreetSection(models.Model):
|
|
bornemindroite = models.PositiveIntegerField(null=True)
|
|
bornemingauche = models.PositiveIntegerField(null=True)
|
|
bornemaxdroite = models.PositiveIntegerField(null=True)
|
|
bornemaxgauche = models.PositiveIntegerField(null=True)
|
|
gid = models.PositiveIntegerField(null=True)
|
|
nomcommune = models.CharField(max_length=50)
|
|
nom = models.CharField(max_length=200)
|
|
domanialite = models.CharField(max_length=50)
|
|
codefuv = models.CharField(max_length=15)
|
|
codetroncon = models.CharField(max_length=15)
|
|
codeinsee = models.CharField(max_length=15, null=True)
|
|
|
|
normalized_name = models.CharField(max_length=200)
|
|
last_update = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
# order by street number to be sure the first result for a given street
|
|
# will be the lowest street number.
|
|
ordering = ['normalized_name', 'nomcommune', 'bornemindroite', 'bornemingauche']
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.nom:
|
|
self.normalized_name = normalize_street(self.nom)
|
|
return super().save(*args, **kwargs)
|