diff --git a/misc/BreCalApi.yaml b/misc/BreCalApi.yaml index 7111d62..f1ac93f 100644 --- a/misc/BreCalApi.yaml +++ b/misc/BreCalApi.yaml @@ -362,6 +362,28 @@ paths: $ref: '#/components/responses/500' '503': $ref: '#/components/responses/503' + /history: + get: + description: This endpoint returns a list of changes made to the specific shipcall + summary: History data + tags: + - static + operationId: historyGet + responses: + '200': + description: list of history entries + content: + application/json: + schema: + $ref: '#/components/schemas/history_list' + '400': + $ref: '#/components/responses/400' + '401': + $ref: '#/components/responses/401' + '500': + $ref: '#/components/responses/500' + '503': + $ref: '#/components/responses/503' components: schemas: credentials: @@ -469,7 +491,6 @@ components: nullable: true evaluation: $ref: '#/components/schemas/EvaluationType' - nullable: true evaluation_message: maxLength: 512 type: string @@ -567,7 +588,7 @@ components: maxLength: 512 description: Additional info text for berth pier_side: - description: true if ship is rotated, false otherwise + description: 'true if ship is rotated, false otherwise' type: boolean nullable: true participant_type: @@ -675,6 +696,32 @@ components: type: array items: $ref: '#/components/schemas/ship' + history: + type: object + description: Single history entry with references + properties: + id: + type: integer + participant_id: + type: integer + shipcall_id: + type: integer + timestamp: + type: string + format: date-time + eta: + type: string + format: date-time + operation: + $ref: '#/components/schemas/OperationType' + type: + $ref: '#/components/schemas/ObjectType' + history_list: + type: array + items: + $ref: '#/components/schemas/history' + x-stoplight: + id: 93b5ff080c12a notification: type: object description: a notification created by the engine if a times entry violates a rule @@ -825,11 +872,23 @@ components: - push EvaluationType: type: string + nullable: true enum: - undefined - green - yellow - red + OperationType: + type: string + enum: + - insert + - update + - delete + ObjectType: + type: string + enum: + - times + - shipcall securitySchemes: ApiKey: type: apiKey diff --git a/misc/update_1.1_to_1.2.sql b/misc/update_1.1_to_1.2.sql index 75179b6..421281e 100644 --- a/misc/update_1.1_to_1.2.sql +++ b/misc/update_1.1_to_1.2.sql @@ -54,9 +54,10 @@ ADD CONSTRAINT `FK_USER_PART` CREATE TABLE `bremen_calling_devel`.`history` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `participant_id` INT UNSIGNED NOT NULL, + `user_id` INT UNSIGNED NOT NULL, `shipcall_id` INT UNSIGNED NOT NULL, `timestamp` DATETIME NOT NULL COMMENT 'Time of saving', - `eta` DATETIME NOT NULL COMMENT 'Current ETA / ETD value (depends if shipcall or times were saved)', + `eta` DATETIME NULL COMMENT 'Current ETA / ETD value (depends if shipcall or times were saved)', `type` INT NOT NULL COMMENT 'shipcall or times', `operation` INT NOT NULL COMMENT 'insert, update or delete', PRIMARY KEY (`id`)) @@ -78,4 +79,9 @@ ADD CONSTRAINT `FK_HISTORY_SHIPCALL` FOREIGN KEY (`shipcall_id`) REFERENCES `bremen_calling_devel`.`shipcall` (`id`) ON DELETE NO ACTION + ON UPDATE NO ACTION, +ADD CONSTRAINT `FK_HISTORY_USER` + FOREIGN KEY (`user_id`) + REFERENCES `bremen_calling_devel`.`user` (`id`) + ON DELETE NO ACTION ON UPDATE NO ACTION; diff --git a/src/server/BreCal/impl/history.py b/src/server/BreCal/impl/history.py index f3a10ff..522071e 100644 --- a/src/server/BreCal/impl/history.py +++ b/src/server/BreCal/impl/history.py @@ -3,6 +3,8 @@ import logging import pydapper from ..schemas import model +from ..schemas.model import History + from .. import local_db def GetHistory(options): @@ -15,8 +17,14 @@ def GetHistory(options): try: pooledConnection = local_db.getPoolConnection() commands = pydapper.using(pooledConnection) + h = History(1, 1, 1, "2020-01-01", "2020-01-01", model.ObjectType.shipcall, model.Operation.insert) + + if "shipcall_id" in options and options["shipcall_id"]: - data = commands.query("SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?", model=model.History, param={"shipcallid" : options["shipcall_id"]}) + data = commands.query("SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?", + model=History.from_query_row, + param={"shipcallid" : options["shipcall_id"]}) + pooledConnection.close() diff --git a/src/server/BreCal/impl/shipcalls.py b/src/server/BreCal/impl/shipcalls.py index 30f1c39..84cee79 100644 --- a/src/server/BreCal/impl/shipcalls.py +++ b/src/server/BreCal/impl/shipcalls.py @@ -5,6 +5,7 @@ import pydapper from ..schemas import model from .. import local_db +from ..services.auth_guard import check_jwt from BreCal.database.update_database import evaluate_shipcall_state @@ -107,7 +108,13 @@ def PostShipcalls(schemaModel): commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]}) # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database - evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id + # evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id + + # save history data + # TODO: set ETA properly + 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_scalar(query, {"scid" : new_id, "pid" : user_data["participant_id"], "uid" : user_data["id"]}) pooledConnection.close() @@ -192,7 +199,13 @@ def PutShipcalls(schemaModel): commands.execute(dquery, param={"existing_id" : elem["id"]}) # apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database - evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["id"]) # schemaModel["id"] refers to the shipcall id + # evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=schemaModel["id"]) # schemaModel["id"] refers to the shipcall id + + # save history data + # TODO: set ETA properly + 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, 2)" + commands.execute(query, {"scid" : schemaModel["id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]}) pooledConnection.close() diff --git a/src/server/BreCal/impl/times.py b/src/server/BreCal/impl/times.py index 718a522..f7a3253 100644 --- a/src/server/BreCal/impl/times.py +++ b/src/server/BreCal/impl/times.py @@ -4,6 +4,7 @@ import pydapper from ..schemas import model from .. import local_db +from ..services.auth_guard import check_jwt from BreCal.database.update_database import evaluate_shipcall_state @@ -83,6 +84,12 @@ def PostTimes(schemaModel): # 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' + # save history data + # TODO: set ETA properly + 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_scalar(query, {"scid" : schemaModel["shipcall_id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]}) + pooledConnection.close() return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} @@ -128,6 +135,12 @@ def PutTimes(schemaModel): # 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' + # save history data + # TODO: set ETA properly + 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, 2)" + commands.execute_scalar(query, {"scid" : schemaModel["shipcall_id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]}) + pooledConnection.close() # if affected_rows == 1: # this doesn't work as expected @@ -154,6 +167,15 @@ def DeleteTimes(options): pooledConnection = local_db.getPoolConnection() commands = pydapper.using(pooledConnection) affected_rows = commands.execute("DELETE FROM times WHERE id = ?id?", param={"id" : options["id"]}) + + # TODO: howto get the shipcall id here? we will need to load the object first + # TODO: set ETA properly + + # save history data + user_data = check_jwt() + query = "INSERT INTO history (participant_id, shipcall_id, user_id, timestamp, eta, type, operation) VALUES (?pid?, 0, ?uid?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 2, 3)" + commands.execute_scalar(query, {"pid" : user_data["participant_id"], "uid" : user_data["id"]}) + pooledConnection.close() if affected_rows == 1: diff --git a/src/server/BreCal/schemas/model.py b/src/server/BreCal/schemas/model.py index faaaac7..e37b3f2 100644 --- a/src/server/BreCal/schemas/model.py +++ b/src/server/BreCal/schemas/model.py @@ -1,5 +1,7 @@ -from dataclasses import field +from dataclasses import field, dataclass from marshmallow import Schema, fields, INCLUDE, ValidationError +from marshmallow_enum import EnumField +from enum import Enum from marshmallow_dataclass import dataclass from typing import List @@ -23,6 +25,38 @@ class Berth(Schema): modified: datetime deleted: bool +class Operation(Enum): + insert = 1, + update = 2, + delete = 3 + +class ObjectType(Enum): + shipcall = 1, + times = 2 + +@dataclass +class History: + def __init__(self, id, participant_id, shipcall_id, timestamp, eta, type, operation): + self.id = id + self.participant_id = participant_id + self.shipcall_id = shipcall_id + self.timestamp = timestamp + self.eta = eta + self.type = type + self.operation = operation + pass + + id: int + participant_id: int + shipcall_id: int + timestamp: datetime + eta: datetime + type: EnumField(ObjectType) + operation: EnumField(Operation) + @classmethod + def from_query_row(self, id, participant_id, ship_id, timestamp, eta, type, operation): + return self(id, participant_id, ship_id, timestamp, eta, ObjectType(type), Operation(operation)) + class Error(Schema): message = fields.String(required=True) @@ -258,17 +292,6 @@ class ShipSchema(Schema): deleted = fields.Int(allow_none=True, metadata={'Required':False}) -@dataclass -class History(Schema): - id: int - participant_id: int - ship_id: int - timestamp: datetime - eta: datetime - type: int - operation: int - - class TimesId(Schema): pass