diff --git a/.jupyter/api_query/__init__.py b/.jupyter/api_query/__init__.py index b0dc0ccd7f926900b7edacab3b971d555f4d9152..942ef1295749765c185fa8a983411d6a309cd218 100644 --- a/.jupyter/api_query/__init__.py +++ b/.jupyter/api_query/__init__.py @@ -15,6 +15,7 @@ from __future__ import absolute_import # import apis into sdk package +from api_query.api.consumer_endpoint_api import ConsumerEndpointApi from api_query.api.export_endpoint_api import ExportEndpointApi from api_query.api.query_endpoint_api import QueryEndpointApi from api_query.api.store_endpoint_api import StoreEndpointApi diff --git a/.jupyter/api_query/api/__init__.py b/.jupyter/api_query/api/__init__.py index b644b1438d02bcc510ef2c6c6cb3b93b50d5b76b..67a6ca4c6a3979c73b8231ecb61bc7cd9c85e0f4 100644 --- a/.jupyter/api_query/api/__init__.py +++ b/.jupyter/api_query/api/__init__.py @@ -3,6 +3,7 @@ from __future__ import absolute_import # flake8: noqa # import apis into api package +from api_query.api.consumer_endpoint_api import ConsumerEndpointApi from api_query.api.export_endpoint_api import ExportEndpointApi from api_query.api.query_endpoint_api import QueryEndpointApi from api_query.api.store_endpoint_api import StoreEndpointApi diff --git a/.jupyter/tuple_publish.ipynb b/.jupyter/tuple_publish.ipynb index 837dfb2c844c4f88e28ce828adcb3350d4fd5dd2..f09ef76bbfc67e73839f2cc9607137df9f3ed0e0 100644 --- a/.jupyter/tuple_publish.ipynb +++ b/.jupyter/tuple_publish.ipynb @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 80, "metadata": { "pycharm": { "name": "#%%\n" @@ -25,7 +25,6 @@ "source": [ "import time\n", "import csv\n", - "import pika\n", "from api_broker.BrokerServiceClient import BrokerServiceClient\n", "from api_authentication.api.authentication_endpoint_api import AuthenticationEndpointApi\n", "from api_authentication.api.user_endpoint_api import UserEndpointApi\n", @@ -36,6 +35,7 @@ "from api_query.api.query_endpoint_api import QueryEndpointApi\n", "from api_identifier.api.identifier_endpoint_api import IdentifierEndpointApi\n", "from api_identifier.api.persistence_endpoint_api import PersistenceEndpointApi\n", + "from api_query.api.consumer_endpoint_api import ConsumerEndpointApi\n", "\n", "authentication = AuthenticationEndpointApi()\n", "user = UserEndpointApi()\n", @@ -46,6 +46,7 @@ "data = TableDataEndpointApi()\n", "identifier = IdentifierEndpointApi()\n", "persistence = PersistenceEndpointApi()\n", + "consumer = ConsumerEndpointApi()\n", "\n", "username = \"test\"\n", "password = \"test\"" @@ -53,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 81, "metadata": { "pycharm": { "name": "#%%\n" @@ -75,12 +76,13 @@ "query.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", "identifier.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", "user.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", - "persistence.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}" + "persistence.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", + "consumer.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 82, "metadata": { "pycharm": { "name": "#%%\n" @@ -119,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 83, "metadata": { "pycharm": { "name": "#%%\n" @@ -130,7 +132,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'created': datetime.datetime(2022, 8, 12, 18, 13, 23, 859000, tzinfo=tzutc()),\n", + "{'created': datetime.datetime(2022, 8, 16, 12, 41, 27, 625000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -140,7 +142,7 @@ " 'titles_after': None,\n", " 'titles_before': None,\n", " 'username': 'test'},\n", - " 'hash': '1785640e50886bbb9769827a63c564270b1d682214e12c3fee5a4678ff95860b',\n", + " 'hash': '686331331ec9e4de5b84cecc9f323cc04efc6d8766eaea07c0b1e47b817ff4e9',\n", " 'id': 1,\n", " 'internal_name': 'fda-userdb-airquality',\n", " 'is_public': None,\n", @@ -160,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 84, "metadata": { "pycharm": { "name": "#%%\n" @@ -171,7 +173,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'created': datetime.datetime(2022, 8, 12, 18, 13, 23, 859000, tzinfo=tzutc()),\n", + "{'created': datetime.datetime(2022, 8, 16, 12, 41, 27, 625000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -181,7 +183,7 @@ " 'titles_after': None,\n", " 'titles_before': None,\n", " 'username': 'test'},\n", - " 'hash': '1785640e50886bbb9769827a63c564270b1d682214e12c3fee5a4678ff95860b',\n", + " 'hash': '686331331ec9e4de5b84cecc9f323cc04efc6d8766eaea07c0b1e47b817ff4e9',\n", " 'id': 1,\n", " 'internal_name': 'fda-userdb-airquality',\n", " 'is_public': None,\n", @@ -199,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 85, "metadata": { "pycharm": { "name": "#%%\n" @@ -210,7 +212,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'container': {'created': datetime.datetime(2022, 8, 12, 18, 13, 23, 859000, tzinfo=tzutc()),\n", + "{'container': {'created': datetime.datetime(2022, 8, 16, 12, 41, 27, 625000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -220,12 +222,12 @@ " 'titles_after': None,\n", " 'titles_before': None,\n", " 'username': 'test'},\n", - " 'hash': '1785640e50886bbb9769827a63c564270b1d682214e12c3fee5a4678ff95860b',\n", + " 'hash': '686331331ec9e4de5b84cecc9f323cc04efc6d8766eaea07c0b1e47b817ff4e9',\n", " 'id': 1,\n", " 'internal_name': 'fda-userdb-airquality',\n", " 'is_public': None,\n", " 'name': 'Airquality'},\n", - " 'created': datetime.datetime(2022, 8, 12, 18, 13, 30, 103000, tzinfo=tzutc()),\n", + " 'created': datetime.datetime(2022, 8, 16, 12, 41, 34, 450000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -255,25 +257,25 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 86, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'contact': None,\n", - " 'container': {'created': datetime.datetime(2022, 8, 12, 18, 13, 23, 859000, tzinfo=tzutc()),\n", + " 'container': {'created': datetime.datetime(2022, 8, 16, 12, 41, 27, 625000, tzinfo=tzutc()),\n", " 'databases': None,\n", - " 'hash': '1785640e50886bbb9769827a63c564270b1d682214e12c3fee5a4678ff95860b',\n", + " 'hash': '686331331ec9e4de5b84cecc9f323cc04efc6d8766eaea07c0b1e47b817ff4e9',\n", " 'id': 1,\n", " 'image': {'id': 1, 'repository': 'mariadb', 'tag': '10.5'},\n", " 'internal_name': 'fda-userdb-airquality',\n", " 'ip_address': None,\n", " 'is_public': None,\n", " 'name': 'Airquality',\n", - " 'port': 30511,\n", + " 'port': 46472,\n", " 'state': None},\n", - " 'created': datetime.datetime(2022, 8, 12, 18, 13, 30, 103000, tzinfo=tzutc()),\n", + " 'created': datetime.datetime(2022, 8, 16, 12, 41, 34, 450000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -288,49 +290,49 @@ " 'exchange': 'airquality',\n", " 'id': 1,\n", " 'image': {'compiled': None,\n", - " 'date_formats': [{'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 279000, tzinfo=tzutc()),\n", + " 'date_formats': [{'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 574000, tzinfo=tzutc()),\n", " 'database_format': '%Y-%c-%d',\n", " 'example': '2022-01-30',\n", " 'has_time': False,\n", " 'id': 1,\n", " 'unix_format': 'yyyy-MM-dd'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 287000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 581000, tzinfo=tzutc()),\n", " 'database_format': '%d.%c.%Y',\n", " 'example': '30.01.2022',\n", " 'has_time': False,\n", " 'id': 2,\n", " 'unix_format': 'yyyy-MM-dd'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 290000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 584000, tzinfo=tzutc()),\n", " 'database_format': '%d.%c.%y',\n", " 'example': '30.01.22',\n", " 'has_time': False,\n", " 'id': 3,\n", " 'unix_format': 'yyyy-MM-dd'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 293000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 587000, tzinfo=tzutc()),\n", " 'database_format': '%c/%d/%Y',\n", " 'example': '01/30/2022',\n", " 'has_time': False,\n", " 'id': 4,\n", " 'unix_format': 'yyyy-MM-dd'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 296000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 590000, tzinfo=tzutc()),\n", " 'database_format': '%c/%d/%y',\n", " 'example': '01/30/22',\n", " 'has_time': False,\n", " 'id': 5,\n", " 'unix_format': 'yyyy-MM-dd'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 299000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 593000, tzinfo=tzutc()),\n", " 'database_format': '%Y-%c-%d %H:%i:%S.%f',\n", " 'example': '2022-01-30 13:44:25.0',\n", " 'has_time': True,\n", " 'id': 6,\n", " 'unix_format': 'yyyy-MM-dd HH:mm:ss.SSSSSS'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 301000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 595000, tzinfo=tzutc()),\n", " 'database_format': '%Y-%c-%d %H:%i:%S',\n", " 'example': '2022-01-30 13:44:25',\n", " 'has_time': True,\n", " 'id': 7,\n", " 'unix_format': 'yyyy-MM-dd HH:mm:ss'},\n", - " {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 303000, tzinfo=tzutc()),\n", + " {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 597000, tzinfo=tzutc()),\n", " 'database_format': '%d.%c.%Y %H:%i:%S',\n", " 'example': '30.01.2022 13:44:25',\n", " 'has_time': True,\n", @@ -387,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 87, "metadata": { "pycharm": { "name": "#%%\n" @@ -398,7 +400,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'container': {'created': datetime.datetime(2022, 8, 12, 18, 13, 23, 859000, tzinfo=tzutc()),\n", + "{'container': {'created': datetime.datetime(2022, 8, 16, 12, 41, 27, 625000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -408,12 +410,12 @@ " 'titles_after': None,\n", " 'titles_before': None,\n", " 'username': 'test'},\n", - " 'hash': '1785640e50886bbb9769827a63c564270b1d682214e12c3fee5a4678ff95860b',\n", + " 'hash': '686331331ec9e4de5b84cecc9f323cc04efc6d8766eaea07c0b1e47b817ff4e9',\n", " 'id': 1,\n", " 'internal_name': 'fda-userdb-airquality',\n", " 'is_public': None,\n", " 'name': 'Airquality'},\n", - " 'created': datetime.datetime(2022, 8, 12, 18, 13, 30, 103000, tzinfo=tzutc()),\n", + " 'created': datetime.datetime(2022, 8, 16, 12, 41, 34, 450000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -457,7 +459,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 88, "metadata": { "pycharm": { "name": "#%%\n" @@ -539,7 +541,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 89, "metadata": { "pycharm": { "name": "#%%\n" @@ -570,7 +572,7 @@ " 'check_expression': None,\n", " 'column_concept': None,\n", " 'column_type': 'date',\n", - " 'date_format': {'created_at': datetime.datetime(2022, 8, 12, 18, 4, 42, 279000, tzinfo=tzutc()),\n", + " 'date_format': {'created_at': datetime.datetime(2022, 8, 16, 12, 39, 54, 574000, tzinfo=tzutc()),\n", " 'database_format': '%Y-%c-%d',\n", " 'example': '2022-01-30',\n", " 'has_time': False,\n", @@ -683,7 +685,7 @@ " 'name': 'Status',\n", " 'references': None,\n", " 'unique': False}],\n", - " 'created': datetime.datetime(2022, 8, 12, 18, 14, 31, 839000, tzinfo=tzutc()),\n", + " 'created': datetime.datetime(2022, 8, 16, 12, 41, 36, 714000, tzinfo=tzutc()),\n", " 'creator': {'affiliation': None,\n", " 'firstname': 'Martin',\n", " 'id': 2,\n", @@ -709,7 +711,30 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 90, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "response = consumer.declare(container_id, database_id, table_id)\n", + "print(response)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 91, "metadata": { "pycharm": { "name": "#%%\n" @@ -742,7 +767,273 @@ "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '36.55', 'status': 'tentative'}\n", "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.2', 'status': 'tentative'}\n", "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '43.51', 'status': 'tentative'}\n", - "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '4.38', 'status': 'tentative'}\n" + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '4.38', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '74.05', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '74.11', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.4', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '5.02', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '35.47', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.28', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '42.88', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '5.34', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '140.15', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '118.87', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '39.43', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.8', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '43.51', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.93', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '118.95', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '102.54', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '5.31', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.52', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '9.36', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '51.36', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '45.83', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '28.15', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '44.48', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.25', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '127.62', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '126.47', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.4', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '3.25', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '26.86', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '7.78', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '39.43', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '7.54', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '106.39', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '90.24', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '35.93', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.28', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '42.22', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '8.83', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '96.55', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '82.07', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '5.74', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.83', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '9.71', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '52.2', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '40.44', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.6', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '42.67', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '4.92', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '91.18', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '90.98', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.22', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.89', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '14.27', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.38', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '23.64', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '31.3', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '43.53', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '37.49', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '25.84', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.87', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '32.74', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '23.68', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '50.23', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '43.63', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '8.46', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.94', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '14.73', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '44.16', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '24.75', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '8.99', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '33.56', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.73', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '48.51', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '48.21', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.18', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.37', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '12.28', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.08', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.28', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '40.87', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.82', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.02', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '23', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.63', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '33.81', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '24.59', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '33.48', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '29.23', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '6.82', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.58', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.15', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '46.87', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '32.39', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.8', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '36.18', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '25.41', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '21.56', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.32', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '12.52', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.04', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.82', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '35.95', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.15', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.67', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '18.91', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.42', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '26.31', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '31.73', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '27.49', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '23.63', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '11.26', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.85', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.23', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '33.83', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '17.5', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.39', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '23.67', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '30.84', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.37', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.84', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.17', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.23', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '9.36', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.25', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '15.98', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '38.22', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '11.67', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.45', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '18.39', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.55', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '25.14', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '31.79', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '14.42', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.49', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '8.26', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.49', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '15.04', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '40.22', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '15.54', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '4.45', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.9', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '28.44', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.82', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.22', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.13', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '9.2', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.03', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.02', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '32.79', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.48', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '14.77', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '18.57', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.21', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '26', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '24.42', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.7', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.5', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '7.98', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.03', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '13.69', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '37.7', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '13.42', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '3.05', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.99', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '25.75', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.8', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.53', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.2', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.08', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '11.57', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.45', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.36', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '28.16', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.12', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '15.26', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '19.95', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.27', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '28.54', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '19.92', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '19.28', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.09', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '7.65', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '0.85', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '13.32', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '35.22', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '15.79', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.1', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.84', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.28', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.97', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.08', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '9.93', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.3', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '15.46', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '32.76', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.63', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '11.29', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '27.76', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '13.83', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '31.88', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.36', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '17.21', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '15.17', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '6.81', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.6', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.58', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '38.17', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '16.84', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '6.1', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.85', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '19.93', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.86', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '16.57', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.05', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '9.9', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.49', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '15.13', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '31.77', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.43', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '11.09', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '20.13', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.12', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.98', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '27.65', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '13.91', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.29', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '8.44', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.37', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.51', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '38.26', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '19.98', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.53', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '22.06', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '24.13', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '14.19', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '13.76', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'CO', 'interval': 'h1', 'unit': 'mg/m3', 'value': '0.17', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'SO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.04', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '8.76', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '2.78', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '12.5', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '35.76', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '9.55', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Stampfenbachstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '8.5', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '21.86', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '13.66', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '20.85', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '32.1', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.6', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Schimmelstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '9.2', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '6.38', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '1.75', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '9.51', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Heubeeribüel', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '38.09', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NOx', 'interval': 'h1', 'unit': 'ppb', 'value': '18.13', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.64', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'NO2', 'interval': 'h1', 'unit': 'µg/m3', 'value': '18.36', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'O3', 'interval': 'h1', 'unit': 'µg/m3', 'value': '32.27', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM10', 'interval': 'h1', 'unit': 'µg/m3', 'value': '10.37', 'status': 'tentative'}\n", + "{'date': '2021-01-01', 'location': 'Rosengartenstrasse', 'parameter': 'PM2.5', 'interval': 'h1', 'unit': 'µg/m3', 'value': '9.75', 'status': 'tentative'}\n" ] }, { @@ -752,7 +1043,7 @@ "traceback": [ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[0;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "Input \u001B[0;32mIn [18]\u001B[0m, in \u001B[0;36m<cell line: 3>\u001B[0;34m()\u001B[0m\n\u001B[1;32m 6\u001B[0m payload \u001B[38;5;241m=\u001B[39m {\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mdate\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m0\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mlocation\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m1\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mparameter\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m2\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124minterval\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m3\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124munit\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m4\u001B[39m],\n\u001B[1;32m 7\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mvalue\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m5\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mstatus\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m6\u001B[39m]}\n\u001B[1;32m 8\u001B[0m response \u001B[38;5;241m=\u001B[39m broker\u001B[38;5;241m.\u001B[39msend(payload)\n\u001B[0;32m----> 9\u001B[0m \u001B[43mtime\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msleep\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m1\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m 10\u001B[0m \u001B[38;5;28mprint\u001B[39m(payload)\n", + "Input \u001B[0;32mIn [91]\u001B[0m, in \u001B[0;36m<cell line: 3>\u001B[0;34m()\u001B[0m\n\u001B[1;32m 6\u001B[0m payload \u001B[38;5;241m=\u001B[39m {\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mdate\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m0\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mlocation\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m1\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mparameter\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m2\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124minterval\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m3\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124munit\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m4\u001B[39m],\n\u001B[1;32m 7\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mvalue\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m5\u001B[39m], \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mstatus\u001B[39m\u001B[38;5;124m'\u001B[39m: row[\u001B[38;5;241m6\u001B[39m]}\n\u001B[1;32m 8\u001B[0m response \u001B[38;5;241m=\u001B[39m broker\u001B[38;5;241m.\u001B[39msend(payload)\n\u001B[0;32m----> 9\u001B[0m \u001B[43mtime\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msleep\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m1\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m 10\u001B[0m \u001B[38;5;28mprint\u001B[39m(payload)\n", "\u001B[0;31mKeyboardInterrupt\u001B[0m: " ] } diff --git a/fda-gateway-service/gateway/src/main/java/at/tuwien/config/GatewayConfig.java b/fda-gateway-service/gateway/src/main/java/at/tuwien/config/GatewayConfig.java index bb3bffa5a8607d95299031163bbb72a67e5af068..9ec6efa4eec65dcddb77e5ec95c2b36e3184f8db 100644 --- a/fda-gateway-service/gateway/src/main/java/at/tuwien/config/GatewayConfig.java +++ b/fda-gateway-service/gateway/src/main/java/at/tuwien/config/GatewayConfig.java @@ -38,6 +38,7 @@ public class GatewayConfig { "/api/container/**/database/**/table/**/data/**", "/api/container/**/database/**/table/**/query/**", "/api/container/**/database/**/table/**/export/**", + "/api/container/**/database/**/table/**/consumer", "/api/container/**/database/**/version/**") .and() .method("POST", "GET", "PUT", "DELETE") diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java index 10cd45680a456d90aeedd3a6f47e5ccd0c6a871a..39ab35871ebc771f1bcbdf3439eea2996562d9ea 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java @@ -1,6 +1,7 @@ package at.tuwien.endpoint; import at.tuwien.entities.database.Database; +import at.tuwien.entities.database.table.Table; import at.tuwien.entities.identifier.Identifier; import at.tuwien.exception.DatabaseNotFoundException; import at.tuwien.exception.IdentifierNotFoundException; @@ -61,6 +62,35 @@ public abstract class AbstractEndpoint { return false; } + protected Boolean hasQueuePermission(Long containerId, Long databaseId, Long tableId, String permissionCode, + Principal principal) { + final Database database; + try { + database = databaseService.find(containerId, databaseId); + } catch (DatabaseNotFoundException e) { + log.debug("failed to find database with id {}", databaseId); + return false; + } + if (principal == null) { + log.debug("failed to grant permission {} because principal is null", permissionCode); + return false; + } + /* modification operations are limited to the creator */ + if (database.getCreator().getUsername().equals(principal.getName())) { + log.debug("grant permission {} because user {} is creator {}", permissionCode, principal.getName(), + database.getCreator().getUsername()); + return true; + } + final Authentication authentication = (Authentication) principal /* with pre-authorization this always holds */; + if (authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals("ROLE_RESEARCHER"))) { + log.debug("failed to grant permission {} because current user misses authority 'ROLE_RESEARCHER'", + permissionCode); + return false; + } + log.debug("failed to grant permission {} because database is not owner by the current user", permissionCode); + return false; + } + protected Boolean hasQueryPermission(Long containerId, Long databaseId, Long queryId, String permissionCode, Principal principal) { final Database database; try { diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ConsumerEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ConsumerEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..eef48d38258db03e2e7b641c9c3873e70d9f71fa --- /dev/null +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ConsumerEndpoint.java @@ -0,0 +1,52 @@ +package at.tuwien.endpoint; + +import at.tuwien.entities.database.table.Table; +import at.tuwien.exception.*; +import at.tuwien.service.*; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.security.Principal; + +@Log4j2 +@CrossOrigin(origins = "*") +@RestController +@RequestMapping("/api/container/{id}/database/{databaseId}/table/{tableId}/consumer") +public class ConsumerEndpoint extends AbstractEndpoint { + + private final TableService tableService; + private final MessageQueueService messageQueueService; + + @Autowired + public ConsumerEndpoint(DatabaseService databaseService, IdentifierService identifierService, + TableService tableService, MessageQueueService messageQueueService) { + super(databaseService, identifierService); + this.tableService = tableService; + this.messageQueueService = messageQueueService; + } + + @PostMapping + @Transactional + @Operation(summary = "Declare consumer", security = @SecurityRequirement(name = "bearerAuth")) + public ResponseEntity<Void> declare(@NotNull @PathVariable("id") Long containerId, + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("tableId") Long tableId, + @NotNull Principal principal) + throws TableNotFoundException, DatabaseNotFoundException, AmqpException, NotAllowedException { + if (!hasDatabasePermission(containerId, databaseId, "QUEUE_CREATE_CONSUMER", principal)) { + log.error("Missing data export permission"); + throw new NotAllowedException("Missing data export permission"); + } + final Table table = tableService.find(containerId, databaseId, tableId); + messageQueueService.createConsumer(table.getTopic(), containerId, databaseId, tableId); + return ResponseEntity.accepted() + .build(); + } + +} diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java index 36e9d24c4f2019b07eeb1626d371cf559894d85d..197da01129319058194c4f138bf4ccefa645344f 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java @@ -40,10 +40,10 @@ public class TableDataEndpoint extends AbstractEndpoint { @Transactional @Operation(summary = "Insert data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> insert(@NotNull @PathVariable("id") Long containerId, - @NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("tableId") Long tableId, - @NotNull @Valid @RequestBody TableCsvDto data, - @NotNull Principal principal) + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("tableId") Long tableId, + @NotNull @Valid @RequestBody TableCsvDto data, + @NotNull Principal principal) throws TableNotFoundException, DatabaseNotFoundException, TableMalformedException, ImageNotSupportedException, ContainerNotFoundException, NotAllowedException, DatabaseConnectionException { if (!hasDatabasePermission(containerId, databaseId, "DATA_INSERT", principal)) { @@ -59,10 +59,10 @@ public class TableDataEndpoint extends AbstractEndpoint { @Transactional @Operation(summary = "Update data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> update(@NotNull @PathVariable("id") Long containerId, - @NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("tableId") Long tableId, - @NotNull @Valid @RequestBody TableCsvUpdateDto data, - @NotNull Principal principal) + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("tableId") Long tableId, + @NotNull @Valid @RequestBody TableCsvUpdateDto data, + @NotNull Principal principal) throws TableNotFoundException, DatabaseNotFoundException, TableMalformedException, ImageNotSupportedException, NotAllowedException, DatabaseConnectionException, QueryMalformedException { if (!hasDatabasePermission(containerId, databaseId, "DATA_UPDATE", principal)) { @@ -98,12 +98,13 @@ public class TableDataEndpoint extends AbstractEndpoint { @Transactional @Operation(summary = "Insert data from csv", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> importCsv(@NotNull @PathVariable("id") Long containerId, - @NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("tableId") Long tableId, - @NotNull @Valid @RequestBody ImportDto data, - @NotNull Principal principal) + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("tableId") Long tableId, + @NotNull @Valid @RequestBody ImportDto data, + @NotNull Principal principal) throws TableNotFoundException, DatabaseNotFoundException, TableMalformedException, - ImageNotSupportedException, ContainerNotFoundException, NotAllowedException, DatabaseConnectionException, QueryMalformedException { + ImageNotSupportedException, ContainerNotFoundException, NotAllowedException, DatabaseConnectionException, + QueryMalformedException { if (!hasDatabasePermission(containerId, databaseId, "DATA_INSERT", principal)) { log.error("Missing data insert permission"); throw new NotAllowedException("Missing data insert permission"); diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java index 0e44adf399641fea249a6f976c0645febcb50104..de0a73ef1c3aec7c3f86c3add780b7a8e14ae8fc 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java @@ -62,7 +62,7 @@ public class RabbitMqServiceImpl implements MessageQueueService { } @Override - public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) { final TypeReference<HashMap<String, Object>> payloadReference = new TypeReference<>() { }; try { diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java index 033143372b67d5dd7a6aec5ed7aa2d48aa017a66..55d47984c84f0e3559864986097201ab42e507e6 100644 --- a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java +++ b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java @@ -15,12 +15,10 @@ import java.util.List; @Slf4j public abstract class AbstractEndpoint { - private final TableService tableService; private final DatabaseService databaseService; @Autowired - protected AbstractEndpoint(TableService tableService, DatabaseService databaseService) { - this.tableService = tableService; + protected AbstractEndpoint(DatabaseService databaseService) { this.databaseService = databaseService; } diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index c9d53209169d32e3e8b84043ebd592c5ab41b3a4..cdeca68350a72257f63a5a082253756551d4f5df 100644 --- a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -13,7 +13,6 @@ import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -30,17 +29,17 @@ import java.util.stream.Collectors; @RequestMapping("/api/container/{id}/database/{databaseId}/table") public class TableEndpoint extends AbstractEndpoint { + private final TableMapper tableMapper; private final TableService tableService; private final MessageQueueService amqpService; - private final TableMapper tableMapper; @Autowired - public TableEndpoint(TableService tableService, DatabaseService databaseService, MessageQueueService amqpService, - TableMapper tableMapper) { - super(tableService, databaseService); - this.tableService = tableService; - this.amqpService = amqpService; + public TableEndpoint(TableMapper tableMapper, TableService tableService, MessageQueueService amqpService, + DatabaseService databaseService) { + super(databaseService); this.tableMapper = tableMapper; + this.amqpService = amqpService; + this.tableService = tableService; } @GetMapping @@ -66,7 +65,7 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<TableBriefDto> create(@NotNull @PathVariable("id") Long containerId, @NotNull @PathVariable("databaseId") Long databaseId, @NotNull @Valid @RequestBody TableCreateDto createDto, - Principal principal) + @NotNull Principal principal) throws ImageNotSupportedException, DatabaseNotFoundException, TableMalformedException, AmqpException, TableNameExistsException, ContainerNotFoundException, UserNotFoundException, QueryMalformedException { if (!hasDatabasePermission(containerId, databaseId, "TABLE_CREATE", principal)) { diff --git a/fda-table-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java b/fda-table-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java index 1a8176b28ef3f92df0f3a523cda6c4b347e94f75..f5ebe4683d95dfeb44c36e438ed2f89797e120ec 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java +++ b/fda-table-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java @@ -1,18 +1,17 @@ package at.tuwien.gateway; -import at.tuwien.api.database.table.TableCsvDto; +import at.tuwien.exception.AmqpException; public interface QueryServiceGateway { /** * Publish new data into a table with given container id, database id, table id. * - * @param containerId The container id. - * @param databaseId The database id. - * @param tableId The table id. - * @param data The data. - * @return The number of inserted tuples. + * @param containerId The container id. + * @param databaseId The database id. + * @param tableId The table id. + * @param authorization The authentication token. */ - Integer publish(Long containerId, Long databaseId, Long tableId, TableCsvDto data); + void declareConsumer(Long containerId, Long databaseId, Long tableId, String authorization) throws AmqpException; } diff --git a/fda-table-service/services/src/main/java/at/tuwien/gateway/impl/QueryServiceGatewayImpl.java b/fda-table-service/services/src/main/java/at/tuwien/gateway/impl/QueryServiceGatewayImpl.java index a85f13e3bb0bd653c3de357eb16bcd433c754922..4e97248b8411bf25cb16ad04cb70635ba79c3dba 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/gateway/impl/QueryServiceGatewayImpl.java +++ b/fda-table-service/services/src/main/java/at/tuwien/gateway/impl/QueryServiceGatewayImpl.java @@ -1,12 +1,10 @@ package at.tuwien.gateway.impl; -import at.tuwien.api.database.table.TableCsvDto; +import at.tuwien.exception.AmqpException; import at.tuwien.gateway.QueryServiceGateway; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -22,11 +20,18 @@ public class QueryServiceGatewayImpl implements QueryServiceGateway { } @Override - public Integer publish(Long containerId, Long databaseId, Long tableId, TableCsvDto data) { - final String url = "/api/container/" + containerId + "/database/" + databaseId + "/table/" + tableId + "/data"; - final ResponseEntity<Integer> response = restTemplate.exchange(url, HttpMethod.POST, - new HttpEntity<>(data), Integer.class); - return response.getBody(); + public void declareConsumer(Long containerId, Long databaseId, Long tableId, String authorization) throws AmqpException { + final String url = "/api/container/" + containerId + "/database/" + databaseId + "/table/" + tableId + "/consumer"; + final HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authorization); + final ResponseEntity<Void> response = restTemplate.exchange(url, HttpMethod.POST, + new HttpEntity<>(null, headers), Void.class); + if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) { + log.error("Failed to declare consumer for table with id {}", tableId); + log.debug("failed to declare consumer for container with id {} database with id {} table with id {}", + containerId, databaseId, tableId); + throw new AmqpException("Failed to declare consumer"); + } } } diff --git a/fda-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/fda-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index 107723c015f18637211e41cec6e4180330f80bdf..1434d7a0853c235ad7cd669f4344bbe10c764383 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/fda-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -25,6 +25,8 @@ import java.sql.SQLException; import java.util.*; import java.util.stream.Collectors; +import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW; + @Log4j2 @Service public class TableServiceImpl extends HibernateConnector implements TableService { @@ -162,7 +164,7 @@ public class TableServiceImpl extends HibernateConnector implements TableService dataSource1.close(); } /* save in metadata database */ - final Table table = tableRepository.save(entity); + final Table table = tableRepository.saveAndFlush(entity); log.info("Created table with id {}", table.getId()); log.debug("created table {}", table); /* save in elastic search */