-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Animations/Frames in Python API #584
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
Changes from 1 commit
cb4552c
b4c3953
54c1386
d19c6ca
efa390e
eb57218
5482b53
b2b8451
a156503
d173cbb
d17a222
49bdc97
4fbdc99
dbb386f
bd2132b
26c3714
0c7bb1c
4acc146
c195e78
6f12ba8
253228f
405e71d
c1727d8
fca8fed
587dfa5
495b015
dd20177
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,7 +163,7 @@ def init_notebook_mode(connected=False): | |
def _plot_html(figure_or_data, config, validate, default_width, | ||
default_height, global_requirejs): | ||
# force no validation if frames is in the call | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
# TODO - add validation for frames in call - # | ||
# TODO - add validation for frames in call - #605 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed now 👍 |
||
if 'frames' in figure_or_data: | ||
figure = tools.return_figure_from_figure_or_data( | ||
figure_or_data, False | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,5 @@ | |
meta_ops, | ||
file_ops, | ||
get_config, | ||
bad_create_animations, | ||
get_uid_by_col_name | ||
get_grid | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
from plotly.session import (sign_in, update_session_plot_options, | ||
get_session_plot_options, get_session_credentials, | ||
get_session_config) | ||
from plotly.grid_objs import grid_objs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This import is what I was asking you about. I don't know if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see. It's a cyclic import issue you're worried about? At worst we can dynamically import it when it's required (not a good pattern in general, fwiw). Let me know if this causes a pain point and I can try put more brain cells towards it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by cyclic import? I'm honestly not too concerned about having it in there at all and just keeping it as a standard import, just wondering if folks didn't want that module to be relying on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
__all__ = None | ||
|
||
|
@@ -384,6 +385,7 @@ def get_figure(file_owner_or_url, file_id=None, raw=False): | |
raise exceptions.PlotlyError( | ||
"The 'file_id' argument must be a non-negative number." | ||
) | ||
|
||
response = requests.get(plotly_rest_url + resource, | ||
headers=headers, | ||
verify=get_config()['plotly_ssl_verification']) | ||
|
@@ -1452,160 +1454,44 @@ def _send_to_plotly(figure, **plot_options): | |
return r | ||
|
||
|
||
def bad_create_animations(fig, kwargs, payload): | ||
def get_grid(grid_url, raw=False): | ||
""" | ||
Makes a post to GRIDS and PLOTS if frames is in the figure. | ||
Returns a JSON figure representation for the specified grid. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎎 This feels a little misleading. Aren't we returning either JSON or a custom object. And, in the JSON case, it's a dict, right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct again! A oversight on my part. |
||
|
||
This function bypasses the current '/clientresp' route of making a POST | ||
request to return back a url. Instead, the V2 REST API is used if frames | ||
is part of the figure's keys. Currently, 'error', 'message' and 'warning' | ||
are hard codded to the empty string. | ||
""" | ||
url_v2_plot = "https://api.plot.ly/v2/plots" | ||
url_v2_grid = "https://api.plot.ly/v2/grids" | ||
auth = HTTPBasicAuth(str(payload['un']), str(payload['key'])) | ||
headers = {'Plotly-Client-Platform': 'python'} | ||
|
||
# add layout if not in fig | ||
if 'layout' not in fig: | ||
fig['layout'] = {} | ||
|
||
# make a copy of fig | ||
fig_with_uids = copy.deepcopy(fig) | ||
|
||
# make grid | ||
cols_dict = {} | ||
frames_cols_dict = {} | ||
counter = 0 | ||
trace_num = 0 | ||
for trace in fig['data']: | ||
for var in ['x', 'y']: | ||
if 'name' in trace: | ||
cols_dict["{name}, {x_or_y}".format(name=trace['name'], | ||
x_or_y=var)] = { | ||
"data": list(trace[var]), "order": counter | ||
} | ||
else: | ||
cols_dict["Trace {num}, {x_or_y}".format(num=trace_num, | ||
x_or_y=var)] = { | ||
"data": list(trace[var]), "order": counter | ||
} | ||
counter += 1 | ||
trace_num += 1 | ||
|
||
# add frames data to grid | ||
for j in range(len(fig['frames'])): | ||
for var in ['x', 'y']: | ||
if 'name' in fig['frames'][j]['data']: | ||
cols_dict["{name}, {x_or_y}".format( | ||
name=fig['frames'][j]['data'][0]['name'], x_or_y=var | ||
)] = { | ||
"data": list(fig['frames'][j]['data'][0][var]), | ||
"order": counter | ||
} | ||
else: | ||
cols_dict["Trace {num}, {x_or_y}".format( | ||
num=trace_num, x_or_y=var | ||
)] = { | ||
"data": list(fig['frames'][j]['data'][0][var]), | ||
"order": counter | ||
} | ||
counter += 1 | ||
trace_num += 1 | ||
|
||
grid_info = { | ||
"data": {"cols": cols_dict}, | ||
"world_readable": True | ||
} | ||
|
||
r = requests.post(url_v2_grid, auth=auth, | ||
headers=headers, json=grid_info) | ||
r_dict = json.loads(r.text) | ||
|
||
# make plot | ||
fid = r_dict['file']['fid'] | ||
cols_index = 0 | ||
for trace in fig_with_uids['data']: | ||
if 'x' in trace: | ||
del trace['x'] | ||
if 'y' in trace: | ||
del trace['y'] | ||
|
||
trace["xsrc"] = "{fid}:{idlocal}".format( | ||
fid=fid, idlocal=r_dict['file']['cols'][cols_index]['uid'] | ||
) | ||
trace["ysrc"] = "{fid}:{idlocal}".format( | ||
fid=fid, idlocal=r_dict['file']['cols'][cols_index + 1]['uid'] | ||
) | ||
cols_index += 2 | ||
|
||
# replace data in frames by grid ids | ||
for j in range(len(fig['frames'])): | ||
if 'x' in fig_with_uids['frames'][j]['data'][0]: | ||
del fig_with_uids['frames'][j]['data'][0]['x'] | ||
if 'y' in fig_with_uids['frames'][j]['data'][0]: | ||
del fig_with_uids['frames'][j]['data'][0]['y'] | ||
|
||
fig_with_uids['frames'][j]['data'][0]["xsrc"] = "{fid}:{idlocal}".format( | ||
fid=fid, idlocal=r_dict['file']['cols'][cols_index]['uid'] | ||
) | ||
fig_with_uids['frames'][j]['data'][0]["ysrc"] = "{fid}:{idlocal}".format( | ||
fid=fid, idlocal=r_dict['file']['cols'][cols_index + 1]['uid'] | ||
) | ||
cols_index += 2 | ||
|
||
plots_info = { | ||
"figure": fig_with_uids, | ||
"world_readable": json.loads(kwargs)['world_readable'] | ||
} | ||
|
||
r = requests.post(url_v2_plot, auth=auth, | ||
headers=headers, json=plots_info) | ||
|
||
r_json = json.loads(r.text) | ||
|
||
r_dict = { | ||
'error': '', | ||
'filename': json.loads(kwargs)['filename'], | ||
'message': '', | ||
'url': r_json['file']['web_url'], | ||
'warning': '' | ||
} | ||
|
||
return r_dict | ||
|
||
|
||
def get_uid_by_col_name(grid_url, col_name): | ||
""" | ||
Search for a column of a grid by name and return the uid of the column. | ||
:param (bool) raw: if False, will output a Grid instance of the JSON grid | ||
being retrieved. If True, raw JSON will be returned. | ||
""" | ||
credentials = get_credentials() | ||
validate_credentials(credentials) | ||
auth = HTTPBasicAuth(credentials['username'], credentials['api_key']) | ||
headers = {'Plotly-Client-Platform': 'python'} | ||
username, api_key = credentials['username'], credentials['api_key'] | ||
headers = {'plotly-username': username, | ||
'plotly-apikey': api_key, | ||
'plotly-version': version.__version__, | ||
'plotly-platform': 'python'} | ||
upload_url = _api_v2.api_url('grids') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did the |
||
|
||
tilda_index = grid_url.find('~') | ||
fid_in_url = grid_url[tilda_index + 1: -1].replace('/', ':') | ||
get_url = upload_url + '/' + fid_in_url | ||
|
||
r = requests.get(get_url, auth=auth, headers=headers) | ||
r_text = json.loads(r.text) | ||
|
||
col_uid = '' | ||
for col in r_text['cols']: | ||
if col_name == col['name']: | ||
col_uid = col['uid'] | ||
break | ||
|
||
all_col_names = ([r_text['cols'][j]['name'] for j in | ||
range(len(r_text['cols']))]) | ||
if col_uid == '': | ||
raise exceptions.PlotlyError( | ||
'The col_name does not match with any column name in your grid. ' | ||
'The column names in your grid are {}'.format(all_col_names)) | ||
if grid_url[-1] == '/': | ||
fid_in_url = grid_url[tilda_index + 1: -1].replace('/', ':') | ||
else: | ||
fid_in_url = grid_url[tilda_index + 1: len(grid_url)] | ||
fid_in_url = fid_in_url.replace('/', ':') | ||
get_url = upload_url + '/' + fid_in_url + '/content' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Youch. This is a confusing block of code. Did you copy-paste this from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What tools are you referring to? Should I be copying how There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
r = requests.get(get_url, headers=headers) | ||
json_res = json.loads(r.text) | ||
if raw is False: | ||
# create a new grid | ||
new_grid = grid_objs.Grid( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a little worried about this one. It's not nearly as bad as the figure splitting I was doing before 💀 but do you think this is an okay approach? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this approach is 💀 at all 😸. It's probably what I would have done. NOW. What I really would have done is to put this code into the constructor of
Does that make sense? Then the body of this function is simply:
|
||
[grid_objs.Column(json_res['cols'][column]['data'], column) | ||
for column in json_res['cols']] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
) | ||
# fill in uids | ||
for column in new_grid: | ||
column.id = json_res['cols'][column.name]['uid'] | ||
return new_grid | ||
else: | ||
return col_uid | ||
return json_res | ||
|
||
|
||
def _open_url(url): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this raise if it can't find a uid? Is there a case where we would want to accept the empty string if a column uid cannot be found?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, it should. It's possible for the column_reference to be the empty string
''
if a grid hasn't been uploaded yet, for instance.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grids are instantiated with
id
s so I don't think that's a risk here. For now, I think it's fine to have it just return theid
of that column, whatever it happens to be.