From 8b43416e4c893b256bc6fc8f1e07f7c00f8209e9 Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 10 Jun 2024 11:53:17 +0200 Subject: [PATCH 1/4] preparing novel endpoints for getlatest. Initially tried with berths, which does not have PUT/POST/DEL endpoints. --- src/server/BreCal/api/__init__.py | 34 +++++++++++++++++++++++++ src/server/BreCal/api/berths.py | 10 ++++++++ src/server/BreCal/impl/__init__.py | 2 +- src/server/BreCal/impl/berths.py | 28 ++++++++++++++++++++ src/server/BreCal/impl/history.py | 1 + src/server/BreCal/impl/login.py | 1 + src/server/BreCal/impl/notifications.py | 1 + src/server/BreCal/impl/participant.py | 1 + src/server/BreCal/impl/shipcalls.py | 1 + src/server/BreCal/impl/ships.py | 4 ++- src/server/BreCal/impl/times.py | 1 + src/server/BreCal/impl/user.py | 1 + 12 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/server/BreCal/api/__init__.py b/src/server/BreCal/api/__init__.py index e69de29..1803976 100644 --- a/src/server/BreCal/api/__init__.py +++ b/src/server/BreCal/api/__init__.py @@ -0,0 +1,34 @@ +import typing +import datetime + +# global dictionary, which informs about the latest change of a given database (e.g., 'berths') +latest_get_request_dict = {database:None for database in ["berths", "history", "notifications", "participant", "shipcalls", "ships", "times", "user"]} + +def update_latest_modification_time(key:str, modification_time:datetime.datetime)->None: + """ + This function updates the {latest_get_request_dict} inplace at the specified {key} with the defined {modification_time}, + *if* the time is more recent than the currently stored value + """ + global latest_get_request_dict + print(f"(update_latest_modification_time INFO): before executing the funtion", latest_get_request_dict, key, modification_time) + value = latest_get_request_dict.get(key,None) + if value is None: + # when there is no value stored for the key, update the value + latest_get_request_dict[key] = modification_time + else: + # when the modification date is more recent than the stored value, update it + if modification_time > value: + latest_get_request_dict[key] = modification_time + print(f"(update_latest_modification_time INFO): after executing the funtion", latest_get_request_dict, key, modification_time) + return + +def get_latest_modification_time(key:str)->typing.Optional[str]: + """ + This function returns the latest modification time in .isoformat, if a datetime is stored for the respective key in {latest_get_request_dict}. + When there has not yet been an update, this function returns None + """ + value = latest_get_request_dict.get(key,None) + if isinstance(value,datetime.datetime): + return value.isoformat() + return value # None + \ No newline at end of file diff --git a/src/server/BreCal/api/berths.py b/src/server/BreCal/api/berths.py index ba8f4ff..1c8a33a 100644 --- a/src/server/BreCal/api/berths.py +++ b/src/server/BreCal/api/berths.py @@ -16,3 +16,13 @@ def GetBerths(): return impl.berths.GetBerths(token) else: return json.dumps("not authenticated"), 403 + +@bp.route('/getlatestberths', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestBerths(): + + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.berths.GetLatestBerths(token) + else: + return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/impl/__init__.py b/src/server/BreCal/impl/__init__.py index 93ec9fc..4577c7f 100644 --- a/src/server/BreCal/impl/__init__.py +++ b/src/server/BreCal/impl/__init__.py @@ -6,4 +6,4 @@ from . import times from . import ships from . import login from . import user -from . import history \ No newline at end of file +from . import history diff --git a/src/server/BreCal/impl/berths.py b/src/server/BreCal/impl/berths.py index 9087856..cc6baf3 100644 --- a/src/server/BreCal/impl/berths.py +++ b/src/server/BreCal/impl/berths.py @@ -1,9 +1,11 @@ import json import logging import pydapper +import datetime from ..schemas import model from .. import local_db +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetBerths(token): """ @@ -14,6 +16,8 @@ def GetBerths(token): pooledConnection = local_db.getPoolConnection() commands = pydapper.using(pooledConnection) data = commands.query("SELECT id, name, `lock`, owner_id, authority_id, created, modified, deleted FROM berth WHERE deleted = 0 ORDER BY name", model=model.Berth) + # update_latest_modification_time(key="berths", modification_time=datetime.datetime.now()) + return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} except Exception as ex: @@ -28,5 +32,29 @@ def GetBerths(token): pooledConnection.close() +def GetLatestBerths(token): + """ + Returns a datetime of the latest modification within the berths database. When there has not yet been a modification, this method returns null. + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="berths") + data = {"key":"berths", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 + + + + + diff --git a/src/server/BreCal/impl/history.py b/src/server/BreCal/impl/history.py index f408001..2fa7e1c 100644 --- a/src/server/BreCal/impl/history.py +++ b/src/server/BreCal/impl/history.py @@ -7,6 +7,7 @@ from ..schemas import model from ..schemas.model import History from .. import local_db +from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetHistory(options): diff --git a/src/server/BreCal/impl/login.py b/src/server/BreCal/impl/login.py index 21b46a6..e208620 100644 --- a/src/server/BreCal/impl/login.py +++ b/src/server/BreCal/impl/login.py @@ -6,6 +6,7 @@ import bcrypt from ..schemas import model from .. import local_db from ..services import jwt_handler +from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetUser(options): diff --git a/src/server/BreCal/impl/notifications.py b/src/server/BreCal/impl/notifications.py index 67619ab..793f4c7 100644 --- a/src/server/BreCal/impl/notifications.py +++ b/src/server/BreCal/impl/notifications.py @@ -4,6 +4,7 @@ import pydapper from ..schemas import model from .. import local_db +from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetNotifications(options): """ diff --git a/src/server/BreCal/impl/participant.py b/src/server/BreCal/impl/participant.py index aad0def..174a3f5 100644 --- a/src/server/BreCal/impl/participant.py +++ b/src/server/BreCal/impl/participant.py @@ -4,6 +4,7 @@ import pydapper from ..schemas import model from .. import local_db +from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetParticipant(options): """ diff --git a/src/server/BreCal/impl/shipcalls.py b/src/server/BreCal/impl/shipcalls.py index 59f9311..c02c7c4 100644 --- a/src/server/BreCal/impl/shipcalls.py +++ b/src/server/BreCal/impl/shipcalls.py @@ -8,6 +8,7 @@ from .. import local_db from ..services.auth_guard import check_jwt from BreCal.database.update_database import evaluate_shipcall_state +from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetShipcalls(options): """ diff --git a/src/server/BreCal/impl/ships.py b/src/server/BreCal/impl/ships.py index b0b0b94..69964d8 100644 --- a/src/server/BreCal/impl/ships.py +++ b/src/server/BreCal/impl/ships.py @@ -1,9 +1,11 @@ import json import logging import pydapper +import datetime from ..schemas import model from .. import local_db +from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetShips(token): """ @@ -11,10 +13,10 @@ def GetShips(token): """ try: - pooledConnection = local_db.getPoolConnection() commands = pydapper.using(pooledConnection) data = commands.query("SELECT id, name, imo, callsign, participant_id, length, width, is_tug, bollard_pull, eni, created, modified, deleted FROM ship ORDER BY name", model=model.Ship) + update_latest_modification_time(key="ships", modification_time=datetime.datetime.now()) return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} diff --git a/src/server/BreCal/impl/times.py b/src/server/BreCal/impl/times.py index 1f16574..966599f 100644 --- a/src/server/BreCal/impl/times.py +++ b/src/server/BreCal/impl/times.py @@ -6,6 +6,7 @@ import pydapper from ..schemas import model from .. import local_db from ..services.auth_guard import check_jwt +from BreCal.api import latest_get_request_dict, update_latest_modification_time from BreCal.database.update_database import evaluate_shipcall_state diff --git a/src/server/BreCal/impl/user.py b/src/server/BreCal/impl/user.py index cd79f89..2855645 100644 --- a/src/server/BreCal/impl/user.py +++ b/src/server/BreCal/impl/user.py @@ -5,6 +5,7 @@ import bcrypt from ..schemas import model from .. import local_db +from BreCal.api import latest_get_request_dict, update_latest_modification_time def PutUser(schemaModel): """ From 02c4e6e88a5e1420af057324595f9f4ad3cd3529 Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 10 Jun 2024 15:57:52 +0200 Subject: [PATCH 2/4] creating getlatest-GET-request for each data model (except for user). --- src/server/BreCal/api/__init__.py | 11 +++++--- src/server/BreCal/api/history.py | 12 +++++++- src/server/BreCal/api/notifications.py | 13 ++++++++- src/server/BreCal/api/participant.py | 9 ++++++ src/server/BreCal/api/shipcalls.py | 9 ++++++ src/server/BreCal/api/ships.py | 10 +++++++ src/server/BreCal/api/times.py | 10 +++++++ src/server/BreCal/impl/berths.py | 3 +- src/server/BreCal/impl/history.py | 24 +++++++++++++++- src/server/BreCal/impl/notifications.py | 24 +++++++++++++++- src/server/BreCal/impl/participant.py | 24 +++++++++++++++- src/server/BreCal/impl/shipcalls.py | 30 +++++++++++++++++++- src/server/BreCal/impl/ships.py | 25 +++++++++++++++-- src/server/BreCal/impl/times.py | 37 +++++++++++++++++++++++-- 14 files changed, 226 insertions(+), 15 deletions(-) diff --git a/src/server/BreCal/api/__init__.py b/src/server/BreCal/api/__init__.py index 1803976..a8b173a 100644 --- a/src/server/BreCal/api/__init__.py +++ b/src/server/BreCal/api/__init__.py @@ -2,7 +2,8 @@ import typing import datetime # global dictionary, which informs about the latest change of a given database (e.g., 'berths') -latest_get_request_dict = {database:None for database in ["berths", "history", "notifications", "participant", "shipcalls", "ships", "times", "user"]} +# initialize all values as null (None) +latest_get_request_dict = {database:None for database in ["berths", "history", "notifications", "participants", "shipcalls", "ships", "times"]} def update_latest_modification_time(key:str, modification_time:datetime.datetime)->None: """ @@ -10,7 +11,7 @@ def update_latest_modification_time(key:str, modification_time:datetime.datetime *if* the time is more recent than the currently stored value """ global latest_get_request_dict - print(f"(update_latest_modification_time INFO): before executing the funtion", latest_get_request_dict, key, modification_time) + value = latest_get_request_dict.get(key,None) if value is None: # when there is no value stored for the key, update the value @@ -19,7 +20,7 @@ def update_latest_modification_time(key:str, modification_time:datetime.datetime # when the modification date is more recent than the stored value, update it if modification_time > value: latest_get_request_dict[key] = modification_time - print(f"(update_latest_modification_time INFO): after executing the funtion", latest_get_request_dict, key, modification_time) + return def get_latest_modification_time(key:str)->typing.Optional[str]: @@ -27,8 +28,10 @@ def get_latest_modification_time(key:str)->typing.Optional[str]: This function returns the latest modification time in .isoformat, if a datetime is stored for the respective key in {latest_get_request_dict}. When there has not yet been an update, this function returns None """ + global latest_get_request_dict + value = latest_get_request_dict.get(key,None) if isinstance(value,datetime.datetime): return value.isoformat() return value # None - \ No newline at end of file + diff --git a/src/server/BreCal/api/history.py b/src/server/BreCal/api/history.py index c1fe5ad..4d087a0 100644 --- a/src/server/BreCal/api/history.py +++ b/src/server/BreCal/api/history.py @@ -7,7 +7,7 @@ bp = Blueprint('history', __name__) @bp.route('/history', methods=['get']) @auth_guard() # no restriction by role -def GetParticipant(): +def GetParticipant(): # #TODO: rename? Naming might be a copy-paste typo if 'Authorization' in request.headers: token = request.headers.get('Authorization') @@ -19,3 +19,13 @@ def GetParticipant(): else: return json.dumps("not authenticated"), 403 + +@bp.route('/getlatesthistory', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestHistory(): + + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.history.GetLatestHistory(token) + else: + return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/notifications.py b/src/server/BreCal/api/notifications.py index 48a2a44..5a7e434 100644 --- a/src/server/BreCal/api/notifications.py +++ b/src/server/BreCal/api/notifications.py @@ -16,4 +16,15 @@ def GetNotifications(): return impl.notifications.GetNotifications(options) else: logging.warning("attempt to load notifications without shipcall id") - return json.dumps("missing argument"), 400 \ No newline at end of file + return json.dumps("missing argument"), 400 + +@bp.route('/getlatestnotifications', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestNotifications(): + + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.notifications.GetLatestNotifications(token) + else: + return json.dumps("not authenticated"), 403 + diff --git a/src/server/BreCal/api/participant.py b/src/server/BreCal/api/participant.py index 65887f9..60d7743 100644 --- a/src/server/BreCal/api/participant.py +++ b/src/server/BreCal/api/participant.py @@ -17,3 +17,12 @@ def GetParticipant(): else: return json.dumps("not authenticated"), 403 +@bp.route('/getlatestparticipants', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestParticipants(): + + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.participant.GetLatestParticipants(token) + else: + return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/shipcalls.py b/src/server/BreCal/api/shipcalls.py index 396dc93..1b22dff 100644 --- a/src/server/BreCal/api/shipcalls.py +++ b/src/server/BreCal/api/shipcalls.py @@ -53,3 +53,12 @@ def PutShipcalls(): return json.dumps("bad format"), 400 return impl.shipcalls.PutShipcalls(loadedModel) + +@bp.route('/getlatestshipcalls', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestShipcalls(): + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.shipcalls.GetLatestShipcalls(token) + else: + return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/ships.py b/src/server/BreCal/api/ships.py index e31147e..6720a4a 100644 --- a/src/server/BreCal/api/ships.py +++ b/src/server/BreCal/api/ships.py @@ -66,3 +66,13 @@ def DeleteShip(): return json.dumps("bad format"), 400 return impl.ships.DeleteShip(options) + +@bp.route('/getlatestships', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestShips(): + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.ships.GetLatestShips(token) + else: + return json.dumps("not authenticated"), 403 + diff --git a/src/server/BreCal/api/times.py b/src/server/BreCal/api/times.py index 2c90397..9e8f30d 100644 --- a/src/server/BreCal/api/times.py +++ b/src/server/BreCal/api/times.py @@ -67,3 +67,13 @@ def DeleteTimes(): else: logging.warning("Times delete missing id argument") return json.dumps("missing argument"), 400 + +@bp.route('/getlatesttimes', methods=['get']) +@auth_guard() # no restriction by role +def GetLatestTimes(): + + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.times.GetLatestTimes(token) + else: + return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/impl/berths.py b/src/server/BreCal/impl/berths.py index cc6baf3..d3ab61b 100644 --- a/src/server/BreCal/impl/berths.py +++ b/src/server/BreCal/impl/berths.py @@ -34,7 +34,8 @@ def GetBerths(token): def GetLatestBerths(token): """ - Returns a datetime of the latest modification within the berths database. When there has not yet been a modification, this method returns null. + Returns a datetime of the latest modification within the 'berth' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} # #TODO: should this become a data model? """ diff --git a/src/server/BreCal/impl/history.py b/src/server/BreCal/impl/history.py index 2fa7e1c..f6b99df 100644 --- a/src/server/BreCal/impl/history.py +++ b/src/server/BreCal/impl/history.py @@ -7,7 +7,7 @@ from ..schemas import model from ..schemas.model import History from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetHistory(options): @@ -38,3 +38,25 @@ def GetHistory(options): return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} + +def GetLatestHistory(token): + """ + Returns a datetime of the latest modification within the 'history' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="history") + data = {"key":"history", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 + diff --git a/src/server/BreCal/impl/notifications.py b/src/server/BreCal/impl/notifications.py index 793f4c7..9ca02ca 100644 --- a/src/server/BreCal/impl/notifications.py +++ b/src/server/BreCal/impl/notifications.py @@ -4,7 +4,7 @@ import pydapper from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetNotifications(options): """ @@ -31,3 +31,25 @@ def GetNotifications(options): return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} +def GetLatestNotifications(token): + """ + Returns a datetime of the latest modification within the 'notification' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="notifications") + data = {"key":"notifications", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 + + diff --git a/src/server/BreCal/impl/participant.py b/src/server/BreCal/impl/participant.py index 174a3f5..2f334a9 100644 --- a/src/server/BreCal/impl/participant.py +++ b/src/server/BreCal/impl/participant.py @@ -4,7 +4,7 @@ import pydapper from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetParticipant(options): """ @@ -34,3 +34,25 @@ def GetParticipant(options): if pooledConnection is not None: pooledConnection.close() + + +def GetLatestParticipants(token): + """ + Returns a datetime of the latest modification within the 'participant' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="participants") + data = {"key":"participants", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 diff --git a/src/server/BreCal/impl/shipcalls.py b/src/server/BreCal/impl/shipcalls.py index c02c7c4..1577605 100644 --- a/src/server/BreCal/impl/shipcalls.py +++ b/src/server/BreCal/impl/shipcalls.py @@ -2,13 +2,14 @@ import json import logging import traceback import pydapper +import datetime from ..schemas import model from .. import local_db from ..services.auth_guard import check_jwt from BreCal.database.update_database import evaluate_shipcall_state -from BreCal.api import latest_get_request_dict, update_latest_modification_time +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetShipcalls(options): """ @@ -141,6 +142,9 @@ def PostShipcalls(schemaModel): user_data = check_jwt() query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 1)" commands.execute(query, {"scid" : new_id, "pid" : user_data["participant_id"], "uid" : user_data["id"]}) + + # track the latest update in the global dictionary + update_latest_modification_time(key="shipcalls",modification_time=datetime.datetime.now()) # new_id return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} @@ -244,6 +248,9 @@ def PutShipcalls(schemaModel): query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 2)" commands.execute(query, {"scid" : schemaModel["id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]}) + # track the latest update in the global dictionary + update_latest_modification_time(key="shipcalls",modification_time=datetime.datetime.now()) # schemaModel["id"] + return json.dumps({"id" : schemaModel["id"]}), 200 except Exception as ex: @@ -258,3 +265,24 @@ def PutShipcalls(schemaModel): if pooledConnection is not None: pooledConnection.close() + +def GetLatestShipcalls(token): + """ + Returns a datetime of the latest modification within the 'shipcall' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="shipcalls") + data = {"key":"shipcalls", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 diff --git a/src/server/BreCal/impl/ships.py b/src/server/BreCal/impl/ships.py index 69964d8..9b2107c 100644 --- a/src/server/BreCal/impl/ships.py +++ b/src/server/BreCal/impl/ships.py @@ -5,7 +5,7 @@ import datetime from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetShips(token): """ @@ -158,4 +158,25 @@ def DeleteShip(options): print(ex) result = {} result["message"] = "call failed" - return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} \ No newline at end of file + return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} + +def GetLatestShips(token): + """ + Returns a datetime of the latest modification within the 'ship' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="ships") + data = {"key":"ships", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 diff --git a/src/server/BreCal/impl/times.py b/src/server/BreCal/impl/times.py index 966599f..6161313 100644 --- a/src/server/BreCal/impl/times.py +++ b/src/server/BreCal/impl/times.py @@ -2,11 +2,12 @@ import json import logging import traceback import pydapper +import datetime from ..schemas import model from .. import local_db from ..services.auth_guard import check_jwt -from BreCal.api import latest_get_request_dict, update_latest_modification_time +from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time from BreCal.database.update_database import evaluate_shipcall_state @@ -92,6 +93,9 @@ def PostTimes(schemaModel): user_data = check_jwt() query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?scid?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 1)" commands.execute(query, {"scid" : schemaModel["shipcall_id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]}) + + # track the latest update in the global dictionary + update_latest_modification_time(key="times", modification_time=datetime.datetime.now()) # new_id return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} @@ -137,6 +141,7 @@ def PutTimes(schemaModel): query += "WHERE id = ?id?" affected_rows = commands.execute(query, param=schemaModel) + new_id = commands.execute_scalar("select last_insert_id()") # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database 'shipcall' evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["shipcall_id"]) # every times data object refers to the 'shipcall_id' @@ -152,6 +157,9 @@ def PutTimes(schemaModel): # if affected_rows == 1: # this doesn't work as expected + # track the latest update in the global dictionary + update_latest_modification_time(key="times", modification_time=datetime.datetime.now()) # new_id + return json.dumps({"id" : schemaModel["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} except Exception as ex: @@ -186,6 +194,10 @@ def DeleteTimes(options): query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, ?shipcall_id?, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 3)" commands.execute(query, {"pid" : user_data["participant_id"], "shipcall_id" : shipcall_id, "uid" : user_data["id"]}) + # track the latest update in the global dictionary + ###### could use a query similar to this one: modification_time = commands.execute_scalar("SELECT modified FROM times WHERE id = ?id?", param={"id" : options["id"]}) # clarify: when modified is null, created should be used... + update_latest_modification_time(key="times", modification_time=datetime.datetime.now()) # options["id"] + if affected_rows == 1: return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} @@ -202,4 +214,25 @@ def DeleteTimes(options): finally: if pooledConnection is not None: - pooledConnection.close() \ No newline at end of file + pooledConnection.close() + +def GetLatestTimes(token): + """ + Returns a datetime of the latest modification within the 'times' database. When there has not yet been a modification, this method returns null. + Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} + + # #TODO: should this become a data model? + """ + + try: + modification_time = get_latest_modification_time(key="times") + data = {"key":"times", "value":modification_time} + + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 From 10db47073fc0b1a4328250955689de5cff54cce7 Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 10 Jun 2024 16:02:27 +0200 Subject: [PATCH 3/4] removed an artifact from GetBerths --- src/server/BreCal/impl/berths.py | 1 - src/server/BreCal/impl/user.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/server/BreCal/impl/berths.py b/src/server/BreCal/impl/berths.py index d3ab61b..2abe922 100644 --- a/src/server/BreCal/impl/berths.py +++ b/src/server/BreCal/impl/berths.py @@ -16,7 +16,6 @@ def GetBerths(token): pooledConnection = local_db.getPoolConnection() commands = pydapper.using(pooledConnection) data = commands.query("SELECT id, name, `lock`, owner_id, authority_id, created, modified, deleted FROM berth WHERE deleted = 0 ORDER BY name", model=model.Berth) - # update_latest_modification_time(key="berths", modification_time=datetime.datetime.now()) return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} diff --git a/src/server/BreCal/impl/user.py b/src/server/BreCal/impl/user.py index 2855645..cd79f89 100644 --- a/src/server/BreCal/impl/user.py +++ b/src/server/BreCal/impl/user.py @@ -5,7 +5,6 @@ import bcrypt from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time def PutUser(schemaModel): """ From ee53d7fd9d51c478eb4419c3ad74e7ab9954dd3c Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 10 Jun 2024 18:21:29 +0200 Subject: [PATCH 4/4] refactoring the getlatest-endpoint. Using a single endpoint to return all databases of interest. Added the endpoint to the application upon building it. --- src/server/BreCal/__init__.py | 2 ++ src/server/BreCal/api/__init__.py | 14 +++++------ src/server/BreCal/api/berths.py | 9 ------- src/server/BreCal/api/history.py | 10 -------- src/server/BreCal/api/latest.py | 18 ++++++++++++++ src/server/BreCal/api/notifications.py | 9 ------- src/server/BreCal/api/participant.py | 9 ------- src/server/BreCal/api/shipcalls.py | 8 ------- src/server/BreCal/api/ships.py | 8 ------- src/server/BreCal/api/times.py | 9 ------- src/server/BreCal/impl/berths.py | 24 ------------------- src/server/BreCal/impl/history.py | 21 ---------------- src/server/BreCal/impl/latest.py | 32 +++++++++++++++++++++++++ src/server/BreCal/impl/login.py | 1 - src/server/BreCal/impl/notifications.py | 22 ----------------- src/server/BreCal/impl/participant.py | 23 ------------------ src/server/BreCal/impl/shipcalls.py | 24 +------------------ src/server/BreCal/impl/ships.py | 24 ++----------------- src/server/BreCal/impl/times.py | 23 +----------------- 19 files changed, 62 insertions(+), 228 deletions(-) create mode 100644 src/server/BreCal/api/latest.py create mode 100644 src/server/BreCal/impl/latest.py diff --git a/src/server/BreCal/__init__.py b/src/server/BreCal/__init__.py index eb832f5..90869b3 100644 --- a/src/server/BreCal/__init__.py +++ b/src/server/BreCal/__init__.py @@ -13,6 +13,7 @@ from .api import ships from .api import login from .api import user from .api import history +from .api import latest from BreCal.brecal_utils.file_handling import get_project_root, ensure_path from BreCal.brecal_utils.test_handling import execute_test_with_pytest, execute_coverage_test @@ -61,6 +62,7 @@ def create_app(test_config=None): app.register_blueprint(login.bp) app.register_blueprint(user.bp) app.register_blueprint(history.bp) + app.register_blueprint(latest.bp) logging.basicConfig(filename='brecaldevel.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s') local_db.initPool(os.path.dirname(app.instance_path)) diff --git a/src/server/BreCal/api/__init__.py b/src/server/BreCal/api/__init__.py index a8b173a..7b19b24 100644 --- a/src/server/BreCal/api/__init__.py +++ b/src/server/BreCal/api/__init__.py @@ -3,7 +3,7 @@ import datetime # global dictionary, which informs about the latest change of a given database (e.g., 'berths') # initialize all values as null (None) -latest_get_request_dict = {database:None for database in ["berths", "history", "notifications", "participants", "shipcalls", "ships", "times"]} +latest_get_request_dict = {database:None for database in ["shipcalls", "ships", "times"]} def update_latest_modification_time(key:str, modification_time:datetime.datetime)->None: """ @@ -23,15 +23,13 @@ def update_latest_modification_time(key:str, modification_time:datetime.datetime return -def get_latest_modification_time(key:str)->typing.Optional[str]: +def get_latest_modification_times()->dict[str,typing.Optional[str]]: """ - This function returns the latest modification time in .isoformat, if a datetime is stored for the respective key in {latest_get_request_dict}. - When there has not yet been an update, this function returns None + This function returns a dictionary, where each key determines the database name, and each value is either an isoformatted datetime + of the latest modification time, or None. """ global latest_get_request_dict - value = latest_get_request_dict.get(key,None) - if isinstance(value,datetime.datetime): - return value.isoformat() - return value # None + latest_dict = {k:v.isoformat() if isinstance(v,datetime.datetime) else v for k,v in latest_get_request_dict.items()} + return latest_dict diff --git a/src/server/BreCal/api/berths.py b/src/server/BreCal/api/berths.py index 1c8a33a..0209851 100644 --- a/src/server/BreCal/api/berths.py +++ b/src/server/BreCal/api/berths.py @@ -17,12 +17,3 @@ def GetBerths(): else: return json.dumps("not authenticated"), 403 -@bp.route('/getlatestberths', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestBerths(): - - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.berths.GetLatestBerths(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/history.py b/src/server/BreCal/api/history.py index 4d087a0..33652a8 100644 --- a/src/server/BreCal/api/history.py +++ b/src/server/BreCal/api/history.py @@ -19,13 +19,3 @@ def GetParticipant(): # #TODO: rename? Naming might be a copy-paste typo else: return json.dumps("not authenticated"), 403 - -@bp.route('/getlatesthistory', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestHistory(): - - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.history.GetLatestHistory(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/latest.py b/src/server/BreCal/api/latest.py new file mode 100644 index 0000000..9d34ad5 --- /dev/null +++ b/src/server/BreCal/api/latest.py @@ -0,0 +1,18 @@ +from flask import Blueprint, request +from webargs.flaskparser import parser +from .. import impl +from ..services.auth_guard import auth_guard +import json + +bp = Blueprint('getlatest', __name__) + + +@bp.route('/getlatest', methods=['get']) +@auth_guard() # no restriction by role +def GetLatest(): + """This endpoint verifies the user and executes impl.latest.GetLatest""" + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.latest.GetLatest(token) + else: + return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/notifications.py b/src/server/BreCal/api/notifications.py index 5a7e434..c8225b5 100644 --- a/src/server/BreCal/api/notifications.py +++ b/src/server/BreCal/api/notifications.py @@ -18,13 +18,4 @@ def GetNotifications(): logging.warning("attempt to load notifications without shipcall id") return json.dumps("missing argument"), 400 -@bp.route('/getlatestnotifications', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestNotifications(): - - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.notifications.GetLatestNotifications(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/participant.py b/src/server/BreCal/api/participant.py index 60d7743..65887f9 100644 --- a/src/server/BreCal/api/participant.py +++ b/src/server/BreCal/api/participant.py @@ -17,12 +17,3 @@ def GetParticipant(): else: return json.dumps("not authenticated"), 403 -@bp.route('/getlatestparticipants', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestParticipants(): - - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.participant.GetLatestParticipants(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/shipcalls.py b/src/server/BreCal/api/shipcalls.py index 1b22dff..7d20949 100644 --- a/src/server/BreCal/api/shipcalls.py +++ b/src/server/BreCal/api/shipcalls.py @@ -54,11 +54,3 @@ def PutShipcalls(): return impl.shipcalls.PutShipcalls(loadedModel) -@bp.route('/getlatestshipcalls', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestShipcalls(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.shipcalls.GetLatestShipcalls(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/ships.py b/src/server/BreCal/api/ships.py index 6720a4a..e1f36b8 100644 --- a/src/server/BreCal/api/ships.py +++ b/src/server/BreCal/api/ships.py @@ -67,12 +67,4 @@ def DeleteShip(): return impl.ships.DeleteShip(options) -@bp.route('/getlatestships', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestShips(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.ships.GetLatestShips(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/api/times.py b/src/server/BreCal/api/times.py index 9e8f30d..9e94a40 100644 --- a/src/server/BreCal/api/times.py +++ b/src/server/BreCal/api/times.py @@ -68,12 +68,3 @@ def DeleteTimes(): logging.warning("Times delete missing id argument") return json.dumps("missing argument"), 400 -@bp.route('/getlatesttimes', methods=['get']) -@auth_guard() # no restriction by role -def GetLatestTimes(): - - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.times.GetLatestTimes(token) - else: - return json.dumps("not authenticated"), 403 diff --git a/src/server/BreCal/impl/berths.py b/src/server/BreCal/impl/berths.py index 2abe922..d020b3f 100644 --- a/src/server/BreCal/impl/berths.py +++ b/src/server/BreCal/impl/berths.py @@ -5,7 +5,6 @@ import datetime from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetBerths(token): """ @@ -31,29 +30,6 @@ def GetBerths(token): pooledConnection.close() -def GetLatestBerths(token): - """ - Returns a datetime of the latest modification within the 'berth' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="berths") - data = {"key":"berths", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500 - - - diff --git a/src/server/BreCal/impl/history.py b/src/server/BreCal/impl/history.py index f6b99df..a592f57 100644 --- a/src/server/BreCal/impl/history.py +++ b/src/server/BreCal/impl/history.py @@ -7,7 +7,6 @@ from ..schemas import model from ..schemas.model import History from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetHistory(options): @@ -39,24 +38,4 @@ def GetHistory(options): return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} -def GetLatestHistory(token): - """ - Returns a datetime of the latest modification within the 'history' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="history") - data = {"key":"history", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500 diff --git a/src/server/BreCal/impl/latest.py b/src/server/BreCal/impl/latest.py new file mode 100644 index 0000000..7bfa4b9 --- /dev/null +++ b/src/server/BreCal/impl/latest.py @@ -0,0 +1,32 @@ +import json +import logging +import pydapper +import datetime + +from ..schemas import model +from .. import local_db +from BreCal.api import get_latest_modification_times + +def GetLatest(token): + """ + Returns a dictionary of the latest modification dates within the databases 'ships', 'shipcalls' and 'times'. When there has not yet been a modification, this method returns null. + Creates a dictionary of type dict[str, typing.Optional[datetime.datetime]] + """ + + try: + data = get_latest_modification_times() + return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} + + except Exception as ex: + logging.error(ex) + print(ex) + result = {} + result["message"] = "call failed" + return json.dumps(result), 500 + + + + + + + diff --git a/src/server/BreCal/impl/login.py b/src/server/BreCal/impl/login.py index e208620..21b46a6 100644 --- a/src/server/BreCal/impl/login.py +++ b/src/server/BreCal/impl/login.py @@ -6,7 +6,6 @@ import bcrypt from ..schemas import model from .. import local_db from ..services import jwt_handler -from BreCal.api import latest_get_request_dict, update_latest_modification_time def GetUser(options): diff --git a/src/server/BreCal/impl/notifications.py b/src/server/BreCal/impl/notifications.py index 9ca02ca..697b761 100644 --- a/src/server/BreCal/impl/notifications.py +++ b/src/server/BreCal/impl/notifications.py @@ -4,7 +4,6 @@ import pydapper from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetNotifications(options): """ @@ -31,25 +30,4 @@ def GetNotifications(options): return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} -def GetLatestNotifications(token): - """ - Returns a datetime of the latest modification within the 'notification' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="notifications") - data = {"key":"notifications", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500 - diff --git a/src/server/BreCal/impl/participant.py b/src/server/BreCal/impl/participant.py index 2f334a9..aad0def 100644 --- a/src/server/BreCal/impl/participant.py +++ b/src/server/BreCal/impl/participant.py @@ -4,7 +4,6 @@ import pydapper from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time def GetParticipant(options): """ @@ -34,25 +33,3 @@ def GetParticipant(options): if pooledConnection is not None: pooledConnection.close() - - -def GetLatestParticipants(token): - """ - Returns a datetime of the latest modification within the 'participant' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="participants") - data = {"key":"participants", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500 diff --git a/src/server/BreCal/impl/shipcalls.py b/src/server/BreCal/impl/shipcalls.py index 1577605..3b72c3c 100644 --- a/src/server/BreCal/impl/shipcalls.py +++ b/src/server/BreCal/impl/shipcalls.py @@ -9,7 +9,7 @@ from .. import local_db from ..services.auth_guard import check_jwt from BreCal.database.update_database import evaluate_shipcall_state -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time +from BreCal.api import update_latest_modification_time def GetShipcalls(options): """ @@ -264,25 +264,3 @@ def PutShipcalls(schemaModel): finally: if pooledConnection is not None: pooledConnection.close() - - -def GetLatestShipcalls(token): - """ - Returns a datetime of the latest modification within the 'shipcall' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="shipcalls") - data = {"key":"shipcalls", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500 diff --git a/src/server/BreCal/impl/ships.py b/src/server/BreCal/impl/ships.py index 9b2107c..857a096 100644 --- a/src/server/BreCal/impl/ships.py +++ b/src/server/BreCal/impl/ships.py @@ -5,7 +5,7 @@ import datetime from ..schemas import model from .. import local_db -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time +from BreCal.api import update_latest_modification_time def GetShips(token): """ @@ -159,24 +159,4 @@ def DeleteShip(options): result = {} result["message"] = "call failed" return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} - -def GetLatestShips(token): - """ - Returns a datetime of the latest modification within the 'ship' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="ships") - data = {"key":"ships", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500 + \ No newline at end of file diff --git a/src/server/BreCal/impl/times.py b/src/server/BreCal/impl/times.py index 6161313..fa9cb79 100644 --- a/src/server/BreCal/impl/times.py +++ b/src/server/BreCal/impl/times.py @@ -7,8 +7,8 @@ import datetime from ..schemas import model from .. import local_db from ..services.auth_guard import check_jwt -from BreCal.api import latest_get_request_dict, update_latest_modification_time, get_latest_modification_time +from BreCal.api import update_latest_modification_time from BreCal.database.update_database import evaluate_shipcall_state def GetTimes(options): @@ -215,24 +215,3 @@ def DeleteTimes(options): finally: if pooledConnection is not None: pooledConnection.close() - -def GetLatestTimes(token): - """ - Returns a datetime of the latest modification within the 'times' database. When there has not yet been a modification, this method returns null. - Always creates an output dictionary with the format {key:string, value:str (datetime isoformat)} - - # #TODO: should this become a data model? - """ - - try: - modification_time = get_latest_modification_time(key="times") - data = {"key":"times", "value":modification_time} - - return json.dumps(data), 200, {'Content-Type': 'application/json; charset=utf-8'} - - except Exception as ex: - logging.error(ex) - print(ex) - result = {} - result["message"] = "call failed" - return json.dumps(result), 500