-
Notifications
You must be signed in to change notification settings - Fork 6
Add interactive plots with Plotly #163
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
Open
javierbg
wants to merge
22
commits into
glotzerlab:main
Choose a base branch
from
javierbg:feature/issue-162-plotly-plots
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
0a50ca2
Add plot viewer module
javierbg 23bdaaa
Add documentation for PlotViewer module
javierbg 73d0ee4
Move plotly to module asset
javierbg 406710d
Extend PlotViewer documentation
javierbg ba7edd8
Add PlotViewer to the changelog
javierbg 954a603
Remove unnecesary id from plot viewer div
javierbg b4985b5
Remove unnecesary console log
javierbg c971876
Minor revisions for #163
javierbg 74c3ce2
Alphabetize PlotViewer module in lists
javierbg 2e7aea5
Use Plotly from CDN instead of local copy
javierbg d62d08a
Rename Matplotlib plot example
javierbg 59b37bc
Add Plotly example
javierbg a898191
Protect access to PlotViewer routes
02df95b
Unify Matplotlib and Plotly examples
e4bd48d
Rename PlotViewer to PlotlyViewer
d271eee
Simplify module registration for PlotlyViewer
226111e
Move PlotlyView argument passing functionality to REST endpoint
javierbg 24a9aa2
Update PlotlyViewer docstring for new callable type
javierbg 121578f
Fix PlotlyViewer for old signac API
152d94a
Merge branch 'main' into feature/issue-162-plotly-plots
cbkerr 3c32907
Partial fix for getting job id
cbkerr 89a5c3d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| # Copyright (c) 2022 The Regents of the University of Michigan | ||
| # All rights reserved. | ||
| # This software is licensed under the BSD 3-Clause License. | ||
| import hashlib | ||
| from typing import Callable, Dict, List, Tuple, Union | ||
|
|
||
| import flask_login | ||
| from flask import abort, jsonify, render_template, request | ||
| from flask.views import View | ||
| from jinja2.exceptions import TemplateNotFound | ||
| from signac import Project | ||
| from signac.job import Job | ||
|
|
||
| from signac_dashboard.dashboard import Dashboard | ||
| from signac_dashboard.module import Module | ||
|
|
||
|
|
||
| class PlotlyView(View): | ||
| decorators = [flask_login.login_required] | ||
|
|
||
| def __init__(self, dashboard, args_function, context): | ||
| self.dashboard = dashboard | ||
| self.args_function = args_function | ||
| self.context = context | ||
|
|
||
| def dispatch_request(self): | ||
| if self.context == "JobContext": | ||
| jobid = request.args.get("jobid") | ||
| job = self.dashboard.project.open_job(id=jobid) | ||
| traces, layout = self.args_function(job) | ||
| elif self.context == "ProjectContext": | ||
| traces, layout = self.args_function(self.dashboard.project) | ||
| else: | ||
| raise NotImplementedError() | ||
| return jsonify({"traces": traces, "layout": layout}) | ||
|
|
||
|
|
||
| class PlotlyViewer(Module): | ||
| """Displays a plot associated with the job. | ||
|
|
||
| The PlotlyViewer module can display an interactive plot by using the | ||
| Plotly JavaScript library. For information on the different accepted | ||
| parameters for the data and layout, refer to the `Plotly JS documentation | ||
| <https://plotly.com/javascript/>`_. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from signac_dashboard.modules import PlotlyViewer | ||
|
|
||
| def plotly_args_function(project): | ||
| return [ | ||
| (# each element on the data list is a different trace | ||
| [{ | ||
| "x": [1, 2, 3, 4, 5], # x coordinates of the trace | ||
| "y": [1, 2, 4, 8, 16] # y coordinates of the trace | ||
| }], | ||
| {"margin": {"t": 0}} # layout specification for the whole plot | ||
| ) | ||
| ] | ||
|
|
||
| plot_module = PlotlyViewer(plotly_args=plotly_args_function, context="ProjectContext") | ||
|
|
||
| :param name: Default name for the card. Ignored if the :code:`plotly_args` | ||
| callable provides one for each card. | ||
| :type name: str | ||
| :param plotly_args: A callable that accepts a job (in the :code:`'JobContext'`) | ||
| or a project (in the :code:`'ProjectContext'`) and returns a tuple of two | ||
| elements: the plotly data and the plotly layout specification, respectively. | ||
| :type plotly_args: callable | ||
| :param context: Supports :code:`'JobContext'` and :code:`'ProjectContext'`. | ||
| :type context: str | ||
| """ | ||
|
|
||
| _supported_contexts = {"JobContext", "ProjectContext"} | ||
| _assets_url_registered = False | ||
|
|
||
| def __init__( | ||
| self, | ||
| name="Plotly Viewer", | ||
| plotly_args: Callable[ | ||
| [Union[Job, Project]], Tuple[List[Dict], Dict] | ||
| ] = lambda _: ([{}], {}), | ||
| context="JobContext", | ||
| template="cards/plotly_viewer.html", | ||
| **kwargs, | ||
| ): | ||
| super().__init__( | ||
| name=name, | ||
| context=context, | ||
| template=template, | ||
| **kwargs, | ||
| ) | ||
| self.plotly_args = plotly_args | ||
| self.card_id = hashlib.sha1(str(id(self)).encode("utf-8")).hexdigest() | ||
|
|
||
| def get_cards(self, job_or_project): | ||
| return [ | ||
| { | ||
| "name": self.name, | ||
| "content": render_template( | ||
| self.template, | ||
| jobid=job_or_project.id, # will not work for project anymore | ||
| endpoint=self.arguments_endpoint(), | ||
| ), | ||
| } | ||
| ] | ||
|
|
||
| def register(self, dashboard: Dashboard): | ||
| # Register routes | ||
| if not PlotlyViewer._assets_url_registered: | ||
|
|
||
| @dashboard.app.route("/module/plotly_viewer/<path:filename>") | ||
| @flask_login.login_required | ||
| def plotly_viewer_asset(filename): | ||
| try: | ||
| return render_template(f"plotly_viewer/{filename}") | ||
| except TemplateNotFound: | ||
| abort(404, "The file requested does not exist.") | ||
|
|
||
| # Register assets | ||
| assets = [ | ||
| "/module/plotly_viewer/js/plotly_viewer.js", | ||
| "https://cdn.plot.ly/plotly-2.16.1.min.js", | ||
| ] | ||
| for asseturl in assets: | ||
| dashboard.register_module_asset({"url": asseturl}) | ||
|
|
||
| PlotlyViewer._assets_url_registered = True | ||
|
|
||
| dashboard.app.add_url_rule( | ||
| self.arguments_endpoint(), | ||
| view_func=PlotlyView.as_view( | ||
| f"plotly-{self.card_id}", dashboard, self.plotly_args, self.context | ||
| ), | ||
| ) | ||
|
|
||
| def arguments_endpoint(self): | ||
| return f"/module/plotly_viewer/{self.card_id}/arguments" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| <div class="plotly_viewer" | ||
| data-endpoint="{{ endpoint }}" | ||
| data-jobid="{{ jobid }}"> | ||
| </div> |
11 changes: 11 additions & 0 deletions
11
signac_dashboard/templates/plotly_viewer/js/plotly_viewer.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| $(document).on('turbolinks:load', function() { | ||
| $('.plotly_viewer').each((index, element) => { | ||
| let endpoint = element.getAttribute("data-endpoint") | ||
| let jobid = element.getAttribute("data-jobid") | ||
| jQuery.get(endpoint, {jobid: jobid}, (data, textStatus, response) => { | ||
| let traces = data["traces"] | ||
| let layout = data["layout"] | ||
| Plotly.newPlot(element, traces, layout) | ||
| }) | ||
| }); | ||
| }) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.