Skip to content

Commit 9fdd442

Browse files
configurable email subject (#148)
configurable email subject
1 parent eca91f8 commit 9fdd442

File tree

15 files changed

+58
-15
lines changed

15 files changed

+58
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
0.5.2 (2023-??)
22
------------------
3-
* Feature: Allow configuring error email addresses via UI.
3+
* Feature: Allow configuring error email addresses and email subject via UI.
44
* Bugfix: . and .. should now be allowed to be used when specifying the templates directory.
55
* Bugfix: corrected cron schedule incorrectly shifting back one day upon save.
66

7-
87
0.5.1 (2023-02-22)
98
------------------
109

notebooker/constants.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class NotebookResultBase(object):
8686
stdout = attr.ib(default=attr.Factory(list))
8787
scheduler_job_id = attr.ib(default=None)
8888
mailfrom = attr.ib(default=None)
89+
email_subject = attr.ib(default=None)
8990
is_slideshow = attr.ib(default=False)
9091

9192
def saveable_output(self):
@@ -123,10 +124,7 @@ class NotebookResultError(NotebookResultBase):
123124
scheduler_job_id = attr.ib(default=None)
124125
mailfrom = attr.ib(default=None)
125126
is_slideshow = attr.ib(default=False)
126-
127-
@property
128-
def email_subject(self):
129-
return ""
127+
email_subject = attr.ib(default=None)
130128

131129
@property
132130
def raw_html(self):

notebooker/execute_notebook.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ def run_report(
227227
mailfrom=mailfrom,
228228
hide_code=hide_code,
229229
is_slideshow=is_slideshow,
230+
email_subject=email_subject,
230231
)
231232
logger.error(
232233
"Report run failed. Saving error result to mongo library %s@%s...",
@@ -472,6 +473,7 @@ def run_report_in_subprocess(
472473
scheduler_job_id=None,
473474
run_synchronously=False,
474475
mailfrom=None,
476+
email_subject=None,
475477
n_retries=3,
476478
is_slideshow=False,
477479
) -> str:
@@ -489,6 +491,7 @@ def run_report_in_subprocess(
489491
:param scheduler_job_id: `Optional[str]` if the job was triggered from the scheduler, this is the scheduler's job id
490492
:param run_synchronously: `bool` If True, then we will join the stderr monitoring thread until the job has completed
491493
:param mailfrom: `str` if passed, then this string will be used in the from field
494+
:param email_subject: `str` if passed, then this string will be used in the email subject
492495
:param n_retries: The number of retries to attempt.
493496
:param is_slideshow: Whether the notebook is a reveal.js slideshow or not.
494497
:return: The unique job_id.
@@ -511,6 +514,7 @@ def run_report_in_subprocess(
511514
hide_code=hide_code,
512515
scheduler_job_id=scheduler_job_id,
513516
is_slideshow=is_slideshow,
517+
email_subject=email_subject,
514518
)
515519

516520
command = (
@@ -553,6 +557,7 @@ def run_report_in_subprocess(
553557
+ (["--is-slideshow"] if is_slideshow else [])
554558
+ ([f"--scheduler-job-id={scheduler_job_id}"] if scheduler_job_id is not None else [])
555559
+ ([f"--mailfrom={mailfrom}"] if mailfrom is not None else [])
560+
+ ([f"--email-subject={email_subject}"] if email_subject else [])
556561
)
557562
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
558563

notebooker/serialization/mongo.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,7 @@ def update_check_status(self, job_id: str, status: JobStatus, **extra):
169169
existing["status"] = status.value
170170
for k, v in extra.items():
171171
if k == "error_info" and v:
172-
self.result_data_store.put(
173-
v,
174-
filename=_error_info_filename(job_id),
175-
encoding="utf-8",
176-
)
172+
self.result_data_store.put(v, filename=_error_info_filename(job_id), encoding="utf-8")
177173
else:
178174
existing[k] = v
179175
self._save_raw_to_db(existing)
@@ -192,6 +188,7 @@ def save_check_stub(
192188
hide_code: bool = False,
193189
scheduler_job_id: Optional[str] = None,
194190
is_slideshow: bool = False,
191+
email_subject: Optional[str] = None,
195192
) -> None:
196193
"""Call this when we are just starting a check. Saves a "pending" job into storage."""
197194
job_start_time = job_start_time or datetime.datetime.now()
@@ -204,6 +201,7 @@ def save_check_stub(
204201
report_name=report_name,
205202
mailto=mailto,
206203
error_mailto=error_mailto,
204+
email_subject=email_subject,
207205
generate_pdf_output=generate_pdf_output,
208206
overrides=overrides or {},
209207
hide_code=hide_code,
@@ -226,9 +224,7 @@ def save_check_result(self, notebook_result: Union[NotebookResultComplete, Noteb
226224
filename=filename_func(notebook_result.job_id),
227225
encoding="utf-8",
228226
)
229-
for json_attribute, filename_func in [
230-
("raw_ipynb_json", _raw_json_filename),
231-
]:
227+
for json_attribute, filename_func in [("raw_ipynb_json", _raw_json_filename)]:
232228
if getattr(notebook_result, json_attribute, None):
233229
self.result_data_store.put(
234230
json.dumps(getattr(notebook_result, json_attribute)),
@@ -306,6 +302,7 @@ def _convert_result(
306302
stdout=result.get("stdout", []),
307303
scheduler_job_id=result.get("scheduler_job_id", None),
308304
is_slideshow=result.get("is_slideshow", False),
305+
email_subject=result.get("email_subject", None),
309306
)
310307
elif cls == NotebookResultPending:
311308
return NotebookResultPending(
@@ -319,6 +316,7 @@ def _convert_result(
319316
report_title=result.get("report_title", result["report_name"]),
320317
mailto=result.get("mailto", ""),
321318
error_mailto=result.get("error_mailto", ""),
319+
email_subject=result.get("email_subject", ""),
322320
hide_code=result.get("hide_code", False),
323321
stdout=result.get("stdout", []),
324322
scheduler_job_id=result.get("scheduler_job_id", None),
@@ -343,6 +341,7 @@ def _convert_result(
343341
report_title=result.get("report_title", result["report_name"]),
344342
mailto=result.get("mailto", ""),
345343
error_mailto=result.get("error_mailto", ""),
344+
email_subject=result.get("email_subject", ""),
346345
hide_code=result.get("hide_code", False),
347346
stdout=result.get("stdout", []),
348347
scheduler_job_id=result.get("scheduler_job_id", False),

notebooker/utils/notebook_execution.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def _send_email(from_email: str, to_email: str, result: Union[NotebookResultComp
1919
report_title = (
2020
result.report_title.decode("utf-8") if isinstance(result.report_title, bytes) else result.report_title
2121
)
22-
subject = result.email_subject or f"Notebooker: {report_title} report completed with status: {result.status.value}"
22+
subject = f"Notebooker: {report_title} report completed with status: {result.status.value}"
23+
if isinstance(result, NotebookResultComplete):
24+
subject = result.email_subject or subject
2325
body = result.email_html or result.raw_html
2426
attachments = []
2527
tmp_dir = None

notebooker/web/routes/report_execution.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class RunReportParams(NamedTuple):
128128
hide_code: bool
129129
scheduler_job_id: Optional[str]
130130
is_slideshow: bool
131+
email_subject: Optional[str]
131132

132133

133134
def validate_run_params(report_name, params, issues: List[str]) -> RunReportParams:
@@ -142,6 +143,7 @@ def validate_run_params(report_name, params, issues: List[str]) -> RunReportPara
142143
generate_pdf_output = params.get("generate_pdf") in ("on", "True", True)
143144
hide_code = params.get("hide_code") in ("on", "True", True)
144145
is_slideshow = params.get("is_slideshow") in ("on", "True", True)
146+
email_subject = validate_title(params.get("email_subject") or "", issues)
145147

146148
out = RunReportParams(
147149
report_title=report_title,
@@ -152,6 +154,7 @@ def validate_run_params(report_name, params, issues: List[str]) -> RunReportPara
152154
hide_code=hide_code,
153155
scheduler_job_id=params.get("scheduler_job_id"),
154156
is_slideshow=is_slideshow,
157+
email_subject=email_subject,
155158
)
156159
logger.info(f"Validated params: {out}")
157160
return out
@@ -174,6 +177,7 @@ def _handle_run_report(
174177
f"hide_code={params.hide_code} "
175178
f"scheduler_job_id={params.scheduler_job_id} "
176179
f"mailfrom={params.mailfrom} "
180+
f"email_subject={params.email_subject} "
177181
f"is_slideshow={params.is_slideshow} "
178182
)
179183
try:
@@ -190,6 +194,7 @@ def _handle_run_report(
190194
hide_code=params.hide_code,
191195
scheduler_job_id=params.scheduler_job_id,
192196
mailfrom=params.mailfrom,
197+
email_subject=params.email_subject,
193198
is_slideshow=params.is_slideshow,
194199
)
195200
return (
@@ -255,6 +260,7 @@ def _rerun_report(job_id, prepare_only=False, run_synchronously=False):
255260
scheduler_job_id=None, # the scheduler will never call rerun
256261
run_synchronously=run_synchronously,
257262
is_slideshow=result.is_slideshow,
263+
email_subject=result.email_subject,
258264
)
259265
return new_job_id
260266

notebooker/web/routes/scheduling.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def update_schedule(report_name):
7676
"mailto": params.mailto,
7777
"error_mailto": params.error_mailto,
7878
"mailfrom": params.mailfrom,
79+
"email_subject": params.email_subject,
7980
"generate_pdf": params.generate_pdf_output,
8081
"hide_code": params.hide_code,
8182
"is_slideshow": params.is_slideshow,
@@ -109,6 +110,7 @@ def create_schedule(report_name):
109110
"mailto": params.mailto,
110111
"error_mailto": params.error_mailto,
111112
"mailfrom": params.mailfrom,
113+
"email_subject": params.email_subject,
112114
"generate_pdf": params.generate_pdf_output,
113115
"hide_code": params.hide_code,
114116
"scheduler_job_id": job_id,

notebooker/web/scheduler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def run_report(
2323
mailfrom: Optional[str] = None,
2424
is_slideshow: bool = False,
2525
error_mailto: Optional[str] = None,
26+
email_subject: Optional[str] = None,
2627
):
2728
"""
2829
This is the entrypoint of the scheduler; APScheduler has to
@@ -44,6 +45,7 @@ def run_report(
4445
mailfrom=mailfrom,
4546
n_retries=0,
4647
is_slideshow=is_slideshow,
48+
email_subject=email_subject,
4749
)
4850
else:
4951
# Fall back to using API. This will not work in readonly mode.
@@ -64,6 +66,8 @@ def run_report(
6466
# natural.
6567
if mailfrom:
6668
payload["mailfrom"] = mailfrom
69+
if email_subject:
70+
payload["email_subject"] = email_subject
6771
logger.info(f"Running report at {url}, payload = {payload}")
6872
result = requests.post(url, params=urllib.parse.urlencode(payload))
6973
logger.info(result.content)

notebooker/web/static/notebooker/scheduler.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ function modifySchedulerModal(row) {
141141
mailto: row.params.mailto,
142142
error_mailto: row.params.error_mailto,
143143
mailfrom: row.params.mailfrom,
144+
email_subject: row.params.email_subject,
144145
cronSchedule: row.cron_schedule,
145146
is_slideshow: row.params.is_slideshow,
146147
}
@@ -169,6 +170,7 @@ function handleAddButtonClick() {
169170
mailto: "",
170171
error_mailto: "",
171172
mailfrom: "",
173+
email_subject: "",
172174
cronSchedule: "",
173175
is_slideshow: "",
174176
}
@@ -272,6 +274,7 @@ $(document).ready(() => {
272274
mailto: formObj.mailto,
273275
error_mailto: formObj.error_mailto,
274276
mailfrom: formObj.mailfrom,
277+
email_subject: formObj.email_subject,
275278
generate_pdf: formObj.generate_pdf,
276279
hide_code: formObj.hide_code,
277280
is_slideshow: formObj.is_slideshow,

notebooker/web/templates/results.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ <h4>Mailing results to:</h4> {{ result.mailto }}
6262
{% if result.error_mailto %}
6363
<h4>Mailing errors to:</h4> {{ result.error_mailto }}
6464
{% endif %}
65+
{% if result.email_subject %}
66+
<h4>Email subject:</h4> {{ result.email_subject }}
67+
{% endif %}
6568
{% if result.overrides %}
6669
<h4>Parameters:</h4>
6770
<div class="ui list">

0 commit comments

Comments
 (0)