# -*- coding: utf-8 -*-
"""Definitions of the report tables
"""
import numpy as np
from callbacks import ON_CREATE_LISTS2, ON_UPDATE_LISTS2
from gluon import current, IS_IN_SET
from gluon.tools import PluginManager
from pydal import Field
AGGREGATE_FUNCS = {
"max": np.max,
"mean": np.min,
"median": np.median,
"min": np.min,
"sem": np.max,
"size": np.size,
"sum": np.sum,
"std": np.std,
"var": np.var}
DEF_COLUMNS_LISTS = \
"""[{
"xtype": "rownumberer"
}, {
"dbfield": "?.?",
"flex": 1,
"text": "?",
"xtype": "gridcolumn"
}]"""
DEF_COLUMNS_METRIC1D = \
"""[{
"aggregate": "?",
"align": "right",
"dbfield": "?.?",
"format": "0.0",
"text": "?",
"xtype": "numbercolumn"
}]"""
DEF_FEATURES = \
"""[{
"ftype": "groupingsummary",
"groupHeaderTpl": "{name}",
"startCollapsed": false
}, {
"ftype": "summary"
}]"""
DEF_GRAPH = {
"colormap": "Pastel1",
"kind": "",
"grid": True,
"linewidth": 0,
"legend": True,
"loglog": False,
"logx": False,
"logy": False,
"rot": 0,
"stacked": True,
"width": 0.8,
"ylim": []}
DEF_PLOT = \
"""{
"kind": "?",
"stacked": false
}"""
DEF_SUMMARY = {
"functions": "",
"labels": ""}
TP_AGG = \
"The aggregation function applies on the metric field. " \
"It is computed on the subset of row having a given values for " \
"the group_field x and y."
TP_COLUMNS = "Configure the column of the grid displayed in the view."
TP_CONDITIONS = \
"Database query to select history records:
" \
"(db.events.event == 'People') & (db.history.data.like('%cdd%'))
"
TP_GROUP_FIELD_LISTS = \
"Row are grouped according to the value of that field. " \
"It can be any field of the history table including those of " \
"the foreign tables, the individual property of the history.data " \
"dictionary, or 'year'."
TP_GROUP_FIELD_METRICS = \
"Metric are computed for each value of that field. " \
"It can be any field of the history table including those of the " \
"foreign tables, the individual property of the history.data dictionary " \
"or 'year'"
TP_FEATURES = "Summary value can be computed for columns."
TP_SORTERS = \
"Entries are sorted according to the value of these fields. "\
"It can be any field of the history table including those of the "\
"foreign tables, the individual property of the history.data dictionary, "\
"or 'year'. Descending order is obtained by using the '~field' construct."
TP_SUMMARY_X = "To sum, for example, the content of each row."
TP_SUMMARY_Y = "To sum, for example, the content of each column."
class Report(object):
"""Create the tables for the reporting: lists, metrics1d, metrics2d,
and graphs.
"""
@staticmethod
def define_tables(db, T):
"""Define tables for the reporting.
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
"""
Report.graphs(db, T)
Report.lists(db, T)
Report.lists2(db, T)
Report.metrics1d(db, T)
Report.metrics2d(db, T)
Report.metrics2d2(db, T)
@staticmethod
def graphs(db, T):
"""graphs table
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
def_plot = (None if db._migrate else DEF_PLOT)
table = db.define_table(
"graphs",
Field("name", "string", length=255, notnull=True, unique=True),
Field("title", "string", length=255),
Field("report_type", "string", length=255, notnull=True),
Field("report_name", "string", length=255, notnull=True),
Field("plot", "text", notnull=True, default=def_plot),
Field("definition", "text"),
migrate="graphs.table")
return table
@staticmethod
def lists(db, T):
"""lists table
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
def_columns = (None if db._migrate else DEF_COLUMNS_LISTS)
def_features = (None if db._migrate else DEF_FEATURES)
table = db.define_table(
"lists",
Field("name", "string", length=255, notnull=True, unique=True),
Field("title", "string", length=255),
Field("conditions", "text", comment=T(TP_CONDITIONS)),
Field("group_field",
"string",
length=255,
comment=T(TP_GROUP_FIELD_LISTS)),
Field("sorters", "list:string", comment=TP_SORTERS),
Field("columns",
"text",
default=def_columns,
comment=T(TP_COLUMNS),
notnull=True),
Field("features",
"text",
default=def_features,
comment=T(TP_FEATURES)),
Field("definition", "text"),
migrate="lists.table")
return table
@staticmethod
def lists2(db, T):
"""lists2 table
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
migrate = current.globalenv["MIGRATE"]
table = db.define_table(
"lists2",
Field("name", "string", length=255, notnull=True, unique=True),
Field("title", "string", length=255),
Field("definition", "text"),
Field("source", "string", length=255, notnull=True),
Field("id_events",
"reference events",
notnull=True,
label="Event"),
Field("eval", "text"),
Field("query", "text"),
Field("group_field",
"string",
length=255,
comment=T(TP_GROUP_FIELD_LISTS)),
Field("sorters", "list:string", comment=TP_SORTERS),
# NOTE
# - notnull when creating the database
# - null later on in order to run callback ON_CREATE_LISTS, ...
Field("columns", "text",
notnull=migrate,
comment=T(TP_COLUMNS)),
Field("summary_group",
"boolean",
default=True,
label=T("Summary row per group")),
Field("summary_all",
"boolean",
default=True,
label=T("Summary row")),
migrate="lists2.table")
sources = PluginManager("event").event.sources.keys()
sources.sort()
db.lists2.source.requires = IS_IN_SET(sources)
db.lists2._before_insert.append(ON_CREATE_LISTS2)
db.lists2._before_update.append(ON_UPDATE_LISTS2)
return table
@staticmethod
def metrics1d(db, T):
"""metrics1d table
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
def_columns = (None if db._migrate else DEF_COLUMNS_METRIC1D)
db.define_table(
"metrics1d",
Field("name", "string", length=255, notnull=True, unique=True),
Field("title", "string", length=255),
Field("conditions", "text", comment=T(TP_CONDITIONS)),
Field("group_field",
"string",
length=255,
comment=T(TP_GROUP_FIELD_METRICS),
notnull=True),
Field("columns",
"text",
default=def_columns,
comment=T(TP_COLUMNS),
notnull=True),
Field("definition", "text"),
migrate="metrics1d.table")
@staticmethod
def metrics2d(db, T):
"""metrics2d table
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
table = db.define_table(
"metrics2d",
Field("name", "string", length=255, notnull=True, unique=True),
Field("title", "string", length=255),
Field("conditions", "text", comment=T(TP_CONDITIONS)),
Field("group_field_x",
"string",
length=255,
notnull=True,
comment=T(TP_GROUP_FIELD_METRICS)),
Field("group_field_y",
"string",
length=255,
notnull=True,
comment=T(TP_GROUP_FIELD_METRICS)),
Field("metric_field_z",
"string",
length=255,
notnull=True,
comment=T(TP_GROUP_FIELD_METRICS)),
Field("aggregation_z",
"string",
length=255,
notnull=True,
comment=T(TP_AGG)),
Field("definition", "text"),
migrate="metrics2d.table")
return table
@staticmethod
def metrics2d2(db, T):
"""metrics2d2 table
Args:
db (pyDAL.DAL): database connection
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
table = db.define_table(
"metrics2d2",
Field("name", "string", length=255, notnull=True, unique=True),
Field("title", "string", length=255),
Field("definition", "text"),
Field("source", "string", length=255, notnull=True),
Field("id_events",
"reference events",
label="Event",
notnull=True),
Field("eval", "text"),
Field("query", "text"),
Field("group_field_x",
"string",
comment=T(TP_GROUP_FIELD_METRICS),
length=255,
notnull=True,
label="Group (x)"),
Field("group_field_y",
"string",
comment=T(TP_GROUP_FIELD_METRICS),
length=255,
notnull=True,
label="Group (y)"),
Field("metric_field_z",
"string",
comment=T(TP_GROUP_FIELD_METRICS),
label="Metric (z)",
length=255,
notnull=True),
Field("aggregate_func_z",
"string",
comment=T(TP_AGG),
default="sum",
label="Aggregate (z)",
length=255,
notnull=True),
Field("summary_x", "json", default=DEF_SUMMARY, label="Per row"),
Field("summary_y",
"json",
default=DEF_SUMMARY,
label="Per column"),
Field("graph", "json", default=DEF_GRAPH),
migrate="metrics2d2.table")
sources = PluginManager("event").event.sources.keys()
sources.sort()
table.source.requires = IS_IN_SET(sources)
funcs = AGGREGATE_FUNCS.keys()
funcs.sort()
table.aggregate_func_z.requires = IS_IN_SET(funcs)
# NOTE: we have to remove the json validator to work !!
table.graph.requires = None
table.summary_x.requires = None
table.summary_y.requires = None
return table
@staticmethod
def tables():
"""Tool returning the list of table names.
Returns:
list
"""
lst = ["graphs",
"lists",
"lists2",
"metrics1d",
"metrics2d"
"metrics2d2"]
return lst