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

Exporting PerspectiveWidget to Static HTML Fails #2846

Open
ruoyu0088 opened this issue Nov 13, 2024 · 0 comments
Open

Exporting PerspectiveWidget to Static HTML Fails #2846

ruoyu0088 opened this issue Nov 13, 2024 · 0 comments

Comments

@ruoyu0088
Copy link

ruoyu0088 commented Nov 13, 2024

I am experiencing issues when converting the PerspectiveWidget object into a static HTML file using the following code:

import perspective
from perspective import widget
import pandas as pd

widget.set_jupyter_html_export(True)

df = pd.DataFrame(dict(
    x=[1.0, 2.0, 3.0],
    y=[1.0, 3.0, 2.0]
))

w = widget.PerspectiveWidget(df)

with open("tmp.html", "w") as f:
    f.write("<html><body>")
    f.write(w._repr_mimebundle_()['text/html'])
    f.write("</body></html>")

However, the output tmp.html file does not display the Perspective view correctly due to the following issues:

  • viewerId is not assigned as a string.
  • viewerAttrs is not a properly formatted JSON object.
  • The call to await perspective.worker().table(data.buffer) raises TypeError.
  • The href attribute for the stylesheet is incorrect.
<html><body><script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective@3.1.4/dist/cdn/perspective.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer@3.1.4/dist/cdn/perspective-viewer.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-datagrid@3.1.4/dist/cdn/perspective-viewer-datagrid.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-d3fc@3.1.4/dist/cdn/perspective-viewer-d3fc.js"></script>
<link rel="stylesheet" crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-themes@3.1.4/dist/css/themes.css" />

<div class="perspective-envelope" id="perspective-envelope-a512580a2efc414e960615bc991de7a9">
    <script type="application/vnd.apache.arrow.file">
    /////9gAAAAQAAAAAAAKAAwABgAFAAgACgAAAAABBAAMAAAACAAIAAAABAAIAAAABAAAAAMAAABw
AAAAMAAAAAQAAACs////AAABAxAAAAAUAAAABAAAAAAAAAABAAAAeQAAANr///8AAAIA1P///wAA
AQMQAAAAGAAAAAQAAAAAAAAAAQAAAHgABgAIAAYABgAAAAAAAgAQABQACAAGAAcADAAAABAAEAAA
AAAAAQIQAAAAIAAAAAQAAAAAAAAABQAAAGluZGV4AAAACAAMAAgABwAIAAAAAAAAAUAAAAD/////
6AAAABQAAAAAAAAADAAWAAYABQAIAAwADAAAAAADBAAYAAAASAAAAAAAAAAAAAoAGAAMAAQACAAK
AAAAfAAAABAAAAADAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAA
AAAYAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAYAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAA
ABgAAAAAAAAAAAAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAAAAADwPwAAAAAAAABAAAAAAAAACEAA
AAAAAADwPwAAAAAAAAhAAAAAAAAAAED/////AAAAAA==

    </script>
    <perspective-viewer style="height: 690px;"></perspective-viewer>
    <script type="module">
        // from MDN
        function base64ToBytes(base64) {
            const binString = atob(base64);
            return Uint8Array.from(binString, (m) => m.codePointAt(0));
        }

        import * as perspective from "https://cdn.jsdelivr.net/npm/@finos/perspective@3.1.4/dist/cdn/perspective.js";
        const viewerId = a512580a2efc414e960615bc991de7a9;
        const currentScript = document.scripts[document.scripts.length - 1];
        const envelope = document.getElementById(`perspective-envelope-${viewerId}`);
        const dataScript = envelope.querySelector('script[type="application/vnd.apache.arrow.file"]');;
        if (!dataScript)
            throw new Error('data script missing for viewer', viewerId);
        const data = base64ToBytes(dataScript.textContent);
        const viewerAttrs = {'group_by': [], 'split_by': [], 'filter': [], 'sort': [], 'aggregates': {}, 'columns': ['index', 'x', 'y'],
        	'expressions': {}, 'plugin': 'Datagrid', 'plugin_config': {}, 'theme': None, 'settings': True, 'title': None, 'version': '3.1.4'};

        // Create a new worker, then a new table promise on that worker.
        const table = await perspective.worker().table(data.buffer);
        const viewer = envelope.querySelector('perspective-viewer');
        viewer.load(table);
        viewer.restore(viewerAttrs);
    </script>
</div>
</body></html>

After applying the modifications below, the output HTML file renders correctly:

widget/__init__.py
***************
*** 17,19 ****
  import importlib
! 
  from string import Template
--- 17,19 ----
  import importlib
! import json
  from string import Template
***************
*** 259,264 ****
                  psp_cdn_perspective_viewer_themes=psp_cdn(
!                     "perspective-viewer-themes", "css/themes.css"
                  ),
                  viewer_id=self.model_id,
!                 viewer_attrs=viewer_attrs,
                  b64_data=b64_data.decode("utf-8"),
--- 259,264 ----
                  psp_cdn_perspective_viewer_themes=psp_cdn(
!                     "perspective-viewer", "css/themes.css"
                  ),
                  viewer_id=self.model_id,
!                 viewer_attrs=json.dumps(viewer_attrs),
                  b64_data=b64_data.decode("utf-8"),
                  
templates/exported_widget.html.template
***************
*** 19,21 ****
          import * as perspective from "$psp_cdn_perspective";
!         const viewerId = $viewer_id;
          const currentScript = document.scripts[document.scripts.length - 1];
--- 19,21 ----
          import * as perspective from "$psp_cdn_perspective";
!         const viewerId = "$viewer_id";
          const currentScript = document.scripts[document.scripts.length - 1];
***************
*** 29,31 ****
          // Create a new worker, then a new table promise on that worker.
!         const table = await perspective.worker().table(data.buffer);
          const viewer = envelope.querySelector('perspective-viewer');
--- 29,32 ----
          // Create a new worker, then a new table promise on that worker.
!         const worker = await perspective.worker();
!         const table = worker.table(data.buffer);
          const viewer = envelope.querySelector('perspective-viewer');
@texodus texodus assigned texodus and unassigned texodus Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants