Reduced log-verbosity on the server
This commit is contained in:
parent
3d76acb2f0
commit
189626d61c
@ -15,13 +15,13 @@ from email.mime.application import MIMEApplication
|
|||||||
|
|
||||||
class EmailHandler():
|
class EmailHandler():
|
||||||
"""
|
"""
|
||||||
Creates an EmailHandler, which is capable of connecting to a mail server at a respective port,
|
Creates an EmailHandler, which is capable of connecting to a mail server at a respective port,
|
||||||
as well as logging into a specific user's mail address.
|
as well as logging into a specific user's mail address.
|
||||||
Upon creating messages, these can be sent via this handler.
|
Upon creating messages, these can be sent via this handler.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
mail_server: address of the server, such as 'smtp.gmail.com' or 'w01d5503.kasserver.com
|
mail_server: address of the server, such as 'smtp.gmail.com' or 'w01d5503.kasserver.com
|
||||||
mail_port:
|
mail_port:
|
||||||
25 - SMTP Port, to send emails
|
25 - SMTP Port, to send emails
|
||||||
110 - POP3 Port, to receive emails
|
110 - POP3 Port, to receive emails
|
||||||
143 - IMAP Port, to receive from IMAP
|
143 - IMAP Port, to receive from IMAP
|
||||||
@ -38,6 +38,9 @@ class EmailHandler():
|
|||||||
|
|
||||||
self.server = smtplib.SMTP_SSL(self.mail_server, self.mail_port) # alternatively, SMTP
|
self.server = smtplib.SMTP_SSL(self.mail_server, self.mail_port) # alternatively, SMTP
|
||||||
|
|
||||||
|
# set the following to 0 to avoid log spamming
|
||||||
|
self.server.set_debuglevel(1) # 0: no debug, 1: debug
|
||||||
|
|
||||||
def check_state(self):
|
def check_state(self):
|
||||||
"""check, whether the server login took place and is open."""
|
"""check, whether the server login took place and is open."""
|
||||||
try:
|
try:
|
||||||
@ -45,7 +48,7 @@ class EmailHandler():
|
|||||||
return status_code==250 # 250: b'2.0.0 Ok'
|
return status_code==250 # 250: b'2.0.0 Ok'
|
||||||
except smtplib.SMTPServerDisconnected:
|
except smtplib.SMTPServerDisconnected:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_connection(self):
|
def check_connection(self):
|
||||||
"""check, whether the server object is connected to the server. If not, connect it. """
|
"""check, whether the server object is connected to the server. If not, connect it. """
|
||||||
try:
|
try:
|
||||||
@ -53,7 +56,7 @@ class EmailHandler():
|
|||||||
except smtplib.SMTPServerDisconnected:
|
except smtplib.SMTPServerDisconnected:
|
||||||
self.server.connect(self.mail_server, self.mail_port)
|
self.server.connect(self.mail_server, self.mail_port)
|
||||||
return
|
return
|
||||||
|
|
||||||
def check_login(self)->bool:
|
def check_login(self)->bool:
|
||||||
"""check, whether the server object is logged in as a user"""
|
"""check, whether the server object is logged in as a user"""
|
||||||
user = self.server.__dict__.get("user",None)
|
user = self.server.__dict__.get("user",None)
|
||||||
@ -61,8 +64,8 @@ class EmailHandler():
|
|||||||
|
|
||||||
def login(self, interactive:bool=True):
|
def login(self, interactive:bool=True):
|
||||||
"""
|
"""
|
||||||
login on the determined mail server's mail address. By default, this function opens an interactive window to
|
login on the determined mail server's mail address. By default, this function opens an interactive window to
|
||||||
type the password without echoing (printing '*******' instead of readable characters).
|
type the password without echoing (printing '*******' instead of readable characters).
|
||||||
|
|
||||||
returns (status_code, status_msg)
|
returns (status_code, status_msg)
|
||||||
"""
|
"""
|
||||||
@ -77,7 +80,7 @@ class EmailHandler():
|
|||||||
def create_email(self, subject:str, message_body:str)->EmailMessage:
|
def create_email(self, subject:str, message_body:str)->EmailMessage:
|
||||||
"""
|
"""
|
||||||
Create an EmailMessage object, which contains the Email's header ("Subject"), content ("Message Body") and the sender's address ("From").
|
Create an EmailMessage object, which contains the Email's header ("Subject"), content ("Message Body") and the sender's address ("From").
|
||||||
The EmailMessage object does not contain the recipients yet, as these will be defined upon sending the Email.
|
The EmailMessage object does not contain the recipients yet, as these will be defined upon sending the Email.
|
||||||
"""
|
"""
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
@ -85,16 +88,16 @@ class EmailHandler():
|
|||||||
#msg["To"] = email_tgts # will be defined in self.send_email
|
#msg["To"] = email_tgts # will be defined in self.send_email
|
||||||
msg.set_content(message_body)
|
msg.set_content(message_body)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def build_recipients(self, email_tgts:list[str]):
|
def build_recipients(self, email_tgts:list[str]):
|
||||||
"""
|
"""
|
||||||
email formatting does not support lists. Instead, items are joined into a comma-space-separated string.
|
email formatting does not support lists. Instead, items are joined into a comma-space-separated string.
|
||||||
Example:
|
Example:
|
||||||
[mail1@mail.com, mail2@mail.com] becomes
|
[mail1@mail.com, mail2@mail.com] becomes
|
||||||
'mail1@mail.com, mail2@mail.com'
|
'mail1@mail.com, mail2@mail.com'
|
||||||
"""
|
"""
|
||||||
return ', '.join(email_tgts)
|
return ', '.join(email_tgts)
|
||||||
|
|
||||||
def open_mime_application(self, path:str)->MIMEApplication:
|
def open_mime_application(self, path:str)->MIMEApplication:
|
||||||
"""open a local file, read the bytes into a MIMEApplication object, which is built with the proper subtype (based on the file extension)"""
|
"""open a local file, read the bytes into a MIMEApplication object, which is built with the proper subtype (based on the file extension)"""
|
||||||
with open(path, 'rb') as file:
|
with open(path, 'rb') as file:
|
||||||
@ -102,24 +105,24 @@ class EmailHandler():
|
|||||||
|
|
||||||
attachment.add_header('Content-Disposition','attachment',filename=str(os.path.basename(path)))
|
attachment.add_header('Content-Disposition','attachment',filename=str(os.path.basename(path)))
|
||||||
return attachment
|
return attachment
|
||||||
|
|
||||||
def attach_file(self, path:str, msg:email.mime.multipart.MIMEMultipart)->None:
|
def attach_file(self, path:str, msg:email.mime.multipart.MIMEMultipart)->None:
|
||||||
"""
|
"""
|
||||||
attach a file to the message. This function opens the file, reads its bytes, defines the mime type by the
|
attach a file to the message. This function opens the file, reads its bytes, defines the mime type by the
|
||||||
path extension. The filename is appended as the header.
|
path extension. The filename is appended as the header.
|
||||||
|
|
||||||
mimetypes: # https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
mimetypes: # https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||||
"""
|
"""
|
||||||
attachment = self.open_mime_application(path)
|
attachment = self.open_mime_application(path)
|
||||||
msg.attach(attachment)
|
msg.attach(attachment)
|
||||||
return
|
return
|
||||||
|
|
||||||
def send_email(self, msg:EmailMessage, email_tgts:list[str], cc_tgts:typing.Optional[list[str]]=None, bcc_tgts:typing.Optional[list[str]]=None, debug:bool=False)->typing.Union[dict,EmailMessage]:
|
def send_email(self, msg:EmailMessage, email_tgts:list[str], cc_tgts:typing.Optional[list[str]]=None, bcc_tgts:typing.Optional[list[str]]=None, debug:bool=False)->typing.Union[dict,EmailMessage]:
|
||||||
"""
|
"""
|
||||||
send a prepared email message to recipients (email_tgts), copy (cc_tgts) and blind copy (bcc_tgts).
|
send a prepared email message to recipients (email_tgts), copy (cc_tgts) and blind copy (bcc_tgts).
|
||||||
Returns a dictionary of feedback, which is commonly empty and the EmailMessage.
|
Returns a dictionary of feedback, which is commonly empty and the EmailMessage.
|
||||||
|
|
||||||
When failing, this function returns an SMTP error instead of returning the default outputs.
|
When failing, this function returns an SMTP error instead of returning the default outputs.
|
||||||
"""
|
"""
|
||||||
# Set the Recipients
|
# Set the Recipients
|
||||||
msg["To"] = self.build_recipients(email_tgts)
|
msg["To"] = self.build_recipients(email_tgts)
|
||||||
@ -130,15 +133,15 @@ class EmailHandler():
|
|||||||
if bcc_tgts is not None:
|
if bcc_tgts is not None:
|
||||||
msg["Bcc"] = self.build_recipients(bcc_tgts)
|
msg["Bcc"] = self.build_recipients(bcc_tgts)
|
||||||
|
|
||||||
# when debugging, do not send the Email, but return the EmailMessage.
|
# when debugging, do not send the Email, but return the EmailMessage.
|
||||||
if debug:
|
if debug:
|
||||||
return {}, msg
|
return {}, msg
|
||||||
|
|
||||||
assert self.check_login(), f"currently not logged in. Cannot send an Email. Make sure to properly use self.login first. "
|
assert self.check_login(), f"currently not logged in. Cannot send an Email. Make sure to properly use self.login first. "
|
||||||
# send the prepared EmailMessage via the server.
|
# send the prepared EmailMessage via the server.
|
||||||
feedback = self.server.send_message(msg)
|
feedback = self.server.send_message(msg)
|
||||||
return feedback, msg
|
return feedback, msg
|
||||||
|
|
||||||
def translate_mail_to_multipart(self, msg:EmailMessage):
|
def translate_mail_to_multipart(self, msg:EmailMessage):
|
||||||
"""EmailMessage does not support HTML and attachments. Hence, one can convert an EmailMessage object."""
|
"""EmailMessage does not support HTML and attachments. Hence, one can convert an EmailMessage object."""
|
||||||
if msg.is_multipart():
|
if msg.is_multipart():
|
||||||
@ -159,11 +162,11 @@ class EmailHandler():
|
|||||||
# attach the remainder of the msg, such as the body, to the MIMEMultipart
|
# attach the remainder of the msg, such as the body, to the MIMEMultipart
|
||||||
msg_new.attach(msg)
|
msg_new.attach(msg)
|
||||||
return msg_new
|
return msg_new
|
||||||
|
|
||||||
def print_email_attachments(self, msg:MIMEMultipart)->list[str]:
|
def print_email_attachments(self, msg:MIMEMultipart)->list[str]:
|
||||||
"""return a list of lines of an Email, which contain 'filename=' as a list. """
|
"""return a list of lines of an Email, which contain 'filename=' as a list. """
|
||||||
return [line_ for line_ in msg.as_string().split("\n") if "filename=" in line_]
|
return [line_ for line_ in msg.as_string().split("\n") if "filename=" in line_]
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.server.__dict__.pop("user",None)
|
self.server.__dict__.pop("user",None)
|
||||||
self.server.__dict__.pop("password",None)
|
self.server.__dict__.pop("password",None)
|
||||||
|
|||||||
@ -59,7 +59,7 @@ def create_validation_error_response(ex:ValidationError, status_code:int=400, cr
|
|||||||
|
|
||||||
if create_log:
|
if create_log:
|
||||||
logging.warning(ex) if ex is not None else logging.warning(message)
|
logging.warning(ex) if ex is not None else logging.warning(message)
|
||||||
print(ex) if ex is not None else print(message)
|
# print(ex) if ex is not None else print(message)
|
||||||
return (serialized_response, status_code)
|
return (serialized_response, status_code)
|
||||||
|
|
||||||
def create_werkzeug_error_response(ex:Forbidden, status_code:int=403, create_log:bool=True)->typing.Tuple[str,int]:
|
def create_werkzeug_error_response(ex:Forbidden, status_code:int=403, create_log:bool=True)->typing.Tuple[str,int]:
|
||||||
@ -71,7 +71,7 @@ def create_werkzeug_error_response(ex:Forbidden, status_code:int=403, create_log
|
|||||||
|
|
||||||
if create_log:
|
if create_log:
|
||||||
logging.warning(ex) if ex is not None else logging.warning(message)
|
logging.warning(ex) if ex is not None else logging.warning(message)
|
||||||
print(ex) if ex is not None else print(message)
|
# print(ex) if ex is not None else print(message)
|
||||||
return serialized_response, status_code
|
return serialized_response, status_code
|
||||||
|
|
||||||
def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None, create_log:bool=True):
|
def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None, create_log:bool=True):
|
||||||
@ -83,5 +83,5 @@ def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Op
|
|||||||
|
|
||||||
if create_log:
|
if create_log:
|
||||||
logging.warning(ex) if ex is not None else logging.warning(message)
|
logging.warning(ex) if ex is not None else logging.warning(message)
|
||||||
print(ex) if ex is not None else print(message)
|
# print(ex) if ex is not None else print(message)
|
||||||
return (serialized_response, status_code)
|
return (serialized_response, status_code)
|
||||||
|
|||||||
@ -52,7 +52,8 @@ class ValidationRules(ValidationRuleFunctions):
|
|||||||
# 'translate' all error codes into readable, human-understandable format.
|
# 'translate' all error codes into readable, human-understandable format.
|
||||||
evaluation_results = [(state, self.describe_error_message(msg)) for (state, msg) in evaluation_results]
|
evaluation_results = [(state, self.describe_error_message(msg)) for (state, msg) in evaluation_results]
|
||||||
|
|
||||||
logging.info(f"Validation results for shipcall {shipcall.id}: {evaluation_results}")
|
if evaluation_results:
|
||||||
|
logging.info(f"Validation results for shipcall {shipcall.id}: {evaluation_results}")
|
||||||
|
|
||||||
# check, what the maximum state flag is and return it
|
# check, what the maximum state flag is and return it
|
||||||
evaluation_state = np.max(np.array([result[0].value for result in evaluation_results])) if len(evaluation_results)>0 else StatusFlags.GREEN.value
|
evaluation_state = np.max(np.array([result[0].value for result in evaluation_results])) if len(evaluation_results)>0 else StatusFlags.GREEN.value
|
||||||
@ -92,7 +93,7 @@ class ValidationRules(ValidationRuleFunctions):
|
|||||||
if evaluation_states_old[0] != evaluation_states_new[0]:
|
if evaluation_states_old[0] != evaluation_states_new[0]:
|
||||||
pooledConnection = getPoolConnection()
|
pooledConnection = getPoolConnection()
|
||||||
commands = pydapper.using(pooledConnection)
|
commands = pydapper.using(pooledConnection)
|
||||||
notification_type = 3 # RED (mapped to time_conflict)
|
notification_type = 3 # RED (mapped to time_conflict)
|
||||||
if evaluation_states_new[0] == 2:
|
if evaluation_states_new[0] == 2:
|
||||||
match evaluation_states_old[0]:
|
match evaluation_states_old[0]:
|
||||||
case 0:
|
case 0:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user