diff --git a/CHANGELOG.md b/CHANGELOG.md index c823a8728a9..bbf46486078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [1.9.4] - 2015-01-11 +### Added +- Offline plotting now works outside of the IPython/Juypter notebook. Here's an example: +``` +from plotly.offline import plot +from plotly.graph_objs import Scatter + +plot([Scatter(x=[1, 2, 3], y=[3, 1, 6])]) +``` + +This command works entirely locally. It writes to a local HTML file with the necessary [plotly.js](https://plot.ly/javascript) code to render the graph. Your browser will open the file after you make the call. + +The call signature is very similar to `plotly.offline.iplot` and `plotly.plotly.plot` and `plotly.plotly.iplot`, so you can basically use these commands interchangeably. + +If you want to publish your graphs to the web, use `plotly.plotly.plot`, as in: + +``` +import plotly.plotly as py +from plotly.graph_objs import Scatter + +py.plot([Scatter(x=[1, 2, 3], y=[5, 1, 6])]) +``` + +This will upload the graph to your online plotly account. + ## [1.9.3] - 2015-12-08 ### Added - Check for `no_proxy` when determining if the streaming request should pass through a proxy in the chunked_requests submodule. Example: `no_proxy='my_stream_url'` and `http_proxy=my.proxy.ip:1234`, then `my_stream_url` will not get proxied. Previously it would. diff --git a/plotly/graph_reference/default-schema.json b/plotly/graph_reference/default-schema.json index d9285f84ec6..f238807bf48 100644 --- a/plotly/graph_reference/default-schema.json +++ b/plotly/graph_reference/default-schema.json @@ -445,7 +445,7 @@ "description": "Sets the global font. Note that fonts used in traces and other layout components inherit from the global font.", "family": { "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The plotly service (at https://plot.ly or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", - "dflt": "\"Open sans\", verdana, arial, sans-serif", + "dflt": "\"Open Sans\", verdana, arial, sans-serif", "noBlank": true, "role": "style", "strict": true, @@ -6424,17 +6424,6 @@ "role": "info", "valType": "number" }, - "zsmooth": { - "description": "Picks a smoothing algorithm use to smooth `z` data.", - "dflt": false, - "role": "style", - "valType": "enumerated", - "values": [ - "fast", - "best", - false - ] - }, "zsrc": { "description": "Sets the source reference on plot.ly for z .", "role": "info", @@ -7044,18 +7033,6 @@ "role": "style", "valType": "boolean" }, - "dx": { - "description": "Sets the x coordinate step. See `x0` for more info.", - "dflt": 1, - "role": "info", - "valType": "number" - }, - "dy": { - "description": "Sets the y coordinate step. See `y0` for more info.", - "dflt": 1, - "role": "info", - "valType": "number" - }, "error_x": { "_deprecated": { "opacity": { @@ -7337,9 +7314,10 @@ "valType": "number" }, "color": { + "arrayOk": true, "description": "Sets the marker color.", - "role": "data", - "valType": "data_array" + "role": "style", + "valType": "color" }, "colorbar": { "bgcolor": { @@ -7740,6 +7718,7 @@ "width": { "arrayOk": true, "description": "Sets the width (in px) of the lines bounding the marker points.", + "dflt": 0, "min": 0, "role": "style", "valType": "number" @@ -7800,16 +7779,6 @@ "h" ] }, - "r": { - "description": "For polar chart only.Sets the radial coordinates.", - "role": "data", - "valType": "data_array" - }, - "rsrc": { - "description": "Sets the source reference on plot.ly for r .", - "role": "info", - "valType": "string" - }, "showlegend": { "description": "Determines whether or not an item corresponding to this trace is shown in the legend.", "dflt": true, @@ -7832,11 +7801,6 @@ "valType": "string" } }, - "t": { - "description": "For polar chart only.Sets the angular coordinates.", - "role": "data", - "valType": "data_array" - }, "text": { "arrayOk": true, "description": "Sets text elements associated with each (x,y) pair. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y) coordinates.", @@ -7849,11 +7813,6 @@ "role": "info", "valType": "string" }, - "tsrc": { - "description": "Sets the source reference on plot.ly for t .", - "role": "info", - "valType": "string" - }, "type": "histogram", "uid": { "dflt": "", @@ -7876,12 +7835,6 @@ "role": "data", "valType": "data_array" }, - "x0": { - "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", - "dflt": 0, - "role": "info", - "valType": "any" - }, "xaxis": { "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", "dflt": "x", @@ -7919,12 +7872,6 @@ "role": "data", "valType": "data_array" }, - "y0": { - "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", - "dflt": 0, - "role": "info", - "valType": "any" - }, "yaxis": { "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.xaxis2`, and so on.", "dflt": "y", @@ -7956,16 +7903,6 @@ "description": "Sets the source reference on plot.ly for y .", "role": "info", "valType": "string" - }, - "z": { - "description": "Sets the aggregation data.", - "role": "data", - "valType": "data_array" - }, - "zsrc": { - "description": "Sets the source reference on plot.ly for z .", - "role": "info", - "valType": "string" } }, "description": "The sample data from which statistics are computed is set in `x` for vertically spanning histograms and in `y` for horizontally spanning histograms. Binning options are set `xbins` and `ybins` respectively if no aggregation data is provided.", @@ -8372,24 +8309,6 @@ "role": "style", "valType": "colorscale" }, - "connectgaps": { - "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the `z` data are filled in.", - "dflt": false, - "role": "info", - "valType": "boolean" - }, - "dx": { - "description": "Sets the x coordinate step. See `x0` for more info.", - "dflt": 1, - "role": "info", - "valType": "number" - }, - "dy": { - "description": "Sets the y coordinate step. See `y0` for more info.", - "dflt": 1, - "role": "info", - "valType": "number" - }, "histfunc": { "description": "Specifies the binning function used for this histogram trace. If *count*, the histogram values are computed by counting the number of values lying inside each bin. If *sum*, *avg*, *min*, *max*, the histogram values are computed using the sum, the average, the minimum or the maximum of the values lying inside each bin respectively.", "dflt": "count", @@ -8441,6 +8360,7 @@ }, "marker": { "color": { + "description": "Sets the aggregation data.", "role": "data", "valType": "data_array" }, @@ -8478,15 +8398,6 @@ "role": "style", "valType": "number" }, - "orientation": { - "description": "Sets the orientation of the bars. With *v* (*h*), the value of the each bar spans along the vertical (horizontal).", - "role": "info", - "valType": "enumerated", - "values": [ - "v", - "h" - ] - }, "reversescale": { "description": "Reverses the colorscale.", "dflt": false, @@ -8521,22 +8432,6 @@ "valType": "string" } }, - "text": { - "description": "Sets the text elements associated with each z value.", - "role": "data", - "valType": "data_array" - }, - "textsrc": { - "description": "Sets the source reference on plot.ly for text .", - "role": "info", - "valType": "string" - }, - "transpose": { - "description": "Transposes the z data.", - "dflt": false, - "role": "info", - "valType": "boolean" - }, "type": "histogram2d", "uid": { "dflt": "", @@ -8559,12 +8454,6 @@ "role": "data", "valType": "data_array" }, - "x0": { - "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", - "dflt": 0, - "role": "info", - "valType": "any" - }, "xaxis": { "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", "dflt": "x", @@ -8597,26 +8486,11 @@ "role": "info", "valType": "string" }, - "xtype": { - "description": "If *array*, the heatmap's x coordinates are given by *x* (the default behavior when `x` is provided). If *scaled*, the heatmap's x coordinates are given by *x0* and *dx* (the default behavior when `x` is not provided).", - "role": "info", - "valType": "enumerated", - "values": [ - "array", - "scaled" - ] - }, "y": { "description": "Sets the sample data to be binned on the y axis.", "role": "data", "valType": "data_array" }, - "y0": { - "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", - "dflt": 0, - "role": "info", - "valType": "any" - }, "yaxis": { "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.xaxis2`, and so on.", "dflt": "y", @@ -8649,15 +8523,6 @@ "role": "info", "valType": "string" }, - "ytype": { - "description": "If *array*, the heatmap's y coordinates are given by *y* (the default behavior when `y` is provided) If *scaled*, the heatmap's y coordinates are given by *y0* and *dy* (the default behavior when `y` is not provided)", - "role": "info", - "valType": "enumerated", - "values": [ - "array", - "scaled" - ] - }, "z": { "description": "Sets the aggregation data.", "role": "data", @@ -9070,12 +8935,6 @@ "role": "style", "valType": "colorscale" }, - "connectgaps": { - "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the `z` data are filled in.", - "dflt": false, - "role": "info", - "valType": "boolean" - }, "contours": { "coloring": { "description": "Determines the coloring method showing the contour values. If *fill*, coloring is done evenly between each contour level If *heatmap*, a heatmap gradient is coloring is applied between each contour level. If *lines*, coloring is done on the contour lines. If *none*, no coloring is applied on this trace.", @@ -9115,18 +8974,6 @@ "valType": "number" } }, - "dx": { - "description": "Sets the x coordinate step. See `x0` for more info.", - "dflt": 1, - "role": "info", - "valType": "number" - }, - "dy": { - "description": "Sets the y coordinate step. See `y0` for more info.", - "dflt": 1, - "role": "info", - "valType": "number" - }, "histfunc": { "description": "Specifies the binning function used for this histogram trace. If *count*, the histogram values are computed by counting the number of values lying inside each bin. If *sum*, *avg*, *min*, *max*, the histogram values are computed using the sum, the average, the minimum or the maximum of the values lying inside each bin respectively.", "dflt": "count", @@ -9215,6 +9062,7 @@ }, "marker": { "color": { + "description": "Sets the aggregation data.", "role": "data", "valType": "data_array" }, @@ -9258,15 +9106,6 @@ "role": "style", "valType": "number" }, - "orientation": { - "description": "Sets the orientation of the bars. With *v* (*h*), the value of the each bar spans along the vertical (horizontal).", - "role": "info", - "valType": "enumerated", - "values": [ - "v", - "h" - ] - }, "reversescale": { "description": "Reverses the colorscale.", "dflt": false, @@ -9301,22 +9140,6 @@ "valType": "string" } }, - "text": { - "description": "Sets the text elements associated with each z value.", - "role": "data", - "valType": "data_array" - }, - "textsrc": { - "description": "Sets the source reference on plot.ly for text .", - "role": "info", - "valType": "string" - }, - "transpose": { - "description": "Transposes the z data.", - "dflt": false, - "role": "info", - "valType": "boolean" - }, "type": "histogram2dcontour", "uid": { "dflt": "", @@ -9339,12 +9162,6 @@ "role": "data", "valType": "data_array" }, - "x0": { - "description": "Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.", - "dflt": 0, - "role": "info", - "valType": "any" - }, "xaxis": { "description": "Sets a reference between this trace's x coordinates and a 2D cartesian x axis. If *x* (the default value), the x coordinates refer to `layout.xaxis`. If *x2*, the x coordinates refer to `layout.xaxis2`, and so on.", "dflt": "x", @@ -9377,26 +9194,11 @@ "role": "info", "valType": "string" }, - "xtype": { - "description": "If *array*, the heatmap's x coordinates are given by *x* (the default behavior when `x` is provided). If *scaled*, the heatmap's x coordinates are given by *x0* and *dx* (the default behavior when `x` is not provided).", - "role": "info", - "valType": "enumerated", - "values": [ - "array", - "scaled" - ] - }, "y": { "description": "Sets the sample data to be binned on the y axis.", "role": "data", "valType": "data_array" }, - "y0": { - "description": "Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.", - "dflt": 0, - "role": "info", - "valType": "any" - }, "yaxis": { "description": "Sets a reference between this trace's y coordinates and a 2D cartesian y axis. If *y* (the default value), the y coordinates refer to `layout.yaxis`. If *y2*, the y coordinates refer to `layout.xaxis2`, and so on.", "dflt": "y", @@ -9429,15 +9231,6 @@ "role": "info", "valType": "string" }, - "ytype": { - "description": "If *array*, the heatmap's y coordinates are given by *y* (the default behavior when `y` is provided) If *scaled*, the heatmap's y coordinates are given by *y0* and *dy* (the default behavior when `y` is not provided)", - "role": "info", - "valType": "enumerated", - "values": [ - "array", - "scaled" - ] - }, "z": { "description": "Sets the aggregation data.", "role": "data", @@ -9461,17 +9254,6 @@ "role": "info", "valType": "number" }, - "zsmooth": { - "description": "Picks a smoothing algorithm use to smooth `z` data.", - "dflt": false, - "role": "style", - "valType": "enumerated", - "values": [ - "fast", - "best", - false - ] - }, "zsrc": { "description": "Sets the source reference on plot.ly for z .", "role": "info", @@ -9484,11 +9266,13 @@ "mesh3d": { "attributes": { "alphahull": { + "description": "Sets the shape of the mesh If *-1*, Delaunay triangulation is used If *>0*, the alpha-shape algorithm is used If *0*, the convex-hull algorithm is used An alternative to the `i`, `j`, `k` indices triplets.", "dflt": -1, "role": "style", "valType": "number" }, "color": { + "description": "Sets the color of the whole mesh", "role": "style", "valType": "color" }, @@ -9843,6 +9627,7 @@ }, "role": "object", "show": { + "description": "Sets whether or not dynamic contours are shown on hover", "dflt": false, "role": "info", "valType": "boolean" @@ -9856,6 +9641,7 @@ } }, "delaunayaxis": { + "description": "Sets the Delaunay axis from which the triangulation of the mesh takes place. An alternative to setting the `i`, `j`, `k` indices triplets.", "dflt": "z", "role": "info", "valType": "enumerated", @@ -9866,6 +9652,7 @@ ] }, "facecolor": { + "description": "Sets the color of each face Overrides *color* and *vertexcolor*.", "role": "data", "valType": "data_array" }, @@ -9875,6 +9662,7 @@ "valType": "string" }, "flatshading": { + "description": "Determines whether or not normal smoothing is applied to the meshes, creating meshes with a low-poly look.", "dflt": false, "role": "style", "valType": "boolean" @@ -9897,10 +9685,12 @@ "valType": "flaglist" }, "i": { + "description": "Sets the indices of x coordinates of the vertices", "role": "data", "valType": "data_array" }, "intensity": { + "description": "Sets the vertex intensity values, used for plotting fields on meshes", "role": "data", "valType": "data_array" }, @@ -9915,6 +9705,7 @@ "valType": "string" }, "j": { + "description": "Sets the indices of y coordinates of the vertices", "role": "data", "valType": "data_array" }, @@ -9924,6 +9715,7 @@ "valType": "string" }, "k": { + "description": "Sets the indices of z coordinates of the vertices", "role": "data", "valType": "data_array" }, @@ -10035,6 +9827,7 @@ "valType": "string" }, "vertexcolor": { + "description": "Sets the color of each vertex Overrides *color*.", "role": "data", "valType": "data_array" }, @@ -10055,6 +9848,7 @@ ] }, "x": { + "description": "Sets the x coordinates of the vertices", "role": "data", "valType": "data_array" }, @@ -10064,6 +9858,7 @@ "valType": "string" }, "y": { + "description": "Sets the y coordinates of the vertices", "role": "data", "valType": "data_array" }, @@ -10073,6 +9868,7 @@ "valType": "string" }, "z": { + "description": "Sets the z coordinates of the vertices", "role": "data", "valType": "data_array" }, @@ -10082,7 +9878,7 @@ "valType": "string" } }, - "description": "" + "description": "Draws sets of triangles with coordinates given by three 1-dimensional arrays in `x`, `y`, `z` and (1) a sets of `i`, `j`, `k` indices (2) Delaunay triangulation or (3) the Alpha-shape algorithm or (4) the Convex-hull algorithm" }, "pie": { "attributes": { @@ -12576,18 +12372,13 @@ } }, "opacity": { - "arrayOk": true, - "description": "Sets the marker opacity.", + "arrayOk": false, + "description": "Sets the marker opacity. Note that the marker opacity for scatter3d traces must be a scalar value for performance reasons. To set a blending opacity value (i.e. which is not transparent), set *marker.color* to an rgba color and use its alpha channel.", "max": 1, "min": 0, "role": "style", "valType": "number" }, - "opacitysrc": { - "description": "Sets the source reference on plot.ly for opacity .", - "role": "info", - "valType": "string" - }, "reversescale": { "description": "Has only an effect if `marker.color` is set to a numerical array. Reverses the colorscale.", "dflt": false, diff --git a/plotly/offline/__init__.py b/plotly/offline/__init__.py index df04b62cc01..30da5a36c18 100644 --- a/plotly/offline/__init__.py +++ b/plotly/offline/__init__.py @@ -6,5 +6,6 @@ from . offline import ( download_plotlyjs, init_notebook_mode, - iplot + iplot, + plot ) diff --git a/plotly/offline/offline.py b/plotly/offline/offline.py index cb9e3762bed..4944b4e7f17 100644 --- a/plotly/offline/offline.py +++ b/plotly/offline/offline.py @@ -10,6 +10,7 @@ import uuid import warnings from pkg_resources import resource_string +import webbrowser import plotly from plotly import tools, utils @@ -57,6 +58,72 @@ def init_notebook_mode(): '')) +def _plot_html(figure_or_data, show_link, link_text, + validate, default_width, default_height): + + figure = tools.return_figure_from_figure_or_data(figure_or_data, validate) + + width = figure.get('layout', {}).get('width', default_width) + height = figure.get('layout', {}).get('height', default_height) + + try: + float(width) + except (ValueError, TypeError): + pass + else: + width = str(width) + 'px' + + try: + float(width) + except (ValueError, TypeError): + pass + else: + width = str(width) + 'px' + + plotdivid = uuid.uuid4() + jdata = json.dumps(figure.get('data', []), cls=utils.PlotlyJSONEncoder) + jlayout = json.dumps(figure.get('layout', {}), cls=utils.PlotlyJSONEncoder) + + config = {} + config['showLink'] = show_link + config['linkText'] = link_text + jconfig = json.dumps(config) + + # TODO: The get_config 'source of truth' should + # really be somewhere other than plotly.plotly + plotly_platform_url = plotly.plotly.get_config().get('plotly_domain', + 'https://plot.ly') + if (plotly_platform_url != 'https://plot.ly' and + link_text == 'Export to plot.ly'): + + link_domain = plotly_platform_url\ + .replace('https://', '')\ + .replace('http://', '') + link_text = link_text.replace('plot.ly', link_domain) + + script = 'Plotly.newPlot("{id}", {data}, {layout}, {config})'.format( + id=plotdivid, + data=jdata, + layout=jlayout, + config=jconfig) + + plotly_html_div = ( + '' + '
' + '
' + '' + '').format( + id=plotdivid, script=script, + height=height, width=width) + + return plotly_html_div, plotdivid, width, height + + def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly', validate=True): """ @@ -103,76 +170,131 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly', raise ImportError('`iplot` can only run inside an IPython Notebook.') from IPython.display import HTML, display - figure = tools.return_figure_from_figure_or_data(figure_or_data, validate) - width = figure.get('layout', {}).get('width', '100%') - height = figure.get('layout', {}).get('height', 525) - try: - float(width) - except (ValueError, TypeError): - pass - else: - width = str(width) + 'px' + plot_html, plotdivid, width, height = _plot_html( + figure_or_data, show_link, link_text, validate, + '100%', 525) - try: - float(width) - except (ValueError, TypeError): - pass - else: - width = str(width) + 'px' + display(HTML(plot_html)) - plotdivid = uuid.uuid4() - jdata = json.dumps(figure.get('data', []), cls=utils.PlotlyJSONEncoder) - jlayout = json.dumps(figure.get('layout', {}), cls=utils.PlotlyJSONEncoder) - config = {} - config['showLink'] = show_link - config['linkText'] = link_text - jconfig = json.dumps(config) +def plot(figure_or_data, + show_link=True, link_text='Export to plot.ly', + validate=True, output_type='file', + include_plotlyjs=True, + filename='temp-plot.html', + auto_open=True): + """ Create a plotly graph locally as an HTML document or string. - # TODO: The get_config 'source of truth' should - # really be somewhere other than plotly.plotly - plotly_platform_url = plotly.plotly.get_config().get('plotly_domain', - 'https://plot.ly') - if (plotly_platform_url != 'https://plot.ly' and - link_text == 'Export to plot.ly'): + Example: + ``` + from plotly.offline import plot + import plotly.graph_objs as go - link_domain = plotly_platform_url\ - .replace('https://', '')\ - .replace('http://', '') - link_text = link_text.replace('plot.ly', link_domain) + plot([ + go.Scatter(x=[1, 2, 3], y=[3, 2 6]) + ], filename='my-graph.html') + ``` + More examples below. - display(HTML( - '' - )) - - script = '\n'.join([ - 'Plotly.plot("{id}", {data}, {layout}, {config}).then(function() {{', - ' $(".{id}.loading").remove();', - '}})' - ]).format(id=plotdivid, - data=jdata, - layout=jlayout, - config=jconfig) - - display(HTML('' - '
' - 'Drawing...
' - '
' - '
' - '' - ''.format(id=plotdivid, script=script, - height=height, width=width))) - - -def plot(): - """ Configured to work with localhost Plotly graph viewer + figure_or_data -- a plotly.graph_objs.Figure or plotly.graph_objs.Data or + dict or list that describes a Plotly graph. + See https://plot.ly/python/ for examples of + graph descriptions. + + Keyword arguments: + show_link (default=True) -- display a link in the bottom-right corner of + of the chart that will export the chart to Plotly Cloud or + Plotly Enterprise + link_text (default='Export to plot.ly') -- the text of export link + validate (default=True) -- validate that all of the keys in the figure + are valid? omit if your version of plotly.js has become outdated + with your version of graph_reference.json or if you need to include + extra, unnecessary keys in your figure. + output_type ('file' | 'div' - default 'file') -- if 'file', then + the graph is saved as a standalone HTML file and `plot` + returns None. + If 'div', then `plot` returns a string that just contains the + HTML
that contains the graph and the script to generate the + graph. + Use 'file' if you want to save and view a single graph at a time + in a standalone HTML file. + Use 'div' if you are embedding these graphs in an HTML file with + other graphs or HTML markup, like a HTML report or an website. + include_plotlyjs (default=True) -- If True, include the plotly.js + source code in the output file or string. + Set as False if your HTML file already contains a copy of the plotly.js + library. + filename (default='temp-plot.html') -- The local filename to save the + outputted chart to. If the filename already exists, it will be + overwritten. This argument only applies if `output_type` is 'file'. + auto_open (default=True) -- If True, open the saved file in a + web browser after saving. + This argument only applies if `output_type` is 'file'. """ - raise NotImplementedError + if output_type not in ['div', 'file']: + raise ValueError( + "`output_type` argument must be 'div' or 'file'. " + "You supplied `" + output_type + "``") + if not filename.endswith('.html') and output_type == 'file': + warnings.warn( + "Your filename `" + filename + "` didn't end with .html. " + "Adding .html to the end of your file.") + filename += '.html' + + plot_html, plotdivid, width, height = _plot_html( + figure_or_data, show_link, link_text, validate, + '100%', '100%') + + figure = tools.return_figure_from_figure_or_data(figure_or_data, validate) + + resize_script = '' + if width == '100%' or height == '100%': + resize_script = ( + '' + '' + ).format(id=plotdivid) + + if output_type == 'file': + with open(filename, 'w') as f: + if include_plotlyjs: + plotly_js_script = ''.join([ + '', + ]) + else: + plotly_js_script = '' + + f.write(''.join([ + '', + '', + '', + plotly_js_script, + plot_html, + resize_script, + '', + ''])) + + url = 'file://' + os.path.abspath(filename) + if auto_open: + webbrowser.open(url) + + return url + elif output_type == 'div': + if include_plotlyjs: + return ''.join([ + '
', + '', + plot_html, + '
' + ]) + else: + return plot_html diff --git a/plotly/tests/test_core/test_offline/test_offline.py b/plotly/tests/test_core/test_offline/test_offline.py new file mode 100644 index 00000000000..34684322e92 --- /dev/null +++ b/plotly/tests/test_core/test_offline/test_offline.py @@ -0,0 +1,80 @@ +""" +test__offline + +""" +from __future__ import absolute_import + +from nose.tools import raises +from unittest import TestCase +import json + +import plotly + +fig = { + 'data': [ + plotly.graph_objs.Scatter(x=[1, 2, 3], y=[10, 20, 30]) + ], + 'layout': plotly.graph_objs.Layout( + title='offline plot' + ) +} + +PLOTLYJS = plotly.offline.offline.get_plotlyjs() + +class PlotlyOfflineTestCase(TestCase): + def setUp(self): + pass + + def _read_html(self, file_url): + """ Read and return the HTML contents from a file_url + in the form e.g. file:///Users/chriddyp/Repos/plotly.py/plotly-temp.html + """ + with open(file_url.replace('file://', '').replace(' ', '')) as f: + return f.read() + + def test_default_plot_generates_expected_html(self): + data_json = json.dumps(fig['data'], cls=plotly.utils.PlotlyJSONEncoder) + layout_json = json.dumps( + fig['layout'], + cls=plotly.utils.PlotlyJSONEncoder) + + html = self._read_html(plotly.offline.plot(fig)) + + # I don't really want to test the entire script output, so + # instead just make sure a few of the parts are in here? + self.assertTrue('Plotly.newPlot' in html) # plot command is in there + self.assertTrue(data_json in html) # data is in there + self.assertTrue(layout_json in html) # so is layout + self.assertTrue(PLOTLYJS in html) # and the source code + # and it's an doc + self.assertTrue(html.startswith('') and html.endswith('')) + + def test_including_plotlyjs(self): + html = self._read_html(plotly.offline.plot(fig, include_plotlyjs=False)) + self.assertTrue(PLOTLYJS not in html) + + def test_div_output(self): + html = plotly.offline.plot(fig, output_type='div') + + self.assertTrue('' not in html and '' not in html) + self.assertTrue(html.startswith('
') and html.endswith('
')) + + def test_autoresizing(self): + resize_code_strings = [ + 'window.addEventListener("resize", ', + 'Plotly.Plots.resize(' + ] + # If width or height wasn't specified, then we add a window resizer + html = self._read_html(plotly.offline.plot(fig)) + for resize_code_string in resize_code_strings: + self.assertTrue(resize_code_string in html) + + # If width or height was specified, then we don't resize + html = plotly.offline.plot({ + 'data': fig['data'], + 'layout': { + 'width': 500, 'height': 500 + } + }) + for resize_code_string in resize_code_strings: + self.assertTrue(resize_code_string not in html) diff --git a/plotly/version.py b/plotly/version.py index be0b610758e..6c08d428eb4 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '1.9.3' +__version__ = '1.9.4'