# -*- coding: utf-8 -*- """plugin_event controllers """ import json import traceback import urllib from datetime import datetime from plugin_dbui import get_id from plugin_event import (Graph, List, Metric2D, ReportException, Source) MSG = T("%s entry(ies) modified in the history table. " "DO NOT FORGET TO MODIFIED CONFIGURATION OF REPORT(s).") MSG_CVT = T(" - Conversion error: %s.") def grid(): """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 or PDF format. The controller extracts the configuration of the report from the database. 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. """ try: report = List(request.vars.id_list) 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) def metric2d(): """The metric 2D is a 2D table rendered by an ``Ext.grid.Panel`` widget and a graph rendered as an image. The context menu associated to 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 controller extracts the configuration of the report from the database. It builds the DataFrame, extracts the the configuration for the ``Ext.data.Store`` as well as the ``Ext.grid.Panel`` and generated the graph. """ extension = request.extension try: report = Metric2D(request.vars.id_metric2d) graph = Graph(report) title = report.get_title() if extension == "html": store = report.get_store_configuration() grid = report.get_grid_configuration() if graph.ax is not None: img = graph.to_svg() img = urllib.quote(img) response.view = "plugin_event/grid_and_graph.html" return dict(cfg_store=store, grid=grid, img=img, title=title) else: response.view = "plugin_event/grid.html" return dict(cfg_store=store, grid=grid, title=title) elif extension == "pdf": data = graph.to_pdf() response.view = "plugin_event/graph.pdf" return dict(data=data, title=title) elif extension == "png": 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 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) 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 of the history.data field have to be kept in synchronisation. This job is done by this controller. """ # decode the request changes = json.loads(request.vars.changes) event = request.vars.fieldEvent model = json.loads(request.vars.fieldData) # build a map of the modified keys. # each operation is a list containing (action, old key, new key). # the action is equal to: add, destroy, update # # NOTE: add and delete action are handle via dictionary copy. # # 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 is None: return # set-up the default values dealing with data conversion for key in model: value = model[key]["value"] vtype = model[key]["type"] if value: try: if vtype == "boolean": value = value.lower() in ("true", "vrai", "y", "yes", "1") elif vtype == "date": value = datetime.strptime(value, "%Y-%m-%d") value = value.isoformat() elif vtype == "float": value = float(value) elif vtype == "integer": value = int(value) if vtype == "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 vtype == "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)