From 1b09e217c4d1c1b49a73ef215caec24c7174b392 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 23 Feb 2018 15:39:31 -0500 Subject: [PATCH 01/14] added fill_percent to params for insert, swap and remove --- plotly/dashboard_objs/dashboard_objs.py | 60 ++++++++++++++++--------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index 1b3b215ed34..e0882d5959a 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -44,9 +44,11 @@ def _box(fileId='', shareKey=None, title=''): } return box - -def _container(box_1=None, box_2=None, size=MASTER_HEIGHT, - sizeUnit='px', direction='vertical'): +# old values +# size=MASTER_HEIGHT, sizeUnit='px', +def _container(box_1=None, box_2=None, + size=50, sizeUnit='%', + direction='vertical'): if box_1 is None: box_1 = _empty_box() if box_2 is None: @@ -60,6 +62,7 @@ def _container(box_1=None, box_2=None, size=MASTER_HEIGHT, 'first': box_1, 'second': box_2 } + return container dashboard_html = (""" @@ -260,6 +263,8 @@ def _make_all_nodes_and_paths(self): all_paths.remove(path_second) return all_nodes, all_paths + # TODO: get rid by the end of PR + # change name to init_container_size ? def _set_container_sizes(self): if self['layout'] is None: return @@ -273,10 +278,9 @@ def _set_container_sizes(self): self['layout']['sizeUnit'] = 'px' for path in all_paths: - if len(path) != 0: - if self._path_to_box(path)['type'] == 'split': - self._path_to_box(path)['size'] = 50 - self._path_to_box(path)['sizeUnit'] = '%' + if len(path) != 0 and self._path_to_box(path)['type'] == 'split': + self._path_to_box(path)['size'] = 50 + self._path_to_box(path)['sizeUnit'] = '%' def _path_to_box(self, path): loc_in_dashboard = self['layout'] @@ -295,6 +299,12 @@ def get_box(self, box_id): loc_in_dashboard = loc_in_dashboard[first_second] return loc_in_dashboard + def set_height(self, dashboard_height): + """Sets the height (in pixels) of dashboard""" + # problem when no box is inserted + self['layout']['size'] = dashboard_height + self['layout']['sizeUnit'] = 'px' + def get_preview(self): """ Returns JSON or HTML respresentation of the dashboard. @@ -413,7 +423,7 @@ def get_preview(self): # display HTML representation return IPython.display.HTML(html_figure) - def insert(self, box, side='above', box_id=None): + def insert(self, box, side='above', box_id=None, fill_percent=50): """ Insert a box into your dashboard layout. @@ -423,7 +433,11 @@ def insert(self, box, side='above', box_id=None): 'left', and 'right'. :param (int) box_id: the box id which is used as the reference box for the insertion of the box. - + :param (float) fill_percent: specifies the percentage of the box area + which the new box is occupying. The default is `fill_percent=50` + which splits the region into two equally sized pieces with `box` + and the box corresponding to `box_id` in this area of the layout. + Example: ``` import plotly.dashboard_objs as dashboard @@ -449,7 +463,9 @@ def insert(self, box, side='above', box_id=None): # doesn't need box_id or side specified for first box if self['layout'] is None: - self['layout'] = _container(box, _empty_box()) + self['layout'] = _container( + box, _empty_box(), size=MASTER_HEIGHT, sizeUnit='px' + ) else: if box_id is None: raise exceptions.PlotlyError( @@ -458,28 +474,38 @@ def insert(self, box, side='above', box_id=None): ) if box_id not in box_ids_to_path: raise exceptions.PlotlyError(ID_NOT_VALID_MESSAGE) + + if fill_percent < 0 or fill_percent > 100: + raise exceptions.PlotlyError( + 'fill_percent must be a number between 0 and 100 ' + 'inclusive' + ) if side == 'above': old_box = self.get_box(box_id) self._insert( - _container(box, old_box, direction='vertical'), + _container(box, old_box, direction='vertical', + size=fill_percent), box_ids_to_path[box_id] ) elif side == 'below': old_box = self.get_box(box_id) self._insert( - _container(old_box, box, direction='vertical'), + _container(old_box, box, direction='vertical', + size=100 - fill_percent), box_ids_to_path[box_id] ) elif side == 'left': old_box = self.get_box(box_id) self._insert( - _container(box, old_box, direction='horizontal'), + _container(box, old_box, direction='horizontal', + size=fill_percent), box_ids_to_path[box_id] ) elif side == 'right': old_box = self.get_box(box_id) self._insert( - _container(old_box, box, direction='horizontal'), + _container(old_box, box, direction='horizontal', + size=100 - fill_percent), box_ids_to_path[box_id] ) else: @@ -489,8 +515,6 @@ def insert(self, box, side='above', box_id=None): "'above', 'below', 'left', and 'right'." ) - self._set_container_sizes() - def remove(self, box_id): """ Remove a box from the dashboard by its box_id. @@ -530,8 +554,6 @@ def remove(self, box_id): else: self['layout'] = None - self._set_container_sizes() - def swap(self, box_id_1, box_id_2): """ Swap two boxes with their specified ids. @@ -580,5 +602,3 @@ def swap(self, box_id_1, box_id_2): for first_second in pairs[0][:-1]: loc_in_dashboard = loc_in_dashboard[first_second] loc_in_dashboard[pairs[0][-1]] = pairs[1] - - self._set_container_sizes() From b149e0d0a77cd08ceb47b90e6567f5fdc6a8255b Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 26 Feb 2018 14:36:02 -0500 Subject: [PATCH 02/14] html preview not working --- plotly/dashboard_objs/dashboard_objs.py | 106 +++++++++++++----------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index e0882d5959a..a377961582b 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -15,10 +15,11 @@ IPython = optional_imports.get_module('IPython') -# default HTML parameters -MASTER_WIDTH = 400 -MASTER_HEIGHT = 400 -FONT_SIZE = 10 +# default parameters for HTML preview +MASTER_WIDTH = 500 +MASTER_HEIGHT = 500 +FONT_SIZE = 8 + ID_NOT_VALID_MESSAGE = ( "Your box_id must be a number in your dashboard. To view a " @@ -77,7 +78,7 @@ def _container(box_1=None, box_2=None, - + ') - 1 dashboard_html = (dashboard_html[:index_to_add_text] + html_text + @@ -214,8 +212,6 @@ def __init__(self, content=None): self['version'] = content['version'] self['settings'] = content['settings'] - self._set_container_sizes() - def _compute_box_ids(self): box_ids_to_path = {} all_nodes = list(node_generator(self['layout'])) @@ -262,25 +258,6 @@ def _make_all_nodes_and_paths(self): all_paths.remove(path_second) return all_nodes, all_paths - # TODO: get rid by the end of PR - # change name to init_container_size ? - def _set_container_sizes(self): - if self['layout'] is None: - return - - all_nodes, all_paths = self._make_all_nodes_and_paths() - - # set dashboard_height proportional to max_path_len - max_path_len = max(len(path) for path in all_paths) - dashboard_height = 500 + 250 * max_path_len - self['layout']['size'] = dashboard_height - self['layout']['sizeUnit'] = 'px' - - for path in all_paths: - if len(path) != 0 and self._path_to_box(path)['type'] == 'split': - self._path_to_box(path)['size'] = 50 - self._path_to_box(path)['sizeUnit'] = '%' - def _path_to_box(self, path): loc_in_dashboard = self['layout'] for first_second in path: @@ -298,12 +275,6 @@ def get_box(self, box_id): loc_in_dashboard = loc_in_dashboard[first_second] return loc_in_dashboard - def resize(self, dashboard_height): - """Sets the height (in pixels) of dashboard""" - # TODO: problem when no box is inserted - self['layout']['size'] = dashboard_height - self['layout']['sizeUnit'] = 'px' - def get_preview(self): """ Returns JSON or HTML respresentation of the dashboard. @@ -393,7 +364,8 @@ def get_preview(self): new_box_h = box_h * (fill_percent / 100.) new_top_left_x_2 = top_left_x - new_top_left_y_2 = top_left_y + box_h * (fill_percent / 100.) + new_top_left_y_2 = (top_left_y + + box_h * (fill_percent / 100.)) new_box_w_2 = box_w new_box_h_2 = box_h * ((100 - fill_percent) / 100.) @@ -442,8 +414,8 @@ def insert(self, box, side='above', box_id=None, fill_percent=50): box from the given 'side' that the new box occupies. For example if you apply the method\n .insert(box=new_box, box_id=2, side='left', fill_percent=20)\n - to a dashboard object, a new box is inserted 20% from the left side - of the box with id #2. Run .get_preview() to see the box ids + to a dashboard object, a new box is inserted 20% from the left + side of the box with id #2. Run .get_preview() to see the box ids assigned to each box in the dashboard layout. Default = 50 Example: From 8517f3fa62ad0a1ba27527346b832fec9afc2d9d Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 28 Feb 2018 17:01:11 -0500 Subject: [PATCH 07/14] change fill_percent example in doc to fill_percent=20 --- plotly/dashboard_objs/dashboard_objs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index 19f84398884..6d5e387bb69 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -434,7 +434,7 @@ def insert(self, box, side='above', box_id=None, fill_percent=50): my_dboard.insert(box_1, 'left', 1) my_dboard.insert(box_1, 'below', 2) my_dboard.insert(box_1, 'right', 3) - my_dboard.insert(box_1, 'above', 4, fill_percent=30) + my_dboard.insert(box_1, 'above', 4, fill_percent=20) my_dboard.get_preview() ``` From 3c0d337c2385be097786a281eba57982a5a84a79 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 28 Feb 2018 18:36:53 -0500 Subject: [PATCH 08/14] sort all node generators to make HTML output consistent for py2 + py3 --- plotly/dashboard_objs/dashboard_objs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index 6d5e387bb69..0fc6c2cb99b 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -215,7 +215,7 @@ def __init__(self, content=None): def _compute_box_ids(self): box_ids_to_path = {} all_nodes = list(node_generator(self['layout'])) - + all_nodes.sort(key=lambda x: x[1]) for node in all_nodes: if (node[1] != () and node[0]['type'] == 'box' and node[0]['boxType'] != 'empty'): @@ -248,6 +248,7 @@ def _insert(self, box_or_container, path): def _make_all_nodes_and_paths(self): all_nodes = list(node_generator(self['layout'])) + all_nodes.sort(key=lambda x: x[1]) # remove path 'second' as it's always an empty box all_paths = [] From b091aaf72b29dcdcb3d15d1422021f083e270db0 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 2 Mar 2018 00:34:37 -0800 Subject: [PATCH 09/14] dashboards now autosize based on GUI --- plotly/dashboard_objs/dashboard_objs.py | 19 +++++++++++++++++++ .../test_dashboard/test_dashboard.py | 5 ++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index 0fc6c2cb99b..db99ce6e537 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -265,6 +265,21 @@ def _path_to_box(self, path): loc_in_dashboard = loc_in_dashboard[first_second] return loc_in_dashboard + def _set_dashboard_size(self): + # set dashboard size to keep consistent with GUI + num_of_boxes = len(self._compute_box_ids()) + if num_of_boxes == 0: + pass + elif num_of_boxes == 1: + self['layout']['size'] = 800 + self['layout']['sizeUnit'] = 'px' + elif num_of_boxes == 2: + self['layout']['size'] = 1500 + self['layout']['sizeUnit'] = 'px' + else: + self['layout']['size'] = 1500 + 350 * (num_of_boxes - 2) + self['layout']['sizeUnit'] = 'px' + def get_box(self, box_id): """Returns box from box_id number.""" box_ids_to_path = self._compute_box_ids() @@ -496,6 +511,8 @@ def insert(self, box, side='above', box_id=None, fill_percent=50): "'above', 'below', 'left', and 'right'." ) + self._set_dashboard_size() + def remove(self, box_id): """ Remove a box from the dashboard by its box_id. @@ -535,6 +552,8 @@ def remove(self, box_id): else: self['layout'] = None + self._set_dashboard_size() + def swap(self, box_id_1, box_id_2): """ Swap two boxes with their specified ids. diff --git a/plotly/tests/test_core/test_dashboard/test_dashboard.py b/plotly/tests/test_core/test_dashboard/test_dashboard.py index ed5d6297fa5..494f4618f8d 100644 --- a/plotly/tests/test_core/test_dashboard/test_dashboard.py +++ b/plotly/tests/test_core/test_dashboard/test_dashboard.py @@ -111,7 +111,7 @@ def test_dashboard_dict(self): } dash = dashboard.Dashboard() - dash.insert(my_box, '', 0) + dash.insert(my_box) dash.insert(my_box, 'above', 1) expected_dashboard = { @@ -131,12 +131,11 @@ def test_dashboard_dict(self): 'sizeUnit': '%', 'type': 'split'}, 'second': {'boxType': 'empty', 'type': 'box'}, - 'size': 500, + 'size': 1500, 'sizeUnit': 'px', 'type': 'split'}, 'settings': {}, 'version': 2 } - self.assertEqual(dash['layout'], expected_dashboard['layout']) From 85d7549efe571a47526a02493fcf1e6cef82ac09 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 5 Mar 2018 08:15:19 -0800 Subject: [PATCH 10/14] version num to 2.5.0 --- CHANGELOG.md | 2 +- plotly/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 324afa96b39..a0b11f8f9b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [2.4.2] - UNRELEASED +## [2.5.0] - UNRELEASED ### Added - New parameter `fill_percent` to the `.insert` method for the dashboards API. You can now insert a box into the dashboard layout and specify what proportion of the original container box it will occupy. Run `help(plotly.dashboard_objs.Dashboard.insert)` for more information on `fill_percent`. diff --git a/plotly/version.py b/plotly/version.py index 5cd7abf832e..e59b17b4f4c 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '2.4.1' +__version__ = '2.5.0' From f8988921b65182a5f21c83ddd0277d687829d903 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 5 Mar 2018 08:39:16 -0800 Subject: [PATCH 11/14] 4 equal height vertical stacked example in doc string --- plotly/dashboard_objs/dashboard_objs.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index db99ce6e537..10916012696 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -161,7 +161,7 @@ class Dashboard(dict): `.get_box()` returns the box located in the dashboard by calling its box id as displayed via `.get_preview()`. - Example: Create a simple Dashboard object + Example 1: Create a simple Dashboard object ``` import plotly.dashboard_objs as dashboard @@ -198,6 +198,25 @@ class Dashboard(dict): my_dboard.remove(1) # my_dboard.get_preview() ``` + + Example 2: 4 vertical boxes of equal height + ``` + import plotly.dashboard_objs as dashboard + + box_1 = { + 'type': 'box', + 'boxType': 'plot', + 'fileId': 'username:some#', + 'title': 'box 1' + } + + my_dboard = dashboard.Dashboard() + my_dboard.insert(box_1) + my_dboard.insert(box_1, 'below', 1) + my_dboard.insert(box_1, 'below', 1) + my_dboard.insert(box_1, 'below', 3) + # my_dboard.get_preview() + ``` """ def __init__(self, content=None): if content is None: From 2c07b126c8acd99f2c01f321488378ba8d26ac6f Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 5 Mar 2018 09:15:33 -0800 Subject: [PATCH 12/14] rename box_1 to box_a, etc --- plotly/dashboard_objs/dashboard_objs.py | 79 +++++++++++++------------ 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/plotly/dashboard_objs/dashboard_objs.py b/plotly/dashboard_objs/dashboard_objs.py index 10916012696..9b6c3c84672 100644 --- a/plotly/dashboard_objs/dashboard_objs.py +++ b/plotly/dashboard_objs/dashboard_objs.py @@ -165,33 +165,33 @@ class Dashboard(dict): ``` import plotly.dashboard_objs as dashboard - box_1 = { + box_a = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:some#', - 'title': 'box 1' + 'title': 'box a' } - box_2 = { + box_b = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:some#', - 'title': 'box 2' + 'title': 'box b' } - box_3 = { + box_c = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:some#', - 'title': 'box 3' + 'title': 'box c' } my_dboard = dashboard.Dashboard() - my_dboard.insert(box_1) + my_dboard.insert(box_a) # my_dboard.get_preview() - my_dboard.insert(box_2, 'above', 1) + my_dboard.insert(box_b, 'above', 1) # my_dboard.get_preview() - my_dboard.insert(box_3, 'left', 2) + my_dboard.insert(box_c, 'left', 2) # my_dboard.get_preview() my_dboard.swap(1, 2) # my_dboard.get_preview() @@ -203,18 +203,18 @@ class Dashboard(dict): ``` import plotly.dashboard_objs as dashboard - box_1 = { + box_a = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:some#', - 'title': 'box 1' + 'title': 'box a' } my_dboard = dashboard.Dashboard() - my_dboard.insert(box_1) - my_dboard.insert(box_1, 'below', 1) - my_dboard.insert(box_1, 'below', 1) - my_dboard.insert(box_1, 'below', 3) + my_dboard.insert(box_a) + my_dboard.insert(box_a, 'below', 1) + my_dboard.insert(box_a, 'below', 1) + my_dboard.insert(box_a, 'below', 3) # my_dboard.get_preview() ``` """ @@ -443,8 +443,10 @@ def insert(self, box, side='above', box_id=None, fill_percent=50): :param (str) side: specifies where your new box is going to be placed relative to the given 'box_id'. Valid values are 'above', 'below', 'left', and 'right'. - :param (int) box_id: the box id which is used as the reference box for - the insertion of the box. + :param (int) box_id: the box id which is used as a reference for the + insertion of the new box. Box ids are memoryless numbers that are + generated on-the-fly and assigned to boxes in the layout each time + .get_preview() is run. :param (float) fill_percent: specifies the percentage of the container box from the given 'side' that the new box occupies. For example if you apply the method\n @@ -457,19 +459,19 @@ def insert(self, box, side='above', box_id=None, fill_percent=50): ``` import plotly.dashboard_objs as dashboard - box_1 = { + box_a = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:some#', - 'title': 'box 1' + 'title': 'box a' } my_dboard = dashboard.Dashboard() - my_dboard.insert(box_1) - my_dboard.insert(box_1, 'left', 1) - my_dboard.insert(box_1, 'below', 2) - my_dboard.insert(box_1, 'right', 3) - my_dboard.insert(box_1, 'above', 4, fill_percent=20) + my_dboard.insert(box_a) + my_dboard.insert(box_a, 'left', 1) + my_dboard.insert(box_a, 'below', 2) + my_dboard.insert(box_a, 'right', 3) + my_dboard.insert(box_a, 'above', 4, fill_percent=20) my_dboard.get_preview() ``` @@ -540,17 +542,16 @@ def remove(self, box_id): ``` import plotly.dashboard_objs as dashboard - box_1 = { + box_a = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:some#', - 'title': 'box 1' + 'title': 'box a' } my_dboard = dashboard.Dashboard() - my_dboard.insert(box_1) + my_dboard.insert(box_a) my_dboard.remove(1) - my_dboard.get_preview() ``` """ @@ -581,23 +582,23 @@ def swap(self, box_id_1, box_id_2): ``` import plotly.dashboard_objs as dashboard - box_1 = { + box_a = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:first#', - 'title': 'first box' + 'title': 'box a' } - box_2 = { + box_b = { 'type': 'box', 'boxType': 'plot', 'fileId': 'username:second#', - 'title': 'second box' + 'title': 'box b' } my_dboard = dashboard.Dashboard() - my_dboard.insert(box_1) - my_dboard.insert(box_2, 'above', 1) + my_dboard.insert(box_a) + my_dboard.insert(box_b, 'above', 1) # check box at box id 1 box_at_1 = my_dboard.get_box(1) @@ -610,13 +611,13 @@ def swap(self, box_id_1, box_id_2): ``` """ box_ids_to_path = self._compute_box_ids() - box_1 = self.get_box(box_id_1) - box_2 = self.get_box(box_id_2) + box_a = self.get_box(box_id_1) + box_b = self.get_box(box_id_2) - box_1_path = box_ids_to_path[box_id_1] - box_2_path = box_ids_to_path[box_id_2] + box_a_path = box_ids_to_path[box_id_1] + box_b_path = box_ids_to_path[box_id_2] - for pairs in [(box_1_path, box_2), (box_2_path, box_1)]: + for pairs in [(box_a_path, box_b), (box_b_path, box_a)]: loc_in_dashboard = self['layout'] for first_second in pairs[0][:-1]: loc_in_dashboard = loc_in_dashboard[first_second] From 7f088432c185b6c20d3cb3649888fb28f8a40e96 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 5 Mar 2018 10:28:51 -0800 Subject: [PATCH 13/14] change version back for testing purposes --- plotly/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/version.py b/plotly/version.py index e59b17b4f4c..cb9dc8a91d3 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '2.5.0' +__version__ = '2.4.2' From 97213c9e394771668d13c646bd86a9e9dd37941c Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 5 Mar 2018 11:01:22 -0800 Subject: [PATCH 14/14] version to 2.5.0 --- plotly/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/version.py b/plotly/version.py index cb9dc8a91d3..e59b17b4f4c 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '2.4.2' +__version__ = '2.5.0'