E-Mail template first steps
This commit is contained in:
parent
fc6c6179b8
commit
02947ce6e5
15
misc/notification_element.html
Normal file
15
misc/notification_element.html
Normal file
@ -0,0 +1,15 @@
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%; min-width: 100%;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-family: Helvetica, sans-serif; font-size: 16px; vertical-align: top; padding-bottom: 16px;" valign="top">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-family: Helvetica, sans-serif; font-size: 16px; vertical-align: top; border-radius: 4px; text-align: center; background-color: #0867ec;" valign="top" align="center" bgcolor="#0867ec"> <a href="https://www.bremen-calling.de" target="_blank" style="border: solid 2px #0867ec; border-radius: 4px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 16px; font-weight: bold; margin: 0; padding: 12px 24px; text-decoration: none; text-transform: capitalize; background-color: #0867ec; border-color: #0867ec; color: #ffffff;">Call To Action</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
144
misc/notification_template.html
Normal file
144
misc/notification_template.html
Normal file
@ -0,0 +1,144 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Bremen calling Benachrichtigung</title>
|
||||
<style media="all" type="text/css">
|
||||
@media all {
|
||||
.btn-primary table td:hover {
|
||||
background-color: #ec0867 !important;
|
||||
}
|
||||
|
||||
.btn-primary a:hover {
|
||||
background-color: #ec0867 !important;
|
||||
border-color: #ec0867 !important;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 640px) {
|
||||
.main p,
|
||||
.main td,
|
||||
.main span {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding: 8px !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
padding-top: 8px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important;
|
||||
}
|
||||
|
||||
.btn table {
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.btn a {
|
||||
font-size: 16px !important;
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
#MessageViewBody a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="font-family: Helvetica, sans-serif; -webkit-font-smoothing: antialiased; font-size: 16px; line-height: 1.3; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f4f5f6; margin: 0; padding: 0;">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f4f5f6; width: 100%;" width="100%" bgcolor="#f4f5f6">
|
||||
<tr>
|
||||
<td style="font-family: Helvetica, sans-serif; font-size: 16px; vertical-align: top;" valign="top"> </td>
|
||||
<td class="container" style="font-family: Helvetica, sans-serif; font-size: 16px; vertical-align: top; max-width: 600px; padding: 0; padding-top: 24px; width: 600px; margin: 0 auto;" width="600" valign="top">
|
||||
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 0;">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Benachrichtung von Bremen calling!</span>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border: 1px solid #eaebed; border-radius: 16px; width: 100%;" width="100%">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper" style="font-family: Helvetica, sans-serif; font-size: 16px; vertical-align: top; box-sizing: border-box; padding: 24px;" valign="top">
|
||||
<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">Hallo,</p>
|
||||
<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">Sie erhalten eine oder mehrere Benachrichtigungen von Bremen calling:</p>
|
||||
|
||||
<!--[[NOTIFICATION_ELEMENTS]]-->
|
||||
|
||||
<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">Wenn Sie diese E-Mails nicht länger erhalten wollen, loggen Sie sich bitte in der Bremen Calling App ein. Im Bereich "Passwort ändern"
|
||||
können Sie auch die Benachrichtungen anpassen.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer" style="clear: both; padding-top: 24px; text-align: center; width: 100%;">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
|
||||
<tr>
|
||||
<td class="content-block" style="font-family: Helvetica, sans-serif; vertical-align: top; color: #9a9ea6; font-size: 16px; text-align: center;" valign="top" align="center">
|
||||
<span class="apple-link" style="color: #9a9ea6; font-size: 16px; text-align: center;">Bremer Schiffsmeldedienst GbR - Hafenkopf II / Überseetor 20 - 28217 Bremen / Germany</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="content-block powered-by" style="font-family: Helvetica, sans-serif; vertical-align: top; color: #9a9ea6; font-size: 16px; text-align: center;" valign="top" align="center">
|
||||
Kontaktieren Sie uns unter <a href="mailto:bremencalling@bsmd.de" style="color: #9a9ea6; font-size: 16px; text-align: center;">bremencalling@bsmd.de</a>.<br />
|
||||
<a href="https://www.bsmd.de" style="color: #9a9ea6; font-size: 16px; text-align: center;">www.bsmd.de</a><br />
|
||||
Tel.: +49 421 38 48 27
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER --></div>
|
||||
</td>
|
||||
<td style="font-family: Helvetica, sans-serif; font-size: 16px; vertical-align: top;" valign="top"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@ -8,8 +8,8 @@ from .. import local_db
|
||||
from ..services.auth_guard import check_jwt
|
||||
|
||||
from BreCal.database.update_database import evaluate_shipcall_state
|
||||
from BreCal.database.sql_utils import get_notification_for_shipcall_and_type
|
||||
from BreCal.database.sql_queries import create_sql_query_shipcall_get, create_sql_query_shipcall_post, create_sql_query_shipcall_put, create_sql_query_history_post, create_sql_query_history_put, SQLQuery
|
||||
from BreCal.database.sql_utils import get_notification_for_shipcall_and_type, get_ship_data_for_id
|
||||
from BreCal.database.sql_queries import create_sql_query_shipcall_get
|
||||
from marshmallow import Schema, fields, ValidationError
|
||||
from BreCal.validators.validation_error import create_validation_error_response
|
||||
|
||||
@ -76,6 +76,7 @@ def PostShipcalls(schemaModel):
|
||||
commands = pydapper.using(pooledConnection)
|
||||
|
||||
# query = SQLQuery.get_shipcall_post(schemaModel) # create_sql_query_shipcall_post(schemaModel)
|
||||
|
||||
query = "INSERT INTO shipcall ("
|
||||
isNotFirst = False
|
||||
for key in schemaModel.keys():
|
||||
@ -134,15 +135,26 @@ def PostShipcalls(schemaModel):
|
||||
# new_id = commands.execute_scalar(lquery)
|
||||
new_id = commands.execute_scalar("select last_insert_id()")
|
||||
|
||||
shipdata = get_ship_data_for_id(schemaModel["ship_id"])
|
||||
message = {shipdata['name']}
|
||||
if "type_value" in schemaModel:
|
||||
match schemaModel["type_value"]:
|
||||
case 1:
|
||||
message += " [ARRIVAL]"
|
||||
case 2:
|
||||
message += " [DEPARTURE]"
|
||||
case 3:
|
||||
message += " [SHIFTING]"
|
||||
|
||||
# add participant assignments if we have a list of participants
|
||||
if 'participants' in schemaModel:
|
||||
# pquery = SQLQuery.get_shipcall_post_update_shipcall_participant_map()
|
||||
pquery = "INSERT INTO shipcall_participant_map (shipcall_id, participant_id, type) VALUES (?shipcall_id?, ?participant_id?, ?type?)"
|
||||
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type) VALUES (?shipcall_id?, ?participant_id?, 0, 1)" # type = 1 is assignment
|
||||
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 1, ?message?)" # type = 1 is assignment
|
||||
|
||||
for participant_assignment in schemaModel["participants"]:
|
||||
commands.execute(pquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "type" : participant_assignment["type"]})
|
||||
commands.execute(nquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"]})
|
||||
commands.execute(nquery, param={"shipcall_id" : new_id, "participant_id" : participant_assignment["participant_id"], "message" : message})
|
||||
|
||||
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database
|
||||
# evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=new_id) # new_id (last insert id) refers to the shipcall id
|
||||
@ -189,8 +201,7 @@ def PutShipcalls(schemaModel):
|
||||
# test if object to update is found
|
||||
|
||||
sentinel = object()
|
||||
# query = SQLQuery.get_shipcall_by_id()
|
||||
# theshipcall = commands.query_single_or_default(query, sentinel, param={"id" : schemaModel["id"]})
|
||||
|
||||
theshipcall = commands.query_single_or_default("SELECT * FROM shipcall where id = ?id?", sentinel, param={"id" : schemaModel["id"]})
|
||||
if theshipcall is sentinel:
|
||||
pooledConnection.close()
|
||||
@ -230,6 +241,17 @@ def PutShipcalls(schemaModel):
|
||||
|
||||
affected_rows = commands.execute(query, param=schemaModel)
|
||||
|
||||
shipdata = get_ship_data_for_id(schemaModel["ship_id"])
|
||||
message = {shipdata['name']}
|
||||
if "type_value" in schemaModel:
|
||||
match schemaModel["type_value"]:
|
||||
case 1:
|
||||
message += " [ARRIVAL]"
|
||||
case 2:
|
||||
message += " [DEPARTURE]"
|
||||
case 3:
|
||||
message += " [SHIFTING]"
|
||||
|
||||
# pquery = SQLQuery.get_shipcall_participant_map_by_shipcall_id()
|
||||
pquery = "SELECT id, participant_id, type FROM shipcall_participant_map where shipcall_id = ?id?"
|
||||
pdata = commands.query(pquery,param={"id" : schemaModel["id"]}) # existing list of assignments
|
||||
@ -256,8 +278,8 @@ def PutShipcalls(schemaModel):
|
||||
found_notification = True
|
||||
break
|
||||
if not found_notification:
|
||||
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type) VALUES (?shipcall_id?, ?participant_id?, 0, 1)" # type = 1 is assignment
|
||||
commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : participant_assignment["participant_id"]})
|
||||
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 1, ?message?)" # type = 1 is assignment
|
||||
commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : participant_assignment["participant_id"], "message" : message})
|
||||
|
||||
# loop across existing pdata entries, deleting those not present in participant list
|
||||
for elem in pdata:
|
||||
@ -278,8 +300,8 @@ def PutShipcalls(schemaModel):
|
||||
commands.execute(nquery, param={"nid" : existing_notification["id"]})
|
||||
else:
|
||||
# create un-assignment notification
|
||||
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type) VALUES (?shipcall_id?, ?participant_id?, 0, 5)"
|
||||
commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : elem["participant_id"]})
|
||||
nquery = "INSERT INTO notification (shipcall_id, participant_id, level, type, message) VALUES (?shipcall_id?, ?participant_id?, 0, 5, ?message?)"
|
||||
commands.execute(nquery, param={"shipcall_id" : schemaModel["id"], "participant_id" : elem["participant_id"], "message" : message})
|
||||
break
|
||||
|
||||
# save history data
|
||||
|
||||
@ -4,8 +4,7 @@ import logging
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import schemas.defs as defs
|
||||
|
||||
from BreCal.schemas import defs
|
||||
config_path = None
|
||||
|
||||
def initPool(instancePath, connection_filename="connection_data_devel.json"):
|
||||
@ -14,7 +13,7 @@ def initPool(instancePath, connection_filename="connection_data_devel.json"):
|
||||
if(config_path == None):
|
||||
config_path = os.path.join(instancePath,f'../../../secure/{connection_filename}') #connection_data_devel.json');
|
||||
|
||||
config_path = "E:/temp/connection_data.json"
|
||||
config_path = "C:/temp/connection_data_devel.json"
|
||||
print (config_path)
|
||||
|
||||
if not os.path.exists(config_path):
|
||||
@ -35,9 +34,13 @@ def initPool(instancePath, connection_filename="connection_data_devel.json"):
|
||||
|
||||
credentials_file = "email_credentials_devel.json"
|
||||
credentials_path = os.path.join(instancePath,f'../../../secure/{credentials_file}')
|
||||
|
||||
credentials_path = "C:/temp/email_credentials_devel.json"
|
||||
|
||||
if not os.path.exists(credentials_path):
|
||||
print ('cannot find ' + os.path.abspath(credentials_path))
|
||||
sys.exit(1)
|
||||
|
||||
f = open(credentials_path);
|
||||
defs.email_credentials = json.load(f)
|
||||
f.close()
|
||||
|
||||
@ -552,6 +552,9 @@ class User:
|
||||
created: datetime
|
||||
modified: datetime
|
||||
|
||||
def __hash__(self):
|
||||
return hash(id)
|
||||
|
||||
@dataclass
|
||||
class Ship:
|
||||
id: int
|
||||
|
||||
@ -61,7 +61,7 @@ def UpdateNotifications():
|
||||
pooledConnection = getPoolConnection()
|
||||
commands = pydapper.using(pooledConnection)
|
||||
|
||||
query = "SELECT * FROM notification WHERE level = 0 AND created < DATE(NOW() - INTERVAL 10 MINUTE)"
|
||||
query = "SELECT * FROM notification WHERE level = 0 AND created < TIMESTAMP(NOW() - INTERVAL 10 MINUTE)"
|
||||
data = commands.query(query, model=model.Notification)
|
||||
for notification in data:
|
||||
commands.execute("UPDATE notification SET level = 1 WHERE id = ?id?", param={"id":notification.id})
|
||||
@ -75,7 +75,7 @@ def SendEmails(email_dict):
|
||||
This function sends emails to all users in the emaildict
|
||||
"""
|
||||
try:
|
||||
conn = smtplib.SMTP_SSL(defs.email_credentials["server"], defs.email_credentials["port"])
|
||||
conn = smtplib.SMTP(defs.email_credentials["server"], defs.email_credentials["port"])
|
||||
conn.set_debuglevel(1)
|
||||
conn.ehlo()
|
||||
conn.starttls()
|
||||
@ -91,11 +91,12 @@ def SendEmails(email_dict):
|
||||
# TODO: pretty-print and format message according to template
|
||||
msg.set_content(message)
|
||||
|
||||
conn.sendmail(user.user_email, user.user_email, msg.as_string())
|
||||
conn.sendmail(defs.email_credentials["sender"], user.user_email, msg.as_string())
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
finally:
|
||||
if conn is not None:
|
||||
conn.quit()
|
||||
|
||||
|
||||
@ -134,22 +135,24 @@ def SendNotifications():
|
||||
for user in users:
|
||||
# send notification to user
|
||||
if user.notify_email:
|
||||
if user not in email_dict:
|
||||
email_dict[user] = ""
|
||||
match notification.type:
|
||||
case 1: # assignment
|
||||
email_dict[user] += "\n"
|
||||
email_dict[user] += "You have been assigned to a new shipcall: " + notification.message
|
||||
email_dict[user] += "You have been assigned to a new shipcall: " + str(notification.message or "")
|
||||
case 2: # next 24 hours
|
||||
email_dict[user] += "\n"
|
||||
email_dict[user] += "A shipcall is scheduled for the next 24 hours: " + notification.message
|
||||
email_dict[user] += "A shipcall is scheduled for the next 24 hours: " + str(notification.message or "")
|
||||
case 3: # Time conflict
|
||||
email_dict[user] += "\n"
|
||||
email_dict[user] += "There is a time conflict in a shipcall: " + notification.message
|
||||
email_dict[user] += "There is a time conflict in a shipcall: " + str(notification.message or "")
|
||||
case 4: # Time conflict resolved
|
||||
email_dict[user] += "\n"
|
||||
email_dict[user] += "A time conflict in a shipcall has been resolved: " + notification.message
|
||||
email_dict[user] += "A time conflict in a shipcall has been resolved: " + str(notification.message or "")
|
||||
case 5: # unassigned
|
||||
email_dict[user] += "\n"
|
||||
email_dict[user] += "You have been unassigned from a shipcall: " + notification.message
|
||||
email_dict[user] += "You have been unassigned from a shipcall: " + str(notification.message or "")
|
||||
if user.notify_whatsapp:
|
||||
# TBD
|
||||
pass
|
||||
@ -165,6 +168,9 @@ def SendNotifications():
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
finally:
|
||||
if pooledConnection is not None:
|
||||
pooledConnection.close()
|
||||
|
||||
def add_function_to_schedule__update_shipcalls(interval_in_minutes:int, options:dict={'past_days':2}):
|
||||
kwargs_ = {"options":options}
|
||||
@ -207,6 +213,10 @@ def eval_next_24_hrs():
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
|
||||
finally:
|
||||
if pooledConnection is not None:
|
||||
pooledConnection.close()
|
||||
|
||||
return
|
||||
|
||||
def setup_schedule(update_shipcalls_interval_in_minutes:int=60):
|
||||
|
||||
Reference in New Issue
Block a user