Added notification generation for next 24hrs shipcalls

This commit is contained in:
Daniel Schick 2024-12-12 11:10:05 +01:00
parent 50cecc6a9d
commit e5d9d051ea
2 changed files with 71 additions and 32 deletions

View File

@ -8,7 +8,7 @@ def create_sql_query_shipcall_get(options:dict)->str:
args: args:
options : dict. A dictionary, which must contains the 'past_days' key (int). Determines the range options : dict. A dictionary, which must contains the 'past_days' key (int). Determines the range
by which shipcalls are filtered. by which shipcalls are filtered.
""" """
if "participant_id" not in options: # if no participant_id is given, all shipcalls are selected if "participant_id" not in options: # if no participant_id is given, all shipcalls are selected
query = ("SELECT s.id as id, ship_id, port_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " + query = ("SELECT s.id as id, ship_id, port_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " +
@ -17,7 +17,7 @@ def create_sql_query_shipcall_get(options:dict)->str:
"evaluation_message, evaluation_time, evaluation_notifications_sent, s.created as created, s.modified as modified, time_ref_point " + "evaluation_message, evaluation_time, evaluation_notifications_sent, s.created as created, s.modified as modified, time_ref_point " +
"FROM shipcall s " + "FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " + "LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " +
"WHERE " + "WHERE " +
"(type = 1 AND " + "(type = 1 AND " +
"((t.id IS NOT NULL AND t.eta_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " + "((t.id IS NOT NULL AND t.eta_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
"(eta >= DATE(NOW() - INTERVAL %d DAY)))) OR " + "(eta >= DATE(NOW() - INTERVAL %d DAY)))) OR " +
@ -61,9 +61,9 @@ def create_sql_query_shipcall_post(schemaModel:dict)->str:
if key == "evaluation": if key == "evaluation":
continue continue
if key == "evaluation_message": if key == "evaluation_message":
continue continue
if key == "type_value": if key == "type_value":
continue continue
if key == "evaluation_value": if key == "evaluation_value":
continue continue
if isNotFirst: if isNotFirst:
@ -214,12 +214,12 @@ def create_sql_query_ship_put(schemaModel:dict):
class SQLQuery(): class SQLQuery():
""" """
This class provides quick access to different SQL query functions, which creates default queries for the BreCal package. This class provides quick access to different SQL query functions, which creates default queries for the BreCal package.
Each method is callable without initializing the SQLQuery object. Each method is callable without initializing the SQLQuery object.
Example: Example:
SQLQuery.get_berths() SQLQuery.get_berths()
When the data violates one of the rules, a marshmallow.ValidationError is raised, which details the issues. When the data violates one of the rules, a marshmallow.ValidationError is raised, which details the issues.
""" """
def __init__(self) -> None: def __init__(self) -> None:
pass pass
@ -233,30 +233,30 @@ class SQLQuery():
def get_history()->str: def get_history()->str:
query = "SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?" query = "SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?"
return query return query
@staticmethod @staticmethod
def get_user()->str: def get_user()->str:
query = "SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +\ query = "SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +\
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +\ "api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +\
"WHERE user_name = ?username? OR user_email = ?username?" "WHERE user_name = ?username? OR user_email = ?username?"
return query return query
@staticmethod @staticmethod
def get_notifications()->str: def get_notifications()->str:
query = "SELECT id, shipcall_id, level, type, message, created, modified FROM notification " + \ query = "SELECT id, shipcall_id, level, type, message, created, modified FROM notification " + \
"WHERE shipcall_id = ?scid?" "WHERE shipcall_id = ?scid?"
return query return query
@staticmethod @staticmethod
def get_participant_by_user_id()->str: def get_participant_by_user_id()->str:
query = "SELECT p.id as id, p.name as name, p.street as street, p.postal_code as postal_code, p.city as city, p.type as type, p.flags as flags, p.created as created, p.modified as modified, p.deleted as deleted FROM participant p INNER JOIN user u WHERE u.participant_id = p.id and u.id = ?userid?" query = "SELECT p.id as id, p.name as name, p.street as street, p.postal_code as postal_code, p.city as city, p.type as type, p.flags as flags, p.created as created, p.modified as modified, p.deleted as deleted FROM participant p INNER JOIN user u WHERE u.participant_id = p.id and u.id = ?userid?"
return query return query
@staticmethod @staticmethod
def get_participants()->str: def get_participants()->str:
query = "SELECT id, name, street, postal_code, city, type, flags, created, modified, deleted FROM participant p ORDER BY p.name" query = "SELECT id, name, street, postal_code, city, type, flags, created, modified, deleted FROM participant p ORDER BY p.name"
return query return query
@staticmethod @staticmethod
def get_shipcalls(options:dict={'past_days':3})->str: def get_shipcalls(options:dict={'past_days':3})->str:
# a pytest proves this method to be identical to create_sql_query_shipcall_get(options) # a pytest proves this method to be identical to create_sql_query_shipcall_get(options)
@ -278,17 +278,26 @@ class SQLQuery():
f"(etd >= DATE(NOW() - INTERVAL {past_days} DAY)))) " + \ f"(etd >= DATE(NOW() - INTERVAL {past_days} DAY)))) " + \
"ORDER BY eta") "ORDER BY eta")
return query return query
def get_next24hrs_shipcalls()->str:
query = ("SELECT s.id as id, ship.name as name FROM shipcall s INNER JOIN ship ON s.ship_id = ship.id LEFT JOIN times t on t.shipcall_id = s.id AND t.participant_type = 8 " + \
"WHERE (type = 1 AND ((t.id IS NOT NULL AND t.eta_berth >= NOW() AND t.eta_berth < (NOW() + INTERVAL 1 DAY))" + \
"OR (eta >= NOW() AND eta < (NOW() + INTERVAL 1 DAY)))) OR " + \
"((type = 2 OR type = 3) AND ((t.id IS NOT NULL AND t.etd_berth >= NOW() AND " + \
"t.etd_berth < (NOW() + INTERVAL 1 DAY)) OR (etd >= NOW() AND etd < (NOW() + INTERVAL 1 DAY))))" + \
"AND s.canceled = 0")
return query
@staticmethod @staticmethod
def get_ships()->str: def get_ships()->str:
query = "SELECT id, name, imo, callsign, participant_id, length, width, is_tug, bollard_pull, eni, created, modified, deleted FROM ship ORDER BY name" query = "SELECT id, name, imo, callsign, participant_id, length, width, is_tug, bollard_pull, eni, created, modified, deleted FROM ship ORDER BY name"
return query return query
@staticmethod @staticmethod
def get_ship_by_id()->str: def get_ship_by_id()->str:
query = "SELECT * FROM ship where id = ?id?" query = "SELECT * FROM ship where id = ?id?"
return query return query
@staticmethod @staticmethod
def get_times()->str: def get_times()->str:
query = "SELECT id, eta_berth, eta_berth_fixed, etd_berth, etd_berth_fixed, lock_time, lock_time_fixed, " + \ query = "SELECT id, eta_berth, eta_berth_fixed, etd_berth, etd_berth_fixed, lock_time, lock_time_fixed, " + \
@ -296,12 +305,12 @@ class SQLQuery():
"berth_id, berth_info, pier_side, participant_type, created, modified, ata, atd, eta_interval_end, etd_interval_end FROM times " + \ "berth_id, berth_info, pier_side, participant_type, created, modified, ata, atd, eta_interval_end, etd_interval_end FROM times " + \
"WHERE times.shipcall_id = ?scid?" "WHERE times.shipcall_id = ?scid?"
return query return query
@staticmethod @staticmethod
def get_user_by_id(): def get_user_by_id():
query = "SELECT * FROM user where id = ?id?" query = "SELECT * FROM user where id = ?id?"
return query return query
@staticmethod @staticmethod
def get_user_put(schemaModel:dict): def get_user_put(schemaModel:dict):
# a pytest proves this method to be identical to create_sql_query_user_put(schemaModel) # a pytest proves this method to be identical to create_sql_query_user_put(schemaModel)
@ -326,7 +335,7 @@ class SQLQuery():
def get_participant_from_id()->str: def get_participant_from_id()->str:
query = "SELECT id, type, flags FROM participant WHERE id=?participant_id?" query = "SELECT id, type, flags FROM participant WHERE id=?participant_id?"
return query return query
@staticmethod @staticmethod
def get_shipcall_post(schemaModel:dict)->str: def get_shipcall_post(schemaModel:dict)->str:
# a pytest proves this method to be identical to create_sql_query_shipcall_post(schemaModel) # a pytest proves this method to be identical to create_sql_query_shipcall_post(schemaModel)
@ -349,28 +358,28 @@ class SQLQuery():
def get_last_insert_id()->str: def get_last_insert_id()->str:
query = "select last_insert_id()" query = "select last_insert_id()"
return query return query
@staticmethod @staticmethod
def get_shipcall_post_last_insert_id()->str: def get_shipcall_post_last_insert_id()->str:
"""alias function. May be deleted soon""" """alias function. May be deleted soon"""
query = SQLQuery.get_last_insert_id() query = SQLQuery.get_last_insert_id()
return query return query
@staticmethod @staticmethod
def get_shipcall_post_update_shipcall_participant_map()->str: def get_shipcall_post_update_shipcall_participant_map()->str:
query = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)" query = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
return query return query
@staticmethod @staticmethod
def create_sql_query_history_post()->str: def create_sql_query_history_post()->str:
query = create_sql_query_history_post() query = create_sql_query_history_post()
return query return query
@staticmethod @staticmethod
def get_shipcall_by_id()->str: def get_shipcall_by_id()->str:
query = "SELECT * FROM shipcall where id = ?id?" query = "SELECT * FROM shipcall where id = ?id?"
return query return query
@staticmethod @staticmethod
def get_shipcall_put(schemaModel:dict)->str: def get_shipcall_put(schemaModel:dict)->str:
# a pytest proves this method to be identical to create_sql_query_shipcall_put(schemaModel) # a pytest proves this method to be identical to create_sql_query_shipcall_put(schemaModel)
@ -384,12 +393,12 @@ class SQLQuery():
query = prefix + body + suffix query = prefix + body + suffix
return query return query
@staticmethod @staticmethod
def get_shipcall_participant_map_by_shipcall_id()->str: def get_shipcall_participant_map_by_shipcall_id()->str:
query = "SELECT id, participant_id, type FROM shipcall_participant_map where shipcall_id = ?id?" query = "SELECT id, participant_id, type FROM shipcall_participant_map where shipcall_id = ?id?"
return query return query
@staticmethod @staticmethod
def get_shipcall_participant_map_by_shipcall_id_and_type()->str: def get_shipcall_participant_map_by_shipcall_id_and_type()->str:
query = "SELECT id, participant_id FROM shipcall_participant_map where (shipcall_id = ?id? AND type=?type?)" query = "SELECT id, participant_id FROM shipcall_participant_map where (shipcall_id = ?id? AND type=?type?)"
@ -432,14 +441,14 @@ class SQLQuery():
def get_ship_delete_by_id()->str: def get_ship_delete_by_id()->str:
query = "UPDATE ship SET deleted = 1 WHERE id = ?id?" query = "UPDATE ship SET deleted = 1 WHERE id = ?id?"
return query return query
@staticmethod @staticmethod
def get_notification_post()->str: def get_notification_post()->str:
raise NotImplementedError() raise NotImplementedError()
# #TODO: this query is wrong and just a proxy for a POST request # #TODO: this query is wrong and just a proxy for a POST request
query = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)" query = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
return query return query
@staticmethod @staticmethod
def get_shipcall_put_notification_state()->str: def get_shipcall_put_notification_state()->str:
raise NotImplementedError() raise NotImplementedError()

View File

@ -4,6 +4,8 @@ from BreCal.schemas import model
from BreCal.local_db import getPoolConnection from BreCal.local_db import getPoolConnection
from BreCal.database.update_database import evaluate_shipcall_state from BreCal.database.update_database import evaluate_shipcall_state
from BreCal.database.sql_queries import create_sql_query_shipcall_get from BreCal.database.sql_queries import create_sql_query_shipcall_get
from BreCal.database.sql_queries import SQLQuery
from BreCal.database.sql_utils import get_notification_for_shipcall_and_type
import threading import threading
import schedule import schedule
@ -58,12 +60,7 @@ def UpdateNotifications():
query = "SELECT * FROM notification WHERE level = 0 AND created < DATE(NOW() - INTERVAL 10 MINUTE)" query = "SELECT * FROM notification WHERE level = 0 AND created < DATE(NOW() - INTERVAL 10 MINUTE)"
data = commands.query(query, model=model.Notification) data = commands.query(query, model=model.Notification)
for notification in data: for notification in data:
notification.level = 1 commands.execute("UPDATE notification SET level = 1 WHERE id = ?id?", param={"id":notification.id})
commands.update(notification)
# TODO
# send "real" notifications to user
# ... E-Mail, messenger
pooledConnection.close() pooledConnection.close()
except Exception as ex: except Exception as ex:
@ -82,6 +79,35 @@ def add_function_to_schedule__send_notifications(vr, interval_in_minutes:int=10)
schedule.every(interval_in_minutes).minutes.do(vr.notifier.send_notifications) schedule.every(interval_in_minutes).minutes.do(vr.notifier.send_notifications)
return return
def eval_next_24_hrs():
try:
pooledConnection = getPoolConnection()
commands = pydapper.using(pooledConnection)
query = SQLQuery.get_next24hrs_shipcalls()
data = commands.query(query)
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 2, ?message?)"
for shipcall in data:
existing_notifications = get_notification_for_shipcall_and_type(shipcall["id"], 2)
query = SQLQuery.get_shipcall_participant_map_by_shipcall_id()
participants = commands.query(query, model=dict, param={"id":shipcall["id"]})
for participant in participants:
if participant["type"] == 1: # BSMD
continue
if participant["type"] == 32: # PORT AUTHORITY
continue
# check if "open" notification already exists
found_notification = False
for existing_notification in existing_notifications:
if existing_notification["participant_id"] == participant["id"] and existing_notification["level"] == 0:
found_notification = True
break
if not found_notification:
commands.execute(nquery, param={"shipcall_id":shipcall["id"], "participant_id": participant["participant_id"], "message":shipcall["name"]})
except Exception as ex:
logging.error(ex)
return
def setup_schedule(update_shipcalls_interval_in_minutes:int=60): def setup_schedule(update_shipcalls_interval_in_minutes:int=60):
@ -94,9 +120,13 @@ def setup_schedule(update_shipcalls_interval_in_minutes:int=60):
add_function_to_evaluate_notifications() add_function_to_evaluate_notifications()
schedule.every().day.at("09:00").do(eval_next_24_hrs)
# placeholder: create/send notifications # placeholder: create/send notifications
# add_function_to_schedule__send_notifications(...) # add_function_to_schedule__send_notifications(...)
# TODO: Add schedule function to evaluate all notifications in level 1 and create actions
return return