diff --git a/misc/notification_element.html b/misc/notification_element.html new file mode 100644 index 0000000..154cf8b --- /dev/null +++ b/misc/notification_element.html @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/misc/notification_template.html b/misc/notification_template.html new file mode 100644 index 0000000..61af32b --- /dev/null +++ b/misc/notification_template.html @@ -0,0 +1,144 @@ + + + + + + Bremen calling Benachrichtigung + + + + + + + + + + + + diff --git a/src/server/BreCal/impl/shipcalls.py b/src/server/BreCal/impl/shipcalls.py index 3fe6c2c..efe03bd 100644 --- a/src/server/BreCal/impl/shipcalls.py +++ b/src/server/BreCal/impl/shipcalls.py @@ -8,8 +8,8 @@ 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 -from BreCal.database.sql_queries import create_sql_query_shipcall_get, create_sql_query_shipcall_post, create_sql_query_shipcall_put, create_sql_query_history_post, create_sql_query_history_put, SQLQuery +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 @@ -76,6 +76,7 @@ def PostShipcalls(schemaModel): 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(): @@ -134,15 +135,26 @@ def PostShipcalls(schemaModel): # 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) VALUES (?shipcall_id?, ?participant_id?, 0, 1)" # type = 1 is assignment + 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"]}) + 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 @@ -189,8 +201,7 @@ def PutShipcalls(schemaModel): # test if object to update is found sentinel = object() - # query = SQLQuery.get_shipcall_by_id() - # theshipcall = commands.query_single_or_default(query, sentinel, param={"id" : schemaModel["id"]}) + theshipcall = commands.query_single_or_default("SELECT * FROM shipcall where id = ?id?", sentinel, param={"id" : schemaModel["id"]}) if theshipcall is sentinel: pooledConnection.close() @@ -230,6 +241,17 @@ def PutShipcalls(schemaModel): 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 @@ -256,8 +278,8 @@ def PutShipcalls(schemaModel): found_notification = True break if not found_notification: - nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type) VALUES (?shipcall_id?, ?participant_id?, 0, 1)" # type = 1 is assignment - commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : participant_assignment["participant_id"]}) + 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: @@ -278,8 +300,8 @@ def PutShipcalls(schemaModel): commands.execute(nquery, param={"nid" : existing_notification["id"]}) else: # create un-assignment notification - nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type) VALUES (?shipcall_id?, ?participant_id?, 0, 5)" - commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : elem["participant_id"]}) + 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 # save history data diff --git a/src/server/BreCal/local_db.py b/src/server/BreCal/local_db.py index 08846e4..f6e04f7 100644 --- a/src/server/BreCal/local_db.py +++ b/src/server/BreCal/local_db.py @@ -4,8 +4,7 @@ import logging import json import os import sys -import schemas.defs as defs - +from BreCal.schemas import defs config_path = None def initPool(instancePath, connection_filename="connection_data_devel.json"): @@ -14,7 +13,7 @@ def initPool(instancePath, connection_filename="connection_data_devel.json"): if(config_path == None): config_path = os.path.join(instancePath,f'../../../secure/{connection_filename}') #connection_data_devel.json'); - config_path = "E:/temp/connection_data.json" + config_path = "C:/temp/connection_data_devel.json" print (config_path) if not os.path.exists(config_path): @@ -35,9 +34,13 @@ def initPool(instancePath, connection_filename="connection_data_devel.json"): credentials_file = "email_credentials_devel.json" credentials_path = os.path.join(instancePath,f'../../../secure/{credentials_file}') + + credentials_path = "C:/temp/email_credentials_devel.json" + if not os.path.exists(credentials_path): print ('cannot find ' + os.path.abspath(credentials_path)) sys.exit(1) + f = open(credentials_path); defs.email_credentials = json.load(f) f.close() diff --git a/src/server/BreCal/schemas/model.py b/src/server/BreCal/schemas/model.py index 4ce7603..5f07741 100644 --- a/src/server/BreCal/schemas/model.py +++ b/src/server/BreCal/schemas/model.py @@ -552,6 +552,9 @@ class User: created: datetime modified: datetime + def __hash__(self): + return hash(id) + @dataclass class Ship: id: int diff --git a/src/server/BreCal/services/schedule_routines.py b/src/server/BreCal/services/schedule_routines.py index 401e9f4..fe6d9db 100644 --- a/src/server/BreCal/services/schedule_routines.py +++ b/src/server/BreCal/services/schedule_routines.py @@ -61,7 +61,7 @@ def UpdateNotifications(): pooledConnection = getPoolConnection() commands = pydapper.using(pooledConnection) - query = "SELECT * FROM notification WHERE level = 0 AND created < DATE(NOW() - INTERVAL 10 MINUTE)" + query = "SELECT * FROM notification WHERE level = 0 AND created < TIMESTAMP(NOW() - INTERVAL 10 MINUTE)" data = commands.query(query, model=model.Notification) for notification in data: commands.execute("UPDATE notification SET level = 1 WHERE id = ?id?", param={"id":notification.id}) @@ -75,7 +75,7 @@ def SendEmails(email_dict): This function sends emails to all users in the emaildict """ try: - conn = smtplib.SMTP_SSL(defs.email_credentials["server"], defs.email_credentials["port"]) + conn = smtplib.SMTP(defs.email_credentials["server"], defs.email_credentials["port"]) conn.set_debuglevel(1) conn.ehlo() conn.starttls() @@ -91,12 +91,13 @@ def SendEmails(email_dict): # TODO: pretty-print and format message according to template msg.set_content(message) - conn.sendmail(user.user_email, user.user_email, msg.as_string()) + conn.sendmail(defs.email_credentials["sender"], user.user_email, msg.as_string()) except Exception as ex: logging.error(ex) finally: - conn.quit() + if conn is not None: + conn.quit() def SendNotifications(): @@ -134,22 +135,24 @@ def SendNotifications(): for user in users: # send notification to user if user.notify_email: + if user not in email_dict: + email_dict[user] = "" match notification.type: case 1: # assignment email_dict[user] += "\n" - email_dict[user] += "You have been assigned to a new shipcall: " + notification.message + email_dict[user] += "You have been assigned to a new shipcall: " + str(notification.message or "") case 2: # next 24 hours email_dict[user] += "\n" - email_dict[user] += "A shipcall is scheduled for the next 24 hours: " + notification.message + email_dict[user] += "A shipcall is scheduled for the next 24 hours: " + str(notification.message or "") case 3: # Time conflict email_dict[user] += "\n" - email_dict[user] += "There is a time conflict in a shipcall: " + notification.message + email_dict[user] += "There is a time conflict in a shipcall: " + str(notification.message or "") case 4: # Time conflict resolved email_dict[user] += "\n" - email_dict[user] += "A time conflict in a shipcall has been resolved: " + notification.message + email_dict[user] += "A time conflict in a shipcall has been resolved: " + str(notification.message or "") case 5: # unassigned email_dict[user] += "\n" - email_dict[user] += "You have been unassigned from a shipcall: " + notification.message + email_dict[user] += "You have been unassigned from a shipcall: " + str(notification.message or "") if user.notify_whatsapp: # TBD pass @@ -161,10 +164,13 @@ def SendNotifications(): # send emails (if any) if len(email_dict) > 0: - SendEmails(email_dict) + SendEmails(email_dict) except Exception as ex: logging.error(ex) + finally: + if pooledConnection is not None: + pooledConnection.close() def add_function_to_schedule__update_shipcalls(interval_in_minutes:int, options:dict={'past_days':2}): kwargs_ = {"options":options} @@ -207,6 +213,10 @@ def eval_next_24_hrs(): except Exception as ex: logging.error(ex) + finally: + if pooledConnection is not None: + pooledConnection.close() + return def setup_schedule(update_shipcalls_interval_in_minutes:int=60):