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

"""
5
import urllib
6
import json
7
import re
8 9


10 11
from datetime import datetime
from plugin_dbui import get_id, INLINE_ALERT
12 13 14 15 16 17
from plugin_event import (do_title,
                          Graph,
                          List,
                          Metric1D,
                          Metric2D,
                          ReportException)
18
from plugin_event import EvtSelector, SelectorActiveItemsException
19 20


21 22 23 24 25 26
MSG = T(" - %s entry(ies) modified in the history table. <br>"
        " - DO NOT FORGET TO MODIFIED THE REPORT CONFIGURATION.")

MSG_CVT = T(" - Conversion error: %s.")


27 28
def graph_mpl():
    """Plot list or metric using the matplotlib library
LE GAC Renaud's avatar
LE GAC Renaud committed
29

30
    """
LE GAC Renaud's avatar
LE GAC Renaud committed
31
    # selector and configuration
32
    try:
LE GAC Renaud's avatar
LE GAC Renaud committed
33
        ui_table = virtdb.selector
34
        selector = EvtSelector(ui_table)
35 36 37

    except SelectorActiveItemsException as e:
        return INLINE_ALERT % (T("Error..."), T(str(e)))
LE GAC Renaud's avatar
LE GAC Renaud committed
38

LE GAC Renaud's avatar
LE GAC Renaud committed
39
    config = db.graphs[request.vars.id_graphs]
LE GAC Renaud's avatar
LE GAC Renaud committed
40

LE GAC Renaud's avatar
LE GAC Renaud committed
41
    # build the graph
LE GAC Renaud's avatar
LE GAC Renaud committed
42 43
    try:
        graph = Graph(config, selector)
LE GAC Renaud's avatar
LE GAC Renaud committed
44

LE GAC Renaud's avatar
LE GAC Renaud committed
45 46 47 48 49
    except (IndexError, TypeError, ValueError) as e:
        return INLINE_ALERT % ("Error", e)

    # build the report title
    title = do_title(config, selector)
LE GAC Renaud's avatar
LE GAC Renaud committed
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64
    # 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()

LE GAC Renaud's avatar
LE GAC Renaud committed
65
    # delegate the rendering to the view
66
    return dict(data=data, title=title)
67 68


69
def grid():
70
    """Render the report through the C{Dbui.grid.Panel}.
71
    The report can be a list, a metrics 1d or 2d.
LE GAC Renaud's avatar
LE GAC Renaud committed
72

73
    The controller extracts the configuration of the report form the database.
LE GAC Renaud's avatar
LE GAC Renaud committed
74
    It builds the DataFrame, extracts the the configuration
75
    for the C{Ext.data.Store} as well as the C{Dbui.grid.Panel}.
76
    The former contains the data displayed in the grid.
LE GAC Renaud's avatar
LE GAC Renaud committed
77

78 79 80 81
    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.
LE GAC Renaud's avatar
LE GAC Renaud committed
82

83
    """
84
    try:
LE GAC Renaud's avatar
LE GAC Renaud committed
85
        ui_table = virtdb.selector
86
        selector = EvtSelector(ui_table)
LE GAC Renaud's avatar
LE GAC Renaud committed
87

88 89
    except SelectorActiveItemsException as e:
        return INLINE_ALERT % (T("Error..."), T(str(e)))
LE GAC Renaud's avatar
LE GAC Renaud committed
90

91
    # extract the report configuration and the build the report
92 93 94 95
    try:
        if "id_lists" in request.vars:
            config = db.lists[request.vars.id_lists]
            report = List(config, selector)
LE GAC Renaud's avatar
LE GAC Renaud committed
96

97 98 99
        elif "id_metrics1d" in request.vars:
            config = db.metrics1d[request.vars.id_metrics1d]
            report = Metric1D(config, selector)
LE GAC Renaud's avatar
LE GAC Renaud committed
100

101 102 103
        elif "id_metrics2d" in request.vars:
            config = db.metrics2d[request.vars.id_metrics2d]
            report = Metric2D(config, selector)
LE GAC Renaud's avatar
LE GAC Renaud committed
104

105 106 107
        else:
            return INLINE_ALERT % ("Error", "Report id is not defined.")

108
    except (IndexError, ReportException, TypeError, ValueError) as e:
109
        return INLINE_ALERT % ("JSON Error...", e)
LE GAC Renaud's avatar
LE GAC Renaud committed
110

111
    # extract the configurations for the Ext.data.Store and Dbui.grid.Panel.
112 113
    store = report.to_store()
    grid = report.to_grid()
114

115
    # build the report title
116
    title = do_title(config, selector)
LE GAC Renaud's avatar
LE GAC Renaud committed
117

118
    # delegate the grid rendering to the view
119
    return dict(cfg_store=store, grid=grid, title=title)
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


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 synchronization.
    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:

146
        if action != "update":
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
            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:
175 176
        value = model[key]["value"]
        type = model[key]["type"]
177 178 179 180 181

        if value:

            try:
                if type == "boolean":
182
                    value = value.lower() in ("true", "vrai", "y", "yes", "1")
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

                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

205
            model[key]["value"] = value
206 207 208 209 210 211 212 213 214 215 216 217

    # 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:
218
            data[key] = (rd[key] if key in rd else model[key]["value"])
219 220 221 222 223 224 225 226 227

        # 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)