Skip to content

Commit 67dabaf

Browse files
authored
Merge pull request #28 from edf825/upstream_man_changes
Add options to: Hide code from emails and PDFs Specify a subject to use if notebook succeeds Specify a separate list of email recipients for errors
2 parents 86fd239 + 266e40c commit 67dabaf

File tree

14 files changed

+279
-19
lines changed

14 files changed

+279
-19
lines changed

notebooker/_entrypoints.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,14 @@ def start_webapp(config: BaseConfig, port, logging_level, debug, base_cache_dir)
126126
help="The unique job ID for this notebook. Can be non-unique, but note that you will overwrite history.",
127127
)
128128
@click.option("--mailto", default="", help="A comma-separated list of email addresses which will receive results.")
129+
@click.option(
130+
"--error-mailto",
131+
default="",
132+
help="A comma-separated list of email addresses which will receive errors. Deafults to --mailto argument."
133+
)
134+
@click.option("--email-subject", default="", help="The subject of the email sent on a successful result.")
129135
@click.option("--pdf-output/--no-pdf-output", default=True, help="Whether we generate PDF output or not.")
136+
@click.option("--hide-code/--show-code", default=False, help="Hide code from email and PDF output.")
130137
@click.option(
131138
"--prepare-notebook-only",
132139
is_flag=True,
@@ -142,7 +149,10 @@ def execute_notebook(
142149
n_retries,
143150
job_id,
144151
mailto,
152+
error_mailto,
153+
email_subject,
145154
pdf_output,
155+
hide_code,
146156
prepare_notebook_only,
147157
):
148158
if report_name is None:
@@ -156,7 +166,10 @@ def execute_notebook(
156166
n_retries,
157167
job_id,
158168
mailto,
169+
error_mailto,
170+
email_subject,
159171
pdf_output,
172+
hide_code,
160173
prepare_notebook_only,
161174
)
162175

notebooker/constants.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class NotebookResultBase(object):
7575
overrides = attr.ib(default=attr.Factory(dict))
7676
mailto = attr.ib(default="")
7777
generate_pdf_output = attr.ib(default=True)
78+
hide_code = attr.ib(default=False)
7879
stdout = attr.ib(default=attr.Factory(list))
7980

8081
def saveable_output(self):
@@ -91,6 +92,7 @@ class NotebookResultPending(NotebookResultBase):
9192
overrides = attr.ib(default=attr.Factory(dict))
9293
mailto = attr.ib(default="")
9394
generate_pdf_output = attr.ib(default=True)
95+
hide_code = attr.ib(default=False)
9496

9597

9698
@attr.s()
@@ -102,13 +104,22 @@ class NotebookResultError(NotebookResultBase):
102104
overrides = attr.ib(default=attr.Factory(dict))
103105
mailto = attr.ib(default="")
104106
generate_pdf_output = attr.ib(default=True)
107+
hide_code = attr.ib(default=False)
108+
109+
@property
110+
def email_subject(self):
111+
return ""
105112

106113
@property
107114
def raw_html(self):
108115
return """<p>This job resulted in an error: <br/><code style="white-space: pre-wrap;">{}</code></p>""".format(
109116
self.error_info
110117
)
111118

119+
@property
120+
def email_html(self):
121+
return self.raw_html
122+
112123

113124
@attr.s(repr=False)
114125
class NotebookResultComplete(NotebookResultBase):
@@ -118,12 +129,15 @@ class NotebookResultComplete(NotebookResultBase):
118129
status = attr.ib(default=JobStatus.DONE)
119130
raw_ipynb_json = attr.ib(default="")
120131
raw_html = attr.ib(default="")
132+
email_html = attr.ib(default="")
121133
update_time = attr.ib(default=datetime.datetime.now())
122134
pdf = attr.ib(default="")
123135
report_title = attr.ib(default="")
124136
overrides = attr.ib(default=attr.Factory(dict))
125137
mailto = attr.ib(default="")
138+
email_subject = attr.ib(default="")
126139
generate_pdf_output = attr.ib(default=True)
140+
hide_code = attr.ib(default=False)
127141
stdout = attr.ib(default=attr.Factory(list))
128142

129143
def html_resources(self):
@@ -143,13 +157,16 @@ def saveable_output(self):
143157
"report_name": self.report_name,
144158
"report_title": self.report_title,
145159
"raw_html": self.raw_html,
160+
"email_html": self.email_html,
146161
"raw_html_resources": self.html_resources(),
147162
"job_id": self.job_id,
148163
"job_start_time": self.job_start_time,
149164
"job_finish_time": self.job_finish_time,
150165
"mailto": self.mailto,
166+
"email_subject": self.email_subject,
151167
"overrides": self.overrides,
152168
"generate_pdf_output": self.generate_pdf_output,
169+
"hide_code": self.hide_code,
153170
"update_time": self.update_time,
154171
}
155172

@@ -158,7 +175,7 @@ def __repr__(self):
158175
"NotebookResultComplete(job_id={job_id}, status={status}, report_name={report_name}, "
159176
"job_start_time={job_start_time}, job_finish_time={job_finish_time}, update_time={update_time}, "
160177
"report_title={report_title}, overrides={overrides}, mailto={mailto}, "
161-
"generate_pdf_output={generate_pdf_output})".format(
178+
"email_subject={email_subject}, generate_pdf_output={generate_pdf_output}, hide_code={hide_code})".format(
162179
job_id=self.job_id,
163180
status=self.status,
164181
report_name=self.report_name,
@@ -168,6 +185,8 @@ def __repr__(self):
168185
report_title=self.report_title,
169186
overrides=self.overrides,
170187
mailto=self.mailto,
188+
email_subject=self.email_subject,
171189
generate_pdf_output=self.generate_pdf_output,
190+
hide_code=self.hide_code,
172191
)
173192
)

notebooker/execute_notebook.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def _run_checks(
3737
template_base_dir: str,
3838
overrides: Dict[AnyStr, Any],
3939
generate_pdf_output: Optional[bool] = True,
40+
hide_code: Optional[bool] = False,
4041
mailto: Optional[str] = "",
42+
error_mailto: Optional[str] = "",
43+
email_subject: Optional[str] = "",
4144
prepare_only: Optional[bool] = False,
4245
notebooker_disable_git: bool = False,
4346
py_template_base_dir: str = "",
@@ -101,7 +104,8 @@ def _run_checks(
101104

102105
logger.info("Saving output notebook as HTML from {}".format(ipynb_executed_path))
103106
html, resources = ipython_to_html(ipynb_executed_path, job_id)
104-
pdf = ipython_to_pdf(raw_executed_ipynb, report_title) if generate_pdf_output else ""
107+
email_html, resources = ipython_to_html(ipynb_executed_path, job_id, hide_code=hide_code)
108+
pdf = ipython_to_pdf(raw_executed_ipynb, report_title, hide_code=hide_code) if generate_pdf_output else ""
105109

106110
notebook_result = NotebookResultComplete(
107111
job_id=job_id,
@@ -110,7 +114,9 @@ def _run_checks(
110114
raw_html_resources=resources,
111115
raw_ipynb_json=raw_executed_ipynb,
112116
raw_html=html,
117+
email_html=email_html,
113118
mailto=mailto,
119+
email_subject=email_subject,
114120
pdf=pdf,
115121
generate_pdf_output=generate_pdf_output,
116122
report_name=template_name,
@@ -131,7 +137,10 @@ def run_report(
131137
template_base_dir=None,
132138
attempts_remaining=2,
133139
mailto="",
140+
error_mailto="",
141+
email_subject="",
134142
generate_pdf_output=True,
143+
hide_code=False,
135144
prepare_only=False,
136145
notebooker_disable_git=False,
137146
py_template_base_dir="",
@@ -163,7 +172,9 @@ def run_report(
163172
template_base_dir,
164173
overrides,
165174
mailto=mailto,
175+
email_subject=email_subject,
166176
generate_pdf_output=generate_pdf_output,
177+
hide_code=hide_code,
167178
prepare_only=prepare_only,
168179
notebooker_disable_git=notebooker_disable_git,
169180
py_template_base_dir=py_template_base_dir,
@@ -182,7 +193,7 @@ def run_report(
182193
report_title=report_title,
183194
error_info=error_info,
184195
overrides=overrides,
185-
mailto=mailto,
196+
mailto=error_mailto or mailto,
186197
generate_pdf_output=generate_pdf_output,
187198
)
188199
logger.error(
@@ -205,7 +216,10 @@ def run_report(
205216
template_base_dir=template_base_dir,
206217
attempts_remaining=attempts_remaining - 1,
207218
mailto=mailto,
219+
error_mailto=error_mailto,
220+
email_subject=email_subject,
208221
generate_pdf_output=generate_pdf_output,
222+
hide_code=hide_code,
209223
prepare_only=prepare_only,
210224
notebooker_disable_git=notebooker_disable_git,
211225
py_template_base_dir=py_template_base_dir,
@@ -291,7 +305,10 @@ def execute_notebook_entrypoint(
291305
n_retries: int,
292306
job_id: str,
293307
mailto: str,
308+
error_mailto: str,
309+
email_subject: str,
294310
pdf_output: bool,
311+
hide_code: bool,
295312
prepare_notebook_only: bool,
296313
):
297314
report_title = report_title or report_name
@@ -312,7 +329,10 @@ def execute_notebook_entrypoint(
312329
logger.info("output_dir = %s", output_dir)
313330
logger.info("template_dir = %s", template_dir)
314331
logger.info("mailto = %s", mailto)
332+
logger.info("error_mailto = %s", error_mailto)
333+
logger.info("email_subject = %s", email_subject)
315334
logger.info("pdf_output = %s", pdf_output)
335+
logger.info("hide_code = %s", hide_code)
316336
logger.info("prepare_notebook_only = %s", prepare_notebook_only)
317337
logger.info("notebooker_disable_git = %s", notebooker_disable_git)
318338
logger.info("py_template_base_dir = %s", py_template_base_dir)
@@ -335,13 +355,16 @@ def execute_notebook_entrypoint(
335355
template_base_dir=template_dir,
336356
attempts_remaining=n_retries - 1,
337357
mailto=mailto,
358+
error_mailto=error_mailto,
359+
email_subject=email_subject,
338360
generate_pdf_output=pdf_output,
361+
hide_code=hide_code,
339362
prepare_only=prepare_notebook_only,
340363
notebooker_disable_git=notebooker_disable_git,
341364
py_template_base_dir=py_template_base_dir,
342365
py_template_subdir=py_template_subdir,
343366
)
344-
if mailto:
367+
if result.mailto:
345368
send_result_email(result, mailto)
346369
if isinstance(result, NotebookResultError):
347370
logger.warning("Notebook execution failed! Output was:")

notebooker/nbtemplates/notebooker_html_output.tpl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@
1616
}
1717
1818
</style>
19+
20+
<!--[if mso]>
21+
<style type="text/css">
22+
div div.cell {
23+
border-style: none;
24+
margin-top: 4;
25+
}
26+
</style>
27+
<![endif]-->
28+
1929
<script>
2030
(function() {
2131
function addToggleCodeButton() {
@@ -63,4 +73,10 @@
6373
document.addEventListener('DOMContentLoaded', addToggleCodeButton);
6474
}());
6575
</script>
66-
{%- endblock html_head -%}
76+
{%- endblock html_head -%}
77+
78+
{% block stream %}
79+
{%- if resources.global_content_filter.include_output_prompt -%}
80+
{{ super() }}
81+
{%- endif -%}
82+
{%- endblock stream %}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
((* extends 'article.tplx' *))
2+
3+
((*- block stream -*))
4+
((*- if resources.global_content_filter.include_output_prompt -*))
5+
((( super() )))
6+
((*- endif -*))
7+
((*- endblock stream -*))

notebooker/serialization/mongo.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def save_check_stub(
9797
overrides: Optional[Dict] = None,
9898
mailto: str = "",
9999
generate_pdf_output: bool = True,
100+
hide_code: bool = False,
100101
) -> None:
101102
""" Call this when we are just starting a check. Saves a "pending" job into storage. """
102103
job_start_time = job_start_time or datetime.datetime.now()
@@ -110,6 +111,7 @@ def save_check_stub(
110111
mailto=mailto,
111112
generate_pdf_output=generate_pdf_output,
112113
overrides=overrides or {},
114+
hide_code=hide_code,
113115
)
114116
self._save_to_db(pending_result)
115117

@@ -176,11 +178,13 @@ def read_file(path):
176178
raw_html_resources=result.get("raw_html_resources", {}),
177179
raw_ipynb_json=result.get("raw_ipynb_json"),
178180
raw_html=result.get("raw_html"),
181+
email_html=result.get("email_html"),
179182
pdf=result.get("pdf", ""),
180183
overrides=result.get("overrides", {}),
181184
generate_pdf_output=result.get("generate_pdf_output", True),
182185
report_title=result.get("report_title", result["report_name"]),
183186
mailto=result.get("mailto", ""),
187+
hide_code=result.get("hide_code", False),
184188
stdout=result.get("stdout", []),
185189
)
186190
elif cls == NotebookResultPending:
@@ -194,6 +198,7 @@ def read_file(path):
194198
generate_pdf_output=result.get("generate_pdf_output", True),
195199
report_title=result.get("report_title", result["report_name"]),
196200
mailto=result.get("mailto", ""),
201+
hide_code=result.get("hide_code", False),
197202
stdout=result.get("stdout", []),
198203
)
199204

@@ -209,6 +214,7 @@ def read_file(path):
209214
generate_pdf_output=result.get("generate_pdf_output", True),
210215
report_title=result.get("report_title", result["report_name"]),
211216
mailto=result.get("mailto", ""),
217+
hide_code=result.get("hide_code", False),
212218
stdout=result.get("stdout", []),
213219
)
214220
else:

notebooker/utils/conversion.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ def get_resources_dir(job_id):
1919
return "{}/resources".format(job_id)
2020

2121

22-
def ipython_to_html(ipynb_path: str, job_id: str) -> (nbformat.NotebookNode, Dict[str, Any]):
22+
def ipython_to_html(ipynb_path: str, job_id: str, hide_code: bool = False) -> (nbformat.NotebookNode, Dict[str, Any]):
2323
c = Config()
2424
c.HTMLExporter.preprocessors = ["nbconvert.preprocessors.ExtractOutputPreprocessor"]
2525
c.HTMLExporter.template_file = pkg_resources.resource_filename(
2626
__name__, "../nbtemplates/notebooker_html_output.tpl"
2727
)
28+
c.HTMLExporter.exclude_input = hide_code
29+
c.HTMLExporter.exclude_output_prompt = hide_code
2830
html_exporter_with_figs = HTMLExporter(config=c)
2931

3032
with open(ipynb_path, "r") as nb_file:
@@ -34,8 +36,14 @@ def ipython_to_html(ipynb_path: str, job_id: str) -> (nbformat.NotebookNode, Dic
3436
return html, resources
3537

3638

37-
def ipython_to_pdf(raw_executed_ipynb: str, report_title: str) -> AnyStr:
38-
pdf_exporter = PDFExporter(Config())
39+
def ipython_to_pdf(raw_executed_ipynb: str, report_title: str, hide_code: bool = False) -> AnyStr:
40+
c = Config()
41+
c.PDFExporter.exclude_input = hide_code
42+
c.PDFExporter.exclude_output_prompt = hide_code
43+
c.HTMLExporter.template_file = pkg_resources.resource_filename(
44+
__name__, "../nbtemplates/notebooker_pdf_output.tplx"
45+
)
46+
pdf_exporter = PDFExporter(c)
3947
resources = ResourcesDict()
4048
resources["metadata"] = ResourcesDict()
4149
resources["metadata"]["name"] = report_title

notebooker/utils/notebook_execution.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ 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], mailto: AnyStr) -> None:
18+
def send_result_email(result: Union[NotebookResultComplete, NotebookResultError]) -> None:
1919
from_email = "[email protected]"
20-
to_email = mailto
20+
to_email = result.mailto
2121
report_title = (
2222
result.report_title.decode("utf-8") if isinstance(result.report_title, bytes) else result.report_title
2323
)
24-
subject = "Notebooker: {} report completed with status: {}".format(report_title, result.status.value)
25-
body = result.raw_html
24+
subject = result.email_subject or f"Notebooker: {report_title} report completed with status: {result.status.value}"
25+
body = result.email_html or result.raw_html
2626
attachments = []
2727
tmp_dir = None
2828
try:
@@ -53,7 +53,7 @@ def send_result_email(result: Union[NotebookResultComplete, NotebookResultError]
5353

5454
msg = ["Please either activate HTML emails, or see the PDF attachment.", body]
5555

56-
logger.info("Sending email to %s with %d attachments", mailto, len(attachments))
56+
logger.info("Sending email to %s with %d attachments", to_email, len(attachments))
5757
mail(from_email, to_email, subject, msg, attachments=attachments)
5858
finally:
5959
if tmp_dir:

0 commit comments

Comments
 (0)