def test_access_top_level(self): # Figure is special, we define top-level objects that always exist. self.assertEqual(go.Figure().data, ()) self.assertEqual(go.Figure().layout.to_plotly_json(), {}) self.assertEqual(go.Figure().frames, ())
def test_nested_frames(self): with self.assertRaisesRegexp(ValueError, "frames"): go.Figure({"frames": [{"frames": []}]}) figure = go.Figure() figure.frames = [{}] with self.assertRaisesRegexp(ValueError, "frames"): figure.to_plotly_json()["frames"][0]["frames"] = [] figure.frames[0].frames = []
def test_update_layout_dict(self): # Create initial figure fig = go.Figure() fig.layout.title.font.size = 10 # Grab copy of original figure orig_fig = go.Figure(fig) fig.update_layout(dict(title=dict(font=dict(family="Courier New")))) orig_fig.layout.update(title_font_family="Courier New") self.assertEqual(fig, orig_fig)
def test_toplevel_underscore_kwarg(self): fig = go.Figure(data=[{ "type": "bar" }], layout_title_text="Hello, Figure title!") self.assertEqual(fig.layout.title.text, "Hello, Figure title!")
def test_skip_invalid_property_name(self): fig = go.Figure( data=[{ "type": "bar", "bogus": 123 }], layout={ "bogus": 23, "title": { "text": "Figure title" } }, frames=[{ "data": [{ "type": "bar", "bogus": 123 }], "layout": { "bogus": 23, "title": "Figure title" }, }], bogus=123, skip_invalid=True, ) fig_dict = fig.to_dict() # Remove trace uid property for trace in fig_dict["data"]: trace.pop("uid", None) self.assertEqual(fig_dict["data"], [{"type": "bar"}]) self.assertEqual(fig_dict["layout"], {"title": { "text": "Figure title" }}) self.assertEqual( fig_dict["frames"], [{ "data": [{ "type": "bar" }], "layout": { "title": { "text": "Figure title" } }, }], )
def test_skip_invalid_property_value(self): fig = go.Figure( data=[{ "type": "bar", "showlegend": "bad_value" }], layout={ "paper_bgcolor": "bogus_color", "title": "Figure title" }, frames=[{ "data": [{ "type": "bar", "showlegend": "bad_value" }], "layout": { "bgcolor": "bad_color", "title": "Figure title" }, }], skip_invalid=True, ) fig_dict = fig.to_dict() # Remove trace uid property for trace in fig_dict["data"]: trace.pop("uid", None) self.assertEqual(fig_dict["data"], [{"type": "bar"}]) self.assertEqual(fig_dict["layout"], {"title": { "text": "Figure title" }}) self.assertEqual( fig_dict["frames"], [{ "data": [{ "type": "bar" }], "layout": { "title": { "text": "Figure title" } }, }], )
def test_raises_invalid_toplevel_kwarg(self): with self.assertRaises(TypeError): go.Figure( data=[{ "type": "bar" }], layout={"title": "Figure title"}, frames=[{ "data": [{ "type": "bar" }], "layout": { "title": "Figure title" } }], bogus=123, )
def test_update_overwrite_data(self): fig = go.Figure( data=[go.Bar(marker_color="blue"), go.Bar(marker_color="yellow")]) fig.update(overwrite=True, data=[go.Marker(y=[1, 3, 2], line_color="yellow")]) self.assertEqual( fig.to_plotly_json()["data"], [{ "type": "scatter", "y": [1, 3, 2], "line": { "color": "yellow" } }], )
def test_update_layout_overwrite(self): fig = go.Figure(layout=go.Layout(annotations=[ go.layout.Annotation(text="one"), go.layout.Annotation(text="two"), ])) fig.update_layout( overwrite=True, annotations=[ go.layout.Annotation(width=10), go.layout.Annotation(width=20), go.layout.Annotation(width=30), go.layout.Annotation(width=40), go.layout.Annotation(width=50), ], ) expected = { "annotations": [ { "width": 10 }, { "width": 20 }, { "width": 30 }, { "width": 40 }, { "width": 50 }, ] } fig.layout.pop("template") self.assertEqual(fig.layout.to_plotly_json(), expected) # Remove all annotations fig.update_layout(overwrite=True, annotations=None) self.assertEqual(fig.layout.annotations, ())
def test_external_server_url(): # Build server url port = find_open_port() server_url = "http://{hostname}:{port}".format(hostname="localhost", port=port) # Build external orca command orca_path = which("orca") cmd_list = [orca_path] + [ "serve", "-p", str(port), "--plotly", pio.orca.config.plotlyjs, "--graph-only", ] # Run orca as subprocess to simulate external orca server DEVNULL = open(os.devnull, "wb") with orca_env(): proc = subprocess.Popen(cmd_list, stdout=DEVNULL) # Start plotly managed orca server so we can ensure it gets shut down properly pio.orca.config.port = port pio.orca.ensure_server() assert pio.orca.status.state == "running" # Configure orca to use external server pio.orca.config.server_url = server_url # Make sure that the locally managed orca server has been shutdown and the local # config options have been cleared assert pio.orca.status.state == "unvalidated" assert pio.orca.config.port is None fig = go.Figure() img_bytes = pio.to_image(fig, format="svg") assert img_bytes.startswith(b"<svg class") # Kill server orca process proc.terminate()
def test_update_overwrite_layout(self): fig = go.Figure(layout=go.Layout(width=1000)) # By default, update works recursively so layout.width should remain fig.update(layout={"title": {"text": "Fig Title"}}) fig.layout.pop("template") self.assertEqual(fig.layout.to_plotly_json(), { "title": { "text": "Fig Title" }, "width": 1000 }) # With overwrite=True, all existing layout properties should be # removed fig.update(overwrite=True, layout={"title": {"text": "Fig2 Title"}}) fig.layout.pop("template") self.assertEqual(fig.layout.to_plotly_json(), {"title": { "text": "Fig2 Title" }})
def test_raises_invalid_property_value(self): with self.assertRaises(ValueError): go.Figure( data=[{ "type": "bar", "showlegend": "bad_value" }], layout={ "paper_bgcolor": "bogus_color", "title": "Figure title" }, frames=[{ "data": [{ "type": "bar", "showlegend": "bad_value" }], "layout": { "bgcolor": "bad_color", "title": "Figure title" }, }], )
def test_raises_invalid_property_name(self): with self.assertRaises(ValueError): go.Figure( data=[{ "type": "bar", "bogus": 123 }], layout={ "bogus": 23, "title": "Figure title" }, frames=[{ "data": [{ "type": "bar", "bogus": 123 }], "layout": { "bogus": 23, "title": "Figure title" }, }], )
def test_pop_layout(self): fig = go.Figure(layout=go.Layout(width=1000)) self.assertEqual(fig.pop("layout"), go.Layout(width=1000)) self.assertEqual(fig.layout, go.Layout())
def test_add_trace_underscore_kwarg(self): fig = go.Figure() fig.add_scatter(y=[2, 1, 3], marker_line_color="green") self.assertEqual(fig.data[0].marker.line.color, "green")
def test_scalar_trace_as_data(self): fig = go.Figure(data=go.Waterfall(y=[2, 1, 3])) self.assertEqual(fig.data, (go.Waterfall(y=[2, 1, 3]), )) fig = go.Figure(data=dict(type="waterfall", y=[2, 1, 3])) self.assertEqual(fig.data, (go.Waterfall(y=[2, 1, 3]), ))
def test_pop_data(self): fig = go.Figure(data=go.Waterfall(y=[2, 1, 3])) self.assertEqual(fig.pop("data"), (go.Waterfall(y=[2, 1, 3]), )) self.assertEqual(fig.data, ())
def gantt_colorscale( chart, colors, title, index_col, show_colorbar, bar_width, showgrid_x, showgrid_y, height, width, tasks=None, task_names=None, data=None, group_tasks=False, show_hover_fill=True, ): """ Refer to FigureFactory.create_gantt() for docstring """ if tasks is None: tasks = [] if task_names is None: task_names = [] if data is None: data = [] showlegend = False for index in range(len(chart)): task = dict( x0=chart[index]["Start"], x1=chart[index]["Finish"], name=chart[index]["Task"], ) if "Description" in chart[index]: task["description"] = chart[index]["Description"] tasks.append(task) # create a scatter trace for every task group scatter_data_dict = dict() # create scatter traces for the start- and endpoints marker_data_dict = dict() if show_hover_fill: hoverinfo = "name" else: hoverinfo = "skip" scatter_data_template = { "x": [], "y": [], "mode": "none", "fill": "toself", "showlegend": False, "hoverinfo": hoverinfo, "legendgroup": "", } marker_data_template = { "x": [], "y": [], "mode": "markers", "text": [], "marker": dict(color="", size=1, opacity=0), "name": "", "showlegend": False, "legendgroup": "", } index_vals = [] for row in range(len(tasks)): if chart[row][index_col] not in index_vals: index_vals.append(chart[row][index_col]) index_vals.sort() # compute the color for task based on indexing column if isinstance(chart[0][index_col], Number): # check that colors has at least 2 colors if len(colors) < 2: raise exceptions.PlotlyError( "You must use at least 2 colors in 'colors' if you " "are using a colorscale. However only the first two " "colors given will be used for the lower and upper " "bounds on the colormap.") # create the list of task names for index in range(len(tasks)): tn = tasks[index]["name"] # Is added to task_names if group_tasks is set to False, # or if the option is used (True) it only adds them if the # name is not already in the list if not group_tasks or tn not in task_names: task_names.append(tn) # Guarantees that for grouped tasks the tasks that are inserted # first are shown at the top if group_tasks: task_names.reverse() for index in range(len(tasks)): tn = tasks[index]["name"] del tasks[index]["name"] # If group_tasks is True, all tasks with the same name belong # to the same row. groupID = index if group_tasks: groupID = task_names.index(tn) tasks[index]["y0"] = groupID - bar_width tasks[index]["y1"] = groupID + bar_width # unlabel color colors = clrs.color_parser(colors, clrs.unlabel_rgb) lowcolor = colors[0] highcolor = colors[1] intermed = (chart[index][index_col]) / 100.0 intermed_color = clrs.find_intermediate_color( lowcolor, highcolor, intermed) intermed_color = clrs.color_parser(intermed_color, clrs.label_rgb) tasks[index]["fillcolor"] = intermed_color color_id = tasks[index]["fillcolor"] if color_id not in scatter_data_dict: scatter_data_dict[color_id] = copy.deepcopy( scatter_data_template) scatter_data_dict[color_id]["fillcolor"] = color_id scatter_data_dict[color_id]["name"] = str(chart[index][index_col]) scatter_data_dict[color_id]["legendgroup"] = color_id # relabel colors with 'rgb' colors = clrs.color_parser(colors, clrs.label_rgb) # if there are already values append the gap if len(scatter_data_dict[color_id]["x"]) > 0: # a gap on the scatterplot separates the rectangles from each other scatter_data_dict[color_id]["x"].append( scatter_data_dict[color_id]["x"][-1]) scatter_data_dict[color_id]["y"].append(None) xs, ys = _get_corner_points( tasks[index]["x0"], tasks[index]["y0"], tasks[index]["x1"], tasks[index]["y1"], ) scatter_data_dict[color_id]["x"] += xs scatter_data_dict[color_id]["y"] += ys # append dummy markers for showing start and end of interval if color_id not in marker_data_dict: marker_data_dict[color_id] = copy.deepcopy( marker_data_template) marker_data_dict[color_id]["marker"]["color"] = color_id marker_data_dict[color_id]["legendgroup"] = color_id marker_data_dict[color_id]["x"].append(tasks[index]["x0"]) marker_data_dict[color_id]["x"].append(tasks[index]["x1"]) marker_data_dict[color_id]["y"].append(groupID) marker_data_dict[color_id]["y"].append(groupID) if "description" in tasks[index]: marker_data_dict[color_id]["text"].append( tasks[index]["description"]) marker_data_dict[color_id]["text"].append( tasks[index]["description"]) del tasks[index]["description"] else: marker_data_dict[color_id]["text"].append(None) marker_data_dict[color_id]["text"].append(None) # add colorbar to one of the traces randomly just for display if show_colorbar is True: k = list(marker_data_dict.keys())[0] marker_data_dict[k]["marker"].update( dict( colorscale=[[0, colors[0]], [1, colors[1]]], showscale=True, cmax=100, cmin=0, )) if isinstance(chart[0][index_col], str): index_vals = [] for row in range(len(tasks)): if chart[row][index_col] not in index_vals: index_vals.append(chart[row][index_col]) index_vals.sort() if len(colors) < len(index_vals): raise exceptions.PlotlyError( "Error. The number of colors in 'colors' must be no less " "than the number of unique index values in your group " "column.") # make a dictionary assignment to each index value index_vals_dict = {} # define color index c_index = 0 for key in index_vals: if c_index > len(colors) - 1: c_index = 0 index_vals_dict[key] = colors[c_index] c_index += 1 # create the list of task names for index in range(len(tasks)): tn = tasks[index]["name"] # Is added to task_names if group_tasks is set to False, # or if the option is used (True) it only adds them if the # name is not already in the list if not group_tasks or tn not in task_names: task_names.append(tn) # Guarantees that for grouped tasks the tasks that are inserted # first are shown at the top if group_tasks: task_names.reverse() for index in range(len(tasks)): tn = tasks[index]["name"] del tasks[index]["name"] # If group_tasks is True, all tasks with the same name belong # to the same row. groupID = index if group_tasks: groupID = task_names.index(tn) tasks[index]["y0"] = groupID - bar_width tasks[index]["y1"] = groupID + bar_width tasks[index]["fillcolor"] = index_vals_dict[chart[index] [index_col]] color_id = tasks[index]["fillcolor"] if color_id not in scatter_data_dict: scatter_data_dict[color_id] = copy.deepcopy( scatter_data_template) scatter_data_dict[color_id]["fillcolor"] = color_id scatter_data_dict[color_id]["legendgroup"] = color_id scatter_data_dict[color_id]["name"] = str(chart[index][index_col]) # relabel colors with 'rgb' colors = clrs.color_parser(colors, clrs.label_rgb) # if there are already values append the gap if len(scatter_data_dict[color_id]["x"]) > 0: # a gap on the scatterplot separates the rectangles from each other scatter_data_dict[color_id]["x"].append( scatter_data_dict[color_id]["x"][-1]) scatter_data_dict[color_id]["y"].append(None) xs, ys = _get_corner_points( tasks[index]["x0"], tasks[index]["y0"], tasks[index]["x1"], tasks[index]["y1"], ) scatter_data_dict[color_id]["x"] += xs scatter_data_dict[color_id]["y"] += ys # append dummy markers for showing start and end of interval if color_id not in marker_data_dict: marker_data_dict[color_id] = copy.deepcopy( marker_data_template) marker_data_dict[color_id]["marker"]["color"] = color_id marker_data_dict[color_id]["legendgroup"] = color_id marker_data_dict[color_id]["x"].append(tasks[index]["x0"]) marker_data_dict[color_id]["x"].append(tasks[index]["x1"]) marker_data_dict[color_id]["y"].append(groupID) marker_data_dict[color_id]["y"].append(groupID) if "description" in tasks[index]: marker_data_dict[color_id]["text"].append( tasks[index]["description"]) marker_data_dict[color_id]["text"].append( tasks[index]["description"]) del tasks[index]["description"] else: marker_data_dict[color_id]["text"].append(None) marker_data_dict[color_id]["text"].append(None) if show_colorbar is True: showlegend = True for k in scatter_data_dict: scatter_data_dict[k]["showlegend"] = showlegend # add colorbar to one of the traces randomly just for display # if show_colorbar is True: # k = list(marker_data_dict.keys())[0] # marker_data_dict[k]["marker"].update( # dict( # colorscale=[[0, colors[0]], [1, colors[1]]], # showscale=True, # cmax=100, # cmin=0, # ) # ) layout = dict( title=title, showlegend=showlegend, height=height, width=width, shapes=[], hovermode="closest", yaxis=dict( showgrid=showgrid_y, ticktext=task_names, tickvals=list(range(len(task_names))), range=[-1, len(task_names) + 1], autorange=False, zeroline=False, ), xaxis=dict( showgrid=showgrid_x, zeroline=False, rangeselector=dict(buttons=list([ dict(count=7, label="1w", step="day", stepmode="backward"), dict(count=1, label="1m", step="month", stepmode="backward"), dict(count=6, label="6m", step="month", stepmode="backward"), dict(count=1, label="YTD", step="year", stepmode="todate"), dict(count=1, label="1y", step="year", stepmode="backward"), dict(step="all"), ])), type="date", ), ) data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)] data += [marker_data_dict[k] for k in sorted(marker_data_dict)] # fig = dict( # data=data, layout=layout # ) fig = go.Figure(data=data, layout=layout) return fig
def test_instantiation(self): native_figure = {"data": [], "layout": {}, "frames": []} go.Figure(native_figure) go.Figure()
def gantt( chart, colors, title, bar_width, showgrid_x, showgrid_y, height, width, tasks=None, task_names=None, data=None, group_tasks=False, show_hover_fill=True, show_colorbar=True, ): """ Refer to create_gantt() for docstring """ if tasks is None: tasks = [] if task_names is None: task_names = [] if data is None: data = [] for index in range(len(chart)): task = dict( x0=chart[index]["Start"], x1=chart[index]["Finish"], name=chart[index]["Task"], ) if "Description" in chart[index]: task["description"] = chart[index]["Description"] tasks.append(task) # create a scatter trace for every task group scatter_data_dict = dict() marker_data_dict = dict() if show_hover_fill: hoverinfo = "name" else: hoverinfo = "skip" scatter_data_template = { "x": [], "y": [], "mode": "none", "fill": "toself", "hoverinfo": hoverinfo, } marker_data_template = { "x": [], "y": [], "mode": "markers", "text": [], "marker": dict(color="", size=1, opacity=0), "name": "", "showlegend": False, } # create the list of task names for index in range(len(tasks)): tn = tasks[index]["name"] # Is added to task_names if group_tasks is set to False, # or if the option is used (True) it only adds them if the # name is not already in the list if not group_tasks or tn not in task_names: task_names.append(tn) # Guarantees that for grouped tasks the tasks that are inserted first # are shown at the top if group_tasks: task_names.reverse() color_index = 0 for index in range(len(tasks)): tn = tasks[index]["name"] del tasks[index]["name"] # If group_tasks is True, all tasks with the same name belong # to the same row. groupID = index if group_tasks: groupID = task_names.index(tn) tasks[index]["y0"] = groupID - bar_width tasks[index]["y1"] = groupID + bar_width # check if colors need to be looped if color_index >= len(colors): color_index = 0 tasks[index]["fillcolor"] = colors[color_index] color_id = tasks[index]["fillcolor"] if color_id not in scatter_data_dict: scatter_data_dict[color_id] = copy.deepcopy(scatter_data_template) scatter_data_dict[color_id]["fillcolor"] = color_id scatter_data_dict[color_id]["name"] = str(tn) scatter_data_dict[color_id]["legendgroup"] = color_id # if there are already values append the gap if len(scatter_data_dict[color_id]["x"]) > 0: # a gap on the scatterplot separates the rectangles from each other scatter_data_dict[color_id]["x"].append( scatter_data_dict[color_id]["x"][-1]) scatter_data_dict[color_id]["y"].append(None) xs, ys = _get_corner_points( tasks[index]["x0"], tasks[index]["y0"], tasks[index]["x1"], tasks[index]["y1"], ) scatter_data_dict[color_id]["x"] += xs scatter_data_dict[color_id]["y"] += ys # append dummy markers for showing start and end of interval if color_id not in marker_data_dict: marker_data_dict[color_id] = copy.deepcopy(marker_data_template) marker_data_dict[color_id]["marker"]["color"] = color_id marker_data_dict[color_id]["legendgroup"] = color_id marker_data_dict[color_id]["x"].append(tasks[index]["x0"]) marker_data_dict[color_id]["x"].append(tasks[index]["x1"]) marker_data_dict[color_id]["y"].append(groupID) marker_data_dict[color_id]["y"].append(groupID) if "description" in tasks[index]: marker_data_dict[color_id]["text"].append( tasks[index]["description"]) marker_data_dict[color_id]["text"].append( tasks[index]["description"]) del tasks[index]["description"] else: marker_data_dict[color_id]["text"].append(None) marker_data_dict[color_id]["text"].append(None) color_index += 1 showlegend = show_colorbar layout = dict( title=title, showlegend=showlegend, height=height, width=width, shapes=[], hovermode="closest", yaxis=dict( showgrid=showgrid_y, ticktext=task_names, tickvals=list(range(len(task_names))), range=[-1, len(task_names) + 1], autorange=False, zeroline=False, ), xaxis=dict( showgrid=showgrid_x, zeroline=False, rangeselector=dict(buttons=list([ dict(count=7, label="1w", step="day", stepmode="backward"), dict(count=1, label="1m", step="month", stepmode="backward"), dict(count=6, label="6m", step="month", stepmode="backward"), dict(count=1, label="YTD", step="year", stepmode="todate"), dict(count=1, label="1y", step="year", stepmode="backward"), dict(step="all"), ])), type="date", ), ) data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)] data += [marker_data_dict[k] for k in sorted(marker_data_dict)] # fig = dict( # data=data, layout=layout # ) fig = go.Figure(data=data, layout=layout) return fig
def test_pop_invalid_key(self): fig = go.Figure(layout=go.Layout(width=1000)) with self.assertRaises(KeyError): fig.pop("bogus")