layout.html 12.5 KB
Newer Older
LE GAC Renaud's avatar
LE GAC Renaud committed
1
{{
2 3 4 5

    #--------------------------------------------------------------------------
    # The python controller return:
    #   - collection_logs (list of MsgCollection) one for each collection
6
    #   - harvester (Storage/Row)
7 8
    #   - logs (list of MSg) one for each publication
    #   - selector (Selector)
9 10
    #--------------------------------------------------------------------------
    #
11
    # prepare the data:
12 13
    #    - configure the Ext.data.Array for the statistics
    #    - configure the Ext.data.Array for the logs
14 15 16 17
    #    - the title of the page
    #    - the DIV block
    #    - the HTML page footer
    #    - export python variable to the javascript
18 19
    #
    #--------------------------------------------------------------------------
20
    import gluon
21
    import json
22
    from datetime import datetime
23

24 25 26 27 28
    #
    # configure the Ext.data.Array for the statistics
    # transform the collection_logs in a series of counters per collection
    #
    cfg = dict(sorters=['collection'])
29

30 31 32 33 34 35 36 37 38 39 40
    cfg['fields'] = [{'name': 'add', 'type': 'int'},
                     {'name': 'error', 'type': 'string'},
                     {'name': 'found', 'type': 'int'},
                     {'name': 'idle', 'type': 'int'},
                     {'name': 'lost', 'type': 'int'},
                     {'name': 'modify', 'type': 'int'},
                     {'name': 'reject', 'type': 'int'},
                     {'name': 'title', 'type': 'string'},
                     {'name': 'url', 'type': 'string'}]

    cfg['data'] = []
41

42
    for collection in collection_logs:
43

44 45
        # count database action for this collection
        count = Storage(idle=0, load=0, modify=0, process=0, reject=0)
46

47 48 49 50 51 52
        for row in logs:
            if row.collection == collection.title:
                count.process += 1
                count[row.action] += 1
            pass
        pass
53

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
        stat = [count.load,
                collection.error,
                collection.found,
                count.idle,
                collection.found - count.process,
                count.modify,
                count.reject,
                collection.title,
                collection.url_hb()]

        cfg['data'].append(stat)
    pass

    cfg_statStore = json.dumps(cfg)

    #
    # configure the Ext.data.Array for the logs
    #
    cfg = dict(groupField='txt',
               sorters=['txt', 'title'])
74

75 76
    cfg['fields'] = [{'name': 'action', 'type': 'string'},
                     {'name': 'collection', 'type': 'string'},
77 78 79 80
                     {'name': 'title', 'type': 'string'},
                     {'name': 'txt', 'type': 'string'},
                     {'name': 'url', 'type': 'string'},
                     {'name': 'year', 'type': 'string'}]
81

82 83
    cfg['data'] = []
    for row in logs:
84 85
        cfg['data'].append([row.action,
                            row.collection,
86
                            row.title,
87 88 89 90
                            row.txt,
                            row.url,
                            row.year])
    pass
91

92 93 94
    cfg_logsStore = json.dumps(cfg)

    #
95
    # Write the Title on the HTML page
96
    #
97 98 99
    period = ""
    if selector.year_start and selector.year_end:
        period = T("from %s to %s") % (selector.year_start, selector.year_end)
LE GAC Renaud's avatar
LE GAC Renaud committed
100

101 102
    elif selector.year_start:
        period = str(selector.year_start)
103

104
    pass
105

106 107 108 109
    project = ""
    if selector.id_projects:
        project = db.projects[selector.id_projects].project
    pass
110

111
    title = "%s %s %s" % (harvester.controller.title(), project, period)
112

113 114 115 116 117
    style = ["font-size: 12px;",
             "font-variant: small-caps;",
             "margin-left: 10px;",
             "margin-bottom: 3ex;",
             "margin-top: 2ex;"]
118

119
    response.write(P(title, _style="".join(style)))
120

121 122 123
    #
    # the DIV blocks, one for each  grid
    #
124 125
    divstat = "grid-%s" % id(cfg_statStore)
    divlogs = "grid-%s" % id(cfg_logsStore)
126

127 128
    response.write(DIV(_id=divstat))
    response.write(DIV(_id=divlogs))
129

130 131 132 133 134
    #
    # the footer, processing time of the request
    #
    delta = (datetime.now()-request.now).total_seconds()
    delta = T('Harvester took %s seconds') % round(delta, 2)
135

136 137 138 139
    style = ["font-size: 10px;",
             "margin-left: 10px;",
             "margin-bottom: 3ex;",
             "margin-top: 2ex;"]
140

141 142
    response.write(P(delta, _style="".join(style)))

143 144 145 146 147 148 149 150
    #
    # convert the harvester
    # either Storage (run_all) or a Row depending on the controller
    #
    if isinstance(harvester, gluon.dal.Row):
        harvester = harvester.as_dict()
    pass

151 152 153 154 155
    #
    # Export python variables to the javascript
    #
    jsvars = ["cfgStatStore = %s" % cfg_statStore,
              "cfgLogsStore = %s" % cfg_logsStore,
156 157 158 159
              "divstat = '%s'" % divstat,
              "divlogs = '%s'" % divlogs,
              "gridStat",
              "gridLog",
160
              "harvester = %s" % json.dumps(harvester),
161
              "menu",
162 163 164
              "trAction = '%s'" % T('Action'),
              "trAdded = '%s'" % T('Added'),
              "trCollection = '%s'" % T('Collection'),
165
              "trEditAndInsert = '%s'" % T('Edit and insert'),
166 167
              "trError = '%s'" % T('Error'),
              "trExisting = '%s'" % T('Existing'),
LE GAC Renaud's avatar
LE GAC Renaud committed
168
              "trFailure = '%s'" % T('Loading failed'),
169
              "trFound = '%s'" % T('Found'),
LE GAC Renaud's avatar
LE GAC Renaud committed
170
              "trLoading = '%s'" % T('Loading...'),
171 172 173 174
              "trLost = '%s'" % T('Lost'),
              "trModified = '%s'" % T('Modified'),
              "trRejected = '%s'" % T('Rejected'),
              "trTitle = '%s'" % T('Title'),
175
              "trTxt ='%s'" % T('Message'),
176 177 178 179 180 181 182 183
              "trStatistics = '%s'" % T('Statistics'),
              "trYear = '%s'" % T('Year')]

    jsvars = "    var %s;" % ',\n'.join(jsvars)
    response.write(SCRIPT(jsvars), escape=False)

}}
<script type="text/javascript">
184 185

    /**
186 187 188 189 190 191
     * Launch the Edit and Insert wizard
     */
    function onEditAndInsert() {

        "use strict";

192
        var li,
193 194 195 196 197 198 199 200 201 202 203
            mask,
            record = this.gridLog.getSelectionModel().getSelection()[0],
            record_id;

        // mask the main ViewPort during the data transfer
        //
        // NOTE 141112:
        // the loadMask option of the component should do the job
        // but I never succeed with Ext JS 4.2.1. Therefore it is done by hand
        //
        mask = Ext.create('Ext.LoadMask', {
LE GAC Renaud's avatar
LE GAC Renaud committed
204
            msg: trLoading,
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
            target: Ext.ComponentQuery.query('xviewport')[0]
        });
        mask.show();

        // extract the record identifier from the publication URL
        // e.g "http://cds.cern.ch/record/1692474"
        li = record.get('url').split("/");
        record_id = li[li.length - 1];

        // launch the EditAndInsert wizard using a loader
        // NOTE 141112: the success function is used to remove the mask
        //
        Ext.create('Ext.Component', {
            loader: {
                url: 'harvest/edit_insert',
                params: {
                    Edit_insert_selectorId_teams: this.harvester.id_teams,
                    Edit_insert_selectorId_projects: this.harvester.id_projects,
223
                    Edit_insert_selectorHost: this.harvester.host,
224 225 226 227 228 229
                    Edit_insert_selectorRecord_id: record_id,
                    Edit_insert_selectorController: this.harvester.controller,
                    Edit_insert_selectorId_categories: this.harvester.id_categories
                },
                autoLoad: true,
                failure: function () {
LE GAC Renaud's avatar
LE GAC Renaud committed
230
                    Ext.Msg.alert(trError, trFailure);
231 232 233 234 235 236 237 238 239 240 241 242 243 244
                },
                loadMask: false,
                scripts: true,
                success: function () {
                    mask.hide();
                },
                timeout: 60000
            },
            renderTo: Ext.getBody()
      });
    }

    /**
     * Renderer the publication title as an URL
245 246 247 248
     *
     * @param {Object} value
     * @param {Object} metadata
     * @param {Ext.data.Model} record
249 250 251 252
     * @param {Number} rowIndex
     * @param {Number} colIndex
     * @param {Ext.data.Store} store
     * @param {Ext.view.view} view
253 254
     *
     */
255
    function rTitle(value, metadata, record) {
LE GAC Renaud's avatar
LE GAC Renaud committed
256 257 258 259 260 261 262 263 264 265 266 267 268

        var pattern = '<a href="{0}" target="_blank">{1}</a>',
            rep,
            title = record.get('title'),
            url = record.get('url');

        if (url.length > 0) {
            rep = Ext.String.format(pattern, url, title);
        } else {
            rep = title;
        }

        return rep;
269 270
    }

271 272 273 274 275 276 277 278 279 280 281 282
    /**
     * Renderer the total label
     *
     * @param {Object} value
     * @param {Object} metadata
     * @param {Ext.data.Model} record
     * @param {Number} rowIndex
     * @param {Number} colIndex
     * @param {Ext.data.Store} store
     * @param {Ext.view.view} view
     *
     */
283 284 285 286 287
    function rTotal() {
        return 'Total';
    }

    //
288
    // instantiate the grid for the STAT
289
    //
290
    gridStat = Ext.create('Ext.grid.Panel', {
291 292 293 294 295 296 297 298
        title: trStatistics,
        store: Ext.create('Ext.data.ArrayStore', cfgStatStore),
        columns: [
            {xtype: 'rownumberer'},
            {
                text: trCollection,
                dataIndex: 'title',
                flex: 4,
LE GAC Renaud's avatar
LE GAC Renaud committed
299
                renderer: rTitle,
300 301
                summaryRenderer: rTotal
            },
302 303 304 305 306 307 308
            {align: 'right', text: trFound, dataIndex: 'found', flex: 1, summaryType: 'sum'},
            {align: 'right', text: trExisting, dataIndex: 'idle', flex: 1, summaryType: 'sum'},
            {align: 'right', text: trAdded, dataIndex: 'add', flex: 1, summaryType: 'sum'},
            {align: 'right', text: trModified, dataIndex: 'modify', flex: 1, summaryType: 'sum'},
            {align: 'right', text: trRejected, dataIndex: 'reject', flex: 1, summaryType: 'sum'},
            {align: 'right', text: trLost, dataIndex: 'lost', flex: 1, summaryType: 'sum'},
            {align: 'right', text: trError, dataIndex: 'error', flex: 1}
309
        ],
LE GAC Renaud's avatar
LE GAC Renaud committed
310
        collapsible: true,
311 312
        features: [{ftype:'summary'}],
        forceFit: true,
313
        padding: "0 40 20 30",
314
        renderTo: divstat
315 316
    });

317
    //
318
    // instantiate the grid for the LOGs
319
    // The rows are group per action message and sorted by title.
320 321
    // Each title is rendered as an URL pointing to the publication
    // in the harvester repository
322
    //
323
    gridLog = Ext.create('Ext.grid.Panel', {
324 325 326
        store: Ext.create('Ext.data.ArrayStore', cfgLogsStore),
        columns: [
            {xtype: 'rownumberer'},
327
            {text: trTitle, dataIndex: 'title', flex: 3, renderer: rTitle},
328 329
            {text: trAction, dataIndex: 'action', flex: 1, hidden: true},
            {text: trTxt, dataIndex: 'txt', flex: 1, hidden: true},
330 331 332 333 334 335 336
            {text: trCollection, dataIndex: 'collection', flex: 1, hidden: true},
            {text: trYear, dataIndex: 'year', flex: 1, hidden: true}
        ],
        features: [
            {ftype:'grouping', groupHeaderTpl: '{name}', startCollapsed: true}
        ],
        forceFit: true,
337
        padding: "10 40 20 30",
338 339 340 341 342 343 344 345 346
        renderTo: divlogs
    });

    //
    // instantiate the context menu to launch the "Edit and Insert" wizard
    //
    menu = Ext.create('Ext.menu.Menu', {
        items : [{
            handler: onEditAndInsert,
347 348 349 350
            iconCls: 'xaction-create',
            itemId: 'editAndInsert',
            scope: this,
            text: trEditAndInsert
351 352 353 354 355
        }]
    });

    gridLog.on('itemcontextmenu', function(view, record, item, index, event) {
        event.stopEvent();
356 357 358 359

        // FIX EXT JS 4.2.1
        // In that version the selection of the row is wrong when the grouping
        // feature is activated. It has been corrected in 4.2.2.
LE GAC Renaud's avatar
LE GAC Renaud committed
360
        // The next lines fix this bug.
361
        // (from www.sencha.com/forum/showthread.php?264961-Grid-Grouping-Bug)
LE GAC Renaud's avatar
LE GAC Renaud committed
362 363 364
        index = item.getAttribute('data-recordid');
        view.getSelectionModel().select(view.dataSource.data.get(index));
        record = view.getSelectionModel().getSelection()[0];
365

366 367 368
        // the editAndInsert action is only available when the record is
        // not already in the database
        if (record.get('action') === 'idle') {
369
            menu.items.get('editAndInsert').setDisabled(true);
370 371
        } else {
            menu.items.get('editAndInsert').setDisabled(false);
372 373 374
        }

        // show the menu
375 376 377 378 379 380 381 382 383 384 385
        menu.showAt(event.getXY());
    });

    // inhibit context menu on the grid header
    gridLog.on("headercontextmenu", function(ct, column, event){
        event.stopEvent();
    });

    // inhibit context menu on the rest of the panel
    gridLog.body.up('.x-panel-body').on('contextmenu', function(event) {
        event.stopEvent();
386 387
    });

388
</script>