diff --git a/src/server/BreCal/database/sql_queries.py b/src/server/BreCal/database/sql_queries.py index 6d2c9d5..ade5b26 100644 --- a/src/server/BreCal/database/sql_queries.py +++ b/src/server/BreCal/database/sql_queries.py @@ -8,7 +8,7 @@ def create_sql_query_shipcall_get(options:dict)->str: args: 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 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 " + "FROM shipcall s " + "LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " + - "WHERE " + + "WHERE " + "(type = 1 AND " + "((t.id IS NOT NULL AND t.eta_berth >= 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": continue if key == "evaluation_message": - continue + continue if key == "type_value": - continue + continue if key == "evaluation_value": continue if isNotFirst: @@ -214,12 +214,12 @@ def create_sql_query_ship_put(schemaModel:dict): class SQLQuery(): """ 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: 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: pass @@ -233,30 +233,30 @@ class SQLQuery(): def get_history()->str: query = "SELECT id, participant_id, shipcall_id, timestamp, eta, type, operation FROM history WHERE shipcall_id = ?shipcallid?" return query - + @staticmethod def get_user()->str: 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 " +\ "WHERE user_name = ?username? OR user_email = ?username?" return query - + @staticmethod def get_notifications()->str: query = "SELECT id, shipcall_id, level, type, message, created, modified FROM notification " + \ "WHERE shipcall_id = ?scid?" return query - + @staticmethod 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?" return query - + @staticmethod def get_participants()->str: query = "SELECT id, name, street, postal_code, city, type, flags, created, modified, deleted FROM participant p ORDER BY p.name" return query - + @staticmethod def get_shipcalls(options:dict={'past_days':3})->str: # 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)))) " + \ "ORDER BY eta") 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 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" return query - + @staticmethod def get_ship_by_id()->str: query = "SELECT * FROM ship where id = ?id?" return query - + @staticmethod def get_times()->str: 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 " + \ "WHERE times.shipcall_id = ?scid?" return query - + @staticmethod def get_user_by_id(): query = "SELECT * FROM user where id = ?id?" return query - + @staticmethod def get_user_put(schemaModel:dict): # 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: query = "SELECT id, type, flags FROM participant WHERE id=?participant_id?" return query - + @staticmethod def get_shipcall_post(schemaModel:dict)->str: # 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: query = "select last_insert_id()" return query - + @staticmethod def get_shipcall_post_last_insert_id()->str: """alias function. May be deleted soon""" query = SQLQuery.get_last_insert_id() return query - + @staticmethod 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?)" return query - + @staticmethod def create_sql_query_history_post()->str: query = create_sql_query_history_post() return query - + @staticmethod def get_shipcall_by_id()->str: query = "SELECT * FROM shipcall where id = ?id?" return query - + @staticmethod def get_shipcall_put(schemaModel:dict)->str: # 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 return query - + @staticmethod def get_shipcall_participant_map_by_shipcall_id()->str: query = "SELECT id, participant_id, type FROM shipcall_participant_map where shipcall_id = ?id?" return query - + @staticmethod 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?)" @@ -432,14 +441,14 @@ class SQLQuery(): def get_ship_delete_by_id()->str: query = "UPDATE ship SET deleted = 1 WHERE id = ?id?" return query - + @staticmethod def get_notification_post()->str: raise NotImplementedError() # #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?)" return query - + @staticmethod def get_shipcall_put_notification_state()->str: raise NotImplementedError() diff --git a/src/server/BreCal/services/schedule_routines.py b/src/server/BreCal/services/schedule_routines.py index 33e19fa..560badc 100644 --- a/src/server/BreCal/services/schedule_routines.py +++ b/src/server/BreCal/services/schedule_routines.py @@ -4,6 +4,8 @@ from BreCal.schemas import model from BreCal.local_db import getPoolConnection 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 SQLQuery +from BreCal.database.sql_utils import get_notification_for_shipcall_and_type import threading import schedule @@ -58,12 +60,7 @@ def UpdateNotifications(): query = "SELECT * FROM notification WHERE level = 0 AND created < DATE(NOW() - INTERVAL 10 MINUTE)" data = commands.query(query, model=model.Notification) for notification in data: - notification.level = 1 - commands.update(notification) - - # TODO - # send "real" notifications to user - # ... E-Mail, messenger + commands.execute("UPDATE notification SET level = 1 WHERE id = ?id?", param={"id":notification.id}) pooledConnection.close() 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) 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): @@ -94,9 +120,13 @@ def setup_schedule(update_shipcalls_interval_in_minutes:int=60): add_function_to_evaluate_notifications() + schedule.every().day.at("09:00").do(eval_next_24_hrs) + # placeholder: create/send notifications # add_function_to_schedule__send_notifications(...) + # TODO: Add schedule function to evaluate all notifications in level 1 and create actions + return