Added logging and a lock (threading) to make sure all notifications are created

This commit is contained in:
Daniel Schick 2025-12-15 17:56:41 +01:00
parent d6becc43ea
commit 39001b37a3

View File

@ -5,11 +5,13 @@ import re
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import datetime import datetime
import threading
from BreCal.database.enums import StatusFlags from BreCal.database.enums import StatusFlags
from BreCal.validators.validation_rule_functions import ValidationRuleFunctions from BreCal.validators.validation_rule_functions import ValidationRuleFunctions
from BreCal.schemas.model import Shipcall from BreCal.schemas.model import Shipcall
from BreCal.local_db import getPoolConnection from BreCal.local_db import getPoolConnection
_evaluation_lock = threading.Lock()
class ValidationRules(ValidationRuleFunctions): class ValidationRules(ValidationRuleFunctions):
""" """
@ -74,7 +76,10 @@ class ValidationRules(ValidationRuleFunctions):
return evaluation_state, violations return evaluation_state, violations
def evaluate_shipcalls(self, shipcall_df:pd.DataFrame)->pd.DataFrame: def evaluate_shipcalls(self, shipcall_df:pd.DataFrame)->pd.DataFrame:
"""apply 'evaluate_shipcall_from_df' to each individual shipcall in {shipcall_df}. Returns shipcall_df ('evaluation', 'evaluation_message', 'evaluation_time' and 'evaluation_notifications_sent' are updated)"""
# Acquire lock to prevent race conditions during evaluation and notification creation
with _evaluation_lock:
"""apply 'evaluate_shipcall_from_df' to each individual shipcall in {shipcall_df}. Returnsshipcall_df ('evaluation', 'evaluation_message', 'evaluation_time' and 'evaluation_notifications_sent' are updated)"""
evaluation_states_old = [state_old for state_old in shipcall_df.loc[:,"evaluation"]] evaluation_states_old = [state_old for state_old in shipcall_df.loc[:,"evaluation"]]
evaluation_states_old = [state_old if not pd.isna(state_old) else 0 for state_old in evaluation_states_old] evaluation_states_old = [state_old if not pd.isna(state_old) else 0 for state_old in evaluation_states_old]
results = shipcall_df.apply(lambda x: self.evaluate_shipcall_from_df(x), axis=1).values # returns tuple (state, message) results = shipcall_df.apply(lambda x: self.evaluate_shipcall_from_df(x), axis=1).values # returns tuple (state, message)
@ -96,7 +101,7 @@ class ValidationRules(ValidationRuleFunctions):
for shipcall_id, state_old_raw, state_new_raw, violation in zip(shipcall_df.index, evaluation_states_old, evaluation_states_new, violations): for shipcall_id, state_old_raw, state_new_raw, violation in zip(shipcall_df.index, evaluation_states_old, evaluation_states_new, violations):
state_old = int(state_old_raw) if state_old_raw is not None else 0 state_old = int(state_old_raw) if state_old_raw is not None else 0
state_new = int(state_new_raw) if state_new_raw is not None else 0 state_new = int(state_new_raw) if state_new_raw is not None else 0
logging.info(f"Shipcall {shipcall_id}: state_old={state_old}, state_new={state_new}")
if state_old == state_new: if state_old == state_new:
continue continue
@ -120,13 +125,17 @@ class ValidationRules(ValidationRuleFunctions):
send_notification = True send_notification = True
if send_notification: if send_notification:
logging.info(f"Creating notification for shipcall {shipcall_id}, type={notification_type}")
query = f"INSERT INTO notification (shipcall_id, type, level, message) VALUES (?shipcall_id?, {notification_type}, 0, ?message?)" query = f"INSERT INTO notification (shipcall_id, type, level, message) VALUES (?shipcall_id?, {notification_type}, 0, ?message?)"
commands.execute(query, param={"shipcall_id" : int(shipcall_id), "message" : violation}) commands.execute(query, param={"shipcall_id" : int(shipcall_id), "message" : violation})
if state_new == 1 and state_old != 0: # this resolves the conflict if state_new == 1 and state_old != 0: # this resolves the time conflict
logging.info(f"Resolving notifications for shipcall {shipcall_id}, type={notification_type}")
query = f"SELECT * from notification where shipcall_id = ?shipcall_id? and type = {notification_type} and level = 0" query = f"SELECT * from notification where shipcall_id = ?shipcall_id? and type = {notification_type} and level = 0"
existing_notification = commands.query(query, param={"shipcall_id" : int(shipcall_id)}) existing_notification = commands.query(query, param={"shipcall_id" : int(shipcall_id)})
logging.info(f"Found {len(existing_notification)} existing notifications (yet unsent)")
if len(existing_notification) > 0: if len(existing_notification) > 0:
logging.info(f"Deleting notification id={existing_notification[0]['id']}")
query = "DELETE from notification where id = ?id?" query = "DELETE from notification where id = ?id?"
commands.execute(query, param={"id" : existing_notification[0]["id"]}) commands.execute(query, param={"id" : existing_notification[0]["id"]})
else: else: