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

Add .save() method to Template class #1066

Open
Huggies23 opened this issue Feb 3, 2020 · 7 comments
Open

Add .save() method to Template class #1066

Huggies23 opened this issue Feb 3, 2020 · 7 comments
Labels
type: enhancement Minor feature or improvement to an existing feature

Comments

@Huggies23
Copy link

Huggies23 commented Feb 3, 2020

Motivation
A standalone html object with embedded output from widget states can be created using the .save(embed = True) method for a panel object.

When creating app which uses a template (Template class) to structure the app, this functionality is not available and the app can only be served on a live instance.

What I would like
An equivalent method of .save() for Template apps which would allow the user to customise the format of the app using a jinja template but also export a standalone html version of the app with fully embedded widget state outputs.

For example, in using the standard panel app creation I can do:

import panel as pn
pn.extension()

select = pn.widgets.Select(value = 'Option 1', options = ['Option 1', 'Option 2', 'Option 3'])

@pn.depends(select.param.value)
def print_selection(selected):
    return f'<h2>You selected: {selected}'

dashboard = pn.Row(select, print_selection)

dashboard.save(filename = 'Test_embed.html', embed = True)

And what I would like to be able to do the same using the Template class:

import panel as pn
pn.extension()

select = pn.widgets.Select(value = 'Option 1', options = ['Option 1', 'Option 2', 'Option 3'])

@pn.depends(select.param.value)
def print_selection(selected):
    return f'<h2>You selected: {selected}'

template = """
{% extends base %}

<!-- goes in body -->
{% block postamble %}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
{% endblock %}

<!-- goes in body -->
{% block contents %}
<p>This is a Panel app with a custom template allowing us to compose multiple Panel objects into a single HTML document.</p>
<br>
<div class="container">
  <div class="row">
    <div class="col-sm">
      {{ embed(roots.widget) }}
    </div>
    <div class="col-sm">
      {{ embed(roots.output) }}
    </div>
  </div>
</div>
{% endblock %}
"""

dashboard = pn.Template(template)

dashboard.add_panel('widget', select)
dashboard.add_panel('output', print_selection)
 
# Want this:
dashboard.save(filename = 'template_embed.html', embed = True)
@philippjfr philippjfr added the type: enhancement Minor feature or improvement to an existing feature label Feb 4, 2020
@Huggies23
Copy link
Author

@philippjfr Has this enhancement been implemented through panel.save() using the template and template_variables arguments?

def save(panel, filename, title=None, resources=None, template=None,
         template_variables=None, embed=False, max_states=1000,
         max_opts=3, embed_json=False, json_prefix='', save_path='./',
         load_path=None, progress=True)

If so, would it be possible to provide a brief example of its usage? Thanks!

@philippjfr
Copy link
Member

It hasn't afaik.

@Huggies23
Copy link
Author

Ah ok, thanks for your swift reply. What are these new args for?

@MarcSkovMadsen
Copy link
Collaborator

I would also like to be able to .save for example apps based on the FastListTemplate and FastGridTemplate.

So does a user here https://discourse.holoviz.org/t/save-panel-template-app-as-a-html/2453

@bryanwhiting
Copy link

+1 - i'd like to save a template class!

@bryanwhiting
Copy link

is this closed per #1224?

@itsgifnotjiff
Copy link

This is still not the case for something like

template = pn.template.MaterialTemplate(
    site="Exp",
    title="Save Experiment",
    main=[pn.panel(ds.HR.sel(lon=slice(-179,179)).hvplot.quadmesh(rasterize=True, geo=True, coastline=True))],
)
template.save("SaveExp.html")

even if you add ressources=INLINE or embed=True you still get at best

---------------------------------------------------------------------------
UndefinedError                            Traceback (most recent call last)
Cell In[49], line 2
      1 # from bokeh.resources import INLINE
----> 2 template.save("SaveExp.html")

File PATH/viz/lib/python3.10/site-packages/panel/template/base.py:473, in BaseTemplate.save(self, filename, title, resources, embed, max_states, max_opts, embed_json, json_prefix, save_path, load_path)
    470 if embed:
    471     raise ValueError("Embedding is not yet supported on Template.")
--> 473 return save(
    474     self, filename, title, resources, self.template,
    475     self._render_variables, embed, max_states, max_opts,
    476     embed_json, json_prefix, save_path, load_path
    477 )

File PATH/viz/lib/python3.10/site-packages/panel/io/save.py:290, in save(panel, filename, title, resources, template, template_variables, embed, max_states, max_opts, embed_json, json_prefix, save_path, load_path, progress, embed_states, as_png, **kwargs)
    288 # Set resource mode
    289 with set_resource_mode(resources):
--> 290     html = file_html(doc, resources, title, **kwargs)
    291 if hasattr(filename, 'write'):
    292     if isinstance(filename, io.BytesIO):

File PATH/viz/lib/python3.10/site-packages/panel/io/save.py:164, in file_html(models, resources, title, template, template_variables, theme, _always_new)
    162 title = _title_from_models(models_seq, title)
    163 bundle = bundle_resources(models_seq, resources)
--> 164 return html_page_for_render_items(
    165     bundle, docs_json, render_items, title=title, template=template,
    166     template_variables=template_variables
    167 )

File PATH/viz/lib/python3.10/site-packages/bokeh/embed/elements.py:150, in html_page_for_render_items(bundle, docs_json, render_items, title, template, template_variables)
    147 elif isinstance(template, str):
    148     template = get_env().from_string("{% extends base %}\n" + template)
--> 150 html = template.render(context)
    151 return html

File PATH/viz/lib/python3.10/site-packages/jinja2/environment.py:1301, in Template.render(self, *args, **kwargs)
   1299     return self.environment.concat(self.root_render_func(ctx))  # type: ignore
   1300 except Exception:
-> 1301     self.environment.handle_exception()

File PATH/viz/lib/python3.10/site-packages/jinja2/environment.py:936, in Environment.handle_exception(self, source)
    931 """Exception handling helper.  This is used internally to either raise
    932 rewritten exceptions or return a rendered traceback for the template.
    933 """
    934 from .debug import rewrite_traceback_stack
--> 936 raise rewrite_traceback_stack(source=source)

File PATH/viz/lib/python3.10/site-packages/panel/template/material/material.html:1, in top-level template code()
----> 1 {% extends "base/base.html" %}
      2 
      3 {% block custom_css %}

File PATH/viz/lib/python3.10/site-packages/panel/template/base/base.html:1, in top-level template code()
----> 1 {% extends "base.html" %}
      2 
      3 <!-- goes in body -->

File PATH/viz/lib/python3.10/site-packages/panel/_templates/base.html:20, in top-level template code()
     18 <!DOCTYPE html>
     19 <html lang="en" {{ html_attrs | default("", true) }}>
---> 20   {% block head %}
     21   <head>
     22   {% block inner_head %}

File PATH/viz/lib/python3.10/site-packages/panel/_templates/base.html:22, in block 'head'()
     20 {% block head %}
     21 <head>
---> 22 {% block inner_head %}
     23   <meta charset="utf-8">
     24   <title>{% block title %}{{ title | e if title else "Panel App" }}{% endblock %}</title>

File PATH/viz/lib/python3.10/site-packages/panel/_templates/base.html:47, in block 'inner_head'()
     45 {%-  endblock js_resources %}
     46 {%  endblock resources %}
---> 47 {%  block postamble %}{% endblock %}
     48 {% endblock inner_head %}
     49 </head>

File PATH/viz/lib/python3.10/site-packages/panel/template/base/base.html:41, in block 'postamble'()
     39 {% block postamble %}
     40     <!-- Template CSS -->
---> 41     {% for css in template_resources['css'].values() %}
     42     <link rel="stylesheet" href="{{ css }}">
     43     {% endfor %}

File PATH/viz/lib/python3.10/site-packages/jinja2/environment.py:466, in Environment.getitem(self, obj, argument)
    464 """Get an item or attribute of an object but prefer the item."""
    465 try:
--> 466     return obj[argument]
    467 except (AttributeError, TypeError, LookupError):
    468     if isinstance(argument, str):

UndefinedError: 'template_resources' is undefined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement Minor feature or improvement to an existing feature
Projects
None yet
Development

No branches or pull requests

5 participants