airquality: update to new Atmo Auvergne-Rhône-Alpes API (#49654)
This commit is contained in:
parent
42bfc161ab
commit
58d340c18b
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-12-21 12:54
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('airquality', '0003_remove_airquality_log_level'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='airquality',
|
||||
name='atmo_aura_api_token',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='ATMO AURA API token'),
|
||||
),
|
||||
]
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2017 Entr'ouvert
|
||||
# Copyright (C) 2017-2020 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
|
||||
|
@ -16,7 +16,6 @@
|
|||
# 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
|
||||
|
@ -24,6 +23,8 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from passerelle.base.models import BaseResource
|
||||
from passerelle.utils.api import endpoint
|
||||
from passerelle.utils.jsonresponse import APIError
|
||||
|
||||
|
||||
class AirQuality(BaseResource):
|
||||
category = _('Misc')
|
||||
|
@ -32,6 +33,10 @@ class AirQuality(BaseResource):
|
|||
(But only supports the Rhône-Alpes region for now).
|
||||
''')
|
||||
|
||||
atmo_aura_api_token = models.CharField(max_length=100,
|
||||
verbose_name=_('ATMO AURA API token'),
|
||||
blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Air Quality')
|
||||
|
||||
|
@ -61,26 +66,53 @@ class AirQuality(BaseResource):
|
|||
return getattr(self, local_method)(request, country, city, **kwargs)
|
||||
|
||||
def air_rhonealpes(self, request, country, city, **kwargs):
|
||||
response = self.requests.get('https://www.atmo-auvergnerhonealpes.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 not self.atmo_aura_api_token:
|
||||
raise APIError('missing access token for ATMO AURA API')
|
||||
insee_codes = {
|
||||
'albertville': '73011',
|
||||
'annecy': '74010',
|
||||
'bourg-en-bresse': '01053',
|
||||
'chambery': '73065',
|
||||
'chamonix': '74056',
|
||||
'grenoble': '38185',
|
||||
'lyon': '69381',
|
||||
'roanne': '42187',
|
||||
'saint-etienne': '42218',
|
||||
'valence': '26362',
|
||||
'vienne': '38544',
|
||||
}
|
||||
if atmo.findtext('JOUR_ATMO/PREVISION_J1/INDICE/VALEUR') != '-':
|
||||
data.update({
|
||||
'forecast': {
|
||||
insee_code = insee_codes.get(city.lower())
|
||||
response = self.requests.get('https://api.atmo-aura.fr/communes/%s/indices' % insee_code,
|
||||
params={'api_token': self.atmo_aura_api_token},
|
||||
)
|
||||
json_response = response.json()
|
||||
today = datetime.datetime.today().strftime('%Y-%m-%d')
|
||||
tomorrow = (datetime.datetime.today() + datetime.timedelta(days=1)).strftime('%Y-%m-%d')
|
||||
response_data = {}
|
||||
for indice in json_response['indices']['data']:
|
||||
if indice['date'] == today:
|
||||
response_data['latest'] = {
|
||||
'date': today,
|
||||
'value': indice['valeur'],
|
||||
}
|
||||
elif indice['date'] == tomorrow:
|
||||
response_data['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}
|
||||
'date': today,
|
||||
'value': indice['valeur'],
|
||||
}
|
||||
}
|
||||
if 'latest' in response_data and 'forecast' in response_data:
|
||||
break
|
||||
|
||||
if 'latest' in response_data:
|
||||
comment_response = self.requests.get('https://api.atmo-aura.fr/commentaire',
|
||||
params={
|
||||
'date': response_data['latest']['date'],
|
||||
'api_token': self.atmo_aura_api_token,
|
||||
}
|
||||
)
|
||||
if comment_response.ok:
|
||||
response_data['comment'] = comment_response.json().get('commentaire')
|
||||
|
||||
return {'data': response_data, 'err': 0}
|
||||
|
|
|
@ -1,56 +1,82 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
import utils
|
||||
import json
|
||||
|
||||
import freezegun
|
||||
from httmock import HTTMock, response
|
||||
|
||||
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>
|
||||
'''
|
||||
SAMPLE_RESPONSE = {
|
||||
"licence": "https://opendatacommons.org/licenses/odbl/",
|
||||
"commune": "LYON-1ER-ARRONDISSEMENT",
|
||||
"code_insee": "69381",
|
||||
"indices": {
|
||||
"current_page": 1,
|
||||
"data": [
|
||||
{
|
||||
"date": "2020-12-22",
|
||||
"valeur": "26.6503231768126",
|
||||
"couleur_html": "#5CCB60",
|
||||
"qualificatif": "Bon",
|
||||
"type_valeur": "prévision",
|
||||
},
|
||||
{
|
||||
"date": "2020-12-21",
|
||||
"valeur": "21.6876695818178",
|
||||
"couleur_html": "#5CCB60",
|
||||
"qualificatif": "Bon",
|
||||
"type_valeur": "prévision",
|
||||
},
|
||||
{
|
||||
"date": "2020-12-20",
|
||||
"valeur": "26.1405508214683",
|
||||
"couleur_html": "#5CCB60",
|
||||
"qualificatif": "Bon",
|
||||
"type_valeur": "prévision",
|
||||
},
|
||||
],
|
||||
},
|
||||
"first_page_url": "https://api.atmo-aura.fr/communes/69381/indices?api_token=XXX&page=1",
|
||||
"from": 1,
|
||||
"last_page": 23,
|
||||
"last_page_url": "https://api.atmo-aura.fr/communes/69381/indices?api_token=XXX&page=23",
|
||||
"next_page_url": "https://api.atmo-aura.fr/communes/69381/indices?api_token=XXX&page=2",
|
||||
"path": "https://api.atmo-aura.fr/communes/69381/indices",
|
||||
"per_page": 50,
|
||||
"prev_page_url": None,
|
||||
"to": 50,
|
||||
"total": 1137,
|
||||
}
|
||||
|
||||
SAMPLE_COMMENT_RESPONSE = {
|
||||
"licence": "https://opendatacommons.org/licenses/odbl/",
|
||||
"commentaire": "Jeudi 11 mai, le temps perturbé a permis d’avoir une bonne qualité de l’air sur la zone de surveillance.",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def airquality(db):
|
||||
return AirQuality.objects.create(slug='atmo')
|
||||
return AirQuality.objects.create(slug='atmo', atmo_aura_api_token='XXX')
|
||||
|
||||
|
||||
def mocked_http(url, request):
|
||||
if url.path.startswith('/commune'):
|
||||
return response(200, SAMPLE_RESPONSE, request=request)
|
||||
if url.path.startswith('/commentaire'):
|
||||
return response(200, SAMPLE_COMMENT_RESPONSE, request=request)
|
||||
|
||||
|
||||
@freezegun.freeze_time('2020-12-21')
|
||||
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.Request.get') as requests_get:
|
||||
requests_get.return_value = mock.Mock(content=SAMPLE_RESPONSE, status_code=200)
|
||||
with HTTMock(mocked_http):
|
||||
resp = app.get(endpoint + '/fr/lyon/', status=200)
|
||||
assert resp.json['data']['latest']['value'] == '4'
|
||||
assert resp.json['data']['latest']['value'] == '21.6876695818178'
|
||||
assert 'Jeudi 11 mai, le temps' in resp.json['data']['comment']
|
||||
|
||||
|
||||
def test_airquality_details_unknown_city(app, airquality):
|
||||
endpoint = utils.generic_endpoint_url('airquality', 'details', slug=airquality.slug)
|
||||
|
|
Loading…
Reference in New Issue