plugin_event.py 6.73 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
"""plugin_event controllers
3 4

"""
5
import json
6
import traceback
7
import urllib
8

9

10 11
from datetime import datetime
from plugin_dbui import get_id
12
from plugin_event import (Graph,
13 14 15 16 17
                          List,
                          Metric2D,
                          ReportException,
                          Source)

18 19 20 21 22
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.")

23

24 25
def grid():
    """The list is rendered such as an ``Ext.grid.Panel`` widget.
26 27 28 29 30 31 32
    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.
33 34

    """
35
    try:
36
        report = List(request.vars.id_list)
37 38 39
        store = report.get_store_configuration()
        grid = report.get_grid_configuration()
        title = report.get_title()
40

41 42
    except (IndexError, ReportException, TypeError, ValueError):
        return CODE(traceback.format_exc()).xml()
43

44
    response.view = "plugin_event/grid.html"
45
    return dict(cfg_store=store, grid=grid, title=title)
46 47


48
def metric2d():
49
    """The metric 2D is a 2D table rendered by an ``Ext.grid.Panel`` widget
50 51 52 53 54 55 56 57 58 59 60
    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.

    """
61 62
    extension = request.extension

63
    try:
64
        report = Metric2D(request.vars.id_metric2d)
65

66
        graph = Graph(report)
67 68 69 70 71 72
        title = report.get_title()

        if extension == "html":
            store = report.get_store_configuration()
            grid = report.get_grid_configuration()

73 74 75
            if graph.ax is not None:
                img = graph.to_svg()
                img = urllib.quote(img)
76

77 78 79 80 81 82
                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)
83 84 85

        elif extension == "pdf":
            data = graph.to_pdf()
86
            response.view = "plugin_event/graph.pdf"
87 88 89 90
            return dict(data=data, title=title)

        elif extension == "png":
            data = graph.to_png()
91
            response.view = "plugin_event/graph.png"
92
            return dict(data=data, title=title)
93

94 95
    except (IndexError, ReportException, TypeError, ValueError):
        return CODE(traceback.format_exc()).xml()
96 97 98 99


def source():
    """Display the content of a source via an ``Ext.grid.Panel``.
100
    Values send by the selector are used to filter the content of the source.
101 102

    """
103 104 105 106 107 108 109 110 111 112 113
    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)
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221


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)