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

Merge branch 'master' into 'production'

Release 0.7.0

See merge request !32
parents 1d2efd3a 8d155af3
...@@ -2,206 +2,104 @@ ...@@ -2,206 +2,104 @@
"""plugin_event controllers """plugin_event controllers
""" """
import traceback
import urllib import urllib
import json
import re
from datetime import datetime from plugin_event import (Graph,
from plugin_dbui import get_id, INLINE_ALERT
from plugin_event import (do_title,
Graph,
List, List,
Metric1D,
Metric2D, Metric2D,
ReportException) ReportException,
from plugin_event import EvtSelector, SelectorActiveItemsException Source)
MSG = T(" - %s entry(ies) modified in the history table. <br>" def grid():
" - DO NOT FORGET TO MODIFIED THE REPORT CONFIGURATION.") """The list is rendered such as an ``Ext.grid.Panel`` widget.
Its context menu allows to export the content of the table as a CSV, LaTeX
MSG_CVT = T(" - Conversion error: %s.") or PDF format.
def graph_mpl(): The controller extracts the configuration of the report from the database.
"""Plot list or metric using the matplotlib library It builds the DataFrame, extracts the configuration for the
``Ext.data.Store`` as well as the ``Ext.grid.Panel``. The former contains
the data displayed in the grid.
""" """
# build the graph
try: try:
graph = Graph(db.graphs, request.vars.id_graphs) report = List(request.vars.id_list)
store = report.get_store_configuration()
except (IndexError, TypeError, ValueError) as e: grid = report.get_grid_configuration()
return INLINE_ALERT % ("GRAPH Error", e) title = report.get_title()
# build the report title
title = do_title(graph)
# 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 except (IndexError, ReportException, TypeError, ValueError):
return dict(data=data, title=title) return CODE(traceback.format_exc()).xml()
response.view = "plugin_event/grid.html"
return dict(cfg_store=store, grid=grid, 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. def metric2d():
It builds the DataFrame, extracts the the configuration """The metric 2D is a 2D table rendered by an ``Ext.grid.Panel`` widget
for the C{Ext.data.Store} as well as the C{Dbui.grid.Panel}. and a graph rendered as an image. The context menu associated to the grid
The former contains the data displayed in the grid. allows to export the content of the table as a CSV, LaTeX or PDF format.
The context menu associated to the image allow to export in PNG or PDF
format.
The report identifier is defined in the viewport The controller extracts the configuration of the report from the database.
and send using the baseParams techniques. Possible identifiers It builds the DataFrame, extracts the the configuration for the
are C{id_lists], C{id_metrics1d} and C{id_metrics2d}. ``Ext.data.Store`` as well as the ``Ext.grid.Panel`` and generated
The identifier is used to instantiate the proper class. the graph.
""" """
# build the report extension = request.extension
try: try:
if "id_lists" in request.vars: report = Metric2D(request.vars.id_metric2d)
report = List(db.lists, request.vars.id_lists)
elif "id_metrics1d" in request.vars: graph = Graph(report)
report = Metric1D(db.metrics1d, request.vars.id_metrics1d) title = report.get_title()
elif "id_metrics2d" in request.vars: if extension == "html":
report = Metric2D(db.metrics2d, request.vars.id_metrics2d) store = report.get_store_configuration()
grid = report.get_grid_configuration()
else: if graph.ax is not None:
return INLINE_ALERT % ("Error", "Report id is not defined.") img = graph.to_svg()
img = urllib.quote(img)
except (IndexError, ReportException, TypeError, ValueError) as e: response.view = "plugin_event/grid_and_graph.html"
return INLINE_ALERT % ("REPORT Error...", e) return dict(cfg_store=store, grid=grid, img=img, title=title)
# extract the configurations for the Ext.data.Store and Dbui.grid.Panel. else:
store = report.to_store() response.view = "plugin_event/grid.html"
grid = report.to_grid() return dict(cfg_store=store, grid=grid, title=title)
# build the report title elif extension == "pdf":
title = do_title(report) data = graph.to_pdf()
response.view = "plugin_event/graph.pdf"
return dict(data=data, title=title)
# delegate the grid rendering to the view elif extension == "png":
return dict(cfg_store=store, grid=grid, title=title) data = graph.to_png()
response.view = "plugin_event/graph.png"
return dict(data=data, title=title)
except (IndexError, ReportException, TypeError, ValueError):
return CODE(traceback.format_exc()).xml()
def userModelConsistency():
""" A user data block is associated to each event.
It is configurable by the user and defined in the events table.
The definition of the user data block (model) and the content def source():
of the history.data field have to be kept in synchronization. """Display the content of a source via an ``Ext.grid.Panel``.
This job is done by this controller. Values send by the selector are used to filter the content of the source.
""" """
# decode the request try:
changes = json.loads(request.vars.changes) report = Source(request.vars.source)
event = request.vars.fieldEvent store = report.get_store_configuration()
model = json.loads(request.vars.fieldData) grid = report.get_grid_configuration()
title = report.get_title()
# build a map of the modified keys.
# each operation is a list containing (action, old key, new key). except (IndexError, ReportException, TypeError, ValueError):
# the action is equal to: add, destroy, update return CODE(traceback.format_exc()).xml()
#
# NOTE: add and delete action are handle via dictionary copy. response.view = "plugin_event/grid.html"
# return dict(cfg_store=store, grid=grid, title=title)
#
update_keys = {}
for action, oldkey, newkey in changes:
if action != "update":
continue
# simplify the case where key1 > key2 > key3 to key1 > key3
# remove the case where key1 > key2 > ... > key1
if update_keys:
ok = False
for k, v in update_keys.iteritems():
if oldkey == v:
update_keys[k] = newkey
ok = True
if k == newkey:
del update_keys[k]
break
if ok:
continue
# basic case
update_keys[oldkey] = newkey
# get the event id
id_event = get_id(db.events, event=event)
if id_event == None:
return
# set-up the default values dealing with data conversion
for key in model:
value = model[key]["value"]
type = model[key]["type"]
if value:
try:
if type == "boolean":
value = value.lower() in ("true", "vrai", "y", "yes", "1")
elif type == "date":
value = datetime.strptime(value, "%Y-%m-%d")
value = value.isoformat()
elif type == "float":
value = float(value)
elif type == "integer":
value = int(value)
if type == "string" and value[0] == "[" and value[-1] == "]":
# reference to a set, pick the first value as default
li = json.loads(value)
value = (li[0] if li else None)
elif type == "reference":
value = None
except ValueError as e:
return MSG_CVT % e
model[key]["value"] = value
# scan and modify the history table
# the delete of old key is implicit since they are not copied
query = db.history.id_events == id_event
rows = db(query).select()
for row in rows:
data = {}
rd = (row.data if isinstance(row.data, dict) else dict())
# copy existing key or add new key
for key in model:
data[key] = (rd[key] if key in rd else model[key]["value"])
# modify key name
for oldkey, newkey in update_keys.iteritems():
if oldkey in rd:
data[newkey] = rd[oldkey]
db(db.history.id == row.id).update(data=data)
return MSG % len(rows)
...@@ -13,3 +13,5 @@ Functions ...@@ -13,3 +13,5 @@ Functions
:toctree: generated/ :toctree: generated/
INHIBIT_CASCADE_DELETE INHIBIT_CASCADE_DELETE
ON_CREATE_LISTS2
ON_UPDATE_LISTS2
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# track_event api documentation build configuration file, created by # plugin_event api documentation build configuration file, created by
# sphinx-quickstart on Sat Apr 11 11:17:03 2015. # sphinx-quickstart on Sat Apr 11 11:17:03 2015.
# #
# This file is execfile()d with the current directory set to its containing dir. # This file is execfile()d with the current directory set to its containing dir.
...@@ -78,9 +78,9 @@ source_suffix = '.rst' ...@@ -78,9 +78,9 @@ source_suffix = '.rst'
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
app = 'track_events' app = 'plugin_event'
now = datetime.now() now = datetime.now()
project = u'track_events API' project = u'plugin_event API'
copyright = u'2012-%s, R. Le Gac, licensed under CeCILL' % now.year copyright = u'2012-%s, R. Le Gac, licensed under CeCILL' % now.year
author = u'R. Le Gac' author = u'R. Le Gac'
......
Dataframes
-----------
A collection of classes and functions to build and to manipulate the sources.
.. currentmodule:: plugin_event
.. autosummary::
:toctree: generated/
active_period
coverage
db2df
debug_df
expand_per_year
full_name
get_column_names
get_items
get_items_per_year
get_items_small
get_items_small_per_year
get_objectlike_items
get_objectlike_items_per_year
get_peoplelike_items
get_peoplelike_items_per_year
get_people_per_year
is_end
is_start
normalize_history_data
query_history
Timer
to_extjs_gridcolumns
to_items_per_year
\ No newline at end of file
plugin_event.BaseReport.to_df plugin_event.Event.get_source
============================= =============================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: BaseReport.to_df .. automethod:: Event.get_source
\ No newline at end of file \ No newline at end of file
plugin_event.Selector.selector plugin_event.Event.get_sources
============================== ==============================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: Selector.selector .. automethod:: Event.get_sources
\ No newline at end of file \ No newline at end of file
plugin_event.ViewportUi.graph_node plugin_event.Event.register_source
================================== ==================================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: ViewportUi.graph_node .. automethod:: Event.register_source
\ No newline at end of file \ No newline at end of file
plugin_event.SelectorUi.selector plugin_event.Event.register_sources
=================================== ===================================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: SelectorUi.selector .. automethod:: Event.register_sources
\ No newline at end of file \ No newline at end of file
plugin_event.Report.lists
=========================
.. currentmodule:: plugin_event
.. automethod:: Report.lists
\ No newline at end of file
plugin_event.Report.graphs plugin_event.Report.lists2
========================== ==========================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: Report.graphs .. automethod:: Report.lists2
\ No newline at end of file \ No newline at end of file
plugin_event.Metric1D.to_store plugin_event.Report.metrics2d2
============================== ==============================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: Metric1D.to_store .. automethod:: Report.metrics2d2
\ No newline at end of file \ No newline at end of file
plugin_event.ViewportUi.object_node plugin_event.Selector.selector_list
=================================== ===================================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: ViewportUi.object_node .. automethod:: Selector.selector_list
\ No newline at end of file \ No newline at end of file
plugin_event.Selector.selector_metric2d
=======================================
.. currentmodule:: plugin_event
.. automethod:: Selector.selector_metric2d
\ No newline at end of file
plugin_event.ViewportUi.metric1d_node plugin_event.Selector.selector_source
===================================== =====================================
.. currentmodule:: plugin_event .. currentmodule:: plugin_event
.. automethod:: ViewportUi.metric1d_node .. automethod:: Selector.selector_source
\ No newline at end of file \ No newline at end of file
...@@ -12,8 +12,9 @@ plugin_event.BaseReport ...@@ -12,8 +12,9 @@ plugin_event.BaseReport
.. autosummary:: .. autosummary::
:toctree: report/ :toctree: report/
~BaseReport.to_df ~BaseReport.get_grid_configuration
~BaseReport.get_store_configuration
~BaseReport.get_title
......
...@@ -12,6 +12,11 @@ plugin_event.Event ...@@ -12,6 +12,11 @@ plugin_event.Event
:toctree: model/ :toctree: model/
~Event.define_paths ~Event.define_paths
~Event.get_source
~Event.get_sources
~Event.register_source
~Event.register_sources
......
plugin_event.EvtSelector
========================
.. currentmodule:: plugin_event
.. autoclass:: EvtSelector
:show-inheritance:
.. rubric:: Methods
.. autosummary::
:toctree: selector/
~EvtSelector.query
~EvtSelector.reset_extra_queries
...@@ -11,7 +11,8 @@ plugin_event.Graph ...@@ -11,7 +11,8 @@ plugin_event.Graph
.. autosummary:: .. autosummary::
:toctree: report/ :toctree: report/
~Graph.to_df ~Graph.bar_like
~Graph.line_like
~Graph.to_pdf ~Graph.to_pdf
~Graph.to_png ~Graph.to_png
~Graph.to_svg ~Graph.to_svg
......
...@@ -11,10 +11,6 @@ plugin_event.List ...@@ -11,10 +11,6 @@ plugin_event.List
.. autosummary:: .. autosummary::
:toctree: report/ :toctree: report/
~List.to_df
~List.to_grid
~List.to_store
......
...@@ -11,9 +11,7 @@ plugin_event.Metric2D ...@@ -11,9 +11,7 @@ plugin_event.Metric2D
.. autosummary:: .. autosummary::
:toctree: report/ :toctree: report/
~Metric2D.to_df ~Metric2D.summaries
~Metric2D.to_grid
~Metric2D.to_store
......
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