passerelle/passerelle/contrib/grandlyon_streetsections/models.py

135 lines
5.4 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 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.compat import json_loads
from passerelle.utils.api import endpoint
COMMUNE_EXTRA_MAPPING = {
'Vaulx-en-Velin': 'VAULX'
}
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')
def section_info(self, request, streetname, streetnumber, commune):
sections = StreetSection.objects.filter(
normalized_name=normalize_street(streetname),
nomcommune__startswith=normalize_commune(commune))
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
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'
return {
'err': 0,
'data': {
'domanialite': section.domanialite,
'codefuv': section.codefuv,
'codetroncon': section.codetroncon,
'nom': section.nom,
'nomcommune': nomcommune,
'nomcommuneorigine': section.nomcommune, # with district
}
}
return {'err': 1}
def daily(self):
super(GrandLyonStreetSections, self).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'):
section, created = StreetSection.objects.get_or_create(
codefuv=value.get('codefuv'),
codetroncon=value.get('codetroncon'))
for attribute in ('nom', 'nomcommune', 'domanialite'):
setattr(section, attribute, value.get(attribute))
for attribute in ('bornemindroite', 'bornemingauche',
'bornemaxdroite', 'bornemaxgauche',
'gid'):
if value.get(attribute) == 'None':
if 'min' in attribute:
attribute_value = 0
elif 'max' in attribute:
attribute_value = 99999
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)
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(StreetSection, self).save(*args, **kwargs)