clearing older methods of the Notifier class. There is now a one-line function, which connects to the Email-server, populates candidates for notifications, creates those notifications one-by-one and sends them. Finally, the database is updated, so a notification is sent only once.
This commit is contained in:
parent
9cef84a5a8
commit
1bdfa8997f
@ -5,12 +5,14 @@ from BreCal.database.sql_queries import SQLQuery
|
|||||||
from BreCal.schemas import model
|
from BreCal.schemas import model
|
||||||
from BreCal.brecal_utils.time_handling import difference_to_then
|
from BreCal.brecal_utils.time_handling import difference_to_then
|
||||||
|
|
||||||
|
from BreCal.services.email_handling import EmailHandler, create_shipcall_evaluation_notification, send_notification
|
||||||
|
from BreCal.database.enums import ParticipantwiseTimeDelta
|
||||||
|
|
||||||
from BreCal.schemas.model import ShipcallType
|
from BreCal.stubs.email_template import get_default_html_email
|
||||||
eta_etd_type_dict = {
|
eta_etd_type_dict = {
|
||||||
ShipcallType.arrival : "Ankunft",
|
model.ShipcallType.arrival : "Ankunft",
|
||||||
ShipcallType.departure : "Abfahrt",
|
model.ShipcallType.departure : "Abfahrt",
|
||||||
ShipcallType.shifting : "Wechselnd"
|
model.ShipcallType.shifting : "Wechselnd"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -46,107 +48,39 @@ class Notifier():
|
|||||||
- iterate over each remaining shipcall and apply .send_notification
|
- iterate over each remaining shipcall and apply .send_notification
|
||||||
- those which are unsent, shall be sent by the respective type
|
- those which are unsent, shall be sent by the respective type
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("skeleton")
|
# set a threshold, when alarm event notifications become eligible
|
||||||
|
time_diff_threshold = float(ParticipantwiseTimeDelta.NOTIFICATION)*60 # m minutes, converted to seconds
|
||||||
# get all shipcalls
|
|
||||||
all_shipcalls = NotImplementedError
|
|
||||||
|
|
||||||
shipcalls = [shipcall for shipcall in all_shipcalls if not shipcall.evaluation_notifications_sent]
|
|
||||||
for shipcall in shipcalls:
|
|
||||||
notification_list = Notifier.send_notification(shipcall, is_test=is_test)
|
|
||||||
|
|
||||||
# #TODO: get all notifications
|
|
||||||
# #TODO: get matching shipcall (based on shipcall_id)
|
|
||||||
|
|
||||||
# #TODO: filter: consider only those, which are not yet sent
|
|
||||||
|
|
||||||
# identify necessity
|
|
||||||
# #TODO: get the 'evaluation_notifications_sent' field from all shipcalls (based on shipcall_id)
|
|
||||||
# if not -> return
|
|
||||||
# USE shipcall.evaluation_notifications_sent
|
|
||||||
|
|
||||||
# #TODO: those which are unsent, shall be created&sent by the respective type -- Note: consider the is_test argument
|
|
||||||
# iterate over the list of Notifier.build_notification_type_list
|
|
||||||
# one might use Notifier.create(..., update_database=True)
|
|
||||||
# use the History (GetHistory -- by shipcall_id) to identify all subscribed users
|
|
||||||
|
|
||||||
# #TODO: update the shipcall dataset ('evaluation_notifications_sent') -- Note: consider the is_test argument
|
|
||||||
|
|
||||||
# #TODO_clarify: how to handle the 'evaluation_notifications_sent', when there is no recipient?
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def send_notification(shipcall:model.Shipcall, is_test:bool=False)->list[model.Notification]:
|
|
||||||
"""
|
|
||||||
Complex-function, which is responsible of creating notification messages, issuing them to users and optionally updating
|
|
||||||
the database. The requirement is, that the notification is required and passes through an internal set of filters.
|
|
||||||
|
|
||||||
Steps:
|
|
||||||
- get all notifications of shipcall_id
|
|
||||||
- identify the assigned list of users
|
|
||||||
- apply all filters. When a filter triggers, exit. If not, create and send a notification.
|
|
||||||
"""
|
|
||||||
update_database = False if is_test else True
|
|
||||||
# #TODO: the concept of old state and new state must be refactored.
|
|
||||||
# old state: read shipcall_id from notifications and look for the latest finding (if None -> EvaluationType.undefined)
|
|
||||||
# new state: read shipcall_id from shipcalls and look for the *current* 'evaluation' (-> EvaluationType(value))
|
|
||||||
|
|
||||||
# get existing notifications by shipcall_id (list)
|
|
||||||
existing_notifications = Notifier.get_existing_notifications(shipcall_id=shipcall.id)
|
|
||||||
old_state = NotImplementedError
|
|
||||||
|
|
||||||
new_state = shipcall.evaluation
|
|
||||||
|
|
||||||
# get User by querying all History objects of a shipcall_id
|
|
||||||
users = Notifier.get_users_via_history(shipcall_id=shipcall.id)
|
|
||||||
|
|
||||||
# identify necessity
|
|
||||||
# state-check: Did the 'evaluation' shift to a higher level of severity?
|
|
||||||
severity_bool = Notifier.check_higher_severity(old_state, new_state)
|
|
||||||
if not severity_bool:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
debug = is_test # if is_test, the Emails will not be issued. Only a print message will be created.
|
||||||
# #TODO: time-based filter. There shall be 'enough' time between the evaluation time and NOW
|
update_database = True if not is_test else False # if_test, the database will not be updated.
|
||||||
evaluation_time = shipcall.evaluation_time
|
time_diff_threshold = time_diff_threshold if not is_test else 0.0 # 0.0 delay when is_test is set.
|
||||||
# latency_bool = #TODO_DIFFERENCE_FROM_NOW_TO_EVALUATION_TIME____THIS_METHOD_ALREADY_EXISTS(evaluation_time)
|
|
||||||
# careful: what is True, what is False?
|
|
||||||
# if latency_booL:
|
|
||||||
# return None
|
|
||||||
|
|
||||||
notification_list = []
|
email_handler = EmailHandler(mail_server='w01d5503.kasserver.com', mail_port=465, mail_address="max.metz@scope-sorting.com")
|
||||||
for user in users:
|
pwd = b'gAAAAABmqJlkXbtJTL1tFiyQNHhF_Y7sgtVI0xEx07ybwbX70Ro1Vp73CLDq49eFSYG-1SswIDQ2JBSORYlWaR-Vh2kIwPHy_lX8SxkySrRvBRzkyZP5x0I='
|
||||||
notification = Notifier.create(
|
|
||||||
shipcall.id,
|
|
||||||
old_state,
|
|
||||||
new_state,
|
|
||||||
user,
|
|
||||||
update_database=update_database,
|
|
||||||
is_test=is_test
|
|
||||||
)
|
|
||||||
notification_list.append(notification)
|
|
||||||
return notification_list
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def publish(shipcall_id, old_state, new_state, user, update_database:bool=False)->typing.Optional[model.Notification]:
|
|
||||||
"""
|
|
||||||
Complex-function, which creates, sends and documents a notification. It serves as a convenience function.
|
|
||||||
The method does not apply internal filters to identify, whether a notification should be created in the first place.
|
|
||||||
|
|
||||||
options:
|
try:
|
||||||
update_database: bool.
|
# login in advance, so the email handler uses a shared connection. It disconnects only once at the end of the call.
|
||||||
# #TODO: instead of update_database, one may also use is_test
|
email_handler.login(interactive=False, pwd=pwd)
|
||||||
"""
|
|
||||||
# 1.) create
|
|
||||||
# ... = Notifier.create(shipcall_id, old_state, new_state, user) # e.g., might return a dictionary of dict[model.NotificationType, str], where str is the message
|
|
||||||
|
|
||||||
# 2.) send
|
# get candidates: find all eligible shipcalls, where the evaluation state is yellow or red & the notifications are not yet sent
|
||||||
# ... = Notifier.send(...) # should contain internal 'logistics', which user the respective handlers to send notifications
|
eligible_shipcalls = Notifier.get_eligible_shipcalls()
|
||||||
|
|
||||||
|
# find all notifications, which belong to the shipcall ids of the eligible_shipcall list
|
||||||
|
# a time_diff_threshold is used to block those notifications, which are still fairly novel
|
||||||
|
eligible_notifications = Notifier.get_eligible_notifications(eligible_shipcalls, time_diff_threshold)
|
||||||
|
|
||||||
|
for notification in eligible_notifications:
|
||||||
|
# get all users, which are attached to the shipcall (uses the History dataset)
|
||||||
|
users = Notifier.get_users_via_history(notification.shipcall_id)
|
||||||
|
|
||||||
|
# filter: only consider the users, which have subscribed to the notification type
|
||||||
|
users = [user for user in users if Notifier.check_user_is_subscribed_to_notification_type(user,notification_type=notification.type)]
|
||||||
|
|
||||||
|
# obtain the mail address of each respective user
|
||||||
|
Notifier.create_and_send_email_notification(email_handler, pwd, users, notification, update_database=update_database, debug=debug)
|
||||||
|
finally:
|
||||||
|
email_handler.close()
|
||||||
|
|
||||||
# 3.) document (mysql database)
|
|
||||||
# if update_database
|
|
||||||
# ... = Notifier.document(...)
|
|
||||||
raise NotImplementedError("skeleton")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -422,7 +356,7 @@ class Notifier():
|
|||||||
# use ship & shipcall data models to prepare the body
|
# use ship & shipcall data models to prepare the body
|
||||||
ship_name = ship.name
|
ship_name = ship.name
|
||||||
eta_etd = Notifier.create_etaetd_string(shipcall.eta, shipcall.etd)
|
eta_etd = Notifier.create_etaetd_string(shipcall.eta, shipcall.etd)
|
||||||
eta_etd_type = eta_etd_type_dict[ShipcallType(shipcall.type)]
|
eta_etd_type = eta_etd_type_dict[model.ShipcallType(shipcall.type)]
|
||||||
evaluation_message = shipcall.evaluation_message
|
evaluation_message = shipcall.evaluation_message
|
||||||
return (ship_name, evaluation_message, eta_etd, eta_etd_type)
|
return (ship_name, evaluation_message, eta_etd, eta_etd_type)
|
||||||
|
|
||||||
@ -435,6 +369,25 @@ class Notifier():
|
|||||||
|
|
||||||
schemas = execute_sql_query_standalone(query=query, param=schemaModel, command_type="execute")
|
schemas = execute_sql_query_standalone(query=query, param=schemaModel, command_type="execute")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_and_send_email_notification(email_handler:EmailHandler, pwd:bytes, users:list[model.User], notification:model.Notification, update_database:bool=True, debug:bool=False):
|
||||||
|
email_tgts = [user.user_email for user in users if user.user_email is not None]
|
||||||
|
|
||||||
|
ship_name, evaluation_message, eta_etd, eta_etd_type = Notifier.prepare_notification_body(notification)
|
||||||
|
|
||||||
|
content = get_default_html_email()
|
||||||
|
files = [] # optional attachments
|
||||||
|
msg_multipart,msg_content = create_shipcall_evaluation_notification(
|
||||||
|
email_handler, ship_name, evaluation_message, eta_etd, eta_etd_type, content, files=files
|
||||||
|
)
|
||||||
|
|
||||||
|
# send the messages via smtlib's SSL functions
|
||||||
|
send_notification(email_handler, email_tgts, msg_multipart, pwd, debug=debug)
|
||||||
|
|
||||||
|
if update_database:
|
||||||
|
Notifier.shipcall_put_update_evaluation_notifications_sent_flag(notification)
|
||||||
|
return
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_user_is_subscribed_to_notification_type(user,notification_type):
|
def check_user_is_subscribed_to_notification_type(user,notification_type):
|
||||||
|
|||||||
@ -287,7 +287,9 @@ def create_shipcall_evaluation_notification(email_handler, ship_name:str, evalua
|
|||||||
return (msg_multipart,content)
|
return (msg_multipart,content)
|
||||||
|
|
||||||
def send_notification(email_handler, email_tgts, msg, pwd, debug=False):
|
def send_notification(email_handler, email_tgts, msg, pwd, debug=False):
|
||||||
email_handler.login(interactive=False, pwd=pwd)
|
already_logged_in = email_handler.check_login()
|
||||||
|
if not already_logged_in:
|
||||||
|
email_handler.login(interactive=False, pwd=pwd)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assert email_handler.check_login()
|
assert email_handler.check_login()
|
||||||
@ -297,5 +299,6 @@ def send_notification(email_handler, email_tgts, msg, pwd, debug=False):
|
|||||||
print(f"(send_notification INFO): debugging state. Would have sent an Email to: {email_tgts}")
|
print(f"(send_notification INFO): debugging state. Would have sent an Email to: {email_tgts}")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
email_handler.close()
|
if not already_logged_in:
|
||||||
|
email_handler.close()
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user