teams-alerter 0.2.5__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/PKG-INFO +1 -1
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/pyproject.toml +1 -1
- teams_alerter-0.2.7/teams_alerter/core.py +178 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter/utils.py +61 -10
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter.egg-info/PKG-INFO +1 -1
- teams_alerter-0.2.5/teams_alerter/core.py +0 -124
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/README.md +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/setup.cfg +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter/__init__.py +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter.egg-info/SOURCES.txt +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter.egg-info/dependency_links.txt +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter.egg-info/requires.txt +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/teams_alerter.egg-info/top_level.txt +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/tests/__init__.py +0 -0
- {teams_alerter-0.2.5 → teams_alerter-0.2.7}/tests/test_core.py +0 -0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "teams-alerter"
|
7
|
-
version = "0.2.
|
7
|
+
version = "0.2.7"
|
8
8
|
description = "Module pour envoyer des alertes Teams via Pub/Sub"
|
9
9
|
authors = [{ name = "Toki", email = "t.bakotondrabe-ext@paris-turf.com" }]
|
10
10
|
readme = "README.md"
|
@@ -0,0 +1,178 @@
|
|
1
|
+
import json
|
2
|
+
import traceback
|
3
|
+
import datetime
|
4
|
+
|
5
|
+
from google.cloud import pubsub_v1
|
6
|
+
from .utils import ErrorUtils, DateUtils, format_email_template, is_json
|
7
|
+
|
8
|
+
|
9
|
+
class TeamsAlerter:
|
10
|
+
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
utils: ErrorUtils,
|
14
|
+
payload: None,
|
15
|
+
):
|
16
|
+
self.utils = utils
|
17
|
+
self.payload = payload
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
def handle_error(error: Exception, utils: ErrorUtils) -> None:
|
21
|
+
error_type = type(error).__name__
|
22
|
+
error_message = str(error)
|
23
|
+
error_traceback = traceback.format_exc()
|
24
|
+
utc_timestamp = DateUtils.get_str_utc_timestamp()
|
25
|
+
utc_timestamp_minus_5min = DateUtils.get_str_utc_timestamp_minus_5min()
|
26
|
+
utc_timestamp_plus_5min = DateUtils.get_str_utc_timestamp_plus_5min()
|
27
|
+
url_log = f"https://console.cloud.google.com/logs/query;cursorTimestamp={utc_timestamp};startTime={utc_timestamp_minus_5min};endTime={utc_timestamp_plus_5min}?referrer=search&hl=fr&inv=1&invt=Ab5Y1Q&project={utils['app_project_id']}"
|
28
|
+
# detail = f"Error type: {error_type}\nError message: {error_message}\nError traceback: {error_traceback}"
|
29
|
+
detail = {"type": error_type, "message": error_message, "traceback": error_traceback}
|
30
|
+
level = "ERROR"
|
31
|
+
|
32
|
+
teams_alerter = TeamsAlerter(utils=utils, payload={})
|
33
|
+
teams_alerter.format_payload(detail, level, url_log, utc_timestamp)
|
34
|
+
teams_alerter.publish_alert()
|
35
|
+
|
36
|
+
def publish_alert(self):
|
37
|
+
# Création d'un éditeur
|
38
|
+
publisher = pubsub_v1.PublisherClient()
|
39
|
+
topic_path = publisher.topic_path(self.utils["topic_project_id"], self.utils["topic_id"])
|
40
|
+
|
41
|
+
# Message à publier
|
42
|
+
data = json.dumps(self.payload).encode("utf-8")
|
43
|
+
|
44
|
+
# Publier le message
|
45
|
+
try:
|
46
|
+
publish_future = publisher.publish(topic_path, data)
|
47
|
+
publish_future.result()
|
48
|
+
|
49
|
+
except Exception as e:
|
50
|
+
self.utils["logger"](f"🟥Une erreur s'est produite lors de la publication du message : {e}")
|
51
|
+
|
52
|
+
def format_payload(self, detail, level, url_log, utc_timestamp):
|
53
|
+
app_list = {
|
54
|
+
"teams": [
|
55
|
+
"health_check_check_pg_wal_slot",
|
56
|
+
"health_check_check_meetings_ids",
|
57
|
+
"health_check_check_races_ids",
|
58
|
+
"health_check_check_runners_ids",
|
59
|
+
"health_check_check_processing_queue_ids",
|
60
|
+
],
|
61
|
+
"email": [
|
62
|
+
"health_check_check_partants_data",
|
63
|
+
"health_check_check_horses_stats",
|
64
|
+
],
|
65
|
+
}
|
66
|
+
|
67
|
+
# base payload
|
68
|
+
self.payload = {
|
69
|
+
# base info
|
70
|
+
"app_name": self.utils["app_name"],
|
71
|
+
"detail": detail,
|
72
|
+
"level": level,
|
73
|
+
"environment": self.utils["env"],
|
74
|
+
"url_log": url_log,
|
75
|
+
"timestamp": utc_timestamp,
|
76
|
+
# alerting info to complete
|
77
|
+
"alert_type": [], # teams, email
|
78
|
+
"teams_channel": "",
|
79
|
+
"teams_template": "",
|
80
|
+
"email_template_html": "",
|
81
|
+
}
|
82
|
+
|
83
|
+
if self.utils["app_name"] in app_list["email"]:
|
84
|
+
self.format_email_template()
|
85
|
+
|
86
|
+
if self.utils["app_name"] in app_list["teams"] or self.utils["app_name"] not in app_list["email"]:
|
87
|
+
self.format_teams_template()
|
88
|
+
|
89
|
+
def format_teams_template(self):
|
90
|
+
self.payload["alert_type"].append("teams")
|
91
|
+
self.payload["teams_channel"] = self.utils["teams_channel"]
|
92
|
+
self.payload["teams_template"] = "card"
|
93
|
+
|
94
|
+
def format_email_template(self):
|
95
|
+
self.payload["alert_type"].append("email")
|
96
|
+
self.payload["email_object"] = "Contrôle DATASTREAM"
|
97
|
+
|
98
|
+
if self.utils["app_name"] == "health_check_check_horses_stats":
|
99
|
+
if is_json(self.payload["detail"]["message"]):
|
100
|
+
# cette ligne plante si message n'est pas json
|
101
|
+
error_info_list = json.loads(self.payload["detail"]["message"])
|
102
|
+
table_data = [("ID CHEVAL", "CHAMP", "POSTGRES", "MONGO", "DIFFERENCE")]
|
103
|
+
for error_info in error_info_list["data"]:
|
104
|
+
table_data.append(
|
105
|
+
(
|
106
|
+
error_info["idCheval"],
|
107
|
+
error_info["champ"],
|
108
|
+
error_info["postgres"],
|
109
|
+
error_info["mongo"],
|
110
|
+
error_info["difference"],
|
111
|
+
)
|
112
|
+
)
|
113
|
+
|
114
|
+
email_object = "Contrôle DATASTREAM - Fiche cheval"
|
115
|
+
self.payload["email_object"] = email_object
|
116
|
+
email_messages = [
|
117
|
+
"""
|
118
|
+
Bonjour, <br>
|
119
|
+
Veuillez trouver ci-dessous le tableau récapitulatif du contrôle effectué sur la fiche cheval dans Datastream.
|
120
|
+
""",
|
121
|
+
f"""
|
122
|
+
Env: <strong>{self.utils["env"]}</strong> <br>
|
123
|
+
Timestamp: {DateUtils.get_str_utc_timestamp()} <br>
|
124
|
+
Champs: <strong>formFigs et/ou totalPrize</strong>
|
125
|
+
""",
|
126
|
+
]
|
127
|
+
|
128
|
+
self.payload["email_template_html"] = format_email_template(
|
129
|
+
email_object, email_messages, table_data, self.utils["app_name"]
|
130
|
+
)
|
131
|
+
else:
|
132
|
+
print(
|
133
|
+
"⚠ ERREUR INTERNE : error_message health_check_check_horses_stats n'est pas un JSON valide :",
|
134
|
+
self.payload["detail"]["message"],
|
135
|
+
)
|
136
|
+
|
137
|
+
elif self.utils["app_name"] == "health_check_check_partants_data":
|
138
|
+
if is_json(self.payload["detail"]["message"]):
|
139
|
+
error_info_list = json.loads(self.payload["detail"]["message"])
|
140
|
+
table_data = [("ID COURSE", "CHAMP", "POSTGRES", "MONGO", "DIFFERENCE")]
|
141
|
+
for error_info in error_info_list["data"]:
|
142
|
+
table_data.append(
|
143
|
+
(
|
144
|
+
error_info["idCourse"],
|
145
|
+
error_info["dateCourse"],
|
146
|
+
error_info["nbInitialRunners_postgres"],
|
147
|
+
error_info["nbInitialRunners_mongo"],
|
148
|
+
error_info["nbNonRunners_postgres"],
|
149
|
+
error_info["nbNonRunners_mongo"],
|
150
|
+
error_info["nonRunners_postgres"],
|
151
|
+
error_info["nonRunners_mongo"],
|
152
|
+
error_info["nbRunners_postgres"],
|
153
|
+
error_info["nbRunners_mongo"],
|
154
|
+
)
|
155
|
+
)
|
156
|
+
email_object = "Contrôle DATASTREAM - Partants (COURSES)"
|
157
|
+
self.payload["email_object"] = email_object
|
158
|
+
|
159
|
+
email_messages = [
|
160
|
+
f"""
|
161
|
+
Bonjour, <br>
|
162
|
+
Veuillez trouver ci-dessous le tableau récapitulatif du contrôle effectué sur les partants des courses du {datetime.date.today().strftime("%d/%m/%Y")} dans Datastream.
|
163
|
+
""",
|
164
|
+
f"""
|
165
|
+
Env: <strong>{self.utils["env"]}</strong> <br>
|
166
|
+
Timestamp: {DateUtils.get_str_utc_timestamp()} <br>
|
167
|
+
Table name: <strong>tb_course</strong>
|
168
|
+
""",
|
169
|
+
]
|
170
|
+
|
171
|
+
self.payload["email_template_html"] = format_email_template(
|
172
|
+
email_object, email_messages, [], self.utils["app_name"]
|
173
|
+
)
|
174
|
+
else:
|
175
|
+
print(
|
176
|
+
"⚠ ERREUR INTERNE : error_message health_check_check_partants_data n'est pas un JSON valide :",
|
177
|
+
self.payload["detail"]["message"],
|
178
|
+
)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import datetime
|
2
|
+
import json
|
2
3
|
|
3
4
|
from typing import TypedDict
|
4
5
|
from google.cloud import logging
|
@@ -31,7 +32,7 @@ class DateUtils:
|
|
31
32
|
return dt.strftime("%Y-%m-%dT%H:%M:%S") + ".000000000Z"
|
32
33
|
|
33
34
|
|
34
|
-
def format_email_template(email_object, email_messages, table_data):
|
35
|
+
def format_email_template(email_object, email_messages, table_data, app_name):
|
35
36
|
html = f"""
|
36
37
|
<html>
|
37
38
|
<head lang="fr">
|
@@ -61,7 +62,7 @@ def format_email_template(email_object, email_messages, table_data):
|
|
61
62
|
|
62
63
|
<tr>
|
63
64
|
<td style="padding:0 16px 24px 16px;">
|
64
|
-
{build_html_table(table_data)}
|
65
|
+
{build_html_table(table_data, app_name)}
|
65
66
|
</td>
|
66
67
|
</tr>
|
67
68
|
|
@@ -90,20 +91,60 @@ def format_email_template(email_object, email_messages, table_data):
|
|
90
91
|
return html
|
91
92
|
|
92
93
|
|
93
|
-
def build_html_table(table_data: list):
|
94
|
+
def build_html_table(table_data: list, app_name: str):
|
95
|
+
if app_name == "health_check_check_horses_stats":
|
96
|
+
html_table = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse; font-family:Segoe UI, Arial, sans-serif;">'
|
97
|
+
|
98
|
+
# format header
|
99
|
+
html_table += "<tr>"
|
100
|
+
for header in table_data[0]:
|
101
|
+
html_table += f'<th style="padding:10px; font-size:14px; line-height:20px; color:#111827; border-bottom:1px solid #bbbbbb;">{header}</th>'
|
102
|
+
html_table += "</tr>"
|
103
|
+
|
104
|
+
# format rows
|
105
|
+
for row in table_data[1:]:
|
106
|
+
html_table += "<tr>"
|
107
|
+
for cell in row:
|
108
|
+
html_table += f'<td style="padding:10px; font-size:14px; line-height:20px; color:#111827; border-bottom:1px solid #bbbbbb;">{cell}</td>'
|
109
|
+
html_table += "</tr>"
|
110
|
+
|
111
|
+
html_table += "</table>"
|
112
|
+
|
113
|
+
return html_table
|
114
|
+
|
115
|
+
elif app_name == "health_check_check_partants_data":
|
116
|
+
return build_html_table_partants_course(table_data)
|
117
|
+
|
118
|
+
|
119
|
+
def build_html_table_partants_course(table_data: list):
|
94
120
|
html_table = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse; font-family:Segoe UI, Arial, sans-serif;">'
|
95
121
|
|
96
122
|
# format header
|
97
|
-
html_table += "
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
123
|
+
html_table += """
|
124
|
+
<tr>
|
125
|
+
<th align="center" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;" rowspan="2">ID Course</th>
|
126
|
+
<th align="center" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;" rowspan="2">Date Course</th>
|
127
|
+
<th align="center" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;" colspan="2">NB initial runners</th>
|
128
|
+
<th align="center" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;" colspan="2">NB non runners</th>
|
129
|
+
<th align="center" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;" colspan="2">Non runners</th>
|
130
|
+
<th align="center" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;" colspan="2">NB runners</th>
|
131
|
+
</tr>
|
132
|
+
<tr>
|
133
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Postgres</th>
|
134
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Mongo</th>
|
135
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Postgres</th>
|
136
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Mongo</th>
|
137
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Postgres</th>
|
138
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Mongo</th>
|
139
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Postgres</th>
|
140
|
+
<th align="left" style="padding:12px 10px; font-size:12px; line-height:16px; color:#374151; text-transform:uppercase; letter-spacing:.5px; border:2px solid #e5e7eb; background:#f9fafb;">Mongo</th>
|
141
|
+
</tr>
|
142
|
+
"""
|
102
143
|
# format rows
|
103
|
-
for row in table_data
|
144
|
+
for row in table_data:
|
104
145
|
html_table += "<tr>"
|
105
146
|
for cell in row:
|
106
|
-
html_table += f'<td style="padding:10px; font-size:14px; line-height:20px; color:#111827; border
|
147
|
+
html_table += f'<td style="padding:10px; font-size:14px; line-height:20px; color:#111827; border:1px solid #bbbbbb;">{cell}</td>'
|
107
148
|
html_table += "</tr>"
|
108
149
|
|
109
150
|
html_table += "</table>"
|
@@ -122,3 +163,13 @@ def build_html_message(email_messages: list[str]):
|
|
122
163
|
</tr>
|
123
164
|
"""
|
124
165
|
return content
|
166
|
+
|
167
|
+
|
168
|
+
def is_json(value):
|
169
|
+
if not isinstance(value, str):
|
170
|
+
return False # ce n'est même pas une chaîne
|
171
|
+
try:
|
172
|
+
json.loads(value)
|
173
|
+
return True
|
174
|
+
except json.JSONDecodeError:
|
175
|
+
return False
|
@@ -1,124 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import traceback
|
3
|
-
|
4
|
-
from google.cloud import pubsub_v1
|
5
|
-
from .utils import ErrorUtils, DateUtils, format_email_template
|
6
|
-
|
7
|
-
|
8
|
-
class TeamsAlerter:
|
9
|
-
|
10
|
-
def __init__(
|
11
|
-
self,
|
12
|
-
utils: ErrorUtils,
|
13
|
-
payload: None,
|
14
|
-
):
|
15
|
-
self.utils = utils
|
16
|
-
self.payload = payload
|
17
|
-
|
18
|
-
@staticmethod
|
19
|
-
def handle_error(error: Exception, utils: ErrorUtils) -> None:
|
20
|
-
error_type = type(error).__name__
|
21
|
-
error_message = str(error)
|
22
|
-
error_traceback = traceback.format_exc()
|
23
|
-
utc_timestamp = DateUtils.get_str_utc_timestamp()
|
24
|
-
utc_timestamp_minus_5min = DateUtils.get_str_utc_timestamp_minus_5min()
|
25
|
-
utc_timestamp_plus_5min = DateUtils.get_str_utc_timestamp_plus_5min()
|
26
|
-
url_log = f"https://console.cloud.google.com/logs/query;cursorTimestamp={utc_timestamp};startTime={utc_timestamp_minus_5min};endTime={utc_timestamp_plus_5min}?referrer=search&hl=fr&inv=1&invt=Ab5Y1Q&project={utils['app_project_id']}"
|
27
|
-
# detail = f"Error type: {error_type}\nError message: {error_message}\nError traceback: {error_traceback}"
|
28
|
-
detail = {"type": error_type, "message": error_message, "traceback": error_traceback}
|
29
|
-
level = "ERROR"
|
30
|
-
|
31
|
-
teams_alerter = TeamsAlerter(utils=utils, payload={})
|
32
|
-
teams_alerter.format_payload(detail, level, url_log, utc_timestamp)
|
33
|
-
teams_alerter.publish_alert()
|
34
|
-
|
35
|
-
def publish_alert(self):
|
36
|
-
# Création d'un éditeur
|
37
|
-
publisher = pubsub_v1.PublisherClient()
|
38
|
-
topic_path = publisher.topic_path(self.utils["topic_project_id"], self.utils["topic_id"])
|
39
|
-
|
40
|
-
# Message à publier
|
41
|
-
data = json.dumps(self.payload).encode("utf-8")
|
42
|
-
|
43
|
-
# Publier le message
|
44
|
-
try:
|
45
|
-
publish_future = publisher.publish(topic_path, data)
|
46
|
-
publish_future.result()
|
47
|
-
|
48
|
-
except Exception as e:
|
49
|
-
self.utils["logger"](f"🟥Une erreur s'est produite lors de la publication du message : {e}")
|
50
|
-
|
51
|
-
def format_payload(self, detail, level, url_log, utc_timestamp):
|
52
|
-
app_list = {
|
53
|
-
"teams": [
|
54
|
-
"health_check_check_pg_wal_slot",
|
55
|
-
"health_check_check_meetings_ids",
|
56
|
-
"health_check_check_races_ids",
|
57
|
-
"health_check_check_partants_data",
|
58
|
-
"health_check_check_runners_ids",
|
59
|
-
],
|
60
|
-
"email": [
|
61
|
-
"health_check_check_horses_stats",
|
62
|
-
],
|
63
|
-
}
|
64
|
-
|
65
|
-
# base payload
|
66
|
-
self.payload = {
|
67
|
-
# base info
|
68
|
-
"app_name": self.utils["app_name"],
|
69
|
-
"detail": detail,
|
70
|
-
"level": level,
|
71
|
-
"environment": self.utils["env"],
|
72
|
-
"url_log": url_log,
|
73
|
-
"timestamp": utc_timestamp,
|
74
|
-
# alerting info to complete
|
75
|
-
"alert_type": [], # teams, email
|
76
|
-
"teams_channel": "",
|
77
|
-
"teams_template": "",
|
78
|
-
"email_template_html": "",
|
79
|
-
}
|
80
|
-
|
81
|
-
if self.utils["app_name"] in app_list["email"]:
|
82
|
-
self.format_email_template()
|
83
|
-
|
84
|
-
if self.utils["app_name"] in app_list["teams"] or self.utils["app_name"] not in app_list["email"]:
|
85
|
-
self.format_teams_template()
|
86
|
-
|
87
|
-
def format_teams_template(self):
|
88
|
-
self.payload["alert_type"].append("teams")
|
89
|
-
self.payload["teams_channel"] = self.utils["teams_channel"]
|
90
|
-
self.payload["teams_template"] = "card"
|
91
|
-
|
92
|
-
def format_email_template(self):
|
93
|
-
self.payload["alert_type"].append("email")
|
94
|
-
self.payload["email_object"] = "Contrôle DATASTREAM"
|
95
|
-
|
96
|
-
if self.utils["app_name"] == "health_check_check_horses_stats":
|
97
|
-
error_info_list = json.loads(self.payload["detail"]["message"])
|
98
|
-
table_data = [("ID CHEVAL", "CHAMP", "POSTGRES", "MONGO", "DIFFERENCE")]
|
99
|
-
for error_info in error_info_list["data"]:
|
100
|
-
table_data.append(
|
101
|
-
(
|
102
|
-
error_info["idCheval"],
|
103
|
-
error_info["champ"],
|
104
|
-
error_info["postgres"],
|
105
|
-
error_info["mongo"],
|
106
|
-
error_info["difference"],
|
107
|
-
)
|
108
|
-
)
|
109
|
-
|
110
|
-
email_object = "Contrôle DATASTREAM - Fiche cheval"
|
111
|
-
self.payload["email_object"] = email_object
|
112
|
-
email_messages = [
|
113
|
-
"""
|
114
|
-
Bonjour, <br>
|
115
|
-
Veuillez trouver ci-dessous le tableau récapitulatif du contrôle effectué sur la fiche cheval dans Datastream.
|
116
|
-
""",
|
117
|
-
f"""
|
118
|
-
Env: <strong>{self.utils["env"]}</strong> <br>
|
119
|
-
Timestamp: {DateUtils.get_str_utc_timestamp()} <br>
|
120
|
-
Champs: <strong>formFigs et/ou totalPrize</strong>
|
121
|
-
""",
|
122
|
-
]
|
123
|
-
|
124
|
-
self.payload["email_template_html"] = format_email_template(email_object, email_messages, table_data)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|