Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions notebooker/_entrypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,14 @@ def start_webapp(config: BaseConfig, port, logging_level, debug, base_cache_dir)
help="The unique job ID for this notebook. Can be non-unique, but note that you will overwrite history.",
)
@click.option("--mailto", default="", help="A comma-separated list of email addresses which will receive results.")
@click.option(
"--error-mailto",
default="",
help="A comma-separated list of email addresses which will receive errors. Deafults to --mailto argument."
)
@click.option("--email-subject", default="", help="The subject of the email sent on a successful result.")
@click.option("--pdf-output/--no-pdf-output", default=True, help="Whether we generate PDF output or not.")
@click.option("--hide-code/--show-code", default=False, help="Hide code from email and PDF output.")
@click.option(
"--prepare-notebook-only",
is_flag=True,
Expand All @@ -140,7 +147,10 @@ def execute_notebook(
n_retries,
job_id,
mailto,
error_mailto,
email_subject,
pdf_output,
hide_code,
prepare_notebook_only,
):
if report_name is None:
Expand All @@ -154,7 +164,10 @@ def execute_notebook(
n_retries,
job_id,
mailto,
error_mailto,
email_subject,
pdf_output,
hide_code,
prepare_notebook_only,
)

Expand Down
21 changes: 20 additions & 1 deletion notebooker/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class NotebookResultBase(object):
overrides = attr.ib(default=attr.Factory(dict))
mailto = attr.ib(default="")
generate_pdf_output = attr.ib(default=True)
hide_code = attr.ib(default=False)
stdout = attr.ib(default=attr.Factory(list))

def saveable_output(self):
Expand All @@ -91,6 +92,7 @@ class NotebookResultPending(NotebookResultBase):
overrides = attr.ib(default=attr.Factory(dict))
mailto = attr.ib(default="")
generate_pdf_output = attr.ib(default=True)
hide_code = attr.ib(default=False)


@attr.s()
Expand All @@ -102,13 +104,22 @@ class NotebookResultError(NotebookResultBase):
overrides = attr.ib(default=attr.Factory(dict))
mailto = attr.ib(default="")
generate_pdf_output = attr.ib(default=True)
hide_code = attr.ib(default=False)

@property
def email_subject(self):
return ""

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

@property
def email_html(self):
return self.raw_html


@attr.s(repr=False)
class NotebookResultComplete(NotebookResultBase):
Expand All @@ -118,12 +129,15 @@ class NotebookResultComplete(NotebookResultBase):
status = attr.ib(default=JobStatus.DONE)
raw_ipynb_json = attr.ib(default="")
raw_html = attr.ib(default="")
email_html = attr.ib(default="")
update_time = attr.ib(default=datetime.datetime.now())
pdf = attr.ib(default="")
report_title = attr.ib(default="")
overrides = attr.ib(default=attr.Factory(dict))
mailto = attr.ib(default="")
email_subject = attr.ib(default="")
generate_pdf_output = attr.ib(default=True)
hide_code = attr.ib(default=False)
stdout = attr.ib(default=attr.Factory(list))

def html_resources(self):
Expand All @@ -143,13 +157,16 @@ def saveable_output(self):
"report_name": self.report_name,
"report_title": self.report_title,
"raw_html": self.raw_html,
"email_html": self.email_html,
"raw_html_resources": self.html_resources(),
"job_id": self.job_id,
"job_start_time": self.job_start_time,
"job_finish_time": self.job_finish_time,
"mailto": self.mailto,
"email_subject": self.email_subject,
"overrides": self.overrides,
"generate_pdf_output": self.generate_pdf_output,
"hide_code": self.hide_code,
"update_time": self.update_time,
}

Expand All @@ -158,7 +175,7 @@ def __repr__(self):
"NotebookResultComplete(job_id={job_id}, status={status}, report_name={report_name}, "
"job_start_time={job_start_time}, job_finish_time={job_finish_time}, update_time={update_time}, "
"report_title={report_title}, overrides={overrides}, mailto={mailto}, "
"generate_pdf_output={generate_pdf_output})".format(
"email_subject={email_subject}, generate_pdf_output={generate_pdf_output}, hide_code={hide_code})".format(
job_id=self.job_id,
status=self.status,
report_name=self.report_name,
Expand All @@ -168,6 +185,8 @@ def __repr__(self):
report_title=self.report_title,
overrides=self.overrides,
mailto=self.mailto,
email_subject=self.email_subject,
generate_pdf_output=self.generate_pdf_output,
hide_code=self.hide_code,
)
)
29 changes: 26 additions & 3 deletions notebooker/execute_notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ def _run_checks(
template_base_dir: str,
overrides: Dict[AnyStr, Any],
generate_pdf_output: Optional[bool] = True,
hide_code: Optional[bool] = False,
mailto: Optional[str] = "",
error_mailto: Optional[str] = "",
email_subject: Optional[str] = "",
prepare_only: Optional[bool] = False,
notebooker_disable_git: bool = False,
py_template_base_dir: str = "",
Expand Down Expand Up @@ -101,7 +104,8 @@ def _run_checks(

logger.info("Saving output notebook as HTML from {}".format(ipynb_executed_path))
html, resources = ipython_to_html(ipynb_executed_path, job_id)
pdf = ipython_to_pdf(raw_executed_ipynb, report_title) if generate_pdf_output else ""
email_html, resources = ipython_to_html(ipynb_executed_path, job_id, hide_code=hide_code)
pdf = ipython_to_pdf(raw_executed_ipynb, report_title, hide_code=hide_code) if generate_pdf_output else ""

notebook_result = NotebookResultComplete(
job_id=job_id,
Expand All @@ -110,7 +114,9 @@ def _run_checks(
raw_html_resources=resources,
raw_ipynb_json=raw_executed_ipynb,
raw_html=html,
email_html=email_html,
mailto=mailto,
email_subject=email_subject,
pdf=pdf,
generate_pdf_output=generate_pdf_output,
report_name=template_name,
Expand All @@ -131,7 +137,10 @@ def run_report(
template_base_dir=None,
attempts_remaining=2,
mailto="",
error_mailto="",
email_subject="",
generate_pdf_output=True,
hide_code=False,
prepare_only=False,
notebooker_disable_git=False,
py_template_base_dir="",
Expand Down Expand Up @@ -163,7 +172,9 @@ def run_report(
template_base_dir,
overrides,
mailto=mailto,
email_subject=email_subject,
generate_pdf_output=generate_pdf_output,
hide_code=hide_code,
prepare_only=prepare_only,
notebooker_disable_git=notebooker_disable_git,
py_template_base_dir=py_template_base_dir,
Expand All @@ -182,7 +193,7 @@ def run_report(
report_title=report_title,
error_info=error_info,
overrides=overrides,
mailto=mailto,
mailto=error_mailto or mailto,
generate_pdf_output=generate_pdf_output,
)
logger.error(
Expand All @@ -205,7 +216,10 @@ def run_report(
template_base_dir=template_base_dir,
attempts_remaining=attempts_remaining - 1,
mailto=mailto,
error_mailto=error_mailto,
email_subject=email_subject,
generate_pdf_output=generate_pdf_output,
hide_code=hide_code,
prepare_only=prepare_only,
notebooker_disable_git=notebooker_disable_git,
py_template_base_dir=py_template_base_dir,
Expand Down Expand Up @@ -291,7 +305,10 @@ def execute_notebook_entrypoint(
n_retries: int,
job_id: str,
mailto: str,
error_mailto: str,
email_subject: str,
pdf_output: bool,
hide_code: bool,
prepare_notebook_only: bool,
):
report_title = report_title or report_name
Expand All @@ -312,7 +329,10 @@ def execute_notebook_entrypoint(
logger.info("output_dir = %s", output_dir)
logger.info("template_dir = %s", template_dir)
logger.info("mailto = %s", mailto)
logger.info("error_mailto = %s", error_mailto)
logger.info("email_subject = %s", email_subject)
logger.info("pdf_output = %s", pdf_output)
logger.info("hide_code = %s", hide_code)
logger.info("prepare_notebook_only = %s", prepare_notebook_only)
logger.info("notebooker_disable_git = %s", notebooker_disable_git)
logger.info("py_template_base_dir = %s", py_template_base_dir)
Expand All @@ -335,13 +355,16 @@ def execute_notebook_entrypoint(
template_base_dir=template_dir,
attempts_remaining=n_retries - 1,
mailto=mailto,
error_mailto=error_mailto,
email_subject=email_subject,
generate_pdf_output=pdf_output,
hide_code=hide_code,
prepare_only=prepare_notebook_only,
notebooker_disable_git=notebooker_disable_git,
py_template_base_dir=py_template_base_dir,
py_template_subdir=py_template_subdir,
)
if mailto:
if result.mailto:
send_result_email(result, mailto)
if isinstance(result, NotebookResultError):
logger.warning("Notebook execution failed! Output was:")
Expand Down
18 changes: 17 additions & 1 deletion notebooker/nbtemplates/notebooker_html_output.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
}

</style>

<!--[if mso]>
<style type="text/css">
div div.cell {
border-style: none;
margin-top: 4;
}
</style>
<![endif]-->

<script>
(function() {
function addToggleCodeButton() {
Expand Down Expand Up @@ -63,4 +73,10 @@
document.addEventListener('DOMContentLoaded', addToggleCodeButton);
}());
</script>
{%- endblock html_head -%}
{%- endblock html_head -%}

{% block stream %}
{%- if resources.global_content_filter.include_output_prompt -%}
{{ super() }}
{%- endif -%}
{%- endblock stream %}
7 changes: 7 additions & 0 deletions notebooker/nbtemplates/notebooker_pdf_output.tplx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
((* extends 'article.tplx' *))

((*- block stream -*))
((*- if resources.global_content_filter.include_output_prompt -*))
((( super() )))
((*- endif -*))
((*- endblock stream -*))
6 changes: 6 additions & 0 deletions notebooker/serialization/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def save_check_stub(
overrides: Optional[Dict] = None,
mailto: str = "",
generate_pdf_output: bool = True,
hide_code: bool = False,
) -> None:
""" Call this when we are just starting a check. Saves a "pending" job into storage. """
job_start_time = job_start_time or datetime.datetime.now()
Expand All @@ -110,6 +111,7 @@ def save_check_stub(
mailto=mailto,
generate_pdf_output=generate_pdf_output,
overrides=overrides or {},
hide_code=hide_code,
)
self._save_to_db(pending_result)

Expand Down Expand Up @@ -176,11 +178,13 @@ def read_file(path):
raw_html_resources=result.get("raw_html_resources", {}),
raw_ipynb_json=result.get("raw_ipynb_json"),
raw_html=result.get("raw_html"),
email_html=result.get("email_html"),
pdf=result.get("pdf", ""),
overrides=result.get("overrides", {}),
generate_pdf_output=result.get("generate_pdf_output", True),
report_title=result.get("report_title", result["report_name"]),
mailto=result.get("mailto", ""),
hide_code=result.get("hide_code", False),
stdout=result.get("stdout", []),
)
elif cls == NotebookResultPending:
Expand All @@ -194,6 +198,7 @@ def read_file(path):
generate_pdf_output=result.get("generate_pdf_output", True),
report_title=result.get("report_title", result["report_name"]),
mailto=result.get("mailto", ""),
hide_code=result.get("hide_code", False),
stdout=result.get("stdout", []),
)

Expand All @@ -209,6 +214,7 @@ def read_file(path):
generate_pdf_output=result.get("generate_pdf_output", True),
report_title=result.get("report_title", result["report_name"]),
mailto=result.get("mailto", ""),
hide_code=result.get("hide_code", False),
stdout=result.get("stdout", []),
)
else:
Expand Down
14 changes: 11 additions & 3 deletions notebooker/utils/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ def get_resources_dir(job_id):
return "{}/resources".format(job_id)


def ipython_to_html(ipynb_path: str, job_id: str) -> (nbformat.NotebookNode, Dict[str, Any]):
def ipython_to_html(ipynb_path: str, job_id: str, hide_code: bool = False) -> (nbformat.NotebookNode, Dict[str, Any]):
c = Config()
c.HTMLExporter.preprocessors = ["nbconvert.preprocessors.ExtractOutputPreprocessor"]
c.HTMLExporter.template_file = pkg_resources.resource_filename(
__name__, "../nbtemplates/notebooker_html_output.tpl"
)
c.HTMLExporter.exclude_input = hide_code
c.HTMLExporter.exclude_output_prompt = hide_code
html_exporter_with_figs = HTMLExporter(config=c)

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


def ipython_to_pdf(raw_executed_ipynb: str, report_title: str) -> AnyStr:
pdf_exporter = PDFExporter(Config())
def ipython_to_pdf(raw_executed_ipynb: str, report_title: str, hide_code: bool = False) -> AnyStr:
c = Config()
c.PDFExporter.exclude_input = hide_code
c.PDFExporter.exclude_output_prompt = hide_code
c.HTMLExporter.template_file = pkg_resources.resource_filename(
__name__, "../nbtemplates/notebooker_pdf_output.tplx"
)
pdf_exporter = PDFExporter(c)
resources = ResourcesDict()
resources["metadata"] = ResourcesDict()
resources["metadata"]["name"] = report_title
Expand Down
10 changes: 5 additions & 5 deletions notebooker/utils/notebook_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ def _output_dir(output_base_dir, report_name, job_id):
return os.path.join(output_base_dir, report_name, job_id)


def send_result_email(result: Union[NotebookResultComplete, NotebookResultError], mailto: AnyStr) -> None:
def send_result_email(result: Union[NotebookResultComplete, NotebookResultError]) -> None:
from_email = "notebooker@notebooker.io"
to_email = mailto
to_email = result.mailto
report_title = (
result.report_title.decode("utf-8") if isinstance(result.report_title, bytes) else result.report_title
)
subject = "Notebooker: {} report completed with status: {}".format(report_title, result.status.value)
body = result.raw_html
subject = result.email_subject or f"Notebooker: {report_title} report completed with status: {result.status.value}"
body = result.email_html or result.raw_html
attachments = []
tmp_dir = None
try:
Expand Down Expand Up @@ -53,7 +53,7 @@ def send_result_email(result: Union[NotebookResultComplete, NotebookResultError]

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

logger.info("Sending email to %s with %d attachments", mailto, len(attachments))
logger.info("Sending email to %s with %d attachments", to_email, len(attachments))
mail(from_email, to_email, subject, msg, attachments=attachments)
finally:
if tmp_dir:
Expand Down
Loading