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

Merge branch '26-node-sources' into 'master'

Resolve "Add a node to see sources content"

Closes #26

See merge request !31
parents 421aaf3c 3ffce7b8
......@@ -6,7 +6,11 @@ import traceback
import urllib
from plugin_event import Graph, List, Metric2D, ReportException
from plugin_event import (Graph,
List,
Metric2D,
ReportException,
Source)
def grid():
......@@ -81,3 +85,21 @@ def metric2d():
except (IndexError, ReportException, TypeError, ValueError):
return CODE(traceback.format_exc()).xml()
def source():
"""Display the content of a source via an ``Ext.grid.Panel``.
Values send by the selector are used to filter the content of the source.
"""
try:
report = Source(request.vars.source)
store = report.get_store_configuration()
grid = report.get_grid_configuration()
title = report.get_title()
except (IndexError, ReportException, TypeError, ValueError):
return CODE(traceback.format_exc()).xml()
response.view = "plugin_event/grid.html"
return dict(cfg_store=store, grid=grid, title=title)
......@@ -27,6 +27,7 @@
'Aggregation Z': 'Agréger (z)',
'Alignments': 'Alignments',
'All columns will be configured when the field is empty': 'Toutes les colonnes seront configurées quand ce champ est vide',
'Application': 'Application',
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
'auth_cas': 'auth_cas',
'auth_event': 'auth_event',
......@@ -311,6 +312,7 @@
'the metrics 2d': 'les métriques 2d',
'The metrics 2d': 'Les métriques 2d',
"The report id '%s' is unknown.": "The report id '%s' is unknown.",
'The sources': 'Les sources',
'This email already has an account': 'This email already has an account',
'Time': 'Temps',
'Timestamp': 'Timestamp',
......
......@@ -11,8 +11,17 @@ Note:
* directSvc
"""
import plugin_dbui as dbui
import plugin_event as event
from plugin_dbui import (configure_forms,
configure_grids,
Dbui)
from plugin_event import (Core,
CoreUi,
Event,
Report,
ReportUi,
Selector,
ViewportUi)
ctrl = request.controller
......@@ -35,7 +44,7 @@ is_dbui = (ctrl == "plugin_dbui") and \
fnct in ("debug", "documentations", "dbui_conf", "index")
if is_dbui:
dbui.Dbui.define_paths(
Dbui.define_paths(
app_about="static/plugin_event/ABOUT.html",
app_css=None,
app_changelog="static/plugin_event/CHANGELOG",
......@@ -51,7 +60,7 @@ if is_dbui:
app_pdf_user="static/plugin_event/docs/pdf/event_user.pdf",
app_script="static/app.js")
event.Event.define_paths("fr")
Event.define_paths("fr")
# ............................................................................
#
......@@ -64,11 +73,11 @@ is_db = (ctrl == "plugin_dbui" and fnct in ("call", "csv", "dbui_conf")) \
if is_db:
# register source for the reporting
event.Event.register_sources()
Event.register_sources()
event.Core.define_tables(db, T)
event.Report.define_tables(db, T)
event.Selector.define_tables(virtdb, db, T)
Core.define_tables(db, T)
Report.define_tables(db, T)
Selector.define_tables(virtdb, db, T)
# ............................................................................
#
......@@ -77,8 +86,8 @@ if is_db:
if ctrl == "plugin_dbui" and fnct in ("call", "dbui_conf"):
dbui.Dbui.initialise_ui()
directSvc = dbui.Dbui.start_directSvc()
Dbui.initialise_ui()
directSvc = Dbui.start_directSvc()
# common configuration for forms and grids
tables = ["auth_group",
......@@ -96,19 +105,19 @@ if ctrl == "plugin_dbui" and fnct in ("call", "dbui_conf"):
"projects",
"teams"]
dbui.configure_forms(tables, plugins=["pFormToolTip"], width=350)
configure_forms(tables, plugins=["pFormToolTip"], width=350)
dbui.configure_grids(tables, plugins=["pGridRowEditorConfirmDelete",
"pGridRowEditorContextMenu",
"pGridRowEditorDblClick",
"pGridToolbar"])
configure_grids(
tables,
plugins=["pGridRowEditorConfirmDelete",
"pGridRowEditorContextMenu",
"pGridRowEditorDblClick",
"pGridToolbar"])
# tune the user interface
event.CoreUi.define(db, T)
event.ReportUi.define(db, T)
selector_panel = event.SelectorUi.selector(virtdb, db, T)
CoreUi.define(db, T)
ReportUi.define(db, T)
# configure the main viewport
if fnct == "dbui_conf":
event.ViewportUi.define(db, T, selector_panel)
ViewportUi.define(db, T, virtdb)
......@@ -30,7 +30,8 @@ from dataframes import (active_period,
to_extjs_gridcolumns,
to_items_per_year)
from event import Event
from event import (Event,
EventException)
from matplotlib_tools import (bicolor,
create_pdf,
......@@ -50,7 +51,8 @@ from report_tools import (BaseReport,
Graph,
List,
Metric2D,
ReportException)
ReportException,
Source)
from ui_core import CoreUi
from ui_report import ReportUi
......
......@@ -7,8 +7,8 @@ import json
from dataframes import to_extjs_gridcolumns
from event import Event
from gluon import current
from gluon.tools import PluginManager
from plugin_dbui import CALLBACK_ERRORS, get_where_query
......@@ -90,7 +90,7 @@ def ON_CREATE_LISTS2(values):
year_start=year,
year_end=year)
func = PluginManager("event").event.sources[values["source"]].func
func = Event.get_source(values["source"])
df = func(**criteria)
......
......@@ -15,6 +15,10 @@ from dataframes import (get_items,
get_people_per_year)
class EventException(BaseException):
pass
class Event(object):
"""Initialise the plugin event.
......@@ -43,6 +47,56 @@ class Event(object):
"lg": "static/plugin_event/locale/event-lang-%s.js" % lg,
"libmin": "static/plugin_event/event-min.js"}
@staticmethod
def get_source(name):
"""Get the source identified by its name.
Raised:
EventException
Args:
name (str): name of the source to be used in the UI.
Returns:
reference:
the function to generate the DataFrame.
"""
event = PluginManager("event").event
if event is None:
raise EventException("Plugin event is not configured.")
sources = event.sources
if sources is None:
raise EventException("Plugin event sources are not configured.")
if name not in sources:
raise EventException("The source %s is not registered" % name)
return sources[name].func
@staticmethod
def get_sources():
"""Return the list of registered sources.
Raises:
EventException
Returns:
iterator:
names of the registered sources.
"""
event = PluginManager("event").event
if event is None:
raise EventException("Plugin event is not configured.")
sources = event.sources
if sources is None:
raise EventException("Sources are not configured.")
return sources.iterkeys()
@staticmethod
def register_source(name, func):
"""Register sources which are used in the reporting section.
......
......@@ -6,8 +6,8 @@ import numpy as np
from callbacks import ON_CREATE_LISTS2, ON_UPDATE_LISTS2
from event import Event
from gluon import current, IS_IN_SET
from gluon.tools import PluginManager
from pydal import Field
......@@ -22,35 +22,6 @@ AGGREGATE_FUNCS = {
"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": "",
......@@ -65,12 +36,6 @@ DEF_GRAPH = {
"width": 0.8,
"ylim": ""}
DEF_PLOT = \
"""{
"kind": "?",
"stacked": false
}"""
DEF_SUMMARY = {
"functions": "",
"labels": ""}
......@@ -82,10 +47,6 @@ TP_AGG = \
TP_COLUMNS = "Configure the column of the grid displayed in the view."
TP_CONDITIONS = \
"Database query to select history records:<br> " \
"(db.events.event == 'People') & (db.history.data.like('%cdd%'))<br>"
TP_GROUP_FIELD_LISTS = \
"Row are grouped according to the value of that field. " \
"It can be any field defined in the source."
......@@ -94,16 +55,11 @@ TP_GROUP_FIELD_METRICS = \
"Metric are computed for each value of that field. " \
"It can be any field defined in the source"
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 defined in the source. "\
"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 ``Lists`` and ``metrics2D`` for the reporting
......@@ -123,84 +79,9 @@ class Report(object):
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
......@@ -256,8 +137,7 @@ class Report(object):
migrate="lists2.table")
sources = PluginManager("event").event.sources.keys()
sources.sort()
sources = sorted(Event.get_sources())
db.lists2.source.requires = IS_IN_SET(sources)
db.lists2._before_insert.append(ON_CREATE_LISTS2)
......@@ -265,89 +145,6 @@ class Report(object):
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
......@@ -416,9 +213,7 @@ class Report(object):
migrate="metrics2d2.table")
sources = PluginManager("event").event.sources.keys()
sources.sort()
sources = sorted(Event.get_sources())
table.source.requires = IS_IN_SET(sources)
funcs = AGGREGATE_FUNCS.keys()
......@@ -441,11 +236,7 @@ class Report(object):
list
"""
lst = ["graphs",
"lists",
"lists2",
"metrics1d",
"metrics2d"
lst = ["lists2",
"metrics2d2"]
return lst
......@@ -27,11 +27,13 @@ class Selector(object):
T (gluon.languages.translator): language translator
"""
Selector.selector(virtdb, db, T)
Selector.selector_list(virtdb, db, T)
Selector.selector_metric2d(virtdb, db, T)
Selector.selector_source(virtdb, db, T)
@staticmethod
def selector(virtdb, db, T):
"""selector table.
def selector_list(virtdb, db, T):
"""selector table for list report.
Note:
selector table are store in a virtual database.
......@@ -48,7 +50,63 @@ class Selector(object):
year = datetime.now().year
table = virtdb.define_table(
"selector",
"selector_list",
Field("year_start", "integer", default=year, label="Start"),
Field("year_end", "integer", label="End"),
Field("id_domains", "reference domains", label="Domain"),
Field("id_teams", "reference teams", label="Team"),
Field("id_projects", "reference projects", label="Project"),
Field("id_fundings", "reference fundings", label=""),
Field("id_people", "reference people", label="Last name"),
Field("people_categories_category", "string", label="Category"),
Field("id_people_code",
"reference people_categories",
label="Quality"),
Field("id_objects", "reference objects", label="Reference"),
Field("object_categories_category", "string", label="Category"),
Field("id_object_code",
"reference object_categories",
label="Code"),
Field("query", "text"))
table.id_domains.requires = IS_IN_DB(db, "domains.domain")
table.id_fundings.requires = IS_IN_DB(db, "fundings.agency")
table.id_objects.requires = IS_IN_DB(db, "objects.reference")
table.id_object_code.requires = IS_IN_DB(db, "object_categories.code")
table.id_people.requires = IS_IN_DB(db, "people.last_name")
table.id_people_code.requires = IS_IN_DB(db, "people_categories.code")
table.id_projects.requires = IS_IN_DB(db, "projects.project")
table.id_teams.requires = IS_IN_DB(db, "teams.team")
return table
@staticmethod
def selector_metric2d(virtdb, db, T):
"""selector table for metric2d report.
Note:
selector table are store in a virtual database.
Args:
virtdb (pyDAL.DAL): connection to the virtual database
db (pyDAL.DAL): connection to the database of the application
T (gluon.languages.translator): language translator
Returns:
pyDAL.Table
"""
year = datetime.now().year