Commit 1b8d8330 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Merge branch 'master' into 'production'

Release 0.6.3

* Require `plugin_dbui` 0.9.7 or higher
* Redesign the model. Tables definition and configuration of user interfaces are performed via a set of class.
* Improve user interfaces by using accordion layout for the selector, card layout for history form,  and table widget to the display the configuration of the event model.  Add actions `close` and `split` to the context menu of the history table.
* Improve the definition of the conditions for list and metrics. It can be written using the web2py syntax for query instead of the `smart_query`. Therefore, conditions can deal with foreign tables of the history table as well as with the `history.data` field.
* Improve the python code by renaming the class `MySelector` as `EvtSelector` and by adding the  method `EvtSelector._active_period`. The latter is required to compute properly the virtual field `EvtSelector._is_start`, `_is_end` as well as `is_over`.
* Remove the obsolete controller and view, `graph_extjs`.
* Activate the lazy translation.
* Graphs can be exported as a PNG or PDF file.
* Factorize the application around the `plugin_event`.

See merge request !26
parents 545b2f41 8154ec8c
*~
*.pdf
.project
.pydevproject
*.pyc
......@@ -21,5 +20,8 @@ private/
sessions/
static/*-debug.js
static/*-min.js
static/docs/
static/docs/*/
static/plugin_event/docs/*/
static/plugin_event/*-debug.js
static/plugin_event/*-min.js
uploads/
--------------------------------- CHANGELOG ----------------------------------
HEAD
0.6.2 (Nov 2016)
- Migrate to ExtJS 6.
- Require plugin_dbui 0.9.5 and plugin_extjs 6.2.0 or higher.
- New syntax for grid column configuration property:
renderer and summaryRenderer.
- Redesing the JavaScript class UserDataMultiDefault.
- Add the custom widget TrackEvents.grid.HistoryGridFilter.
0.6.0 (Oct 2016)
- Require plugin_dbui 0.9.0 or higher.
- Change the javaScript namespaces to Dbui and TrackEVents.
- Update the database schema by adding the table domains.
- Add the modules matplotlib_tools
- Add the script lbfr_report.py
0.5.6 (Jun 2016)
- Migrate to w2pext 2.14.6 and plugin_dbui 0.8.3
- Release requires web2py 2.14 or higher due to a change in pyDAL.
- New project organization where build_version is in w2pext/uitlities.
- Few bugs fixed.
0.5.3 (Feb 2016)
- Migrate to plugin_dbui 0.8.2
- Apply pslint and jshint rules to the source codes.
- Migrate the API documentation from epydoc to sphinx.
- Update to run via a docker image.
0.5.2 (Jan 2016)
- Use plugin_dbui 0.8.1 and the compress version of the plugins but ace.
- List entries can be sorted in descending order.
- Add a selection criteria on the field history.data in the report selector.
- Fix bugs selecting people categories and quality in reports
- Fix few bugs in the List class.
0.5.0 - 0.5.1 (May 2015)
- Major relealse non backward compatible
- Add the tables objects and object_categories for a more generic tool.
- List, Metric1D and 2D works with missing data. It appends when several
events are merged with different user data blocks.
- Re-enforce column configuration rules: xtype has always to be defined,
dataIndex is required with eval.
- Use the DBURIS construct.
- Add the script fix-duplicate.py to remove duplicate entries in the
history table.
0.4.5 (Mar 2015)
- Major release non-backward compatible.
- Migrate to plugin_dbui 0.6.2.6, pluggin_ace 1.1.8
and to python-pandas 0.15.2
- Migrate from sqlite to mariadb.
- Migrate to matplotlib 1.3.1 instead of Ext.chart.
- Add the user data block in the history / events tables:
MyApp.UserData, MyApp.UserDataMultiDefault,
MyApp.form.plugin.UserModelConsistency and the controller mytools.
- Remove obsolete tables and codes.
- Redesing the viewport using a more generic organisation
(configure, metadata, events, lists, metrics and graphs)
- Use the pGridExport plugin in views/report/grid.html
- Redesing the report section using pandas DataFrame and the
new classes List, Metrid1D and Metric2D.
- Refactor the lists and metrics table and add the graphs table.
- Add user documentation explaining how to create list, metric and
graph. It relies on the python-sphinx 1.3.9.
70753e8 (Jul 14)
- Migrate to plugin_dbui 0.6.1.6 and Web2py 2.9.5.
- Define the length of each string field to garanty Web2py compatibility.
- Use the controllers plugin_dbui/about, documentations and versions.
- Implement new reporting tools based on the javascrip inline script
technique, the Ext.grid.Panel widget and on the views database table.
- Remove the reporting tools based on the plugin_report which is much
more complicated with respect to the new approach.
05d5393 (Mar 14)
- Migrate to plugin_dbui 0.6.0.18 and ExtJS 4.2.1
- This version is not backward compatible
0.3.1-14-gf93ae68 (Sept 13)
- Migrate to plugin_dbui 0.4.15.2 and Ext JS 3.4.1.1
- Incubator for the generic plugin report.
- Report are customized by users via the plugin report.
0.3.0 (Aug 13)
- Rename field stage_xxx as trainee_xxx
- Minor polishing of the interface.
- Add the logic for user loggin.
- Add the help page documentation and about.
0.2.0 (Feb 13)
- Migrate to plugin_dbui 0.4.10.1 to develop filter on date.
- Add the class MySelector, Base, Hardware and Person.
- Build the list of history, hardware and people.
- Simplify the database schema introducing the concept of events
removing budgets, hardware, lines and responsibilities tables.
- Add a new node wizard to enter person and training.
- Count the number of people per category for a given time range.
- Polish the interface
- Rename the table categories, agencies, levels as people_categories,
fundings and organization_level.
- Reports can be extract as a csv, tex and pdf files.
0.1.0 (July 12)
- First running version based on dbui 0.4.7.6
static/plugin_event/CHANGELOG
\ No newline at end of file
static/plugin_event/LICENSE
\ No newline at end of file
0.6.2
\ No newline at end of file
static/plugin_event/VERSION
\ No newline at end of file
# -*- coding: utf-8 -*-
"""Configure the build process for the plugin_event
"""
# ............................................................................
#
# JAVASCRIPT LIBRARY
# JavaScript source code and compile version
# paths are relative to the static directory
#
SENCHA_CLS = [
"plugin_event/src",
"plugin_dbui/src",
"plugin_extjs/packages/core/src",
"plugin_extjs/classic/classic/src",
"plugin_extjs/classic/classic/overrides"]
SENCHA_NS = "Ext,Dbui"
JSLIBDEBUG = "plugin_event/event-debug.js"
JSLIBMIN = "plugin_event/event-min.js"
# ...........................................................................
#
# DOCUMENTATION
# output directories for documentation
# paths are relative to the static directory
#
DOCS_DIR = "plugin_event/docs"
JSDOC = "plugin_event/docs/jsduck"
LATEXDOC = "plugin_event/docs/latex"
PDFDOC = "plugin_event/docs/pdf"
# ...........................................................................
#
# PLUGINS
# event
# paths are relative to the application directory
#
PLUGINS_FILES = {}
PLUGINS_FILES["default"] = [
"controllers/plugin_%s.py",
"models/plugin_%s.py",
"modules/plugin_%s",
"private/plugin_%s",
"static/plugin_%s",
"views/plugin_%s"]
PLUGINS_FILES["event"] = [
"controllers/plugin_event.py",
"modules/plugin_event",
"static/plugin_event",
"views/plugin_event"]
# ............................................................................
#
# REFERENCE FILES
#
CHANGELOG = "static/plugin_event/CHANGELOG"
VERSION = "static/plugin_event/VERSION"
""" Controllers """
def documentations():
"""Main controller to give access to the documentation.
"""
return dict()
# -*- coding: utf-8 -*-
""" Controllers
"""
@auth.requires(True, requires_login=not request.is_local)
def index():
"""Main Controller to run the application launching the plugin dbui.
......@@ -26,13 +21,7 @@ def index():
else:
session.role = ADMIN
if 'debug' in request.vars:
del request.vars['debug']
url = URL('plugin_dbui', 'debug', args=request.args, vars=request.vars)
else:
url = URL('plugin_dbui', 'index', args=request.args, vars=request.vars)
url = URL('plugin_dbui', 'index', args=request.args, vars=request.vars)
return redirect(url)
......
""" mytool controllers
A collection of tools specific to the applications
# -*- coding: utf-8 -*-
"""plugin_event controllers
"""
import urllib
import json
import re
from datetime import datetime
from plugin_dbui import get_id
from plugin_dbui import get_id, INLINE_ALERT
from plugin_event import (do_title,
Graph,
List,
Metric1D,
Metric2D,
ReportException)
from plugin_event import EvtSelector, SelectorActiveItemsException
MSG = T(" - %s entry(ies) modified in the history table. <br>"
" - DO NOT FORGET TO MODIFIED THE REPORT CONFIGURATION.")
......@@ -14,6 +24,101 @@ MSG = T(" - %s entry(ies) modified in the history table. <br>"
MSG_CVT = T(" - Conversion error: %s.")
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)
except (IndexError, TypeError, ValueError) as e:
return INLINE_ALERT % ("Error", e)
# build the report title
title = do_title(config, selector)
# extract the image
# encode special character to be used in the image URI
extension = request.extension
if extension == "html":
data = graph.to_svg()
data = urllib.quote(data)
elif extension == "pdf":
data = graph.to_pdf()
elif extension == "png":
data = graph.to_png()
# delegate the rendering to the view
return dict(data=data, title=title)
def grid():
"""Render the report through the C{Dbui.grid.Panel}.
The report can be a list, a metrics 1d or 2d.
The controller extracts the configuration of the report form the database.
It builds the DataFrame, extracts the the configuration
for the C{Ext.data.Store} as well as the C{Dbui.grid.Panel}.
The former contains the data displayed in the grid.
The report identifier is defined in the viewport
and send using the baseParams techniques. Possible identifiers
are C{id_lists], C{id_metrics1d} and C{id_metrics2d}.
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
try:
if "id_lists" in request.vars:
config = db.lists[request.vars.id_lists]
report = List(config, selector)
elif "id_metrics1d" in request.vars:
config = db.metrics1d[request.vars.id_metrics1d]
report = Metric1D(config, selector)
elif "id_metrics2d" in request.vars:
config = db.metrics2d[request.vars.id_metrics2d]
report = Metric2D(config, selector)
else:
return INLINE_ALERT % ("Error", "Report id is not defined.")
except (IndexError, ReportException, TypeError, ValueError) as e:
return INLINE_ALERT % ("JSON 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)
# delegate the grid rendering to the view
return dict(cfg_store=store, grid=grid, title=title)
def userModelConsistency():
""" A user data block is associated to each event.
It is configurable by the user and defined in the events table.
......@@ -38,7 +143,7 @@ def userModelConsistency():
update_keys = {}
for action, oldkey, newkey in changes:
if action != 'update':
if action != "update":
continue
# simplify the case where key1 > key2 > key3 to key1 > key3
......@@ -67,14 +172,14 @@ def userModelConsistency():
# set-up the default values dealing with data conversion
for key in model:
value = model[key]['value']
type = model[key]['type']
value = model[key]["value"]
type = model[key]["type"]
if value:
try:
if type == "boolean":
value = value.lower() in ('true', 'vrai', 'y', 'yes', '1')
value = value.lower() in ("true", "vrai", "y", "yes", "1")
elif type == "date":
value = datetime.strptime(value, "%Y-%m-%d")
......@@ -97,7 +202,7 @@ def userModelConsistency():
except ValueError as e:
return MSG_CVT % e
model[key]['value'] = value
model[key]["value"] = value
# scan and modify the history table
# the delete of old key is implicit since they are not copied
......@@ -110,7 +215,7 @@ def userModelConsistency():
# copy existing key or add new key
for key in model:
data[key] = (rd[key] if key in rd else model[key]['value'])
data[key] = (rd[key] if key in rd else model[key]["value"])
# modify key name
for oldkey, newkey in update_keys.iteritems():
......@@ -119,4 +224,4 @@ def userModelConsistency():
db(db.history.id == row.id).update(data=data)
return MSG % len(rows)
\ No newline at end of file
return MSG % len(rows)
""" Report controllers
"""
import urllib
import json
import re
from plugin_dbui import INLINE_ALERT
from report_objects import (do_title,
Graph,
List,
Metric1D,
Metric2D,
ReportException)
from selector import MySelector, SelectorActiveItemsException
def graph_extjs():
"""Plot 2D metric as a C{Ext.char.series.Area} using the ExtJS library.
This action is kept as a test bench of ExtJS chart classes.
Deprecated: use the more generic graph_mpl
"""
ui_table = virtdb.selector
selector = MySelector(ui_table, exclude_fields=('metric',))
# homomorphism between metric2D and graph
config = db.graphs[request.vars.id_graphs]
config.group_field_y = config.field_horizontal
config.group_field_x = config.field_stacked
config.metric_field_z = 'people.id'
config.aggregation_z = 'size'
# generate the metric according to user specification
report = Metric2D(config, selector)
# extract the configurations for the Ext.data.Store
store = report.to_store()
# Extra the stack keys (value for metric.field_vertical)
stack_keys = [el for el in report.df.columns]
# delegate the rendering of the graph to the view
return dict(cfg_store=json.dumps(store), stack_keys=stack_keys, view=config)
def graph_mpl():
"""Plot list or metric using the matplotlib library
"""
# selector and configuration
try:
ui_table = virtdb.selector
selector = MySelector(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)
except (IndexError, TypeError, ValueError) as e:
return INLINE_ALERT % ("Error", e)
# build the report title
title = do_title(config, selector)
# delegate the rendering to the view
return dict(data=graph.to_svg(), title=title)
def grid():
"""Render the report through the C{Dbui.grid.Panel}.
The report can be a list, a metrics 1d or 2d.
The controller extracts the configuration of the report form the database.
It builds the DataFrame, extracts the the configuration
for the C{Ext.data.Store} as well as the C{Dbui.grid.Panel}.
The former contains the data displayed in the grid.
The report identifier is defined in the viewport
and send using the baseParams techniques. Possible identifiers
are C{id_lists], C{id_metrics1d} and C{id_metrics2d}.
The identifier is used to instantiate the proper class.
"""
try:
ui_table = virtdb.selector
selector = MySelector(ui_table)
except SelectorActiveItemsException as e:
return INLINE_ALERT % (T("Error..."), T(str(e)))
# extract the report configuration and the build the report
try:
if "id_lists" in request.vars:
config = db.lists[request.vars.id_lists]
report = List(config, selector)
elif "id_metrics1d" in request.vars:
config = db.metrics1d[request.vars.id_metrics1d]
report = Metric1D(config, selector)
elif "id_metrics2d" in request.vars:
config = db.metrics2d[request.vars.id_metrics2d]
report = Metric2D(config, selector)
else:
return INLINE_ALERT % ("Error", "Report id is not defined.")
except (IndexError, ReportException, TypeError, ValueError) as e:
return INLINE_ALERT % ("JSON 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)
# delegate the grid rendering to the view
return dict(cfg_store=store, grid=grid, title=title)
......@@ -7,9 +7,9 @@ A collections of functions to be call before a database operation is completed.
Functions
^^^^^^^^^
.. currentmodule:: callbacks
.. currentmodule:: plugin_event
.. autosummary::
:toctree: generated/
~INHIBIT_CASCADE_DELETE
INHIBIT_CASCADE_DELETE
......@@ -78,6 +78,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
app = 'track_events'
now = datetime.now()
project = u'track_events API'
copyright = u'2012-%s, R. Le Gac, licensed under CeCILL' % now.year
......@@ -204,7 +205,7 @@ html_theme = 'sphinxdoc'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'track_events_api_doc'
htmlhelp_basename = '%s_api_doc' % app
# -- Options for LaTeX output --------------------------------------------------
......@@ -218,14 +219,23 @@ latex_elements = {
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
# Latex figure (float) alignment
'figure_align': 'htb',
# fancy chapter title
'fncychap': '\\usepackage[Glenn]{fncychap}',
# Use the Computer Modern fonts instead of Times and Helvetica
'fontpkg': ''
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
(master_doc,
'track_events_api.tex',
u'track\\_events API',
'%s_api.tex' % app,
u'%s API documentation' % app.replace('_', '\\_'),
u'R. Le Gac',
'manual'),
]
......@@ -256,7 +266,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'track_events_api', u'API',
(master_doc, '%s_api' %app, u'%s API' % app,
[author], 1)
]
......@@ -270,8 +280,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'track_events_api', u'track_events API',
author, 'track_events_api', 'One line description of project.',
(master_doc, str(app), u'%s API' % app,
author, '%s_api' % app, 'Documentation for the Python API.',
'Miscellaneous'),
]
......
report_objects.BaseReport.to_df
===============================
.. currentmodule:: report_objects
.. automethod:: BaseReport.to_df
\ No newline at end of file
callbacks.INHIBIT_CASCADE_DELETE
================================
.. currentmodule:: callbacks
.. autofunction:: INHIBIT_CASCADE_DELETE
\ No newline at end of file