Commit f0f7587f authored by Sebastien Robin's avatar Sebastien Robin

erp5_officejs: make graph charts similar to other gadget fields for unification

This way it would be even more easier to use graphs, and we avoid inventing additional API.
Method updateConfiguration has been removed, it is possible to call render several times if needed.
parent 6336fcb5
......@@ -10,114 +10,126 @@
<dt>render</dt>
<dd>render a graph</dd>
<dl>
<dt data-parameter-required="required" data-parameter-type="object">configuration_dict</dt>
<dd><code style="display:block;white-space:pre-wrap">
<dt data-parameter-required="required" data-parameter-type="object">option_dict</dt>
<dd>
Schema available here : <a href="gadget_officejs_interface_widget_graph_html_schema.json">gadget_officejs_interface_widget_graph_html_schema.json</a><br>
<code style="display:block;white-space:pre-wrap">
Generic graph gadget. The purpose of this gadget is to provide an unique
API for various charting libraries
Options supported are :
Please see json schema for supported parameters.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Parameters to generate a graph",
"description": "Field parameters for rendering graph",
"properties": {
"layout": {
"description": "definition of layout",
"value": {
"description": "Parameters to generate a graph",
"properties": {
"title": {
"description": "The title of the graph",
"type": "string"
},
"axis_dict": {
"layout": {
"description": "definition of layout",
"properties": {
".*": {
"description": "layout definition of one axis",
"title": {
"description": "The title of the graph",
"type": "string"
},
"axis_dict": {
"properties": {
"title": {
"description": "label to display on axis n",
"type": "string"
},
"scale_type": {
"description": "type of axis",
"enum": [
"linear",
"logarithmic"
],
"default": "linear",
"type": "string"
}, },
"value_type": {
"description": "value type for data on this axis",
"enum": [
"number",
"date",
"string"
],
"default": "number",
"type": "string"
}, },
"position": {
"description": "where to place the axis, only y axis for now",
"enum": [
"left",
"right"
],
"type": "string"
".*": {
"description": "layout definition of one axis",
"properties": {
"title": {
"description": "label to display on axis n",
"type": "string"
},
"scale_type": {
"description": "type of axis",
"enum": [
"linear",
"logarithmic"
],
"default": "linear",
"type": "string"
},
"value_type": {
"description": "value type for data on this axis",
"enum": [
"number",
"date",
"string"
],
"default": "number",
"type": "string"
},
"position": {
"description": "where to place the axis, only y axis for now",
"enum": [
"left",
"right"
],
"type": "string"
}
},
"type": "object",
"additionalProperties": false
}
},
"type": "object",
"additionalProperties": false
"type": "object"
}
},
"type": "object"
}
},
"type": "object",
"additionalProperties": false
},
"data": {
"description": "the list of data sets",
"items": {
"properties": {
"value_dict": {
"[0-9]*": {
"description": "values for the axis number n",
"type": "array"
"type": "object",
"additionalProperties": false
},
"data": {
"description": "the list of data sets",
"items": {
"properties": {
"value_dict": {
"[0-9]*": {
"description": "values for the axis number n",
"type": "array"
},
"type": "object"
},
"type": {
"description": "type of trace that should be displayed",
"enum": [
"pie",
"bar",
"scatter",
"marker",
"surface",
"line"
],
"default": "scatter",
"type": "string"
},
"title": {
"description": "label for this data set",
"type": "string"
},
"label_list": {
"description": "title for every value",
"type": "array"
},
"axis_mapping_id_dict": {
"[0-9]*": {
"description": "mapping id for the axis number n, this is optional and allows to have several scales per axis, several data set might use same name if they should be grouped on the same scale",
"type": "string"
},
"type": "object"
}
},
"additionalProperties": false,
"type": "object"
},
"type": {
"description": "type of trace that should be displayed",
"enum": [
"pie",
"bar",
"scatter",
"marker",
"surface",
"line"
],
"default": "scatter",
"type": "string"
},
"title": {
"description": "label for this data set",
"type": "string"
},
"axis_mapping_id_dict": {
"[0-9]*": {
"description": "mapping id for the axis number n, this is optional and allows to have several scales per axis, several data set might use same name if they should be grouped on the same scale",
"type": "string"
},
"type": "object"
}
},
"additionalProperties": false,
"type": "object"
"type": "array"
}
},
"type": "array"
}
"additionalProperties": false
},
},
"additionalProperties": false
"type": "object"
}
Options were inspired by https://plot.ly/javascript/ which supports various types
......@@ -132,6 +144,7 @@
Example of options:
{value:
{data: [{ value_dict: {"0": [0, 1, 2],
"1": [0, 1, 4]
},
......@@ -140,7 +153,7 @@
axis_mapping_id_dict: {"1": "1_1"},
title: "first data set"
},
{ value_dict: {"0": [0, 1, 3],,
{ value_dict: {"0": [0, 1, 3],
"1": [0, 10, 40]
},
type: "scatter",
......@@ -153,17 +166,10 @@
"1_2": {"title": "y axis label for second data set", "position": "right"}
},
title: "Title for my global graph"}
});
}
}
</code></dd>
</dl>
</dl>
<dl>
<dt>updateConfiguration</dt>
<dd>update Configuration of graph (implies redrawing)</dd>
<dl>
<dt data-parameter-required="required" data-parameter-type="object">configuration_dict</dt>
<dd>same format as the configuration_dict passed to render</dd>
</dl>
</dl>
</body>
</html>
\ No newline at end of file
......@@ -24,6 +24,9 @@
type = 'bar',
x_label, y_label;
if (configuration_dict.constructor === String) {
configuration_dict = JSON.parse(configuration_dict);
}
/* title seems to be ignored by Chart.js, so do not handle it for now */
......@@ -91,7 +94,7 @@
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (configuration_dict) {
.declareMethod('render', function (option_dict) {
var gadget = this,
......@@ -100,28 +103,11 @@
chart;
container = gadget.element.querySelector(".graph-content");
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(option_dict.value);
console.log("graph_data_and_parameter", graph_data_and_parameter);
chart = new Chart(container, graph_data_and_parameter);
gadget.property_dict.chart = chart;
})
.declareMethod('updateConfiguration', function (configuration_dict) {
/* Update the graph with new data/configuration.
Chart.js support update of data, so you update values, it should nicely retransform the graph
with new values. However, it does sounds to works well when everything is changed (like more
lines, or deep reconfiguration). So just fully redraw a graph instead. This might be improved
later.
*/
var gadget = this,
graph_data_and_parameter,
container = gadget.element.querySelector(".graph-content"),
chart;
gadget.property_dict.chart.destroy();
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
chart = new Chart(container, graph_data_and_parameter);
gadget.property_dict.chart = chart;
});
......
......@@ -27,6 +27,11 @@
serie_value_list = [],
serie_value_mapping = {},
i, j;
if (configuration_dict.constructor === String) {
configuration_dict = JSON.parse(configuration_dict);
}
axis_dict = layout.axis_dict || {};
x_label = (axis_dict[0] || {}).title;
y_label = (axis_dict[1] || {}).title;
......@@ -142,24 +147,17 @@
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (configuration_dict) {
.declareMethod('render', function (option_dict) {
var gadget = this,
container,
graph_data_and_parameter;
container = gadget.element.querySelector(".graph-content");
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(option_dict.value);
gadget.property_dict.graph = new Dygraph(container,
graph_data_and_parameter.dygraph_data,
graph_data_and_parameter.dygraph_parameter_dict);
})
.declareMethod('updateConfiguration', function (configuration_dict) {
var gadget = this,
graph_data_and_parameter;
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
gadget.property_dict.graph.updateOptions({'file': graph_data_and_parameter.dygraph_data});
});
......
......@@ -17,9 +17,15 @@
i, j, AXIS_MAPPING_DICT,
mapped_axis_list,
axis_list_dict = {};
AXIS_MAPPING_DICT = {0: "x",
1: "y",
2: "z"};
if (configuration_dict.constructor === String) {
configuration_dict = JSON.parse(configuration_dict);
}
console.log("getGraphDataAndParameterFromConfiguration 1");
graph_data_and_parameter.data_list = [];
graph_data_and_parameter.layout = {modeBarButtonsToRemove: ['sendDataToCloud']};
......@@ -115,35 +121,30 @@
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (configuration_dict) {
.declareMethod('render', function (option_dict) {
var gadget = this,
container,
graph_data_and_parameter,
chart;
chart,
graph_value;
container = gadget.element.querySelector(".graph-content");
gadget.property_dict.container = container;
console.log("container inside iframe");
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
graph_value = option_dict.value;
if (graph_value.constructor === String) {
graph_value = JSON.parse(graph_value);
}
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(graph_value);
console.log("graph_data_and_parameter", graph_data_and_parameter);
if (gadget.property_dict.already_rendered === true) {
Plotly.purge(container);
}
Plotly.plot(container, graph_data_and_parameter.data_list, graph_data_and_parameter.layout, graph_data_and_parameter.bar_config);
gadget.property_dict.already_rendered = true;
})
.declareMethod('updateConfiguration', function (configuration_dict) {
/* Update the graph with new data/configuration.
There is many functions in Plotly for updating style, adding points to data, for adding new series of data, etc.
Though, this is way too complex to guess what has been changed to know if we could just call a few functions to
take into account changes. Therefore just erase and redraw the whole graph.
*/
var gadget = this,
graph_data_and_parameter;
console.log("updateConfiguration");
graph_data_and_parameter = getGraphDataAndParameterFromConfiguration(configuration_dict);
Plotly.purge(gadget.property_dict.container);
Plotly.plot(gadget.property_dict.container, graph_data_and_parameter.data_list, graph_data_and_parameter.layout, graph_data_and_parameter.bar_config);
});
}(window, rJS, RSVP, Plotly));
......
......@@ -61,8 +61,8 @@
}
}
gadget.property_dict.graph_data_dict.data = graph_data_list;
return gadget.property_dict.graph_widget.updateConfiguration(
gadget.property_dict.graph_data_dict);
return gadget.property_dict.graph_widget.render(
{value: gadget.property_dict.graph_data_dict});
});
}
};
......
import json
data = {"data": [{ "value_dict": {0: ["2016-02-01", "2016-02-02", "2016-02-03", "2016-02-04"],
1: [0, 1, 3, 2]},
"type": "line",
"title": "Value"
},
{ "value_dict": {0: ["2016-02-01", "2016-02-02", "2016-02-03", "2016-02-04"],
1: [1, 2, 4, 3]},
"type": "line",
"title": "Value2"
}],
"layout": {"axis_dict" : {0: {"title": "date", "value_type": "date"},
1: {"title": "value"}
},
"title": "Simple Graph"}
}
return json.dumps(data)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Foo_getSimpleGraphData</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -11,8 +11,6 @@
<value>
<list>
<string>default</string>
<string>editable</string>
<string>enabled</string>
<string>gadget_url</string>
<string>title</string>
</list>
......@@ -58,10 +56,6 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
......@@ -91,17 +85,9 @@
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
<value> <string>my_iframe_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -129,7 +115,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: {\'graph_gadget\': \'unsafe/gadget_officejs_widget_graph_chart.html\'}</string> </value>
<value> <string>python: here.Foo_getSimpleGraphData()</string> </value>
</item>
</dictionary>
</pickle>
......@@ -142,7 +128,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python:field.restrictedTraverse(\'gadget_erp5_foo_simple_graph.html\').absolute_url()</string> </value>
<value> <string>python: "unsafe/gadget_officejs_widget_graph_chart.html"</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -11,8 +11,6 @@
<value>
<list>
<string>default</string>
<string>editable</string>
<string>enabled</string>
<string>gadget_url</string>
<string>title</string>
</list>
......@@ -58,10 +56,6 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
......@@ -91,17 +85,9 @@
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
<value> <string>my_iframe_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -129,7 +115,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: {\'graph_gadget\': \'unsafe/gadget_officejs_widget_graph_dygraph.html\'}</string> </value>
<value> <string>python: here.Foo_getSimpleGraphData()</string> </value>
</item>
</dictionary>
</pickle>
......@@ -142,7 +128,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python:field.restrictedTraverse(\'gadget_erp5_foo_simple_graph.html\').absolute_url()</string> </value>
<value> <string>python: "unsafe/gadget_officejs_widget_graph_dygraph.html"</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -11,8 +11,6 @@
<value>
<list>
<string>default</string>
<string>editable</string>
<string>enabled</string>
<string>gadget_url</string>
<string>title</string>
</list>
......@@ -87,17 +85,9 @@
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
<value> <string>my_iframe_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -125,7 +115,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: {\'graph_gadget\': \'unsafe/gadget_officejs_widget_graph_plotly.html\'}</string> </value>
<value> <string>python: here.Foo_getSimpleGraphData()</string> </value>
</item>
</dictionary>
</pickle>
......@@ -138,7 +128,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python:field.restrictedTraverse(\'gadget_erp5_foo_simple_graph.html\').absolute_url()</string> </value>
<value> <string>python: "unsafe/gadget_officejs_widget_graph_plotly.html"</string> </value>
</item>
</dictionary>
</pickle>
......
<!DOCTYPE html>
<html>
<head>
<!--
data-i18n=No records
data-i18n=Records
-->
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>ERP5 Test Result Performance</title>
<!-- renderjs -->
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<!-- custom script -->
<script src="gadget_erp5_foo_simple_graph.js" type="text/javascript"></script>
<body>
<div class="document-content">
</div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_erp5_foo_simple_graph.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*jslint indent: 2, nomen: true */
/*global window, rJS, RSVP, console */
(function (window, rJS, RSVP) {
"use strict";
rJS(window)
.ready(function (gadget) {
gadget.property_dict = {};
return gadget.getElement()
.push(function (element) {
gadget.property_dict.element = element;
gadget.property_dict.deferred = RSVP.defer();
});
})
//////////////////////////////////////////////
// acquired method
//////////////////////////////////////////////
//////////////////////////////////////////////
// initialize the gadget content
//////////////////////////////////////////////
.declareMethod("render", function (option_dict) {
console.log("foo_simple_graph, render, options", option_dict);
var gadget = this;
gadget.property_dict.option_dict = option_dict.value;
gadget.renderGraph(); //Launched as service, not blocking
})
.declareJob("renderGraph", function () {
var gadget = this,
option_dict = gadget.property_dict.option_dict;
return gadget.declareGadget(
option_dict.graph_gadget,
{scope: "graph",
sandbox: "iframe",
element: gadget.property_dict.element.querySelector(".document-content")
})
.push(function (graph_gadget) {
gadget.property_dict.graph_widget = graph_gadget;
return graph_gadget.render(
{data: [{ value_dict: {0: [new Date("2016-02-01"), new Date("2016-02-02"), new Date("2016-02-03"), new Date("2016-02-04")],
1: [0, 1, 3, 2]},
type: "line",
title: "Value"
},
{ value_dict: {0: [new Date("2016-02-01"), new Date("2016-02-02"), new Date("2016-02-03"), new Date("2016-02-04")],
1: [1, 2, 4, 3]},
type: "line",
title: "Value2"
}],
layout: {axis_dict : {0: {"title": "date"},
1: {"title": "value"}
},
title: "Simple Graph"}
});
});
});
}(window, rJS, RSVP));
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_erp5_foo_simple_graph.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment