Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More templates #2

Merged
merged 3 commits into from
Jun 21, 2024
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
33 changes: 30 additions & 3 deletions mandr/app.py
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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')
Expand All @@ -24,10 +26,12 @@ def render_views(*path):
first_name=first_name
)


def render_info(*path):
mander = fetch_mander(*path)
return '<pre class="text-xs">' + json.dumps({k: str(v) for k, v in mander.fetch().items() if not k.startswith("_")}, indent=2) + '</pre>'


def render_logs(*path):
mander = fetch_mander(*path)
view_nav_templ = read_template('partials/logs.html')
Expand All @@ -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 = []
Expand All @@ -59,19 +66,22 @@ 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())
res = render_top_nav(*args)
res += render_mid_nav(*args)
return t.render(body=res)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')

@app.route('/', defaults={'path': ''}, methods=['GET', 'POST'])
@app.route('/<path:path>', methods=['GET', 'POST'])
def home(path):
if 'favicon' in path:
return Response('', status=400)
Expand All @@ -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)
5 changes: 3 additions & 2 deletions mandr/infomander.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand All @@ -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)
Expand Down
9 changes: 6 additions & 3 deletions mandr/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


def sklearn_model_repr(pipeline):
print('pipeline', pipeline)
return estimator_html_repr(pipeline)


Expand Down Expand Up @@ -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]
Expand All @@ -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
46 changes: 46 additions & 0 deletions mandr/templates/sketchpad.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
Template Sketchpad
</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<script src="//unpkg.com/alpinejs" defer></script>
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
<script src="https://cdn.jsdelivr.net/gh/koaning/justcharts/justcharts.js"></script>
<script src="https://unpkg.com/htmx.org@1.9.12" integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2" crossorigin="anonymous"></script>
</head>
<body>
<main>
<div class="m-4">
<h1 class="text-4xl font-bold">Template Sketchpad</h1>
<small class="text-gray-400">The template can be edited on the right and will be live updated.</small>
<div class="flex pt-4">
<div class="w-1/2 m-2">
<form name="main" id="template-form">
{% for child in children %}<br/>
<a href="/sketchpad/{{child}}" class='text-sm text-gray-400'>{{child}}</a>
{% endfor %}
<p class="text-md font-bold">Template</p>
<textarea name='template' class="w-full h-64 p-2 bg-gray-100 rounded-md font-mono" placeholder="Enter ManderMarkdown text here"></textarea>
</form>
<button class="btn primary rounded-md bg-gray-100" hx-trigger='click' hx-post='/render/{{mander_path}}' hx-include="#template-form" hx-target="#template-out" >Submit</button>
</div>
<div class="w-1/2 m-2">
<p class="text-md font-bold">Rendered output</p>
<div class="w-full border-dotted border-2 rounded-md">
<article class="prose p-2">
<div id="template-out"></div>
</article>
<br/><br/>
</div>
</div>
</div>
</div>
</main>
</body>
</html>