misc: add new airquality connector (#16303)

This commit is contained in:
Frédéric Péters 2017-05-12 15:34:56 +02:00
parent 33fd286d47
commit 3c6173483f
7 changed files with 184 additions and 0 deletions

View File

View File

@ -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',
},
),
]

View File

@ -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}

View File

@ -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 %}

View File

@ -111,6 +111,7 @@ INSTALLED_APPS = (
'orange',
'family',
'passerelle.apps.opengis',
'passerelle.apps.airquality',
# backoffice templates and static
'gadjo',
)

57
tests/test_airquality.py Normal file
View File

@ -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 davoir une bonne qualité de lair sur la zone de surveillance.
Vendredi 12, la succession dépisodes pluvio-orageux favorise le maintien de la qualité de lair qui restera bonne.
Samedi 13, les conditions météorologiques proches de celles de la veille devraient conduire à une bonne qualité de lair sur lensemble 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)