I know this is out of the scope of most communities, but we are a scientific one, using Python and a lot of graphs (Matplotlib) that we share with each other over Discourse.
It would be so amazing to be able to share an interactive graph instead of a static jpg/png
Consider this example:
import matplotlib.pyplot as plt
import mpld3
import numpy as np
Fs = 4000
f = 100
sample = 200
x = np.arange(sample)
y = np.sin(2 * np.pi * f * x / Fs)
fig, ax = plt.subplots(1,1)
ax.plot(x, y, marker=".")
html_str = mpld3.fig_to_html(fig)
Html_file= open("index.html","w")
Html_file.write(html_str)
Html_file.close()
You would be able to open the index.html file and it will show that (only that it will be INTERACTIVE…)
So, I feel like we’re half way there, right?
Perhaps just uploading the html file so it will run in some iframe?
The method you described would be possible via a custom plugin, combining both a markdown-it style plugin and possibly a nice object storage for the HTMLs. However I’d be cautious about the security of it, letting user inputted code run anywhere on the server. Discourse already does a lot of work just to sanitize a post for HTML and JS to be safely parsed in the backend.
You might have better luck instead trying to embed a Python interpreter in the browser and let it run the code on client, skipping the whole HTML creation. JupyterLite would let you run JupyterLab on the browser with UI. From the documentation, it looks like you can have it auto run code on creation of the iframe. You can feed in the code with a custom HighlightJS plugin that will create an actionable button on any Python code block, or have it run automatically.
So something like:
User sees a post with a Python code block.
User hovers over the code block, where a button appears to let them run the code in browser.
User clicks on the button, which creates an iframe to JupyterLite below the code, importing the code from the code block.
Since this would just be a HighlightJS plugin that just creates an iframe to JupyterLite, you can create it all within a Theme Component, no server code needed.
One thing to note: JupyterLite is described as not being feature complete to JupyterLab, and I’m not sure how it goes about handling package imports, so you might have to host it yourself. JupyterLite uses Pyodide, which supports any wheel on PyPI, so it should be good
I think that the security risk is not that big in case the index.html runs on the client end (on the user’s browser, not on the Discourse server).
Also the advantage in the mpld3 approach is that it is a single-file with no dependencies. If you run raw Python you have to also be able to upload all dependencies like csv files, data files, etc. that are the source of what you want to plot.
Having said that, to be able to run raw Python scripts inside a Discourse post would be just fabulous!
Oh! I completely misunderstood how the HTML would be generated. If the user is pre-generating the HTML, you could have them iframe it from an external site, with the domain whitelisted.
For example, codepen, which should be allowed by default:
<iframe height="300" width="800" style="width: 100%;" scrolling="no" title="mplD3 example, actually working" src="https://codepen.io/gully/embed/BawMGr?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/gully/pen/BawMGr">
mplD3 example, actually working</a> by gully (<a href="https://codepen.io/gully">@gully</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
That’s really awesome!
I have a couple of questions:
Do you think it will work with ipympl (otherwise the figures are not interactive, which invalidates the whole point)
Do you think it can work with an underlying project folder where we can set PYTHONPATH env variable before launching Jupyter so that users can import functions and dependencies from the joined project?
and yes, I do have a budget if this is reasonable and feasible and can be contributed as open source for the entire Discourse community.
@Alteras Thanks this solution is really straightforward and works if you can find a way to upload the “index.heml” file to the Discourse (which is easy to enable .html upload) and serve it in the iframe code. To only problem, it’s not actually working… when I try that, it just downloads the index.html instead of rendering it in the iframe. Perhaps there’s a meta tag to tell the iframe to show the file instead of downloading?
Ahh, I see. Discourse by default sets any non-image attachments with the Content-Disposition as “attachment” which will treat it as a download only.
You might be able to do something similar to the Inline PDF Previews, which seems to handle this same problem, but for PDF uploads.