Docker-in-Docker (DinD) capabilities of public runners deactivated. More info

Commit 3f63bef0 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Redesign BaseReport and inherited classes.

parent db9f1c72
......@@ -28,25 +28,15 @@ def graph_mpl():
"""Plot list or metric using the matplotlib library
"""
# selector and configuration
try:
ui_table = virtdb.selector
selector = EvtSelector(ui_table)
except SelectorActiveItemsException as e:
return INLINE_ALERT % (T("Error..."), T(str(e)))
config = db.graphs[request.vars.id_graphs]
# build the graph
try:
graph = Graph(config, selector)
graph = Graph(db.graphs, request.vars.id_graphs)
except (IndexError, TypeError, ValueError) as e:
return INLINE_ALERT % ("Error", e)
return INLINE_ALERT % ("GRAPH Error", e)
# build the report title
title = do_title(config, selector)
title = do_title(graph)
# extract the image
# encode special character to be used in the image URI
......@@ -81,39 +71,29 @@ def grid():
The identifier is used to instantiate the proper class.
"""
try:
ui_table = virtdb.selector
selector = EvtSelector(ui_table)
except SelectorActiveItemsException as e:
return INLINE_ALERT % (T("Error..."), T(str(e)))
# extract the report configuration and the build the report
# build the report
try:
if "id_lists" in request.vars:
config = db.lists[request.vars.id_lists]
report = List(config, selector)
report = List(db.lists, request.vars.id_lists)
elif "id_metrics1d" in request.vars:
config = db.metrics1d[request.vars.id_metrics1d]
report = Metric1D(config, selector)
report = Metric1D(db.metrics1d, request.vars.id_metrics1d)
elif "id_metrics2d" in request.vars:
config = db.metrics2d[request.vars.id_metrics2d]
report = Metric2D(config, selector)
report = Metric2D(db.metrics2d, request.vars.id_metrics2d)
else:
return INLINE_ALERT % ("Error", "Report id is not defined.")
except (IndexError, ReportException, TypeError, ValueError) as e:
return INLINE_ALERT % ("JSON Error...", e)
return INLINE_ALERT % ("REPORT Error...", e)
# extract the configurations for the Ext.data.Store and Dbui.grid.Panel.
store = report.to_store()
grid = report.to_grid()
# build the report title
title = do_title(config, selector)
title = do_title(report)
# delegate the grid rendering to the view
return dict(cfg_store=store, grid=grid, title=title)
......
......@@ -13,29 +13,34 @@ from pandas import DataFrame, MultiIndex, to_datetime
from plugin_dbui import get_id, Store
from pydal.helpers.methods import smart_query
from pydal.objects import FieldVirtual
from selector import EvtSelector
from StringIO import StringIO
MSG_NO_DATAINDEX = "The property dataIndex is required when eval is used."
MSG_NO_EVT_ID = "Identifier of the event is not defined."
MSG_NO_XTYPE = "The property xtype is missing."
REG_DBFIELD = re.compile("\w+\.\w+(?:\.\w+)?", re.UNICODE)
REG_EVT_ID = re.compile("history\.id_events *={1,2} *(\d+)")
REG_PYQUERY = re.compile("[\( ]*\w+\.\w+\.\w+")
REG_SINGLE_DBFIELD = re.compile("^ *\w+\.\w+(\.\w+)? *$", re.UNICODE)
def do_title(config, selector):
def do_title(report):
"""Build the report title.
Args:
config (gluon.dal.Row): the list configuration parameter.
selector (EvtSelector): selector handling period of time.
report (BaseReport): the report
Returns:
str:
"""
db = current.globalenv["db"]
db = report.db
config = report.config
selector = report.selector
T = current.T
# from the configuration
......@@ -150,25 +155,36 @@ class BaseReport(object):
"""Base class to build list, metric or graph reports.
Args:
config (gluon.dal.Row): the configuration parameter for the report.
selector (EvtSelector): the selector handling user criteria.
table (gluon.dal.Table): table containing configurations for reports.
id_report (int): identifier of the report in the table.
"""
def __init__(self, config, selector):
def __init__(self, table, id_report):
db = current.globalenv["db"]
db = table._db
self.db = db
self.df = None
self.config = config
self.rows = None
self.selector = selector
# Extract the configuration for the report configuration
config = table[id_report]
# Extract the event identifier located in the condition field
conditions = config.conditions
mtch = REG_EVT_ID.search(conditions)
if mtch is None:
raise ReportException(current.T(MSG_NO_EVT_ID))
id_event = int(mtch.group(1))
# Instantiate the selector
virtdb = current.globalenv["virtdb"]
selector = EvtSelector(virtdb.selector, id_event)
# apply the condition criteria used to filter the history records
# condition can be written as a smart query: history.id_events == 7
# or like a python query: db.events.event == "People"
if "conditions" in config:
condition = config.conditions
# minimal protection to avoid injection flow
# the beginning of the python query should be like:
......@@ -177,14 +193,18 @@ class BaseReport(object):
# ((db.table.field
# ( ( db.table.field
#
if REG_PYQUERY.match(condition):
q_conditions = eval(condition, None, {"db": db})
if REG_PYQUERY.match(conditions):
q_conditions = eval(conditions, None, {"db": db})
else:
q_conditions = smart_query(db.history, config.conditions)
q_conditions = smart_query(db.history, conditions)
selector.append_query(q_conditions)
# keep track of configuration and selector
self.config = config
self.selector = selector
def _do_data(self, maps):
"""Build a temporarily list with the raw data for each series.
This method handle the "year" database field.
......@@ -274,19 +294,23 @@ class Graph(BaseReport):
scater plots, *etc*.
Args:
config (gluon.dal.Row):
the configuration parameter for the graph.
table (gluon.dal.Table):
table containing configurations for reports.
selector (EvtSelector):
selector handling period of time.
id_report (int):
identifier of the report in the table.
backend (str):
the name of the matplotlib backend uses to produce figure.
"""
def __init__(self, config, selector, backend="Agg"):
def __init__(self, table, id_report, backend="Agg"):
BaseReport.__init__(self, config, selector)
self.db = table._db
self.config = config = table[id_report]
self.df = None
self.rows = None
# set the matplotlib back end
#
......@@ -315,18 +339,18 @@ class Graph(BaseReport):
report_type = config.report_type
report_name = config.report_name
report_id = get_id(db[report_type], name=report_name)
report_config = db[report_type][report_id]
if report_type == "lists":
report = List(report_config, selector)
report = List(db.lists, report_id)
elif report_type == "metrics1d":
report = Metric1D(report_config, selector)
report = Metric1D(db.metrics1d, report_id)
elif report_type == "metrics2d":
report = Metric2D(report_config, selector)
report = Metric2D(db.metrics2d, report_id)
self.df = report.to_df()
self.selector = report.selector
# build the graph from the DataFrame
self._do_graph()
......@@ -458,16 +482,16 @@ class List(BaseReport):
Its configuration is returned by the method *to_store*.
Args:
config (gluon.dal.Row): the configuration parameter for the list.
selector (EvtSelector): selector handling period of time.
table (gluon.dal.Table): table containing configurations for reports.
id_report (int): identifier of the report in the table.
"""
def __init__(self, config, selector):
def __init__(self, table, id_report):
BaseReport.__init__(self, config, selector)
BaseReport.__init__(self, table, id_report)
# decode column configuration
columns = [Storage(el) for el in json.loads(config.columns)]
columns = [Storage(el) for el in json.loads(self.config.columns)]
# check column configuration
# add database field map (tablename, fieldname, keyname)
......@@ -789,13 +813,15 @@ class Metric1D(List):
A summary information can also be computed for each column or rows.
Args:
config (gluon.dal.Row): the configuration parameter for the metric.
selector (EvtSelector): selector handling period of time.
table (gluon.dal.Table): table containing configurations for reports.
id_report (int): identifier of the report in the table.
"""
def __init__(self, config, selector):
def __init__(self, table, id_report):
BaseReport.__init__(self, table, id_report)
BaseReport.__init__(self, config, selector)
config = self.config
# group by attributes
field_groupby = config.group_field
......@@ -1001,13 +1027,13 @@ class Metric2D(BaseReport):
In fact, all the computation methods of the ``pandas.DataFrame`` class.
Args:
config (gluon.dal.Row): the configuration parameter for the metric.
selector (EvtSelector): selector handling period of time.
table (gluon.dal.Table): table containing configurations for reports.
id_report (int): identifier of the report in the table.
"""
def __init__(self, config, selector):
def __init__(self, table, id_report):
BaseReport.__init__(self, config, selector)
BaseReport.__init__(self, table, id_report)
self._do_metric()
# replace undefined value by 0
......
......@@ -129,7 +129,7 @@ class EvtSelector(SelectorActiveItems):
"""Main selector to build list, metric or graph according to
the user criteria:
- focus on history record
- focus on history record and on a given event.
- Select record active during the selected period of time.
- Selection can be performed on category defined as a string.
- For each record computes virtual field: ``age``, ``coverage``,
......@@ -144,8 +144,10 @@ class EvtSelector(SelectorActiveItems):
categories, year_end, year_start and year are systematically
excluded.
id_event (int): the event identifier
"""
def __init__(self, table, exclude_fields=()):
def __init__(self, table, id_event=0, exclude_fields=()):
li = ["data",
"id_object_categories",
......@@ -158,9 +160,10 @@ class EvtSelector(SelectorActiveItems):
li.extend(exclude_fields)
SelectorActiveItems.__init__(self, table, exclude_fields=li)
# add virtual fields
self._db = db = current.globalenv["db"]
self._id_event = id_event
# add virtual fields
history = db.history
virtual = Field.Virtual
......@@ -172,13 +175,9 @@ class EvtSelector(SelectorActiveItems):
history.is_over = virtual("is_over", self._is_over, ftype="boolean")
history.is_start = virtual("is_start", self._is_start, ftype="boolean")
# keep track of the identifier of the people event
# since it is used by period_activity
self._id_event_people = get_id(db.events, event="People")
def _active_period(self, id_people, id_domain, id_team):
"""Determine the period when a person is active in a given
domain and team.
domain and team and for a given event
Args:
id_people (int):
......@@ -191,7 +190,7 @@ class EvtSelector(SelectorActiveItems):
stop (datetime or None)
"""
rawsql = RAWSQL_ACTIVITY % (self._id_event_people,
rawsql = RAWSQL_ACTIVITY % (self._id_event,
id_people,
id_domain,
id_team)
......
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