git_brcal/src/server/BreCal/impl/shipcalls.py

340 lines
15 KiB
Python

import json
import logging
import traceback
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
from BreCal.database.sql_utils import get_notification_for_shipcall_and_type, get_ship_data_for_id
from BreCal.database.sql_queries import create_sql_query_shipcall_get
from marshmallow import Schema, fields, ValidationError
from BreCal.validators.validation_error import create_validation_error_response
def GetShipcalls(options):
"""
No parameters, gets all entries
"""
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
# query = SQLQuery.get_shipcalls(options)
query = create_sql_query_shipcall_get(options)
data = commands.query(query, model=model.Shipcall.from_query_row, buffered=True)
for shipcall in data:
# participant_query = SQLQuery.get_participants()
# participants = commands.query(participant_query, model=dict, param={"shipcall_id" : shipcall.id}, buffered=False)
# for record in participants:
participant_query = "SELECT participant_id, type FROM shipcall_participant_map WHERE shipcall_id=?shipcall_id?";
for record in commands.query(participant_query, model=dict, param={"shipcall_id" : shipcall.id}, buffered=False):
# model.Participant_Assignment = model.Participant_Assignment()
pa = model.Participant_Assignment(record["participant_id"], record["type"])
shipcall.participants.append(pa)
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex)
print(ex)
result = {}
result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally:
if pooledConnection is not None:
pooledConnection.close()
def PostShipcalls(schemaModel):
"""
This function *executes* a post-request for shipcalls. The function is accessible as part of an API route.
The common sequence is:
a) issue a request to the Flask API
b) BreCal.api.shipcalls.PostShipcalls, to verify the incoming request (which includes an authentification guard)
c) BreCal.impl.shipcalls.PostShipcalls, to execute the incoming request
:param schemaModel: The deserialized dict of the request
e.g.,
{
'ship_id': 1, 'type': 1, 'eta': datetime.datetime(2023, 7, 23, 7, 18, 19),
'voyage': '43B', 'tug_required': False, 'pilot_required': True, 'flags': 0,
'pier_side': False, 'bunkering': True, 'recommended_tugs': 2, 'type_value': 1, 'evaluation_value': 0}
}
"""
# This creates a *new* entry
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
# query = SQLQuery.get_shipcall_post(schemaModel) # create_sql_query_shipcall_post(schemaModel)
query = "INSERT INTO shipcall ("
isNotFirst = False
for key in schemaModel.keys():
if key == "id":
continue
if key == "participants":
continue
if key == "created":
continue
if key == "modified":
continue
if key == "evaluation":
continue
if key == "evaluation_message":
continue
if key == "type_value":
continue
if key == "evaluation_value":
continue
if isNotFirst:
query += ","
isNotFirst = True
query += key
query += ") VALUES ("
isNotFirst = False
for key in schemaModel.keys():
param_key = key
if key == "id":
continue
if key == "participants":
continue
if key == "created":
continue
if key == "modified":
continue
if key == "evaluation":
continue
if key == "evaluation_message":
continue
if key == "type":
param_key = "type_value"
if key == "type_value":
continue
if key == "evaluation":
param_key = "evaluation_value"
if key == "evaluation_value":
continue
if isNotFirst:
query += ","
isNotFirst = True
query += "?" + param_key + "?"
query += ")"
commands.execute(query, schemaModel)
# lquery = SQLQuery.get_shipcall_post_last_insert_id()
# new_id = commands.execute_scalar(lquery)
new_id = commands.execute_scalar("select last_insert_id()")
shipdata = get_ship_data_for_id(schemaModel["ship_id"])
message = shipdata['name']
if "type_value" in schemaModel:
match schemaModel["type_value"]:
case 1:
message += " [ARRIVAL]"
case 2:
message += " [DEPARTURE]"
case 3:
message += " [SHIFTING]"
# add participant assignments if we have a list of participants
if 'participants' in schemaModel:
# pquery = SQLQuery.get_shipcall_post_update_shipcall_participant_map()
pquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 1, ?message?)" # type = 1 is assignment
for participant_assignment in schemaModel["participants"]:
commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]})
commands.execute(nquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "message" : message})
# 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
# save history data
# TODO: set ETA properly
user_data = check_jwt()
# query = SQLQuery.create_sql_query_history_post()
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"]})
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
except ValidationError as ex:
return create_validation_error_response(ex, status_code=400, create_log=True)
except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex)
print(ex)
result = {}
result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally:
if pooledConnection is not None:
pooledConnection.close()
def PutShipcalls(schemaModel):
"""
:param schemaModel: The deserialized dict of the request
"""
# This updates an *existing* entry
try:
pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection)
user_data = check_jwt()
# test if object to update is found
sentinel = object()
theshipcall = commands.query_single_or_default("SELECT * FROM shipcall where id = ?id?", sentinel, param={"id" : schemaModel["id"]})
if theshipcall is sentinel:
pooledConnection.close()
return json.dumps("no such record"), 404, {'Content-Type': 'application/json; charset=utf-8'}
was_canceled = theshipcall["canceled"]
# query = SQLQuery.get_shipcall_put(schemaModel)
query = "UPDATE shipcall SET "
isNotFirst = False
for key in schemaModel.keys():
param_key = key
if key == "id":
continue
if key == "participants":
continue
if key == "created":
continue
if key == "modified":
continue
if key == "evaluation":
continue
if key == "evaluation_message":
continue
if key == "type":
param_key = "type_value"
if key == "type_value":
continue
if key == "evaluation":
param_key = "evaluation_value"
if key == "evaluation_value":
continue
if isNotFirst:
query += ", "
isNotFirst = True
query += key + " = ?" + param_key + "? "
query += "WHERE id = ?id?"
affected_rows = commands.execute(query, param=schemaModel)
shipdata = get_ship_data_for_id(schemaModel["ship_id"])
message = shipdata['name']
if "type_value" in schemaModel:
match schemaModel["type_value"]:
case 1:
message += " [ARRIVAL]"
case 2:
message += " [DEPARTURE]"
case 3:
message += " [SHIFTING]"
# pquery = SQLQuery.get_shipcall_participant_map_by_shipcall_id()
pquery = "SELECT id, participant_id, type FROM shipcall_participant_map where shipcall_id = ?id?"
pdata = commands.query(pquery,param={"id" : schemaModel["id"]}) # existing list of assignments
# loop across passed participant ids, creating entries for those not present in pdata
existing_notifications = get_notification_for_shipcall_and_type(schemaModel["id"], 1) # type = 1 is assignment
for participant_assignment in schemaModel["participants"]:
found_participant = False
for elem in pdata:
if elem["participant_id"] == participant_assignment["participant_id"] and elem["type"] == participant_assignment["type"]:
found_participant = True
break
if not found_participant:
# nquery = SQLQuery.get_shipcall_post_update_shipcall_participant_map()
spquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
commands.execute(spquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]})
# create a notification but only if there is no existing notification in level 0
found_notification = False
for existing_notification in existing_notifications:
if existing_notification["participant_id"] == participant_assignment["participant_id"] and existing_notification["level"] == 1:
found_notification = True
break
if not found_notification:
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 1, ?message?)" # type = 1 is assignment
commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : participant_assignment["participant_id"], "message" : message})
# loop across existing pdata entries, deleting those not present in participant list
for elem in pdata:
found_participant = False
for participant_assignment in schemaModel["participants"]:
if(participant_assignment["participant_id"] == elem["participant_id"] and participant_assignment["type"] == elem["type"]):
found_participant = True
break;
if not found_participant:
# dquery = SQLQuery.get_shipcall_participant_map_delete_by_id()
dquery = "DELETE FROM shipcall_participant_map WHERE id = ?existing_id?"
commands.execute(dquery, param={"existing_id" : elem["id"]})
# TODO: Create un-assignment notification but only if level > 0 else delete existing notification
for existing_notification in existing_notifications:
if existing_notification["participant_id"] == elem["participant_id"]:
if existing_notification["level"] == 0:
nquery = "DELETE FROM notification WHERE id = ?nid?"
commands.execute(nquery, param={"nid" : existing_notification["id"]})
else:
# create un-assignment notification
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 5, ?message?)"
commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : elem["participant_id"], "message" : message})
break
if schemaModel["canceled"] is not None:
if schemaModel["canceled"] and not was_canceled:
# create a canceled notification for all currently assigned participants
stornoNotificationQuery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 7, ?message?)"
for participant_assignment in schemaModel["participants"]:
commands.execute(stornoNotificationQuery, param={"shipcall_id" : schemaModel["id"], "participant_id" : participant_assignment["participant_id"], "message" : message})
# save history data
# TODO: set ETA properly
# query = SQLQuery.create_sql_query_history_put()
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"]})
return json.dumps({"id" : schemaModel["id"]}), 200
except ValidationError as ex:
return create_validation_error_response(ex, status_code=400, create_log=True)
except Exception as ex:
logging.error(traceback.format_exc())
logging.error(ex)
print(ex)
result = {}
result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally:
if pooledConnection is not None:
pooledConnection.close()