Commit 0f1699fd authored by Sebastien Robin's avatar Sebastien Robin

erp5_test_result: add a javascript graph on performance tests

parent b17b78cd
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view_performance_graph</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>3.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Performance Graph</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/TestResult_viewPerformanceGraph</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: \'PERF\' in (here.getTitle() or \'\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from Products.ERP5Type.Document import newTempBase
from Products.ZSQLCatalog.SQLCatalog import ComplexQuery, SimpleQuery
#from Products.ERP5Type.Document import newTempBase
#from Products.ZSQLCatalog.SQLCatalog import ComplexQuery, SimpleQuery
from DateTime import DateTime
"""
rev_query_list = []
if isinstance(kw.get('from_date'), DateTime):
rev_query_list.append(SimpleQuery(creation_date=kw['from_date'],
......@@ -9,23 +11,25 @@ if isinstance(kw.get('from_date'), DateTime):
if isinstance(kw.get('at_date'), DateTime):
rev_query_list.append(SimpleQuery(creation_date=kw['at_date'],
comparison_operator='<='))
"""
test_result_list = []
revision = None
new_test_result_list = []
context.log("rev_query_list", rev_query_list)
if rev_query_list:
result = context.searchFolder(title='PERF-ERP5-MASTER', simulation_state='stopped',
revision=ComplexQuery(logical_operator='AND', *rev_query_list),
sort_on=(('delivery.start_date', 'ASC'),),src__=1)
context.log("result", result)
for test in context.searchFolder(title='PERF-ERP5-MASTER', simulation_state='stopped',
revision=ComplexQuery(logical_operator='AND', *rev_query_list),
portal = context.getPortalObject()
if query:
for test in portal.test_result_module.searchFolder(title='PERF-ERP5-MASTER', simulation_state='stopped',
full_text=query,
sort_on=(('delivery.start_date', 'ASC'),)):
test = test.getObject()
if revision != test.getReference():
revision = test.getReference()
test_result = {'rev': str(revision)}
revision_list = []
for revision_part in revision.split(','):
repository, commit_hash = revision_part.split('-')
revision_list.append('%s-%s' % (repository, commit_hash[0:8]))
revision = ",".join(revision_list)
test_result = {'revision': str(revision)}
test_result_list.append(test_result)
for prop in 'all_tests', 'failures', 'errors':
test_result[prop] = test_result.get(prop, 0) + test.getProperty(prop, 0)
......@@ -35,17 +39,23 @@ if rev_query_list:
for k, v in line.items():
timing_dict.setdefault(k, []).append(v)
normalize = kw.get('normalize')
normalize = kw.get('normalize', 1)
base_result = {}
for test_result in test_result_list:
if test_result['errors'] < test_result['all_tests']:
new_test_result = newTempBase(context, '')
new_test_result = {}
for k, v in test_result.pop('timing_dict').items():
if v:
v = sum(v) / len(v)
test_result[k] = v / base_result.setdefault(k, normalize and v or 1)
new_test_result.edit(**test_result)
# too much value is not productive
if k in ('all_tests', 'errors', 'failures') or k.find('_') > 0 and k.split('_')[1] in ('200', '300', '400', '500', '600', '700', '800', '900'):
continue
new_test_result[k] = v / base_result.setdefault(k, normalize and v or 1)
new_test_result['revision'] = test_result['revision']
new_test_result.update(**new_test_result)
new_test_result['_links'] = {'self': {}} # required by jio.allDocs API
new_test_result_list.append(new_test_result)
return new_test_result_list
import json
context.log("new_test_result_list", new_test_result_list)
return json.dumps({'_embedded': {'contents':new_test_result_list}})
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>*args, **kw</string> </value>
<value> <string>test_title=None, from_date=None, to_date=None, query=None, title=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
from base64 import urlsafe_b64encode
import json
list_method_template = ""
# list_method_template is a hack until better API is found.
# it allows to make jio.all_docs calling a python script
# http://10.0.80.187:2200/erp5/web_site_module/renderjs_runner/hateoas/ERP5Document_getHateoas
portal = context.getPortalObject()
custom_search_template_no_editable = "%(root_url)s/%(script_id)s?mode=search" + \
"&relative_url=%(relative_url)s" \
"&list_method=%(list_method)s" \
"&default_param_json=%(default_param_json)s" \
"&test_title=%(test_title)s" \
"{&query,select_list*,limit*,sort_on*,local_roles*}"
list_method_template = custom_search_template_no_editable % {
"root_url": portal.absolute_url(),
"script_id": "TestResultModule_getTestPerfResultList",
"relative_url": context.getRelativeUrl().replace("/", "%2F"),
"list_method": "TestResultModule_getTestPerfResultList",
"default_param_json": urlsafe_b64encode(json.dumps({'test_title': context.getTitle()})),
"test_title": context.getTitle()
}
return {'list_method_template': list_method_template}
<?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>TestResult_getPerformanceGraphGadgetConfigurationDict</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<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/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>performance_graph_gadget</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>TestResult_viewPerformanceGraph</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>TestResult_view</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Performance Graph</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>default</string>
<string>editable</string>
<string>gadget_url</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>performance_graph_gadget</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<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>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<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>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Performance Graph</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: here.TestResult_getPerformanceGraphGadgetConfigurationDict()</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python:field.restrictedTraverse(\'test_result_js/gadget_erp5_test_result_performance_graph.html\').absolute_url()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test_result_js</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!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_test_result_performance_graph.js" type="text/javascript"></script>
<body>
<div class="ui-field-contain" >
<label data-i18n="From Date:">From Date:</label>
<input type="date" name="from_date" title="From Date">
</div>
<div class="ui-field-contain" >
<label data-i18n="At Date:">At Date:</label>
<input type="date" name="at_date" title="At Date">
</div>
<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_test_result_performance_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, loopEventListener */
(function (window, rJS, RSVP, loopEventListener) {
"use strict";
var WIDGET_GRAPH_URL = "../gadget_officejs_widget_graph_chart.html";
function getDateAsString(date) {
var date_string = "" + date.getFullYear() + "-",
month = date.getMonth() + 1,
day = date.getDate();
if (month < 10) {
date_string = date_string + "0";
}
date_string = date_string + month + "-";
if (day < 10) {
date_string = date_string + "0";
}
date_string = date_string + day;
return date_string;
}
function getCheckDateAndRefreshGraphFunction(gadget) {
return function checkDateAndRefreshGraph() {
var from_date, at_date, i;
from_date = gadget.property_dict.element.querySelector('[name="from_date"]').value;
at_date = gadget.property_dict.element.querySelector('[name="at_date"]').value;
if (at_date !== "" && from_date !== "") {
console.log("will need to get data");
return gadget.jio_allDocs({
"list_method_template": gadget.property_dict.option_dict.list_method_template,
"query": '( delivery.start_date: >= "' + from_date + '" AND delivery.start_date: < "' + at_date + '" )',
"limit": [],
"select_list": [],
"sort_on": []
})
.push(function (data_list) {
console.log('data_list', data_list);
var graph_data_list = [],
revision, line_list = data_list.data.rows, value, i, j,
property_list = [];
if (line_list.length !== 0) {
Object.keys(line_list[0].value).forEach(function (property) {
if (property !== "revision") {
property_list.push(property);
}
});
if (property_list.length !== 0) {
property_list.forEach(function (property) {
var i, data = {};
console.log("property", property);
data.type = "line";
data.title = property;
data.value_dict = {0: [], 1: []};
for (i = 0 ; i < line_list.length; i = i + 1) {
data.value_dict[0].push(line_list[i].value.revision);
data.value_dict[1].push(line_list[i].value[property]);
}
graph_data_list.push(data);
});
}
}
gadget.property_dict.graph_data_dict.data = graph_data_list;
return gadget.property_dict.graph_widget.updateConfiguration(
gadget.property_dict.graph_data_dict);
});
}
};
}
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
//////////////////////////////////////////////
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
//////////////////////////////////////////////
// initialize the gadget content
//////////////////////////////////////////////
.declareMethod("render", function (option_dict) {
console.log("gadget_erp5_test_result_performance_graph, render, options", option_dict);
var gadget = this;
gadget.property_dict.option_dict = option_dict.value;
return new RSVP.Queue()
.push(function () {
return gadget.property_dict.deferred.resolve();
});
})
/////////////////////////////////////////
// Render text content gadget
/////////////////////////////////////////
.declareService(function () {
var gadget = this,
graph_gadget = null;
return new RSVP.Queue()
.push(function () {
return gadget.property_dict.deferred.promise;
})
.push(function () {
return RSVP.all([
gadget.declareGadget(
WIDGET_GRAPH_URL,
{
scope: "graph",
element: gadget.property_dict.element.querySelector(".document-content")
})
]);
})
.push(function (result) {
graph_gadget = result[0];
gadget.property_dict.graph_widget = graph_gadget;
gadget.property_dict.graph_data_dict = {data: [{
value_dict: {0: [],
1: []},
type: "line"
}],
layout: {axis_dict : {0: {"title": "commit"},
1: {"title": "performance"}
},
title: "Test Result Performance"}
};
return graph_gadget.render(
gadget.property_dict.graph_data_dict
);
})
.push(function () {
var now = new Date(), last_month, tomorrow;
last_month = new Date(now.valueOf() - 86400 * 1000 * 30); // - 30 days
tomorrow = new Date(now.valueOf() + 86400 * 1000 * 1); // + 1 day
gadget.property_dict.element.querySelector('[name="from_date"]').value = getDateAsString(last_month);
gadget.property_dict.element.querySelector('[name="at_date"]').value = getDateAsString(tomorrow);
})
.push(function () {
getCheckDateAndRefreshGraphFunction(gadget)();
});
})
.declareService(function () {
var gadget = this;
return loopEventListener(
gadget.property_dict.element.querySelector('[name="from_date"]'),
'change',
false,
function () {
getCheckDateAndRefreshGraphFunction(gadget)();
});
})
.declareService(function () {
var gadget = this;
return loopEventListener(
gadget.property_dict.element.querySelector('[name="at_date"]'),
'change',
false,
function () {
getCheckDateAndRefreshGraphFunction(gadget)();
});
});
}(window, rJS, RSVP, loopEventListener));
<?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_test_result_performance_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>
......@@ -28,6 +28,7 @@ Test Result | jump_viewvc_revision
Test Result | view
Test Result | view_graph
Test Result | view_node_list
Test Result | view_performance_graph
Test Suite Module | view
Test Suite Repository | view
Test Suite | view
\ No newline at end of file
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