Commit 963cc543 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Add the StorageFactory and StorageForList class.

parent 4430ad94
......@@ -6,12 +6,14 @@
- the content of the report is defined in the database
"""
import json
import re
from datetime import date, timedelta
from gluon.dal import smart_query
from gluon.storage import Storage
from reporting_tools import Report, repr_duration, to_dataFields
from plugin_dbui import Store
from reporting_tools import Report, repr_duration, StoreForListView
def list():
......@@ -27,64 +29,30 @@ def list():
"""
# alias
now = date.today()
list = db.lists[request.vars.id_lists]
report = Report(virtdb.list_selector, exclude_fields=('category',
'period_end',
'period_start',
'year'))
# apply filter condition from the list
# apply the list condition
if list.conditions:
q_conditions = smart_query(db.history, list.conditions)
report.append_query(q_conditions)
# configure the Ext.data.ArrayStore
cfg = dict()
cfg['data'] = []
cfg['fields'] = to_dataFields(list.columns)
if list.group_field:
cfg['groupField'] = list.group_field
# build the configuration for the Ext.data.Store
store = StoreForListView(list, report.select(db.history))
if list.sorters:
cfg['sorters'] = list.sorters
# encode the Ext.grid.column.dataIndex to match the Ext.data.Field name
s = list.columns.replace("\n", "").replace(" ", "")
s = re.sub("dataIndex:'(\w+)\.(\w+)'", "dataIndex:'\\1\\2'", s)
s = re.sub("dataIndex:'(\w+)\.(\w+)\.(\w+)'", "dataIndex:'\\1\\2\\3'", s)
list.columns = s
# fill the data block
# It is mandatory to preserve the data fields order
for row in report.select(db.history):
li = []
for field in cfg['fields']:
# standard database type
if field['name'].count('.') == 1:
value = row[field['name']]
# json database type
elif field['name'].count('.') == 2:
i = field['name'].rindex(".")
fieldname = field['name'][:i]
key = field['name'][i+1:]
if key in row[fieldname]:
value = row[fieldname][key]
else:
value = ""
if isinstance(value, date):
value = value.strftime("%Y-%m-%d")
elif isinstance(value, timedelta):
value = value.total_seconds()
li.append(value)
cfg['data'].append(li)
# delegate the grid rendering to the view
response.view = 'report/grid.html'
return dict(cfg_store=cfg, view=list)
return dict(cfg_store=store.as_json(), view=list)
def metric1D():
......
......@@ -8,7 +8,8 @@ import re
from datetime import date, timedelta
from gluon import current
from gluon.dal import Field
from plugin_dbui import Selector
from gluon.storage import Storage
from plugin_dbui import Selector, Store
def repr_duration(value):
......@@ -35,71 +36,6 @@ def repr_duration(value):
return ''
def to_dataFields(cfg_columns):
"""Convert the cfg_columns configuration for and Ext.grid.Panel
into a fields configuration for an Ext.data.ArrayStore.
@type cfg_columns: str
@param cfg_columns: a list of Ext.grid.column.Column configuration
encoded into a string.
@rtype: list of dictionary
@return: a list of Ext.data.Field configuration
"""
li = []
db = current.globalenv['db']
# extract the list of database field names from the column configuration
columns = re.findall("dataIndex *: *'([a-z_\.]+)'", cfg_columns)
# convert the database field into and Ext.data.Field
for column in columns:
cfg = dict()
cfg['name'] = column
# standard field
if column.count(".") == 1:
tablename, fieldname = column.split('.')
field = db[tablename][fieldname]
# json type
elif column.count(".") == 2:
i = column.rindex(".")
tablename, fieldname = column[:i].split('.')
field = db[tablename][fieldname]
cfg['type'] = field.type
if field.type in ('string', 'text', 'json'):
cfg['type'] = 'string'
elif field.type == 'date':
cfg['type'] = 'date'
cfg['dateFormat'] = 'Y-m-d'
elif field.type == 'datetime':
cfg['type'] = 'date'
cfg['dateFormat'] = 'Y-m-d H:i:s'
elif field.type == 'double':
cfg['type'] = 'float'
elif field.type == 'integer':
cfg['type'] = 'int'
elif field.type == 'time':
cfg['type'] = 'date'
cfg['dateFormat'] = 'H:i:s'
else:
cfg['type'] = field.type
li.append(cfg)
return li
class SelectorActiveItems(Selector):
"""Selector to get records active during a given period of time.
......@@ -389,3 +325,182 @@ class Report(SelectorActiveItems):
def reset_extra_queries(self):
self._extra_queries = []
class StoreFactory(object):
"""Base factory to generate the Ext.data.Store configuration for
the list, metrics and graphs view.
"""
def __init__(self):
self.cfg_store = Store(data=[], fields=[])
def as_dict(self):
"""Get the store configuration as a dict.
@rtype: dict
@return: the Ext.data.Store configuration.
"""
return dict(self.cfg_store)
def as_json(self):
"""Get the store configuration as a JSON string.
@rtype: str
@return: the Ext.data.Store configuration as a JSON string.
"""
return json.dumps(self.cfg_store)
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/
"""
def __init__(self, list, rows):
"""
@type list: gluon.dal.Row
@param list: parameters defining the list.
@type rows: gluon.dal.Rows
@param rows: data to be loaded on the store.
"""
StoreFactory.__init__(self)
self._map = dict()
self._get_fields(list)
self._get_data(rows)
def _get_fields(self, list):
""" Generate the Ext.data.Store.fields property.
@type list: gluon.dal.Row
@param list: parameters defining the list.
@note: The name of the Ext.data.Field is extract from the column
definition of the list. It is encoded as table.dbfield or
table.dbfield.key. the latter is for JSON type dbfield. It can not be
used as is and has to be converted as table.dbfield[key].
@note: The name of the store Ext.data.dbfield can not contains dot.
Therefore, the name of the database dbfield is converted by
removing dots.
@rtype: list of Ext.data.Field configuration
@return: list of dictionaries configuring Ext.data.Field.
"""
db = current.globalenv['db']
# extract the list of database dbfield names from the list definition
columns = re.findall("dataIndex *: *'([a-z_\.]+)'", list.columns)
# convert the database dbfield into and Ext.data.Field
for column in columns:
name_items = column.split('.')
name = ''.join(name_items)
# get the database dbfield link to the column.
# keep a map between the column in the store and the
# database dbfield for a later use when retrieving the data.
if len(name_items) == 2:
tablename, fieldname = name_items
keyname = None
elif len(name_items) == 3:
tablename, fieldname, keyname = name_items
else:
raise StoreFactoryException("Can't decode column name.")
dbfield = db[tablename][fieldname]
self._map[name] = (tablename, fieldname, keyname)
# build the configuration for the Ext.data.Field
cfg = Storage()
cfg.name = name
cfg.type = dbfield.type
if dbfield.type in ('string', 'text', 'json'):
cfg.type = 'string'
elif dbfield.type == 'date':
cfg.type = 'date'
cfg.dateFormat = 'Y-m-d'
elif dbfield.type == 'datetime':
cfg.type = 'date'
cfg.dateFormat = 'Y-m-d H:i:s'
elif dbfield.type == 'double':
cfg.type = 'float'
elif dbfield.type == 'integer':
cfg.type = 'int'
elif dbfield.type == 'time':
cfg.type = 'date'
cfg.dateFormat = 'H:i:s'
self.cfg_store.fields.append(cfg)
def _get_data(self, rows):
""" Generate the Ext.data.Store.data property.
@type rows: gluon.dal.Rows
@param rows: data block to be loaded on the store.
@rtype: list of dictionary
@return: each dictionary contains the data for a row.
One key, value pair for each Ext.data.Field. The key is
equal to the Ext.data.Field name.
"""
for row in rows:
di = dict()
for field in self.cfg_store.fields:
tablename, fieldname, keyname = self._map[field.name]
# get the value for a standard database field
if keyname == None:
value = row[tablename][fieldname]
# get the value for a JSON database field
else:
if keyname in row[tablename][fieldname]:
value = row[tablename][fieldname][keyname]
else:
value = ""
# encode date
if isinstance(value, date):
value = value.strftime("%Y-%m-%d")
# encode time delta
elif isinstance(value, timedelta):
value = value.total_seconds()
di[field.name] = value
self.cfg_store.data.append(di)
......@@ -16,16 +16,17 @@
#
# The title and the DIV block
#
divgrid = id(cfg_store)
divgrid = "grid-%s" % id(cfg_store)
response.write(H2(view.title, _class="dbui-h2 dbui-small-cap"))
response.write(DIV(_id="grid-%s" % divgrid))
response.write(DIV(_id=divgrid))
#
# Export python variables to the javascript
#
jsvars = ["cfgStore = %s" % json.dumps(cfg_store),
jsvars = ["cfgStore = %s" % cfg_store,
"columns = %s" % view.columns,
"divgrid = '%s'" % divgrid,
"features = %s" % view.features]
jsvars = " var %s;" % ',\n'.join(jsvars)
......@@ -41,12 +42,12 @@
//
grid = Ext.create('App.grid.Panel', {
plugins: ['pGridExport'],
store: Ext.create('Ext.data.ArrayStore', cfgStore),
store: cfgStore,
columns: columns,
features: features,
forceFit: true,
padding: "10 40 20 30",
renderTo: 'grid-{{=divgrid}}'
renderTo: divgrid
});
</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