diff --git a/mandr/app.py b/mandr/app.py index 6abe75c7d..2a9acd385 100644 --- a/mandr/app.py +++ b/mandr/app.py @@ -1,8 +1,9 @@ -from flask import Flask, Response +from flask import Flask, Response, request from pathlib import Path import json from jinja2 import Template from .infomander import InfoMander, VIEWS_KEY +from .templates import TemplateRenderer from rich.console import Console console = Console() @@ -13,6 +14,7 @@ def fetch_mander(*path): return InfoMander('/'.join(path)) + def render_views(*path): mander = fetch_mander(*path) view_nav_templ = read_template('partials/views.html') @@ -24,10 +26,12 @@ def render_views(*path): first_name=first_name ) + def render_info(*path): mander = fetch_mander(*path) return '
' + json.dumps({k: str(v) for k, v in mander.fetch().items() if not k.startswith("_")}, indent=2) + '
' + def render_logs(*path): mander = fetch_mander(*path) view_nav_templ = read_template('partials/logs.html') @@ -36,15 +40,18 @@ def render_logs(*path): first_name=list(mander['_logs'].items())[0][0] ) + def render_artifacts(*path): mander = fetch_mander(*path) view_nav_templ = read_template('partials/artifacts.html') return view_nav_templ.render(artifacts=list(mander['_artifacts'].items())) + def read_template(path): p = Path(__file__).parent / 'templates' / path return Template(p.read_text()) + def render_top_nav(*args): nav_temp = read_template('partials/nav-top.html') path_pairs = [] @@ -59,10 +66,12 @@ def render_top_nav(*args): console.log(f'{path_pairs=}') return nav_temp.render(path_pairs=path_pairs, links_out=links_out) + def render_mid_nav(*args): nav_temp = read_template('partials/nav-mid.html') return nav_temp.render(path='/'.join(args)) + def render_mander(*args): p = Path(__file__).parent / 'templates' / 'page.html' t = Template(p.read_text()) @@ -70,8 +79,9 @@ def render_mander(*args): res += render_mid_nav(*args) return t.render(body=res) -@app.route('/', defaults={'path': ''}) -@app.route('/') + +@app.route('/', defaults={'path': ''}, methods=['GET', 'POST']) +@app.route('/', methods=['GET', 'POST']) def home(path): if 'favicon' in path: return Response('', status=400) @@ -87,7 +97,24 @@ def home(path): return render_logs(*path_parts[1:]) if path_parts[0] == 'artifacts': return render_artifacts(*path_parts[1:]) + if path_parts[0] == 'sketchpad': + return render_sketchpad(*path_parts[1:]) + if path_parts[0] == 'render': + return render_template(*path_parts[1:]) return render_mander(*path.split('/')) + +def render_sketchpad(*path): + mander = fetch_mander(*path) + children = [f'{m.path}' for m in mander.children()] + return read_template('sketchpad.html').render(children=sorted(children), mander_path=mander.path) + + +def render_template(*path): + mander = fetch_mander(*path) + template_rendered = TemplateRenderer(mander) + return template_rendered.render(request.form['template']) + + if __name__ == '__main__': app.run(debug=True, reload=True) diff --git a/mandr/infomander.py b/mandr/infomander.py index 25f46432e..55ba1249d 100644 --- a/mandr/infomander.py +++ b/mandr/infomander.py @@ -20,6 +20,7 @@ class InfoMander: """Represents a dictionary, on disk, with a path-like structure.""" def __init__(self, path): # Set local disk paths + self.path = path self.project_path = Path('.datamander/' + path) self.cache = Cache(self.project_path / STATS_FOLDER) @@ -39,7 +40,7 @@ def __init__(self, path): def _check_key(self, key): if key in [ARTIFACTS_KEY, TEMPLATES_KEY, VIEWS_KEY, LOGS_KEY]: raise ValueError(f'Cannot overwrite {key} key. This is reserved for internal use.') - + def add_info(self, key, value, method='overwrite'): if method == 'overwrite': self.cache[key] = value @@ -62,7 +63,7 @@ def add_artifact(self, key, obj, **metadata): if not file_location.parent.exists(): file_location.parent.mkdir(parents=True) dump(obj, file_location) - self._add_to_key(ARTIFACTS_KEY, key, {'path': file_location, **metadata}) + self._add_to_key(ARTIFACTS_KEY, key, {'obj': obj, **metadata}) def add_view(self, key, html): self._check_key(key) diff --git a/mandr/templates.py b/mandr/templates.py index 994b0c3b7..b87aa8b9c 100644 --- a/mandr/templates.py +++ b/mandr/templates.py @@ -7,6 +7,7 @@ def sklearn_model_repr(pipeline): + print('pipeline', pipeline) return estimator_html_repr(pipeline) @@ -45,6 +46,7 @@ def insert_custom_ui(self, template): # For each registered element, check if it is there. for name, func in registry.items(): element_of_interest = f'<{name}' + print('element_of_interest', element_of_interest) start = template.find(element_of_interest) end = template[start:].find("/>") substr = template[start:start + end + 2] @@ -53,11 +55,12 @@ def insert_custom_ui(self, template): params = {k: self.clean_value(v) for k, v in elems} for k, v in params.items(): if v.startswith('@mander'): - params[k] = self.datamander.get(v) + params[k] = self.mander.get(v) ui = func(**params) - return template.replace(substr, ui) + template = template.replace(substr, ui) return template def render(self, template): final_template = self.insert_custom_ui(template) - return markdown.markdown(Template(final_template).render(**self.datamander.fetch())) + res = markdown.markdown(Template(final_template).render(**self.mander.fetch())) + return res diff --git a/mandr/templates/sketchpad.html b/mandr/templates/sketchpad.html new file mode 100644 index 000000000..c03295ba1 --- /dev/null +++ b/mandr/templates/sketchpad.html @@ -0,0 +1,46 @@ + + + + + + + Template Sketchpad + + + + + + + + + + +
+
+

Template Sketchpad

+ The template can be edited on the right and will be live updated. +
+
+
+ {% for child in children %}
+ {{child}} + {% endfor %} +

Template

+ +
+ +
+
+

Rendered output

+
+
+
+
+

+
+
+
+
+
+ + \ No newline at end of file