From fa21064a03c858f39e5aa1152581f97b29b2c8c7 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Tue, 26 Jul 2022 19:32:11 +0200 Subject: [PATCH] Added load test to verify API, fixed the database service --- .jupyter/api_authentication/__init__.py | 1 + .../api/user_endpoint_api.py | 105 ++ .../api_authentication/models/__init__.py | 1 + .../api_query/api/table_data_endpoint_api.py | 4 +- .jupyter/api_table/__init__.py | 1 - .jupyter/api_table/models/__init__.py | 1 - .jupyter/load_test.py | 273 +++++ .jupyter/test.ipynb | 1013 ----------------- .../at/tuwien/endpoints/UserEndpoint.java | 11 + .../java/at/tuwien/service/UserService.java | 9 + .../tuwien/service/impl/UserServiceImpl.java | 13 +- .../tuwien/gateway/QueryServiceGateway.java | 3 +- .../service/impl/IdentifierServiceImpl.java | 1 + .../java/at/tuwien/api/user/UserBriefDto.java | 14 - .../at/tuwien/api/user/UserThemeSetDto.java | 22 + .../at/tuwien/api/user/UserUpdateDto.java | 5 - .../at/tuwien/endpoint/AbstractEndpoint.java | 4 +- .../java/at/tuwien/mapper/StoreMapper.java | 1 + .../tuwien/service/impl/QueryServiceImpl.java | 13 +- fda-ui/layouts/default.vue | 42 +- .../database/_database_id/sql.vue | 26 - fda-ui/pages/container/index.vue | 10 +- fda-ui/pages/login.vue | 3 +- fda-ui/pages/user/index.vue | 48 +- 24 files changed, 533 insertions(+), 1091 deletions(-) create mode 100644 .jupyter/load_test.py delete mode 100644 .jupyter/test.ipynb create mode 100644 fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java delete mode 100644 fda-ui/pages/container/_container_id/database/_database_id/sql.vue diff --git a/.jupyter/api_authentication/__init__.py b/.jupyter/api_authentication/__init__.py index 2c48db2277..1caebb3b03 100644 --- a/.jupyter/api_authentication/__init__.py +++ b/.jupyter/api_authentication/__init__.py @@ -43,4 +43,5 @@ from api_authentication.models.user_forgot_dto import UserForgotDto from api_authentication.models.user_password_dto import UserPasswordDto from api_authentication.models.user_reset_dto import UserResetDto from api_authentication.models.user_roles_dto import UserRolesDto +from api_authentication.models.user_theme_set_dto import UserThemeSetDto from api_authentication.models.user_update_dto import UserUpdateDto diff --git a/.jupyter/api_authentication/api/user_endpoint_api.py b/.jupyter/api_authentication/api/user_endpoint_api.py index 91343f15a8..40ba9632a0 100644 --- a/.jupyter/api_authentication/api/user_endpoint_api.py +++ b/.jupyter/api_authentication/api/user_endpoint_api.py @@ -920,3 +920,108 @@ class UserEndpointApi(object): _preload_content=params.get('_preload_content', True), _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) + + def update_theme(self, body, id, **kwargs): # noqa: E501 + """Update user theme # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + >>> thread = api.update_theme(body, id, async_req=True) + >>> result = thread.get() + + :param async_req bool + :param UserThemeSetDto body: (required) + :param int id: (required) + :return: None + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async_req'): + return self.update_theme_with_http_info(body, id, **kwargs) # noqa: E501 + else: + (data) = self.update_theme_with_http_info(body, id, **kwargs) # noqa: E501 + return data + + def update_theme_with_http_info(self, body, id, **kwargs): # noqa: E501 + """Update user theme # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + >>> thread = api.update_theme_with_http_info(body, id, async_req=True) + >>> result = thread.get() + + :param async_req bool + :param UserThemeSetDto body: (required) + :param int id: (required) + :return: None + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['body', 'id'] # noqa: E501 + all_params.append('async_req') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method update_theme" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'body' is set + if ('body' not in params or + params['body'] is None): + raise ValueError("Missing the required parameter `body` when calling `update_theme`") # noqa: E501 + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `update_theme`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + if 'body' in params: + body_params = params['body'] + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['*/*']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = ['bearerAuth'] # noqa: E501 + + return self.api_client.call_api( + '/api/user/{id}/theme', 'PUT', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type=None, # noqa: E501 + auth_settings=auth_settings, + async_req=params.get('async_req'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) diff --git a/.jupyter/api_authentication/models/__init__.py b/.jupyter/api_authentication/models/__init__.py index 11e22a17ba..0cd55935b6 100644 --- a/.jupyter/api_authentication/models/__init__.py +++ b/.jupyter/api_authentication/models/__init__.py @@ -35,4 +35,5 @@ from api_authentication.models.user_forgot_dto import UserForgotDto from api_authentication.models.user_password_dto import UserPasswordDto from api_authentication.models.user_reset_dto import UserResetDto from api_authentication.models.user_roles_dto import UserRolesDto +from api_authentication.models.user_theme_set_dto import UserThemeSetDto from api_authentication.models.user_update_dto import UserUpdateDto diff --git a/.jupyter/api_query/api/table_data_endpoint_api.py b/.jupyter/api_query/api/table_data_endpoint_api.py index 5cfffc839b..31b90e9df3 100644 --- a/.jupyter/api_query/api/table_data_endpoint_api.py +++ b/.jupyter/api_query/api/table_data_endpoint_api.py @@ -259,7 +259,7 @@ class TableDataEndpointApi(object): auth_settings = ['bearerAuth'] # noqa: E501 return self.api_client.call_api( - '/api/container/{id}/database/{databaseId}/table/{tableId}/data', 'GET', + '/api/container/{id}/database/{databaseId}/table/{tableId}/data', 'HEAD', path_params, query_params, header_params, @@ -380,7 +380,7 @@ class TableDataEndpointApi(object): auth_settings = ['bearerAuth'] # noqa: E501 return self.api_client.call_api( - '/api/container/{id}/database/{databaseId}/table/{tableId}/data', 'HEAD', + '/api/container/{id}/database/{databaseId}/table/{tableId}/data', 'GET', path_params, query_params, header_params, diff --git a/.jupyter/api_table/__init__.py b/.jupyter/api_table/__init__.py index beaceff5a7..37441edea5 100644 --- a/.jupyter/api_table/__init__.py +++ b/.jupyter/api_table/__init__.py @@ -24,7 +24,6 @@ from api_table.models.api_error_dto import ApiErrorDto from api_table.models.column_create_dto import ColumnCreateDto from api_table.models.column_dto import ColumnDto from api_table.models.concept_dto import ConceptDto -from api_table.models.granted_authority_dto import GrantedAuthorityDto from api_table.models.image_date_dto import ImageDateDto from api_table.models.table_brief_dto import TableBriefDto from api_table.models.table_create_dto import TableCreateDto diff --git a/.jupyter/api_table/models/__init__.py b/.jupyter/api_table/models/__init__.py index c7a86dfadf..65818bef09 100644 --- a/.jupyter/api_table/models/__init__.py +++ b/.jupyter/api_table/models/__init__.py @@ -18,7 +18,6 @@ from api_table.models.api_error_dto import ApiErrorDto from api_table.models.column_create_dto import ColumnCreateDto from api_table.models.column_dto import ColumnDto from api_table.models.concept_dto import ConceptDto -from api_table.models.granted_authority_dto import GrantedAuthorityDto from api_table.models.image_date_dto import ImageDateDto from api_table.models.table_brief_dto import TableBriefDto from api_table.models.table_create_dto import TableCreateDto diff --git a/.jupyter/load_test.py b/.jupyter/load_test.py new file mode 100644 index 0000000000..4f395a950b --- /dev/null +++ b/.jupyter/load_test.py @@ -0,0 +1,273 @@ +#!/bin/env python3 + +import time +import os +import shutil +import uuid +import sys + +import api_query.rest +from api_authentication.api.authentication_endpoint_api import AuthenticationEndpointApi +from api_authentication.api.user_endpoint_api import UserEndpointApi +from api_container.api.container_endpoint_api import ContainerEndpointApi +from api_database.api.container_database_endpoint_api import ContainerDatabaseEndpointApi +from api_table.api.table_endpoint_api import TableEndpointApi +from api_query.api.table_data_endpoint_api import TableDataEndpointApi +from api_query.api.query_endpoint_api import QueryEndpointApi +from api_identifier.api.identifier_endpoint_api import IdentifierEndpointApi +from api_identifier.api.persistence_endpoint_api import PersistenceEndpointApi + +authentication = AuthenticationEndpointApi() +user = UserEndpointApi() +container = ContainerEndpointApi() +database = ContainerDatabaseEndpointApi() +table = TableEndpointApi() +query = QueryEndpointApi() +data = TableDataEndpointApi() +identifier = IdentifierEndpointApi() +persistence = PersistenceEndpointApi() + +token = "" + + +def create_user(username): + response = user.register({ + "username": username, + "password": username, + "email": username + "@gmail.com" + }) + print("created user") + return response + + +def auth_user(username): + response = authentication.authenticate_user1({ + "username": username, + "password": username + }) + print("authenticated user") + token = response.token + container.api_client.default_headers = {"Authorization": "Bearer " + token} + database.api_client.default_headers = {"Authorization": "Bearer " + token} + table.api_client.default_headers = {"Authorization": "Bearer " + token} + data.api_client.default_headers = {"Authorization": "Bearer " + token} + query.api_client.default_headers = {"Authorization": "Bearer " + token} + identifier.api_client.default_headers = {"Authorization": "Bearer " + token} + user.api_client.default_headers = {"Authorization": "Bearer " + token} + persistence.api_client.default_headers = {"Authorization": "Bearer " + token} + return response + + +def create_container(): + response = container.create1({ + "name": "Airquality " + str(uuid.uuid1()), + "repository": "mariadb", + "tag": "10.5" + }) + print("created container") + return response + + +def start_container(container_id): + response = container.modify({ + "action": "start" + }, container_id) + time.sleep(5) + print("started container") + return response + + +def create_database(container_id, is_public=True): + response = database.create({ + "name": "Airquality " + str(uuid.uuid1()), + "description": "Hourly measurements in Zürich, Switzerland", + "is_public": is_public + }, container_id) + print("created database") + return response + + +def update_database(container_id, database_id, is_public=True): + response = database.update({ + "description": "This dataset includes daily values from 1983 to the current day, divided into annual files. This includes the maximum hourly average and the number of times the hourly average limit value for ozone was exceeded and the daily averages for sulfur dioxide (SO2), carbon monoxide (CO), nitrogen oxide (NOx), nitrogen monoxide (NO), nitrogen dioxide (NO2), particulate matter (PM10 and PM2.5). ) and particle number (PN), provided that they are of sufficient quality. The values of the completed day for the current year are updated every 30 minutes after midnight (UTC+1).", + "publisher": "Technical University of Vienna", + "license": { + "identifier": "CC0-1.0", + "uri": "https://creativecommons.org/publicdomain/zero/1.0/legalcode" + }, + "language": "en", + "is_public": is_public, + "publication": "2022-07-19" + }, container_id, database_id) + print("updated database") + return response + + +def create_table(container_id, database_id, columns=None): + if columns is None: + columns = [{ + "name": "Date", + "type": "date", + "dfid": 1, + "unique": False, + "primary_key": False, + "null_allowed": True, + }, { + "name": "Location", + "type": "string", + "unique": False, + "primary_key": False, + "null_allowed": True, + }, { + "name": "Parameter", + "type": "string", + "unique": False, + "primary_key": False, + "null_allowed": True, + }, { + "name": "Interval", + "type": "string", + "unique": False, + "primary_key": False, + "null_allowed": True, + }, { + "name": "Unit", + "type": "string", + "unique": False, + "primary_key": False, + "null_allowed": True, + }, { + "name": "Value", + "type": "decimal", + "unique": False, + "primary_key": False, + "null_allowed": True, + }, { + "name": "Status", + "type": "string", + "unique": False, + "primary_key": False, + "null_allowed": True, + }] + response = table.create({ + "name": "Airquality " + str(uuid.uuid1()), + "description": "Airquality in Zürich, Switzerland", + "columns": columns + }, container_id, database_id) + print("created table") + return response + + +def find_table(container_id, database_id, table_id): + response = table.find_by_id(container_id, database_id, table_id) + print("found table") + return response + + +def fill_table(container_id, database_id, table_id): + shutil.copyfile(os.getcwd() + "/resources/ugz_ogd_air_h1_2021.csv", "/tmp/ugz_ogd_air_h1_2021.csv") + response = data.import_csv({ + "location": "/tmp/ugz_ogd_air_h1_2021.csv", + "separator": ",", + "quote": "\"", + "skip_lines": 1 + }, container_id, database_id, table_id) + print("filled table") + return response + + +def create_query(container_id, database_id, statement, page=0, size=3): + try: + response = query.execute({ + "statement": statement + }, container_id, database_id, page=page, size=size) + print("executed query") + return response + except api_query.rest.ApiException as e: + print(e) + + +def create_identifier(container_id, database_id, query_id, visibility="everyone"): + response = identifier.create({ + "qid": query_id, + "title": "Airquality", + "description": "Subset used for a scientific article", + "visibility": visibility, + "creators": [{ + "name": "Weise, Martin", + "affiliation": "TU Wien", + "orcid": "0000-0003-4216-302X" + }, { + "name": "Rauber, Andreas", + "affiliation": "TU Wien", + "orcid": "0000-0002-9272-6225" + }], + "publication": "2022-07-16", + "related_identifiers": [{ + "value": "http://localhost:3000/container/" + str(container_id) + "/database/" + str(database_id), + "type": "URL", + "relation": "IsCitedBy" + }] + }, token, container_id, database_id) + print("created identifier") + return response + + +if __name__ == '__main__': + # + # create 1 user and 3 containers (public, private, public) + # + create_user("test1") + auth_user("test1") + # container 1 + cid = create_container().id + start_container(cid) + dbid = create_database(cid).id + update_database(cid, dbid) + tid = create_table(cid, dbid).id + tname = find_table(cid, dbid, tid).internal_name + fill_table(cid, dbid, tid) + create_query(cid, dbid, "select `id` from `" + tname + "`") + create_query(cid, dbid, "select `date` from `" + tname + "`") + qid = create_query(cid, dbid, "select `date`, `location`, `status` from `" + tname + "`").id + create_query(cid, dbid, "select `foo` from `" + tname + "`") + create_identifier(cid, dbid, qid) + # container 2 (=private) + cid = create_container().id + start_container(cid) + dbid = create_database(cid, False).id + update_database(cid, dbid, is_public=False) + tid = create_table(cid, dbid).id + tname = find_table(cid, dbid, tid).internal_name + fill_table(cid, dbid, tid) + qid = create_query(cid, dbid, "select `id` from `" + tname + "`").id + create_identifier(cid, dbid, qid, visibility="self") + qid = create_query(cid, dbid, "select `id` from `" + tname + "`").id + create_identifier(cid, dbid, qid) + # container 3 with 3 tables + cid = create_container().id + start_container(cid) + dbid = create_database(cid).id + update_database(cid, dbid) + create_table(cid, dbid, columns=[]) + create_table(cid, dbid, columns=[{ + "name": "primary", + "type": "string", + "unique": True, + "primary_key": True, + "null_allowed": False, + }]) + create_table(cid, dbid, columns=[{ + "name": "primary", + "type": "number", + "unique": True, + "primary_key": True, + "null_allowed": False, + }]) + create_table(cid, dbid, columns=[{ + "name": "primary", + "type": "date", + "unique": True, + "primary_key": True, + "null_allowed": False, + }]) diff --git a/.jupyter/test.ipynb b/.jupyter/test.ipynb deleted file mode 100644 index 7c276b67ef..0000000000 --- a/.jupyter/test.ipynb +++ /dev/null @@ -1,1013 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "# Test Jupyter Notebook" - ] - }, - { - "cell_type": "code", - "execution_count": 113, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "import time\n", - "import os\n", - "import shutil\n", - "import uuid\n", - "\n", - "from api_authentication.api.authentication_endpoint_api import AuthenticationEndpointApi\n", - "from api_authentication.api.user_endpoint_api import UserEndpointApi\n", - "from api_container.api.container_endpoint_api import ContainerEndpointApi\n", - "from api_database.api.container_database_endpoint_api import ContainerDatabaseEndpointApi\n", - "from api_table.api.table_endpoint_api import TableEndpointApi\n", - "from api_query.api.table_data_endpoint_api import TableDataEndpointApi\n", - "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", - "\n", - "authentication = AuthenticationEndpointApi()\n", - "user = UserEndpointApi()\n", - "container = ContainerEndpointApi()\n", - "database = ContainerDatabaseEndpointApi()\n", - "table = TableEndpointApi()\n", - "query = QueryEndpointApi()\n", - "data = TableDataEndpointApi()\n", - "identifier = IdentifierEndpointApi()\n", - "persistence = PersistenceEndpointApi()\n", - "\n", - "username = \"user\"\n", - "password = \"user\"\n", - "email = \"someone@example.com\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Create user" - ] - }, - { - "cell_type": "code", - "execution_count": 114, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'affiliation': None,\n", - " 'authorities': [{'authority': 'ROLE_RESEARCHER'}],\n", - " 'containers': None,\n", - " 'databases': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'identifiers': None,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'}\n" - ] - } - ], - "source": [ - "response = user.register({\n", - " \"username\": username,\n", - " \"password\": password,\n", - " \"email\": email\n", - "})\n", - "print(response)" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Create token" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 115, - "outputs": [], - "source": [ - "response = authentication.authenticate_user1({\n", - " \"username\": username,\n", - " \"password\": password\n", - "})\n", - "user_id = response.id\n", - "token = response.token\n", - "container.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", - "database.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", - "table.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", - "data.api_client.default_headers = {\"Authorization\": \"Bearer \" + token}\n", - "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}" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Create container" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 116, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'created': datetime.datetime(2022, 7, 26, 13, 5, 36, 339000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'hash': '5dc114629602472d0bb93f4aedd6a38f161dd59ed129247511c83767fcba0a19',\n", - " 'id': 1,\n", - " 'internal_name': 'fda-userdb-airquality',\n", - " 'is_public': None,\n", - " 'name': 'Airquality'}\n" - ] - } - ], - "source": [ - "response = container.create1({\n", - " \"name\": \"Airquality\",\n", - " \"repository\": \"mariadb\",\n", - " \"tag\": \"10.5\"\n", - "})\n", - "container_id = response.id\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Start container" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 117, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'created': datetime.datetime(2022, 7, 26, 13, 5, 36, 339000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'hash': '5dc114629602472d0bb93f4aedd6a38f161dd59ed129247511c83767fcba0a19',\n", - " 'id': 1,\n", - " 'internal_name': 'fda-userdb-airquality',\n", - " 'is_public': None,\n", - " 'name': 'Airquality'}\n" - ] - } - ], - "source": [ - "response = container.modify({\n", - " \"action\": \"start\"\n", - "}, container_id)\n", - "time.sleep(5)\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Create database" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 118, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'container': {'created': datetime.datetime(2022, 7, 26, 13, 5, 36, 339000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'hash': '5dc114629602472d0bb93f4aedd6a38f161dd59ed129247511c83767fcba0a19',\n", - " 'id': 1,\n", - " 'internal_name': 'fda-userdb-airquality',\n", - " 'is_public': None,\n", - " 'name': 'Airquality'},\n", - " 'created': datetime.datetime(2022, 7, 26, 13, 5, 42, 453000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'description': 'Hourly measurements in Zürich, Switzerland',\n", - " 'engine': 'mariadb:10.5',\n", - " 'id': 1,\n", - " 'is_public': True,\n", - " 'name': 'Airquality'}\n" - ] - } - ], - "source": [ - "response = database.create({\n", - " \"name\": \"Airquality\",\n", - " \"description\": \"Hourly measurements in Zürich, Switzerland\",\n", - " \"is_public\": True\n", - "}, container_id)\n", - "database_id = response.id\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Inspect database" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 119, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'contact': None,\n", - " 'container': {'created': datetime.datetime(2022, 7, 26, 13, 5, 36, 339000, tzinfo=tzutc()),\n", - " 'databases': None,\n", - " 'hash': '5dc114629602472d0bb93f4aedd6a38f161dd59ed129247511c83767fcba0a19',\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': 35358,\n", - " 'state': None},\n", - " 'created': datetime.datetime(2022, 7, 26, 13, 5, 42, 453000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'containers': None,\n", - " 'databases': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'identifiers': None,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'deleted': None,\n", - " 'description': 'Hourly measurements in Zürich, Switzerland',\n", - " 'exchange': 'airquality',\n", - " 'id': 1,\n", - " 'image': {'compiled': None,\n", - " 'date_formats': [{'created_at': datetime.datetime(2022, 7, 26, 13, 1, 15, 747000, 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, 7, 26, 13, 1, 15, 753000, 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, 7, 26, 13, 1, 15, 756000, 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, 7, 26, 13, 1, 15, 759000, 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, 7, 26, 13, 1, 15, 762000, 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, 7, 26, 13, 1, 15, 765000, 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, 7, 26, 13, 1, 15, 767000, 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, 7, 26, 13, 1, 15, 770000, tzinfo=tzutc()),\n", - " 'database_format': '%d.%c.%Y %H:%i:%S',\n", - " 'example': '30.01.2022 13:44:25',\n", - " 'has_time': True,\n", - " 'id': 8,\n", - " 'unix_format': 'dd.MM.yyyy HH:mm:ss'}],\n", - " 'default_port': 3306,\n", - " 'dialect': 'org.hibernate.dialect.MariaDBDialect',\n", - " 'driver_class': 'org.mariadb.jdbc.Driver',\n", - " 'environment': [{'iid': 1,\n", - " 'key': 'ROOT',\n", - " 'type': 'PRIVILEGED_USERNAME',\n", - " 'value': 'root'},\n", - " {'iid': 1,\n", - " 'key': 'MARIADB_ROOT_PASSWORD',\n", - " 'type': 'PRIVILEGED_PASSWORD',\n", - " 'value': 'mariadb'},\n", - " {'iid': 1,\n", - " 'key': 'MARIADB_USER',\n", - " 'type': 'USERNAME',\n", - " 'value': 'mariadb'},\n", - " {'iid': 1,\n", - " 'key': 'MARIADB_PASSWORD',\n", - " 'type': 'PASSWORD',\n", - " 'value': 'mariadb'}],\n", - " 'hash': None,\n", - " 'id': 1,\n", - " 'jdbc_method': 'mariadb',\n", - " 'repository': 'mariadb',\n", - " 'size': None,\n", - " 'tag': '10.5'},\n", - " 'internal_name': 'airquality',\n", - " 'is_public': True,\n", - " 'language': None,\n", - " 'license': None,\n", - " 'name': 'Airquality',\n", - " 'publication': None,\n", - " 'publisher': None,\n", - " 'subjects': [],\n", - " 'tables': []}\n" - ] - } - ], - "source": [ - "response = database.find_by_id(container_id, database_id)\n", - "database_exchange = response.exchange\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Add metadata to database" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 120, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'container': {'created': datetime.datetime(2022, 7, 26, 13, 5, 36, 339000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'hash': '5dc114629602472d0bb93f4aedd6a38f161dd59ed129247511c83767fcba0a19',\n", - " 'id': 1,\n", - " 'internal_name': 'fda-userdb-airquality',\n", - " 'is_public': None,\n", - " 'name': 'Airquality'},\n", - " 'created': datetime.datetime(2022, 7, 26, 13, 5, 42, 453000, tzinfo=tzutc()),\n", - " 'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'description': 'This dataset includes daily values from 1983 to the current '\n", - " 'day, divided into annual files. This includes the maximum '\n", - " 'hourly average and the number of times the hourly average '\n", - " 'limit value for ozone was exceeded and the daily averages for '\n", - " 'sulfur dioxide (SO2), carbon monoxide (CO), nitrogen oxide '\n", - " '(NOx), nitrogen monoxide (NO), nitrogen dioxide (NO2), '\n", - " 'particulate matter (PM10 and PM2.5). ) and particle number '\n", - " '(PN), provided that they are of sufficient quality. The '\n", - " 'values of the completed day for the current year are updated '\n", - " 'every 30 minutes after midnight (UTC+1).',\n", - " 'engine': 'mariadb:10.5',\n", - " 'id': 1,\n", - " 'is_public': True,\n", - " 'name': 'Airquality'}\n" - ] - } - ], - "source": [ - "response = database.update({\n", - " \"description\": \"This dataset includes daily values from 1983 to the current day, divided into annual files. This includes the maximum hourly average and the number of times the hourly average limit value for ozone was exceeded and the daily averages for sulfur dioxide (SO2), carbon monoxide (CO), nitrogen oxide (NOx), nitrogen monoxide (NO), nitrogen dioxide (NO2), particulate matter (PM10 and PM2.5). ) and particle number (PN), provided that they are of sufficient quality. The values of the completed day for the current year are updated every 30 minutes after midnight (UTC+1).\",\n", - " \"publisher\": \"Technical University of Vienna\",\n", - " \"license\": {\n", - " \"identifier\": \"CC0-1.0\",\n", - " \"uri\": \"https://creativecommons.org/publicdomain/zero/1.0/legalcode\"\n", - " },\n", - " \"language\": \"en\",\n", - " \"is_public\": True,\n", - " \"publication\": \"2022-07-19\"\n", - "}, container_id, database_id)\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Create table" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 121, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'creator': {'affiliation': None,\n", - " 'authorities': None,\n", - " 'email': 'someone@example.com',\n", - " 'email_verified': False,\n", - " 'firstname': None,\n", - " 'id': 2,\n", - " 'lastname': None,\n", - " 'orcid': None,\n", - " 'theme_dark': False,\n", - " 'titles_after': None,\n", - " 'titles_before': None,\n", - " 'username': 'user'},\n", - " 'id': 1,\n", - " 'internal_name': 'airquality_a7a429a6-0ce3-11ed-9462-8c8caada74c3',\n", - " 'name': 'Airquality a7a429a6-0ce3-11ed-9462-8c8caada74c3'}\n" - ] - } - ], - "source": [ - "response = table.create({\n", - " \"name\": \"Airquality \" + str(uuid.uuid1()),\n", - " \"description\": \"Airquality in Zürich, Switzerland\",\n", - " \"columns\": [{\n", - " \"name\": \"Date\",\n", - " \"type\": \"date\",\n", - " \"dfid\": 1,\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }, {\n", - " \"name\": \"Location\",\n", - " \"type\": \"string\",\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }, {\n", - " \"name\": \"Parameter\",\n", - " \"type\": \"string\",\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }, {\n", - " \"name\": \"Interval\",\n", - " \"type\": \"string\",\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }, {\n", - " \"name\": \"Unit\",\n", - " \"type\": \"string\",\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }, {\n", - " \"name\": \"Value\",\n", - " \"type\": \"decimal\",\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }, {\n", - " \"name\": \"Status\",\n", - " \"type\": \"string\",\n", - " \"unique\": False,\n", - " \"primary_key\": False,\n", - " \"null_allowed\": True,\n", - " }]\n", - "}, container_id, database_id)\n", - "table_id = response.id\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Inspect table" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 122, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'columns': [{'auto_generated': True,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'number',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 1,\n", - " 'internal_name': 'id',\n", - " 'is_null_allowed': False,\n", - " 'is_primary_key': True,\n", - " 'name': 'id',\n", - " 'references': None,\n", - " 'unique': True},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'date',\n", - " 'date_format': {'created_at': datetime.datetime(2022, 7, 26, 13, 1, 15, 747000, 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", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 2,\n", - " 'internal_name': 'date',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Date',\n", - " 'references': None,\n", - " 'unique': False},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'string',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 3,\n", - " 'internal_name': 'location',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Location',\n", - " 'references': None,\n", - " 'unique': False},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'string',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 4,\n", - " 'internal_name': 'parameter',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Parameter',\n", - " 'references': None,\n", - " 'unique': False},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'string',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 5,\n", - " 'internal_name': 'interval',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Interval',\n", - " 'references': None,\n", - " 'unique': False},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'string',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 6,\n", - " 'internal_name': 'unit',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Unit',\n", - " 'references': None,\n", - " 'unique': False},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'decimal',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 7,\n", - " 'internal_name': 'value',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Value',\n", - " 'references': None,\n", - " 'unique': False},\n", - " {'auto_generated': False,\n", - " 'check_expression': None,\n", - " 'column_concept': None,\n", - " 'column_type': 'string',\n", - " 'date_format': None,\n", - " 'decimal_digits_after': None,\n", - " 'decimal_digits_before': None,\n", - " 'enum_values': [],\n", - " 'foreign_key': None,\n", - " 'id': 8,\n", - " 'internal_name': 'status',\n", - " 'is_null_allowed': True,\n", - " 'is_primary_key': False,\n", - " 'name': 'Status',\n", - " 'references': None,\n", - " 'unique': False}],\n", - " 'created': datetime.datetime(2022, 7, 26, 13, 5, 43, 660000, tzinfo=tzutc()),\n", - " 'description': 'Airquality in Zürich, Switzerland',\n", - " 'id': 1,\n", - " 'internal_name': 'airquality_a7a429a6-0ce3-11ed-9462-8c8caada74c3',\n", - " 'name': 'Airquality a7a429a6-0ce3-11ed-9462-8c8caada74c3',\n", - " 'topic': 'airquality_a7a429a6-0ce3-11ed-9462-8c8caada74c3'}\n" - ] - } - ], - "source": [ - "response = table.find_by_id(container_id, database_id, table_id)\n", - "table_internal_name = response.internal_name\n", - "table_topic = response.topic\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Import .csv" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 123, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "None\n" - ] - } - ], - "source": [ - "shutil.copyfile(os.getcwd() + \"/resources/ugz_ogd_air_h1_2021.csv\", \"/tmp/ugz_ogd_air_h1_2021.csv\")\n", - "response = data.import_csv({\n", - " \"location\": \"/tmp/ugz_ogd_air_h1_2021.csv\",\n", - " \"separator\": \",\",\n", - " \"quote\": \"\\\"\",\n", - " \"skip_lines\": 1\n", - "}, container_id, database_id, table_id)\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Create subset" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 124, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'id': 1,\n", - " 'result': [{'date': '2021-01-01T00:00:00Z',\n", - " 'interval': 'h1',\n", - " 'location': 'Schimmelstrasse',\n", - " 'parameter': 'NOx',\n", - " 'status': 'tentative',\n", - " 'unit': 'ppb',\n", - " 'value': 41.66},\n", - " {'date': '2021-01-01T00:00:00Z',\n", - " 'interval': 'h1',\n", - " 'location': 'Schimmelstrasse',\n", - " 'parameter': 'NO',\n", - " 'status': 'tentative',\n", - " 'unit': 'µg/m3',\n", - " 'value': 21.64},\n", - " {'date': '2021-01-01T00:00:00Z',\n", - " 'interval': 'h1',\n", - " 'location': 'Schimmelstrasse',\n", - " 'parameter': 'NO2',\n", - " 'status': 'tentative',\n", - " 'unit': 'µg/m3',\n", - " 'value': 46.49}],\n", - " 'result_number': 52548}\n" - ] - } - ], - "source": [ - "response = query.execute({\n", - " \"statement\": \"select `date`, `location`, `parameter`, `interval`, `unit`, `value`, `status` from `\" + table_internal_name + \"` where `location` = 'Schimmelstrasse'\"\n", - "}, container_id, database_id, page=0, size=3)\n", - "query_id = response.id\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "Create invalid subset" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 125, - "outputs": [ - { - "ename": "ApiException", - "evalue": "(400)\nReason: Bad Request\nHTTP response headers: HTTPHeaderDict({'transfer-encoding': 'chunked', 'Vary': 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers', 'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block', 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'X-Frame-Options': 'DENY', 'Content-Type': 'application/json', 'Date': 'Tue, 26 Jul 2022 13:05:46 GMT'})\nHTTP response body: b'{\"status\":\"BAD_REQUEST\",\"message\":\"Failed to execute and map time-versioned query\",\"code\":\"error.table.malformed\"}'\n", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mApiException\u001B[0m Traceback (most recent call last)", - "Input \u001B[0;32mIn [125]\u001B[0m, in \u001B[0;36m<cell line: 1>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mquery\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mexecute\u001B[49m\u001B[43m(\u001B[49m\u001B[43m{\u001B[49m\n\u001B[1;32m 2\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mstatement\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mselect `date`, `location`, `parameter`, `interval`, `unit`, `value`, `status` from `\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m \u001B[49m\u001B[38;5;241;43m+\u001B[39;49m\u001B[43m \u001B[49m\u001B[43mtable_internal_name\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m+\u001B[39;49m\u001B[43m \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43m` where `foo` = \u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mbar\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\n\u001B[1;32m 3\u001B[0m \u001B[43m}\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcontainer_id\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mdatabase_id\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mpage\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m0\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43msize\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m3\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m 4\u001B[0m \u001B[38;5;28mprint\u001B[39m(response)\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/api/query_endpoint_api.py:57\u001B[0m, in \u001B[0;36mQueryEndpointApi.execute\u001B[0;34m(self, body, id, database_id, **kwargs)\u001B[0m\n\u001B[1;32m 55\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mexecute_with_http_info(body, \u001B[38;5;28mid\u001B[39m, database_id, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs) \u001B[38;5;66;03m# noqa: E501\u001B[39;00m\n\u001B[1;32m 56\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m---> 57\u001B[0m (data) \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mexecute_with_http_info\u001B[49m\u001B[43m(\u001B[49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43mid\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mdatabase_id\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m \u001B[38;5;66;03m# noqa: E501\u001B[39;00m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m data\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/api/query_endpoint_api.py:140\u001B[0m, in \u001B[0;36mQueryEndpointApi.execute_with_http_info\u001B[0;34m(self, body, id, database_id, **kwargs)\u001B[0m\n\u001B[1;32m 137\u001B[0m \u001B[38;5;66;03m# Authentication setting\u001B[39;00m\n\u001B[1;32m 138\u001B[0m auth_settings \u001B[38;5;241m=\u001B[39m [\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mbearerAuth\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;66;03m# noqa: E501\u001B[39;00m\n\u001B[0;32m--> 140\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mapi_client\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcall_api\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 141\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m/api/container/\u001B[39;49m\u001B[38;5;132;43;01m{id}\u001B[39;49;00m\u001B[38;5;124;43m/database/\u001B[39;49m\u001B[38;5;132;43;01m{databaseId}\u001B[39;49;00m\u001B[38;5;124;43m/query\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mPUT\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 142\u001B[0m \u001B[43m \u001B[49m\u001B[43mpath_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 143\u001B[0m \u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 144\u001B[0m \u001B[43m \u001B[49m\u001B[43mheader_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 145\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbody_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 146\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mform_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 147\u001B[0m \u001B[43m \u001B[49m\u001B[43mfiles\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mlocal_var_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 148\u001B[0m \u001B[43m \u001B[49m\u001B[43mresponse_type\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mQueryResultDto\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;66;43;03m# noqa: E501\u001B[39;49;00m\n\u001B[1;32m 149\u001B[0m \u001B[43m \u001B[49m\u001B[43mauth_settings\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mauth_settings\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 150\u001B[0m \u001B[43m \u001B[49m\u001B[43masync_req\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43masync_req\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 151\u001B[0m \u001B[43m \u001B[49m\u001B[43m_return_http_data_only\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m_return_http_data_only\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 152\u001B[0m \u001B[43m \u001B[49m\u001B[43m_preload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m_preload_content\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mTrue\u001B[39;49;00m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 153\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_timeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m_request_timeout\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 154\u001B[0m \u001B[43m \u001B[49m\u001B[43mcollection_formats\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcollection_formats\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/api_client.py:316\u001B[0m, in \u001B[0;36mApiClient.call_api\u001B[0;34m(self, resource_path, method, path_params, query_params, header_params, body, post_params, files, response_type, auth_settings, async_req, _return_http_data_only, collection_formats, _preload_content, _request_timeout)\u001B[0m\n\u001B[1;32m 279\u001B[0m \u001B[38;5;124;03m\"\"\"Makes the HTTP request (synchronous) and returns deserialized data.\u001B[39;00m\n\u001B[1;32m 280\u001B[0m \n\u001B[1;32m 281\u001B[0m \u001B[38;5;124;03mTo make an async request, set the async_req parameter.\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 313\u001B[0m \u001B[38;5;124;03m then the method will return the response directly.\u001B[39;00m\n\u001B[1;32m 314\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 315\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m async_req:\n\u001B[0;32m--> 316\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m__call_api\u001B[49m\u001B[43m(\u001B[49m\u001B[43mresource_path\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 317\u001B[0m \u001B[43m \u001B[49m\u001B[43mpath_params\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mheader_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 318\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mfiles\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 319\u001B[0m \u001B[43m \u001B[49m\u001B[43mresponse_type\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mauth_settings\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 320\u001B[0m \u001B[43m \u001B[49m\u001B[43m_return_http_data_only\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcollection_formats\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 321\u001B[0m \u001B[43m \u001B[49m\u001B[43m_preload_content\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m_request_timeout\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 322\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m 323\u001B[0m thread \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mpool\u001B[38;5;241m.\u001B[39mapply_async(\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m__call_api, (resource_path,\n\u001B[1;32m 324\u001B[0m method, path_params, query_params,\n\u001B[1;32m 325\u001B[0m header_params, body,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 329\u001B[0m collection_formats,\n\u001B[1;32m 330\u001B[0m _preload_content, _request_timeout))\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/api_client.py:148\u001B[0m, in \u001B[0;36mApiClient.__call_api\u001B[0;34m(self, resource_path, method, path_params, query_params, header_params, body, post_params, files, response_type, auth_settings, _return_http_data_only, collection_formats, _preload_content, _request_timeout)\u001B[0m\n\u001B[1;32m 145\u001B[0m url \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mconfiguration\u001B[38;5;241m.\u001B[39mhost \u001B[38;5;241m+\u001B[39m resource_path\n\u001B[1;32m 147\u001B[0m \u001B[38;5;66;03m# perform request and return response\u001B[39;00m\n\u001B[0;32m--> 148\u001B[0m response_data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 149\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mquery_params\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheader_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 150\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mpost_params\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 151\u001B[0m \u001B[43m \u001B[49m\u001B[43m_preload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_preload_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 152\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_timeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_timeout\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 154\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mlast_response \u001B[38;5;241m=\u001B[39m response_data\n\u001B[1;32m 156\u001B[0m return_data \u001B[38;5;241m=\u001B[39m response_data\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/api_client.py:366\u001B[0m, in \u001B[0;36mApiClient.request\u001B[0;34m(self, method, url, query_params, headers, post_params, body, _preload_content, _request_timeout)\u001B[0m\n\u001B[1;32m 358\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mrest_client\u001B[38;5;241m.\u001B[39mPOST(url,\n\u001B[1;32m 359\u001B[0m query_params\u001B[38;5;241m=\u001B[39mquery_params,\n\u001B[1;32m 360\u001B[0m headers\u001B[38;5;241m=\u001B[39mheaders,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 363\u001B[0m _request_timeout\u001B[38;5;241m=\u001B[39m_request_timeout,\n\u001B[1;32m 364\u001B[0m body\u001B[38;5;241m=\u001B[39mbody)\n\u001B[1;32m 365\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m method \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mPUT\u001B[39m\u001B[38;5;124m\"\u001B[39m:\n\u001B[0;32m--> 366\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrest_client\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mPUT\u001B[49m\u001B[43m(\u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 367\u001B[0m \u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mquery_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 368\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 369\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mpost_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 370\u001B[0m \u001B[43m \u001B[49m\u001B[43m_preload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_preload_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 371\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_timeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_timeout\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 372\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 373\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m method \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mPATCH\u001B[39m\u001B[38;5;124m\"\u001B[39m:\n\u001B[1;32m 374\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mrest_client\u001B[38;5;241m.\u001B[39mPATCH(url,\n\u001B[1;32m 375\u001B[0m query_params\u001B[38;5;241m=\u001B[39mquery_params,\n\u001B[1;32m 376\u001B[0m headers\u001B[38;5;241m=\u001B[39mheaders,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 379\u001B[0m _request_timeout\u001B[38;5;241m=\u001B[39m_request_timeout,\n\u001B[1;32m 380\u001B[0m body\u001B[38;5;241m=\u001B[39mbody)\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/rest.py:273\u001B[0m, in \u001B[0;36mRESTClientObject.PUT\u001B[0;34m(self, url, headers, query_params, post_params, body, _preload_content, _request_timeout)\u001B[0m\n\u001B[1;32m 271\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mPUT\u001B[39m(\u001B[38;5;28mself\u001B[39m, url, headers\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, query_params\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, post_params\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m,\n\u001B[1;32m 272\u001B[0m body\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, _preload_content\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m, _request_timeout\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m):\n\u001B[0;32m--> 273\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mPUT\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 274\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 275\u001B[0m \u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mquery_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 276\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mpost_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 277\u001B[0m \u001B[43m \u001B[49m\u001B[43m_preload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_preload_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 278\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_timeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_timeout\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 279\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/Projects/fda-services/.jupyter/api_query/rest.py:222\u001B[0m, in \u001B[0;36mRESTClientObject.request\u001B[0;34m(self, method, url, query_params, headers, body, post_params, _preload_content, _request_timeout)\u001B[0m\n\u001B[1;32m 219\u001B[0m logger\u001B[38;5;241m.\u001B[39mdebug(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mresponse body: \u001B[39m\u001B[38;5;132;01m%s\u001B[39;00m\u001B[38;5;124m\"\u001B[39m, r\u001B[38;5;241m.\u001B[39mdata)\n\u001B[1;32m 221\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;241m200\u001B[39m \u001B[38;5;241m<\u001B[39m\u001B[38;5;241m=\u001B[39m r\u001B[38;5;241m.\u001B[39mstatus \u001B[38;5;241m<\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m299\u001B[39m:\n\u001B[0;32m--> 222\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m ApiException(http_resp\u001B[38;5;241m=\u001B[39mr)\n\u001B[1;32m 224\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m r\n", - "\u001B[0;31mApiException\u001B[0m: (400)\nReason: Bad Request\nHTTP response headers: HTTPHeaderDict({'transfer-encoding': 'chunked', 'Vary': 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers', 'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block', 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'X-Frame-Options': 'DENY', 'Content-Type': 'application/json', 'Date': 'Tue, 26 Jul 2022 13:05:46 GMT'})\nHTTP response body: b'{\"status\":\"BAD_REQUEST\",\"message\":\"Failed to execute and map time-versioned query\",\"code\":\"error.table.malformed\"}'\n" - ] - } - ], - "source": [ - "response = query.execute({\n", - " \"statement\": \"select `date`, `location`, `parameter`, `interval`, `unit`, `value`, `status` from `\" + table_internal_name + \"` where `foo` = 'bar'\"\n", - "}, container_id, database_id, page=0, size=3)\n", - "print(response)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java index aa3e3a1cf0..0ea6bd53e6 100644 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java @@ -155,6 +155,17 @@ public class UserEndpoint { .body(userMapper.userToUserDto(entity)); } + @PutMapping("/{id}/theme") + @Transactional + @PreAuthorize("hasPermission(#id, 'UPDATE_THEME')") + @Operation(summary = "Update user theme", security = @SecurityRequirement(name = "bearerAuth")) + public ResponseEntity<Void> updateTheme(@NotNull @PathVariable("id") Long id, + @NotNull @Valid @RequestBody UserThemeSetDto data) throws UserNotFoundException { + userService.updateTheme(id, data); + return ResponseEntity.accepted() + .build(); + } + @PutMapping("/{id}/password") @Transactional @PreAuthorize("hasRole('ROLE_DEVELOPER') or hasPermission(#id, 'UPDATE_PASSWORD')") diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java index d7877d78a2..695ff08f25 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java @@ -88,6 +88,15 @@ public interface UserService { User updateRoles(Long id, UserRolesDto data) throws UserNotFoundException, RoleNotFoundException, RoleUniqueException; + /** + * Sets the theme for the provided user. + * + * @param id The user id. + * @param data The theme. + * @throws UserNotFoundException The user was not found. + */ + void updateTheme(Long id, UserThemeSetDto data) throws UserNotFoundException; + /** * Updates a user with the given id and updated password. * diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java index e508c53cbd..4ab0e32aa2 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java @@ -19,7 +19,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.validation.ConstraintViolationException; -import java.security.Principal; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -212,6 +211,18 @@ public class UserServiceImpl implements UserService { return entity; } + @Override + @Transactional + public void updateTheme(Long id, UserThemeSetDto data) throws UserNotFoundException { + /* check */ + final User user = find(id); + /* save */ + user.setThemeDark(data.getThemeDark()); + final User entity = userRepository.save(user); + log.info("Updated user with id {}", entity.getId()); + log.debug("updated user {}", entity); + } + @Override @Transactional public User updatePassword(Long id, UserPasswordDto data) throws UserNotFoundException { diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java b/fda-identifier-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java index 715611d99b..aadd8f2132 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/gateway/QueryServiceGateway.java @@ -21,6 +21,5 @@ public interface QueryServiceGateway { * @throws RemoteUnavailableException The remote service is not available. */ QueryDto find(Long containerId, Long databaseId, IdentifierCreateDto identifier, String authorization) - throws QueryNotFoundException, - RemoteUnavailableException; + throws QueryNotFoundException, RemoteUnavailableException; } diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java index f96736a2e8..6058d35080 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java @@ -126,6 +126,7 @@ public class IdentifierServiceImpl implements IdentifierService { relatedIdentifierRepository.save(id); }); } + entity.setQueryNormalized(query.getQueryNormalized()); final Identifier identifier = identifierRepository.save(entity); log.info("Created identifier with id {}", identifier.getId()); log.debug("created identifier {}", identifier); diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserBriefDto.java index 1b27ee0e22..464843d2fb 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserBriefDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserBriefDto.java @@ -1,12 +1,10 @@ package at.tuwien.api.user; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.Parameter; import lombok.*; import javax.validation.constraints.NotNull; -import java.util.List; @Getter @Setter @@ -20,10 +18,6 @@ public class UserBriefDto { @Parameter(name = "id") private Long id; - @ToString.Exclude - @Parameter(name = "user authorities") - private List<GrantedAuthorityDto> authorities; - @NotNull @Parameter(name = "user name") private String username; @@ -53,12 +47,4 @@ public class UserBriefDto { @Parameter(name = "theme dark") private Boolean themeDark; - @NotNull - @Parameter(name = "mail address") - private String email; - - @JsonProperty("email_verified") - @Parameter(name = "mail address verified") - private Boolean emailVerified; - } diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java new file mode 100644 index 0000000000..e329522dcd --- /dev/null +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java @@ -0,0 +1,22 @@ +package at.tuwien.api.user; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Getter +@Setter +@ToString +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserThemeSetDto { + + @NotNull + @JsonProperty("theme_dark") + @Parameter(name = "theme dark") + private Boolean themeDark; +} diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java index ecff259995..e7fb05063f 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java @@ -37,9 +37,4 @@ public class UserUpdateDto { @Parameter(name = "orcid") private String orcid; - @NotNull - @JsonProperty("theme_dark") - @Parameter(name = "theme dark") - private Boolean themeDark; - } 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 65a1b63b16..d065595722 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 @@ -41,7 +41,7 @@ public abstract class AbstractEndpoint { return true; } /* view-only operations are allowed on public databases */ - if (database.getIsPublic() && List.of("DATA_EXPORT", "DATA_VIEW", "DATA_HISTORY").contains(permissionCode)) { + if (database.getIsPublic() && List.of("DATA_EXPORT", "DATA_VIEW", "DATA_HISTORY", "QUERY_VIEW_ALL").contains(permissionCode)) { log.debug("grant permission {} because database is public", permissionCode); return true; } @@ -76,7 +76,7 @@ public abstract class AbstractEndpoint { return true; } /* view-only operations are allowed on public databases */ - if (database.getIsPublic() && List.of("QUERY_VIEW_ALL", "QUERY_VIEW", "QUERY_EXPORT").contains( + if (database.getIsPublic() && List.of("QUERY_VIEW_ALL", "QUERY_VIEW", "QUERY_EXPORT", "QUERY_RE_EXECUTE").contains( permissionCode)) { log.debug("grant permission {} because database is public", permissionCode); return true; diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java index 9fdb2e03a6..783f2242a4 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java @@ -165,6 +165,7 @@ public interface StoreMapper { .lastModified(data.getTimestamp(7) != null ? data.getTimestamp(7) .toInstant() : null) .query(data.getString(8)) + .queryNormalized(data.getString(8)) .queryHash(data.getString(9)) .resultHash(data.getString(10) != null ? data.getString(10) : null) .resultNumber(data.getLong(11)) diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java index 64dd9d603c..159549e16c 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java @@ -177,9 +177,8 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService @Override @Transactional(readOnly = true) public ExportResource findOne(Long containerId, Long databaseId, Long queryId) - throws DatabaseNotFoundException, ImageNotSupportedException, - ContainerNotFoundException, FileStorageException, QueryStoreException, QueryNotFoundException, - QueryMalformedException, DatabaseConnectionException { + throws DatabaseNotFoundException, ImageNotSupportedException, FileStorageException, QueryStoreException, + QueryNotFoundException, QueryMalformedException, DatabaseConnectionException { final String filename = RandomStringUtils.randomAlphabetic(40) + ".csv"; /* find */ final Database database = databaseService.find(containerId, databaseId); @@ -210,8 +209,8 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService @Override @Transactional public Long count(Long containerId, Long databaseId, Long tableId, Instant timestamp) - throws DatabaseNotFoundException, TableNotFoundException, - ImageNotSupportedException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException { + throws DatabaseNotFoundException, TableNotFoundException, ImageNotSupportedException, + DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException { /* find */ final Database database = databaseService.find(containerId, databaseId); final Table table = tableService.find(containerId, databaseId, tableId); @@ -277,7 +276,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService for (int i = 0; i < data.getKeys().size(); i++) { preparedStatement.setObject(i, values.get(i)); } - } catch(SQLException e) { + } catch (SQLException e) { log.error("Failed to delete tuples"); throw new TableMalformedException("Failed to delete tuples"); } finally { @@ -306,7 +305,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService .executeUpdate(); queryMapper.dropTemporaryTableSQL(connection, table) .executeUpdate(); - } catch(SQLException e) { + } catch (SQLException e) { log.error("Failed to create/insert/drop temporary table"); throw new TableMalformedException("Failed to create/insert/drop temporary table"); } finally { diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue index 292f620d62..132f3ead0c 100644 --- a/fda-ui/layouts/default.vue +++ b/fda-ui/layouts/default.vue @@ -1,5 +1,5 @@ <template> - <v-app dark> + <v-app> <v-navigation-drawer v-model="drawer" fixed app> <v-img contain @@ -104,6 +104,10 @@ export default { data () { return { drawer: false, + user: { + theme_dark: null + }, + loadingUser: true, items: [ { icon: mdiHome, @@ -157,9 +161,6 @@ export default { username () { return this.$store.state.user && this.$store.state.user.username }, - nextTheme () { - return this.$vuetify.theme.dark ? 'Light' : 'Dark' - }, container () { return this.$store.state.container }, @@ -171,15 +172,29 @@ export default { }, db () { return this.$store.state.db + }, + config () { + if (this.token === null) { + return {} + } + return { + headers: { Authorization: `Bearer ${this.token}` } + } } }, watch: { $route () { this.loadDB() + if (this.token) { + this.loadUser() + .then(() => this.setTheme()) + } } }, mounted () { this.loadDB() + this.loadUser() + .then(() => this.setTheme()) }, methods: { logout () { @@ -197,6 +212,25 @@ export default { console.error('Failed to load database', err) } } + }, + async loadUser () { + if (!this.token) { + return + } + try { + this.loadingUser = true + const res = await this.$axios.put('/api/auth', {}, this.config) + console.debug('user data', res.data) + this.user = res.data + } catch (err) { + console.error('user data', err) + this.$toast.error('Failed to load user') + this.error = true + } + this.loadingUser = false + }, + setTheme () { + this.$vuetify.theme.dark = this.user.theme_dark } } } diff --git a/fda-ui/pages/container/_container_id/database/_database_id/sql.vue b/fda-ui/pages/container/_container_id/database/_database_id/sql.vue deleted file mode 100644 index b11cc07697..0000000000 --- a/fda-ui/pages/container/_container_id/database/_database_id/sql.vue +++ /dev/null @@ -1,26 +0,0 @@ -<template> - <div> - <h3> - SQL - </h3> - ... - </div> -</template> -<script> - -export default { - name: 'SQL', - components: { - }, - data () { - return {} - }, - mounted () { - }, - methods: { - } -} -</script> - -<style> -</style> diff --git a/fda-ui/pages/container/index.vue b/fda-ui/pages/container/index.vue index ab3e7495d4..b855d8dda5 100644 --- a/fda-ui/pages/container/index.vue +++ b/fda-ui/pages/container/index.vue @@ -18,6 +18,7 @@ <thead> <tr> <th>Name</th> + <th>Visibility</th> <th>Engine</th> <th>Creator</th> <th>Created</th> @@ -25,7 +26,7 @@ </thead> <tbody> <tr v-if="containers.length === 0" aria-readonly="true"> - <td colspan="4"> + <td colspan="5"> <span v-if="!loading">(no databases)</span> </td> </tr> @@ -35,6 +36,13 @@ class="database" @click="loadDatabase(item)"> <td>{{ item.name }}</td> + <td> + <v-skeleton-loader v-if="!item.database" type="text" width="50" class="mt-1" /> + <span v-if="item.database"> + <span v-if="item.database.is_public">Public</span> + <span v-if="!item.database.is_public">Private <v-icon right>mdi-eye-off</v-icon></span> + </span> + </td> <td> <span v-if="item.database">{{ item.database.engine }}</span> <v-skeleton-loader v-if="!item.database" type="text" width="100" class="mt-1" /> diff --git a/fda-ui/pages/login.vue b/fda-ui/pages/login.vue index b4eddfafe1..07539ffc0f 100644 --- a/fda-ui/pages/login.vue +++ b/fda-ui/pages/login.vue @@ -87,10 +87,9 @@ export default { this.$refs.form.validate() }, async login () { - const url = '/api/auth' try { this.loading = true - const res = await this.$axios.post(url, this.loginAccount) + const res = await this.$axios.post('/api/auth', this.loginAccount) console.debug('login user', res.data) this.$store.commit('SET_TOKEN', res.data.token) const user = { ...res.data } diff --git a/fda-ui/pages/user/index.vue b/fda-ui/pages/user/index.vue index b90d1ffd84..b6222f1bf7 100644 --- a/fda-ui/pages/user/index.vue +++ b/fda-ui/pages/user/index.vue @@ -114,14 +114,6 @@ label="ORCID" /> </v-col> </v-row> - <v-row dense> - <v-col cols="5"> - <v-switch - v-model="user.theme_dark" - inset - label="Dark Mode" /> - </v-col> - </v-row> <v-row dense> <v-col cols="5"> <v-btn @@ -164,6 +156,23 @@ <pre>{{ $refs.form3 }}</pre> </v-form> </v-card-text> + <v-divider /> + <v-card-title>Theme</v-card-title> + <v-card-text> + <v-form v-model="valid4" @submit.prevent="submit"> + <v-row dense> + <v-col cols="5"> + <v-switch + v-model="user.theme_dark" + inset + label="Dark Mode" + :disabled="error" + :loading="loadingTheme" + @click="toggleTheme" /> + </v-col> + </v-row> + </v-form> + </v-card-text> </v-card> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> @@ -198,6 +207,7 @@ export default { { text: 'Databases', to: '/container', activeClass: '' } ], loading: false, + loadingTheme: false, error: false } }, @@ -275,7 +285,7 @@ export default { async changePassword () { try { this.loading = true - const res = await this.$axios.put('/api/user/' + this.user.id + '/password', this.reset, this.config) + const res = await this.$axios.put(`/api/user/${this.user.id}/password`, this.reset, this.config) console.debug('password', res.data) this.error = false this.$toast.success('Successfully changed the password') @@ -288,7 +298,7 @@ export default { async updateInfo () { try { this.loading = true - const res = await this.$axios.put('/api/user/' + this.user.id, { + const res = await this.$axios.put(`/api/user/${this.user.id}`, { titles_before: this.user.titles_before, titles_after: this.user.titles_after, firstname: this.user.firstname, @@ -305,6 +315,24 @@ export default { this.error = true } this.loading = false + }, + async toggleTheme () { + if (this.loading) { + return + } + try { + this.loadingTheme = true + const res = await this.$axios.put(`/api/user/${this.user.id}/theme`, { + theme_dark: this.user.theme_dark + }, this.config) + console.debug('theme set', res.data) + this.$vuetify.theme.dark = this.user.theme_dark + } catch (err) { + console.error('theme set', err) + this.$toast.error('Failed to update theme') + this.error = true + } + this.loadingTheme = false } } } -- GitLab