misc: add new airquality connector (#16303)
This commit is contained in:
parent
33fd286d47
commit
3c6173483f
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0005_resourcelog'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AirQuality',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=50)),
|
||||
('slug', models.SlugField()),
|
||||
('description', models.TextField()),
|
||||
('log_level', models.CharField(default=b'INFO', max_length=10, verbose_name='Log Level', choices=[(b'NOTSET', b'NOTSET'), (b'DEBUG', b'DEBUG'), (b'INFO', b'INFO'), (b'WARNING', b'WARNING'), (b'ERROR', b'ERROR'), (b'CRITICAL', b'CRITICAL'), (b'FATAL', b'FATAL')])),
|
||||
('users', models.ManyToManyField(to='base.ApiUser', blank=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Air Quality',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,76 @@
|
|||
# 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 datetime
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from passerelle.base.models import BaseResource
|
||||
from passerelle.utils.api import endpoint
|
||||
|
||||
class AirQuality(BaseResource):
|
||||
category = _('Misc')
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Air Quality')
|
||||
|
||||
@endpoint(pattern='^(?P<country>\w+)/(?P<city>\w+)/$')
|
||||
def details(self, request, country, city, **kwargs):
|
||||
methods = {
|
||||
('fr', 'albertville'): 'air_rhonealpes',
|
||||
('fr', 'annecy'): 'air_rhonealpes',
|
||||
('fr', 'bourg-en-bresse'): 'air_rhonealpes',
|
||||
('fr', 'chambery'): 'air_rhonealpes',
|
||||
('fr', 'chamonix'): 'air_rhonealpes',
|
||||
('fr', 'grenoble'): 'air_rhonealpes',
|
||||
('fr', 'lyon'): 'air_rhonealpes',
|
||||
('fr', 'roanne'): 'air_rhonealpes',
|
||||
('fr', 'saint-etienne'): 'air_rhonealpes',
|
||||
('fr', 'valence'): 'air_rhonealpes',
|
||||
('fr', 'vienne'): 'air_rhonealpes',
|
||||
}
|
||||
local_method = methods.get((country, city))
|
||||
if not local_method:
|
||||
raise Http404()
|
||||
return getattr(self, local_method)(request, country, city, **kwargs)
|
||||
|
||||
def air_rhonealpes(self, request, country, city, **kwargs):
|
||||
response = self.requests.get('http://www.air-rhonealpes.fr/site/indice/XML/ATMO/now/%s' % city)
|
||||
atmo = ET.fromstring(response.content)
|
||||
data = {
|
||||
'latest': {
|
||||
'date': datetime.datetime.strptime(
|
||||
atmo.find('JOUR_ATMO/MESURE/INDICE/JOUR_INDICE').attrib['jour'],
|
||||
'%d/%m/%Y').strftime('%Y-%m-%d'),
|
||||
'value': atmo.findtext('JOUR_ATMO/MESURE/INDICE/VALEUR'),
|
||||
},
|
||||
'comment': atmo.findtext('JOUR_ATMO/COMMENTAIRE').strip(),
|
||||
}
|
||||
if atmo.findtext('JOUR_ATMO/PREVISION_J1/INDICE/VALEUR') != '-':
|
||||
data.update({
|
||||
'forecast': {
|
||||
'j1': {
|
||||
'date': datetime.datetime.strptime(
|
||||
atmo.findtext('JOUR_ATMO/PREVISION_J1/INDICE/JOUR_INDICE'),
|
||||
'%d/%m/%Y').strftime('%Y-%m-%d'),
|
||||
'value': atmo.findtext('JOUR_ATMO/PREVISION_J1/INDICE/VALEUR'),
|
||||
},
|
||||
},
|
||||
})
|
||||
return {'data': data, 'err': 0}
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "passerelle/manage/service_view.html" %}
|
||||
{% load i18n passerelle %}
|
||||
|
||||
{% block endpoints %}
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
This API provides a unique format for the air quality data of various places.
|
||||
(But only supports the Rhône-Alpes region for now).
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>{% trans 'Details:' %} <a href="details/fr/lyon/"
|
||||
>{{ site_base_uri }}{{ object.get_absolute_url }}details/<i>country</i>/<i>city</i>/</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block security %}
|
||||
<p>
|
||||
{% trans 'The data is open.' %}
|
||||
</p>
|
||||
{% endblock %}
|
|
@ -111,6 +111,7 @@ INSTALLED_APPS = (
|
|||
'orange',
|
||||
'family',
|
||||
'passerelle.apps.opengis',
|
||||
'passerelle.apps.airquality',
|
||||
# backoffice templates and static
|
||||
'gadjo',
|
||||
)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
import mock
|
||||
import utils
|
||||
import json
|
||||
|
||||
from passerelle.apps.airquality.models import AirQuality
|
||||
|
||||
SAMPLE_RESPONSE = '''<?xml version='1.0' ?>
|
||||
<ATMO>
|
||||
<JOUR_ATMO>
|
||||
12/05/2017 <VILLE>Indice ATMO de l'agglomération de Lyon</VILLE>
|
||||
<TYPE_INDICE>PARTIEL</TYPE_INDICE>
|
||||
<COMMENTAIRE>
|
||||
<![CDATA[
|
||||
Jeudi 11 mai, le temps perturbé a permis d’avoir une bonne qualité de l’air sur la zone de surveillance.
|
||||
Vendredi 12, la succession d’épisodes pluvio-orageux favorise le maintien de la qualité de l’air qui restera bonne.
|
||||
Samedi 13, les conditions météorologiques proches de celles de la veille devraient conduire à une bonne qualité de l’air sur l’ensemble du territoire.
|
||||
]]>
|
||||
</COMMENTAIRE>
|
||||
<MESURE>
|
||||
<INDICE>
|
||||
<JOUR_INDICE jour="12/05/2017" />
|
||||
<VALEUR>4</VALEUR>
|
||||
<SOUS_INDICES>
|
||||
<SOUSINDICE NOM="POUSSIERE">2</SOUSINDICE>
|
||||
<SOUSINDICE NOM="DIOXYDE D'AZOTE">3</SOUSINDICE>
|
||||
<SOUSINDICE NOM="OZONE">4</SOUSINDICE>
|
||||
</SOUS_INDICES>
|
||||
</INDICE>
|
||||
</MESURE>
|
||||
<PREVISION_J1>
|
||||
<INDICE>
|
||||
<JOUR_INDICE>13/05/2017</JOUR_INDICE>
|
||||
<VALEUR>4</VALEUR>
|
||||
</INDICE>
|
||||
</PREVISION_J1>
|
||||
</JOUR_ATMO>
|
||||
</ATMO>
|
||||
'''
|
||||
|
||||
@pytest.fixture
|
||||
def airquality(db):
|
||||
return AirQuality.objects.create(slug='atmo')
|
||||
|
||||
|
||||
def test_airquality_details(app, airquality):
|
||||
endpoint = utils.generic_endpoint_url('airquality', 'details', slug=airquality.slug)
|
||||
assert endpoint == '/airquality/atmo/details'
|
||||
with mock.patch('passerelle.utils.LoggedRequest.get') as requests_get:
|
||||
requests_get.return_value = mock.Mock(content=SAMPLE_RESPONSE, status_code=200)
|
||||
resp = app.get(endpoint + '/fr/lyon/', status=200)
|
||||
assert resp.json['data']['latest']['value'] == '4'
|
||||
|
||||
def test_airquality_details_unknown_city(app, airquality):
|
||||
endpoint = utils.generic_endpoint_url('airquality', 'details', slug=airquality.slug)
|
||||
resp = app.get(endpoint + '/fr/paris/', status=404)
|
Loading…
Reference in New Issue