# Copyright (c) 2022 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
from ast import literal_eval
from collections import OrderedDict
from flask import abort, render_template, request
from jinja2.exceptions import TemplateNotFound
from markupsafe import escape
from signac_dashboard.module import Module
[docs]class DocumentEditor(Module):
"""Provides an interface to edit the job document.
This module shows keys in the job document with a form that allows users
to edit their contents. When saving, the edited strings are parsed into
JSON-compatible Python data structures (e.g., :py:class:`list` and
:py:class:`dict`). Job document keys beginning with an underscore
:code:`_` are treated as private and are not displayed.
:param context: Supports :code:`'JobContext'`.
:type context: str
"""
_supported_contexts = {"JobContext"}
def __init__(
self,
name="Document Editor",
context="JobContext",
template="cards/document_editor.html",
**kwargs,
):
super().__init__(
name=name,
context=context,
template=template,
**kwargs,
)
def get_cards(self, job):
doc = OrderedDict(sorted(job.document.items(), key=lambda t: t[0]))
for key in doc:
if key.startswith("_"):
# Don't allow users to edit "private" keys that begin with _
del doc[key]
else:
doc[key] = escape(repr(doc[key]))
return [
{
"name": self.name,
"content": render_template(self.template, document=doc, jobid=job._id),
}
]
def register(self, dashboard):
# Register routes
@dashboard.app.route("/module/document_editor/update", methods=["POST"])
def document_editor_update():
jobid = request.form.get("jobid")
job = dashboard.project.open_job(id=jobid)
for key, value in request.form.items():
if key.startswith("doc:"):
key = key[4:]
try:
job.doc[key] = literal_eval(value)
except (SyntaxError, TypeError, ValueError) as e:
return (
f"Error in key <strong>{key}</strong>: {e}",
422,
)
return "Saved."
@dashboard.app.route("/module/document_editor/<path:filename>")
def document_editor_asset(filename):
path = f"document_editor/{filename}"
try:
return render_template(path)
except TemplateNotFound:
abort(404, "The file requested does not exist.")
# Register assets
assets = ["js/document_editor.js"]
for assetfile in assets:
dashboard.register_module_asset(
{
"file": f"templates/document_editor/{assetfile}",
"url": f"/module/document_editor/{assetfile}",
}
)