Skip to content

Commit 018e535

Browse files
Error email improvements (#146)
1 parent b73432f commit 018e535

File tree

17 files changed

+112
-36
lines changed

17 files changed

+112
-36
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
0.5.2 (2023-??)
2+
------------------
3+
* Feature: Allow configuring error email addresses via UI.
4+
5+
6+
17
0.5.1 (2023-02-22)
28
------------------
39

notebooker/constants.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class NotebookResultBase(object):
8080
status = attr.ib(default=JobStatus.ERROR)
8181
overrides = attr.ib(default=attr.Factory(dict))
8282
mailto = attr.ib(default="")
83+
error_mailto = attr.ib(default="")
8384
generate_pdf_output = attr.ib(default=True)
8485
hide_code = attr.ib(default=False)
8586
stdout = attr.ib(default=attr.Factory(list))
@@ -100,6 +101,7 @@ class NotebookResultPending(NotebookResultBase):
100101
report_title = attr.ib(default="")
101102
overrides = attr.ib(default=attr.Factory(dict))
102103
mailto = attr.ib(default="")
104+
error_mailto = attr.ib(default="")
103105
generate_pdf_output = attr.ib(default=True)
104106
hide_code = attr.ib(default=False)
105107
scheduler_job_id = attr.ib(default=None)
@@ -115,6 +117,7 @@ class NotebookResultError(NotebookResultBase):
115117
report_title = attr.ib(default="")
116118
overrides = attr.ib(default=attr.Factory(dict))
117119
mailto = attr.ib(default="")
120+
error_mailto = attr.ib(default="")
118121
generate_pdf_output = attr.ib(default=True)
119122
hide_code = attr.ib(default=False)
120123
scheduler_job_id = attr.ib(default=None)
@@ -155,6 +158,7 @@ class NotebookResultComplete(NotebookResultBase):
155158
report_title = attr.ib(default="")
156159
overrides = attr.ib(default=attr.Factory(dict))
157160
mailto = attr.ib(default="")
161+
error_mailto = attr.ib(default="")
158162
email_subject = attr.ib(default="")
159163
generate_pdf_output = attr.ib(default=True)
160164
hide_code = attr.ib(default=False)
@@ -185,6 +189,7 @@ def saveable_output(self):
185189
"job_start_time": self.job_start_time,
186190
"job_finish_time": self.job_finish_time,
187191
"mailto": self.mailto,
192+
"error_mailto": self.error_mailto,
188193
"email_subject": self.email_subject,
189194
"overrides": self.overrides,
190195
"generate_pdf_output": self.generate_pdf_output,
@@ -200,9 +205,9 @@ def __repr__(self):
200205
return (
201206
"NotebookResultComplete(job_id={job_id}, status={status}, report_name={report_name}, "
202207
"job_start_time={job_start_time}, job_finish_time={job_finish_time}, update_time={update_time}, "
203-
"report_title={report_title}, overrides={overrides}, mailto={mailto}, mailfrom={mailfrom}"
204-
"email_subject={email_subject}, generate_pdf_output={generate_pdf_output}, hide_code={hide_code}, "
205-
"scheduler_job_id={scheduler_job_id}, is_slideshow={is_slideshow})".format(
208+
"report_title={report_title}, overrides={overrides}, mailto={mailto}, error_mailto={error_mailto}, "
209+
"mailfrom={mailfrom}, email_subject={email_subject}, generate_pdf_output={generate_pdf_output}, "
210+
"hide_code={hide_code}, scheduler_job_id={scheduler_job_id}, is_slideshow={is_slideshow})".format(
206211
job_id=self.job_id,
207212
status=self.status,
208213
report_name=self.report_name,
@@ -212,6 +217,7 @@ def __repr__(self):
212217
report_title=self.report_title,
213218
overrides=self.overrides,
214219
mailto=self.mailto,
220+
error_mailto=self.error_mailto,
215221
mailfrom=self.mailfrom,
216222
email_subject=self.email_subject,
217223
generate_pdf_output=self.generate_pdf_output,

notebooker/execute_notebook.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def _run_checks(
4444
generate_pdf_output: Optional[bool] = True,
4545
hide_code: Optional[bool] = False,
4646
mailto: Optional[str] = "",
47+
error_mailto: Optional[str] = "",
4748
email_subject: Optional[str] = "",
4849
prepare_only: Optional[bool] = False,
4950
notebooker_disable_git: bool = False,
@@ -76,7 +77,9 @@ def _run_checks(
7677
generate_pdf_output : `Optional[bool]`
7778
Whether to generate PDF output or not. NB this requires xelatex to be installed on the executor.
7879
mailto : `Optional[str]`
79-
Comma-separated email addresses to send on completion (or error).
80+
Comma-separated email addresses to send on completion.
81+
error_mailto : `Optional[str]`
82+
Comma-separated email addresses to send on error.
8083
prepare_only : `Optional[bool]`
8184
Internal usage. Whether we want to do everything apart from executing the notebook.
8285
scheduler_job_id : `Optional[str]`
@@ -128,6 +131,7 @@ def _run_checks(
128131
raw_html=html,
129132
email_html=email_html,
130133
mailto=mailto,
134+
error_mailto=error_mailto,
131135
email_subject=email_subject,
132136
pdf=pdf,
133137
generate_pdf_output=generate_pdf_output,
@@ -191,6 +195,7 @@ def run_report(
191195
template_base_dir,
192196
overrides,
193197
mailto=mailto,
198+
error_mailto=error_mailto,
194199
email_subject=email_subject,
195200
generate_pdf_output=generate_pdf_output,
196201
hide_code=hide_code,
@@ -215,7 +220,8 @@ def run_report(
215220
report_title=report_title,
216221
error_info=error_info,
217222
overrides=overrides,
218-
mailto=error_mailto or mailto,
223+
mailto=mailto,
224+
error_mailto=error_mailto,
219225
generate_pdf_output=generate_pdf_output,
220226
scheduler_job_id=scheduler_job_id,
221227
mailfrom=mailfrom,
@@ -402,8 +408,7 @@ def execute_notebook_entrypoint(
402408
mailfrom=mailfrom,
403409
is_slideshow=is_slideshow,
404410
)
405-
if result.mailto:
406-
send_result_email(result, config.DEFAULT_MAILFROM)
411+
send_result_email(result, config.DEFAULT_MAILFROM)
407412
logger.info(f"Here is the result!{result}")
408413
if isinstance(result, NotebookResultError):
409414
logger.warning("Notebook execution failed! Output was:")
@@ -458,6 +463,7 @@ def run_report_in_subprocess(
458463
report_name,
459464
report_title,
460465
mailto,
466+
error_mailto,
461467
overrides,
462468
*,
463469
hide_code=False,
@@ -476,6 +482,7 @@ def run_report_in_subprocess(
476482
:param report_name: `str` The report which we are executing
477483
:param report_title: `str` The user-specified title of the report
478484
:param mailto: `Optional[str]` Who the results will be emailed to
485+
:param error_mailto: `Optional[str]` Who the errors will be emailed to
479486
:param overrides: `Optional[Dict[str, Any]]` The parameters to be passed into the report
480487
:param generate_pdf_output: `bool` Whether we're generating a PDF. Defaults to False.
481488
:param prepare_only: `bool` Whether to do everything except execute the notebook. Useful for testing.
@@ -486,6 +493,8 @@ def run_report_in_subprocess(
486493
:param is_slideshow: Whether the notebook is a reveal.js slideshow or not.
487494
:return: The unique job_id.
488495
"""
496+
if error_mailto is None:
497+
error_mailto = ""
489498
job_id = str(uuid.uuid4())
490499
job_start_time = datetime.datetime.now()
491500
result_serializer = initialize_serializer_from_config(base_config)
@@ -497,6 +506,7 @@ def run_report_in_subprocess(
497506
status=JobStatus.SUBMITTED,
498507
overrides=overrides,
499508
mailto=mailto,
509+
error_mailto=error_mailto,
500510
generate_pdf_output=generate_pdf_output,
501511
hide_code=hide_code,
502512
scheduler_job_id=scheduler_job_id,
@@ -530,6 +540,8 @@ def run_report_in_subprocess(
530540
report_title,
531541
"--mailto",
532542
mailto,
543+
"--error-mailto",
544+
error_mailto,
533545
"--overrides-as-json",
534546
json.dumps(overrides),
535547
"--pdf-output" if generate_pdf_output else "--no-pdf-output",

notebooker/serialization/mongo.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ def save_check_stub(
187187
status: JobStatus = JobStatus.PENDING,
188188
overrides: Optional[Dict] = None,
189189
mailto: str = "",
190+
error_mailto: str = "",
190191
generate_pdf_output: bool = True,
191192
hide_code: bool = False,
192193
scheduler_job_id: Optional[str] = None,
@@ -202,6 +203,7 @@ def save_check_stub(
202203
job_start_time=job_start_time,
203204
report_name=report_name,
204205
mailto=mailto,
206+
error_mailto=error_mailto,
205207
generate_pdf_output=generate_pdf_output,
206208
overrides=overrides or {},
207209
hide_code=hide_code,
@@ -299,6 +301,7 @@ def _convert_result(
299301
generate_pdf_output=result.get("generate_pdf_output", True),
300302
report_title=result.get("report_title", result["report_name"]),
301303
mailto=result.get("mailto", ""),
304+
error_mailto=result.get("error_mailto", ""),
302305
hide_code=result.get("hide_code", False),
303306
stdout=result.get("stdout", []),
304307
scheduler_job_id=result.get("scheduler_job_id", None),
@@ -315,6 +318,7 @@ def _convert_result(
315318
generate_pdf_output=result.get("generate_pdf_output", True),
316319
report_title=result.get("report_title", result["report_name"]),
317320
mailto=result.get("mailto", ""),
321+
error_mailto=result.get("error_mailto", ""),
318322
hide_code=result.get("hide_code", False),
319323
stdout=result.get("stdout", []),
320324
scheduler_job_id=result.get("scheduler_job_id", None),
@@ -338,6 +342,7 @@ def _convert_result(
338342
generate_pdf_output=result.get("generate_pdf_output", True),
339343
report_title=result.get("report_title", result["report_name"]),
340344
mailto=result.get("mailto", ""),
345+
error_mailto=result.get("error_mailto", ""),
341346
hide_code=result.get("hide_code", False),
342347
stdout=result.get("stdout", []),
343348
scheduler_job_id=result.get("scheduler_job_id", False),

notebooker/utils/notebook_execution.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,7 @@ def _output_dir(output_base_dir, report_name, job_id):
1515
return os.path.join(output_base_dir, report_name, job_id)
1616

1717

18-
def send_result_email(result: Union[NotebookResultComplete, NotebookResultError], default_mailfrom: str) -> None:
19-
if result.mailfrom:
20-
mailfrom = result.mailfrom
21-
else:
22-
mailfrom = default_mailfrom
23-
to_email = result.mailto
18+
def _send_email(from_email: str, to_email: str, result: Union[NotebookResultComplete, NotebookResultError]) -> None:
2419
report_title = (
2520
result.report_title.decode("utf-8") if isinstance(result.report_title, bytes) else result.report_title
2621
)
@@ -57,8 +52,23 @@ def send_result_email(result: Union[NotebookResultComplete, NotebookResultError]
5752
msg = ["Please either activate HTML emails, or see the PDF attachment.", body]
5853

5954
logger.info("Sending email to %s with %d attachments", to_email, len(attachments))
60-
mail(mailfrom, to_email, subject, msg, attachments=attachments)
55+
mail(from_email, to_email, subject, msg, attachments=attachments)
6156
finally:
6257
if tmp_dir:
6358
logger.info("Cleaning up temporary email attachment directory %s", tmp_dir)
6459
shutil.rmtree(tmp_dir)
60+
61+
62+
def send_result_email(result: Union[NotebookResultComplete, NotebookResultError], default_mailfrom: str) -> None:
63+
if result.mailfrom:
64+
mailfrom = result.mailfrom
65+
else:
66+
mailfrom = default_mailfrom
67+
if isinstance(result, NotebookResultComplete):
68+
to_email = result.mailto
69+
else:
70+
to_email = result.error_mailto or result.mailto
71+
if not to_email:
72+
logger.info("Not sending email as no recipients specified")
73+
return
74+
_send_email(from_email=mailfrom, to_email=to_email, result=result)

notebooker/web/routes/report_execution.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def run_report_http(report_name):
122122
class RunReportParams(NamedTuple):
123123
report_title: AnyStr
124124
mailto: AnyStr
125+
error_mailto: AnyStr
125126
mailfrom: AnyStr
126127
generate_pdf_output: bool
127128
hide_code: bool
@@ -135,6 +136,7 @@ def validate_run_params(report_name, params, issues: List[str]) -> RunReportPara
135136
report_title = validate_title(params.get("report_title") or report_name, issues)
136137
# Get mailto email address
137138
mailto = validate_mailto(params.get("mailto"), issues)
139+
error_mailto = validate_mailto(params.get("error_mailto"), issues)
138140
mailfrom = validate_mailto(params.get("mailfrom"), issues)
139141
# "on" comes from HTML, "True" comes from urlencoded JSON params
140142
generate_pdf_output = params.get("generate_pdf") in ("on", "True", True)
@@ -144,6 +146,7 @@ def validate_run_params(report_name, params, issues: List[str]) -> RunReportPara
144146
out = RunReportParams(
145147
report_title=report_title,
146148
mailto=mailto,
149+
error_mailto=error_mailto,
147150
mailfrom=mailfrom,
148151
generate_pdf_output=generate_pdf_output,
149152
hide_code=hide_code,
@@ -165,6 +168,7 @@ def _handle_run_report(
165168
f"Handling run report with parameters report_name={report_name} "
166169
f"report_title={params.report_title}"
167170
f"mailto={params.mailto} "
171+
f"error_mailto={params.error_mailto} "
168172
f"overrides_dict={overrides_dict} "
169173
f"generate_pdf_output={params.generate_pdf_output} "
170174
f"hide_code={params.hide_code} "
@@ -180,6 +184,7 @@ def _handle_run_report(
180184
report_name=report_name,
181185
report_title=params.report_title,
182186
mailto=params.mailto,
187+
error_mailto=params.error_mailto,
183188
overrides=overrides_dict,
184189
generate_pdf_output=params.generate_pdf_output,
185190
hide_code=params.hide_code,
@@ -242,6 +247,7 @@ def _rerun_report(job_id, prepare_only=False, run_synchronously=False):
242247
result.report_name,
243248
title,
244249
result.mailto,
250+
result.error_mailto,
245251
result.overrides,
246252
hide_code=result.hide_code,
247253
generate_pdf_output=result.generate_pdf_output,

notebooker/web/routes/scheduling.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def update_schedule(report_name):
7474
"overrides": overrides_dict,
7575
"report_title": params.report_title,
7676
"mailto": params.mailto,
77+
"error_mailto": params.error_mailto,
7778
"mailfrom": params.mailfrom,
7879
"generate_pdf": params.generate_pdf_output,
7980
"hide_code": params.hide_code,
@@ -106,6 +107,7 @@ def create_schedule(report_name):
106107
"overrides": overrides_dict,
107108
"report_title": params.report_title,
108109
"mailto": params.mailto,
110+
"error_mailto": params.error_mailto,
109111
"mailfrom": params.mailfrom,
110112
"generate_pdf": params.generate_pdf_output,
111113
"hide_code": params.hide_code,

notebooker/web/scheduler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def run_report(
2222
# new parameters should be added below and be optional to avoid migrations
2323
mailfrom: Optional[str] = None,
2424
is_slideshow: bool = False,
25+
error_mailto: Optional[str] = None,
2526
):
2627
"""
2728
This is the entrypoint of the scheduler; APScheduler has to
@@ -33,6 +34,7 @@ def run_report(
3334
report_name,
3435
report_title,
3536
mailto,
37+
error_mailto,
3638
overrides,
3739
hide_code=hide_code,
3840
generate_pdf_output=generate_pdf,
@@ -50,6 +52,7 @@ def run_report(
5052
"overrides": json.dumps(overrides),
5153
"report_title": report_title,
5254
"mailto": mailto,
55+
"error_mailto": error_mailto,
5356
"generate_pdf": generate_pdf,
5457
"hide_code": hide_code,
5558
"scheduler_job_id": scheduler_job_id,

notebooker/web/static/notebooker/scheduler.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ function modifySchedulerModal(row) {
139139
hide_code: row.params.hide_code,
140140
generate_pdf: row.params.generate_pdf,
141141
mailto: row.params.mailto,
142+
error_mailto: row.params.error_mailto,
142143
mailfrom: row.params.mailfrom,
143144
cronSchedule: row.cron_schedule,
144145
is_slideshow: row.params.is_slideshow,
@@ -166,6 +167,7 @@ function handleAddButtonClick() {
166167
hide_code: "",
167168
generate_pdf: "",
168169
mailto: "",
170+
error_mailto: "",
169171
mailfrom: "",
170172
cronSchedule: "",
171173
is_slideshow: "",
@@ -268,6 +270,7 @@ $(document).ready(() => {
268270
overrides: formObj.overrides,
269271
cron_schedule: formObj.cronSchedule,
270272
mailto: formObj.mailto,
273+
error_mailto: formObj.error_mailto,
271274
mailfrom: formObj.mailfrom,
272275
generate_pdf: formObj.generate_pdf,
273276
hide_code: formObj.hide_code,

notebooker/web/templates/results.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ <h4>Report Template Name:</h4> {{ report_name }}
5959
{% if result.mailto %}
6060
<h4>Mailing results to:</h4> {{ result.mailto }}
6161
{% endif %}
62+
{% if result.error_mailto %}
63+
<h4>Mailing errors to:</h4> {{ result.error_mailto }}
64+
{% endif %}
6265
{% if result.overrides %}
6366
<h4>Parameters:</h4>
6467
<div class="ui list">

0 commit comments

Comments
 (0)