Commit aca48336 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Add the class StoreForYear2DView and first tentative to produce graph.

parent 5766e3d0
......@@ -16,9 +16,38 @@ from plugin_dbui import Store
from reporting_tools import (Report,
StoreForListView,
StoreForMetric1DView,
StoreForMetric2DView)
StoreForMetric2DView,
StoreForYear2DView)
def graph():
"""Plot 2D metric as a Ext.char.series.Area
"""
metric = db.metrics[request.vars.id_metrics]
selector = virtdb.metric_selector_2D
report = Report(selector, exclude_fields=('category',
'metric',
'period_end',
'period_start',
'year'))
# configure the Ext.data.Store
if metric.field_vertical == 'year':
store = StoreForYear2DView(metric, report)
else:
store = StoreForMetric2DView(metric, report)
# Extra the stack keys (value for metric.field_vertical)
stack_keys = store.values_h
# delegate the rendering of the graph to the view
response.view = 'report/graph.html'
return dict(cfg_store=store.as_json(), stack_keys=stack_keys, view=metric)
def list():
"""List renders by an Ext.grid.Panel.
......@@ -146,7 +175,11 @@ def metric2D():
'year'))
# configure the Ext.data.Store
store = StoreForMetric2DView(metric, report)
if metric.field_vertical == 'year':
store = StoreForYear2DView(metric, report)
else:
store = StoreForMetric2DView(metric, report)
# customize the standard view (Ext.grid.Panel)
view = Storage(columns=[], features=[], title=metric.title)
......
# -*- coding: utf-8 -*-
""" graph_selector
"""
virtdb.define_table('graph_selector',
Field('period_start', 'date', default='2012-01-01'),
Field('period_end', 'date', default='%s-12-31' % year),
Field('id_teams', 'reference teams', label=T('Team')),
Field('category', 'string'),
Field('id_people_categories', 'reference people_categories', label= T("Quality")),
Field('id_projects', 'reference projects', label=T('Project')),
Field('metric', 'string', default='sum_fte'))
virtdb.graph_selector.category.requires = IS_IN_SET(PEOPLE_CATEGORIES)
virtdb.graph_selector.id_people_categories.requires = IS_IN_DB(db, 'people_categories.code')
virtdb.graph_selector.id_projects.requires = IS_IN_DB(db, 'projects.project')
virtdb.graph_selector.id_teams.requires = IS_IN_DB(db, 'teams.team')
virtdb.graph_selector.metric.requires = IS_IN_SET(('count', 'sum_fte'))
#-------------------------------------------------------------------------------
#
# FIELDS CONFIGURATiON
#
#-------------------------------------------------------------------------------
fieldsModifier = dbui.FieldsModifier('graph_selector')
fieldsModifier.configure_field('period_start', flex=1)
fieldsModifier.configure_field('period_end', flex=1)
fieldsModifier.merge_fields('period_start', 'period_end', fieldLabel=T('Period'))
mytype = 'xcomboboxuserreset'
text = T('select...')
fieldsModifier.configure_field('id_people_categories', emptyText=text, xtype=mytype)
fieldsModifier.configure_field('id_teams', emptyText=text, xtype=mytype)
fieldsModifier.configure_field('id_projects', emptyText=text, xtype=mytype)
#-------------------------------------------------------------------------------
#
# FORM CONFIGURATiON
#
#-------------------------------------------------------------------------------
......@@ -124,10 +124,22 @@ for row in db(db.metrics.id > 0).select(orderby=db.metrics.metric):
#-------------------------------------------------------------------------------
#
# GRAPH
# Define by the 2D metrics
#
#-------------------------------------------------------------------------------
graphNode = Node(T('The graphs'))
for row in db(db.metrics.id > 0).select(orderby=db.metrics.metric):
if not row.field_horizontal:
continue
leaf = PanelWithUrlSelector(virtdb.graph_selector,
baseUrl=URL('report', 'graph'),
baseParams={'id_metrics': row.id})
graphNode.add_child(row.metric, leaf)
#-------------------------------------------------------------------------------
#
# VIEWPORT
......
......@@ -275,6 +275,76 @@ class Report(SelectorActiveItems):
return data
def get_metric_per_year(self, groupby, metric):
"""Compute the metric per year:
- history records follow the selector criteria.
- history record are grouped by year
The metric is:
- the number of items in a group
- the sum of fte for the items in the group
- the average age for the items in the group
@type grouby: str
@param groupby: database field encoded as C{tablename.fieldname}.
@type metric: str
@param metric: the metric to be used either count or sum_fte.
@rtype: list of dictionary
@return: the keys of the dictionary are C{group} and one
entrie per value of the groupby field. The value of the
group key is the year.
"""
db = current.globalenv['db']
# save the period parameters
ref_start = self.period_start
ref_end = self.period_end
# the vertical axis, the range of years
years = range(ref_start.year, ref_end.year + 1)
# the horizontal axis, the group by values
tablename, fieldname = groupby.split('.')
field = db[tablename][fieldname]
rows = db().select(field, distinct=True)
values_h = [row[fieldname] for row in rows]
# prepare a template dictionary for each store row
tpl_row_h = dict.fromkeys(values_h, '')
# scan the database
# data contains a list of dictionary
# the keys are group and one entries per value of the group by field
data = []
for year in years:
self.period_start = date(year, 1, 1)
self.period_end = date(year, 12, 31)
di = dict(group=year)
di.update(tpl_row_h)
for el in self.get_metric(groupby):
if metric == 'count':
di[el['group']] = el['count']
elif metric == 'sum_fte':
di[el['group']] = el['sum_fte']
data.append(di)
# restore the period parameters
self.perios_start = ref_start
self.period_end = ref_end
return data
def query(self, table):
"""Supersede the base class method to handle category constraints.
......@@ -335,15 +405,15 @@ class StoreFactory(object):
class StoreFactoryException(BaseException): pass
class StoreForListView(StoreFactory):
"""Generate the Ext.data.Store configuration for the list view.
@note: The name of the Ext.data.Field is extract from the column
definition of the list where it is equal to table.dbfield or
table.dbfield.key. The dot is remove in the Ext.data.Field name.
Therefore the dataIndex used in the grid column configuration
has to be modified accordingly/
@note: The name of the Ext.data.Field is extract from the column
definition of the list where it is equal to table.dbfield or
table.dbfield.key. The dot is remove in the Ext.data.Field name.
Therefore the dataIndex used in the grid column configuration
has to be modified accordingly/
"""
def __init__(self, list, rows):
......@@ -636,4 +706,67 @@ class StoreForMetric2DView(StoreFactory):
# build the tmp_di block of the store
for k in tmp_di.iterkeys():
self.cfg_store.data.append(tmp_di[k])
class StoreForYear2DView(StoreFactory):
def __init__(self, metric, report):
"""
@type metric: gluon.dal.objects.Row
@param metric: the definition of the metric
@type report: Report
@param report:
"""
db = current.globalenv['db']
StoreFactory.__init__(self)
# get value along the horizontal axis
tablename, fieldname = metric.field_horizontal.split('.')
field = db[tablename][fieldname]
rows = db().select(field, distinct=True)
self.values_h = [row[fieldname] for row in rows]
self._get_fields()
self._get_data(report, metric.field_horizontal, report.metric)
def _get_fields(self):
""" Generate the Ext.data.Store.fields property.
The store contains the field group for the year and
one field per value of the horizontal database field.
Data are grouped per the group field values.
"""
self.cfg_store.fields = [dict(name='group', type='string')]
for value in self.values_h:
self.cfg_store.fields.append(dict(name=str(value), type='float'))
self.cfg_store.sorters = ['group']
def _get_data(self, report, groupby, metric):
""" Generate the Ext.data.Store.data property.
It is a list of dictionaries. Each of them contains the data
for one row. One key, value pair for each Ext.data.Field.
The key is equal to the Ext.data.Field name.
@type report: Report
@param report:
@type groupby: str
@param groupby: the database field use for horizontal axis
@type metric: str
@param metric: the metric to be used either count or sum_fte.
"""
self.cfg_store.data.extend(report.get_metric_per_year(groupby, metric))
\ No newline at end of file
{{
#--------------------------------------------------------------------------
#
# The python controller return the variables:
# - cfg_store (string)
# - stack_keys (list)
# - view (Storage)
#
#--------------------------------------------------------------------------
#
# prepare the data
# - build unique DIV identifier
# - Title and DIV block
#
#--------------------------------------------------------------------------
import json
#
# unique identifier for the DIV block associated to the grid
#
divchart = "chart-%s" % id(cfg_store)
response.write(H2(view.title, _class="dbui-h2 dbui-small-cap"))
response.write(DIV(_id=divchart))
#
# Export python variables to the javascript
#
jsvars = ["axes",
"cfgStore = %s" % cfg_store,
"chart",
"divchart = '%s'" % divchart,
"series",
"seriesKeys = %s" % json.dumps(stack_keys)]
jsvars = " var %s;" % ',\n'.join(jsvars)
response.write(SCRIPT(jsvars), escape=False)
}}
<script>
console.log(1, seriesKeys);
console.log(2, cfgStore);
// the axes
axes = [{
type: 'Numeric',
fields: seriesKeys,
grid: true,
minimum: 0,
minorTickSteps: 1,
position: 'left'
}, {
type: 'Category',
fields: ['group'],
grid: true,
label: {
rotate: {
degrees: 270
}
},
position: 'bottom'
}];
// how to display the series
series = [{
type: 'area',
//type: 'column',
style: {
opacity: 0.8
},
xField: 'group',
yField: seriesKeys
}];
// instantiate the chart
chart = Ext.create('Ext.chart.Chart', {
axes: axes,
legend: {
labelFont: '8px Helvetica, sans-serif',
position: 'right'
},
series: series,
store: cfgStore,
width: 600,
height: 400,
renderTo: divchart
});
// inhibit context menu on the rest of the panel
chart.getEl().up('.x-panel-body').on('contextmenu', function(event) {
event.stopEvent();
});
</script>
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