Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / controllers / root.py @ c9245ffc

History | View | Annotate | Download (25.8 KB)

1
# -*- coding: utf-8 -*-
2
# vim:set expandtab tabstop=4 shiftwidth=4: 
3
"""Vigiboard Controller"""
4

    
5
from datetime import datetime
6
from time import mktime
7
import math
8
import urllib
9

    
10
from tg.exceptions import HTTPNotFound
11
from tg import expose, validate, require, flash, \
12
    tmpl_context, request, config, session, redirect, url
13
from tw.forms import validators
14
from pylons.i18n import ugettext as _
15
from pylons.i18n import lazy_ugettext as l_
16
from pylons.controllers.util import abort
17
from sqlalchemy import asc
18
from sqlalchemy.sql import func
19
from repoze.what.predicates import Any, not_anonymous
20

    
21
from vigilo.models.configure import DBSession
22
from vigilo.models import Event, EventHistory, CorrEvent, SupItem, \
23
                            HostGroup, ServiceGroup, StateName, User
24
from vigilo.models.functions import sql_escape_like
25
from vigilo.models.secondary_tables import EVENTSAGGREGATE_TABLE
26

    
27
from vigilo.turbogears.controllers.autocomplete \
28
    import make_autocomplete_controller
29
from vigiboard.controllers.vigiboardrequest import VigiboardRequest
30
from vigiboard.controllers.vigiboard_controller import VigiboardRootController
31
from vigiboard.widgets.edit_event import edit_event_status_options
32
from vigiboard.lib.base import BaseController
33

    
34
__all__ = ('RootController', 'get_last_modification_timestamp', 
35
           'date_to_timestamp')
36

    
37
# pylint: disable-msg=R0201
38
class RootController(VigiboardRootController):
39
    """
40
    Le controller général de vigiboard
41
    """
42
    autocomplete = make_autocomplete_controller(BaseController)
43

    
44
    def process_form_errors(self, *argv, **kwargv):
45
        """
46
        Gestion des erreurs de validation : On affiche les erreurs
47
        puis on redirige vers la dernière page accédée.
48
        """
49
        for k in tmpl_context.form_errors:
50
            flash("'%s': %s" % (k, tmpl_context.form_errors[k]), 'error')
51
        if request.environ.get('HTTP_REFERER') :
52
            redirect(request.environ.get('HTTP_REFERER'
53
                ).split(request.environ.get('HTTP_HOST'))[1])
54
        else :
55
            redirect('/')
56

    
57
    @validate(validators={
58
            'page': validators.Int(min=1),
59
        }, error_handler=process_form_errors)
60
    @expose('events_table.html')
61
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
62
    def default(self, page=1, hostgroup=None, servicegroup=None,
63
            host=None, service=None, output=None, trouble_ticket=None,
64
            from_date=None, to_date=None, *argv, **krgv):
65
        """
66
        Page d'accueil de Vigiboard. Elle affiche, suivant la page demandée
67
        (page 1 par defaut), la liste des événements, rangés par ordre de prise
68
        en compte, puis de sévérité.
69
        Pour accéder à cette page, l'utilisateur doit être authentifié.
70

71
        @param page: Numéro de la page souhaitée, commence à 1
72
        @param host: Si l'utilisateur souhaite sélectionner seulement certains
73
                     événements suivant leur hôte, il peut placer une expression
74
                     ici en suivant la structure du LIKE en SQL
75
        @param service: Idem que host mais sur les services
76
        @param output: Idem que host mais sur le text explicatif
77
        @param trouble_ticket: Idem que host mais sur les tickets attribués
78

79
        Cette méthode permet de satisfaire les exigences suivantes : 
80
        - VIGILO_EXIG_VIGILO_BAC_0040, 
81
        - VIGILO_EXIG_VIGILO_BAC_0050,
82
        - VIGILO_EXIG_VIGILO_BAC_0060,
83
        - VIGILO_EXIG_VIGILO_BAC_0070,
84
        - VIGILO_EXIG_VIGILO_BAC_0100,
85
        - VIGILO_EXIG_VIGILO_BAC_0110.
86
        """
87
        if not page:
88
            page = 1
89

    
90
        username = request.environ['repoze.who.identity']['repoze.who.userid']
91
        user = User.by_user_name(username)
92

    
93
        aggregates = VigiboardRequest(user)
94
        aggregates.add_table(
95
            CorrEvent,
96
            aggregates.items.c.hostname,
97
            aggregates.items.c.servicename
98
        )
99
        aggregates.add_join((Event, CorrEvent.idcause == Event.idevent))
100
        aggregates.add_join((aggregates.items, 
101
            Event.idsupitem == aggregates.items.c.idsupitem))
102
        aggregates.add_order_by(asc(aggregates.items.c.hostname))
103
        
104
        search = {
105
            'host': '',
106
            'service': '',
107
            'output': '',
108
            'tt': '',
109
            'from_date': '',
110
            'to_date': '',
111
            'hostgroup': '',
112
            'servicegroup': '',
113
        }
114

    
115
        # Application des filtres si nécessaire
116
        if hostgroup:
117
            search['hostgroup'] = hostgroup
118
            hostgroup = sql_escape_like(hostgroup)
119
            aggregates.add_join((HostGroup, HostGroup.idgroup == \
120
                aggregates.items.c.idhostgroup))
121
            aggregates.add_filter(HostGroup.name.ilike('%%%s%%' % hostgroup))
122

    
123
        if servicegroup:
124
            search['servicegroup'] = servicegroup
125
            servicegroup = sql_escape_like(servicegroup)
126
            aggregates.add_join((ServiceGroup, ServiceGroup.idgroup == \
127
                aggregates.items.c.idservicegroup))
128
            aggregates.add_filter(
129
                ServiceGroup.name.ilike('%%%s%%' % servicegroup))
130

    
131
        if host:
132
            search['host'] = host
133
            host = sql_escape_like(host)
134
            aggregates.add_filter(aggregates.items.c.hostname.ilike(
135
                '%%%s%%' % host))
136

    
137
        if service:
138
            search['service'] = service
139
            service = sql_escape_like(service)
140
            aggregates.add_filter(aggregates.items.c.servicename.ilike(
141
                '%%%s%%' % service))
142

    
143
        if output:
144
            search['output'] = output
145
            output = sql_escape_like(output)
146
            aggregates.add_filter(Event.message.ilike('%%%s%%' % output))
147

    
148
        if trouble_ticket:
149
            search['tt'] = trouble_ticket
150
            trouble_ticket = sql_escape_like(trouble_ticket)
151
            aggregates.add_filter(CorrEvent.trouble_ticket.ilike(
152
                '%%%s%%' % trouble_ticket))
153

    
154
        if from_date:
155
            search['from_date'] = from_date
156
            try:
157
                # TRANSLATORS: Format de date et heure.
158
                from_date = datetime.strptime(
159
                    from_date, _('%Y-%m-%d %I:%M:%S %p'))
160
            except ValueError:
161
                # On ignore silencieusement la date invalide reçue.
162
                pass
163
            else:
164
                aggregates.add_filter(CorrEvent.timestamp_active >= from_date)
165

    
166
        if to_date:
167
            search['to_date'] = to_date
168
            try:
169
                # TRANSLATORS: Format de date et heure.
170
                to_date = datetime.strptime(
171
                    to_date, _('%Y-%m-%d %I:%M:%S %p'))
172
            except ValueError:
173
                # On ignore silencieusement la date invalide reçue.
174
                pass
175
            else:
176
                aggregates.add_filter(CorrEvent.timestamp_active <= to_date)
177

    
178
        # Calcul des éléments à afficher et du nombre de pages possibles
179
        total_rows = aggregates.num_rows()
180
        items_per_page = int(config['vigiboard_items_per_page'])
181

    
182
        # Si le numéro de page dépasse le nombre de pages existantes,
183
        # on redirige automatiquement vers la 1ère page.
184
        if total_rows and items_per_page * (page-1) > total_rows:
185
            redirect('/', page=1, **search)
186

    
187
        id_first_row = items_per_page * (page-1)
188
        id_last_row = min(id_first_row + items_per_page, total_rows)
189

    
190
        aggregates.format_events(id_first_row, id_last_row)
191
        aggregates.generate_tmpl_context()
192

    
193
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
194
        if not total_rows:
195
            id_first_row = 0
196
        else:
197
            id_first_row += 1
198

    
199
        return dict(
200
            hostname = None,
201
            servicename = None,
202
            events = aggregates.events,
203
            plugins = get_plugins_instances(),
204
            rows_info = {
205
                'id_first_row': id_first_row,
206
                'id_last_row': id_last_row,
207
                'total_rows': total_rows,
208
            },
209
            nb_pages = nb_pages,
210
            page = page,
211
            event_edit_status_options = edit_event_status_options,
212
            search = search,
213
            refresh_times = config['vigiboard_refresh_times'],
214
        )
215

    
216
    @validate(validators={
217
            'idcorrevent': validators.Int(not_empty=True),
218
            'page': validators.Int(min=1),
219
        }, error_handler=process_form_errors)
220
    @expose('raw_events_table.html')
221
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
222
    def masked_events(self, idcorrevent, page=1):
223
        """
224
        Affichage de la liste des événements bruts masqués dans un
225
        événement corrélé (agrégés).
226

227
        @param idevent: identifiant de l'événement souhaité
228
        """
229
        if not page:
230
            page = 1
231

    
232
        username = request.environ['repoze.who.identity']['repoze.who.userid']
233
        events = VigiboardRequest(User.by_user_name(username), False)
234
        events.add_table(
235
            Event,
236
            events.items.c.hostname,
237
            events.items.c.servicename,
238
        )
239
        events.add_join((EVENTSAGGREGATE_TABLE, \
240
            EVENTSAGGREGATE_TABLE.c.idevent == Event.idevent))
241
        events.add_join((CorrEvent, CorrEvent.idcorrevent == \
242
            EVENTSAGGREGATE_TABLE.c.idcorrevent))
243
        events.add_join((events.items, 
244
            Event.idsupitem == events.items.c.idsupitem))
245
        events.add_filter(Event.idevent != CorrEvent.idcause)
246
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
247

    
248
        # Vérification que l'événement existe
249
        total_rows = events.num_rows()
250
        if total_rows < 1:
251
            flash(_('No masked event or access denied'), 'error')
252
            redirect('/')
253

    
254
        # Calcul des éléments à afficher et du nombre de pages possibles
255
        items_per_page = int(config['vigiboard_items_per_page'])
256

    
257
        id_first_row = items_per_page * (page-1)
258
        id_last_row = min(id_first_row + items_per_page, total_rows)
259

    
260
        events.format_events(id_first_row, id_last_row)
261
        events.generate_tmpl_context()
262

    
263
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
264
        if not total_rows:
265
            id_first_row = 0
266
        else:
267
            id_first_row += 1
268

    
269
        return dict(
270
            idcorrevent = idcorrevent,
271
            hostname = None,
272
            servicename = None,
273
            events = events.events,
274
            plugins = get_plugins_instances(),
275
            rows_info = {
276
                'id_first_row': id_first_row,
277
                'id_last_row': id_last_row,
278
                'total_rows': total_rows,
279
            },
280
            nb_pages = nb_pages,
281
            page = page,
282
            search = {
283
                'host': '',
284
                'service': '',
285
                'output': '',
286
                'tt': '',
287
                'from_date': '',
288
                'to_date': '',
289
                'hostgroup': '',
290
                'servicegroup': '',
291
            },
292
           refresh_times=config['vigiboard_refresh_times'],
293
        )
294

    
295
    @validate(validators={
296
            'idevent': validators.Int(not_empty=True),
297
            'page': validators.Int(min=1),
298
        }, error_handler=process_form_errors)
299
    @expose('history_table.html')
300
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
301
    def event(self, idevent, page=1):
302
        """
303
        Affichage de l'historique d'un événement brut.
304
        Pour accéder à cette page, l'utilisateur doit être authentifié.
305

306
        @param idevent: identifiant de l'événement brut souhaité.
307
        @type idevent: C{int}
308

309
        Cette méthode permet de satisfaire
310
        l'exigence VIGILO_EXIG_VIGILO_BAC_0080.
311
        """
312
        if not page:
313
            page = 1
314

    
315
        username = request.environ['repoze.who.identity']['repoze.who.userid']
316
        events = VigiboardRequest(User.by_user_name(username), False)
317
        events.add_table(
318
            Event,
319
            events.items.c.hostname,
320
            events.items.c.servicename,
321
        )
322
        events.add_join((EVENTSAGGREGATE_TABLE, \
323
            EVENTSAGGREGATE_TABLE.c.idevent == Event.idevent))
324
        events.add_join((CorrEvent, CorrEvent.idcorrevent == \
325
            EVENTSAGGREGATE_TABLE.c.idcorrevent))
326
        events.add_join((events.items, 
327
            Event.idsupitem == events.items.c.idsupitem))
328
        events.add_filter(Event.idevent == idevent)
329

    
330
        if events.num_rows() != 1:
331
            flash(_('No such event or access denied'), 'error')
332
            redirect('/')
333

    
334
        events.format_events(0, 1)
335
        events.generate_tmpl_context()
336
        history = events.format_history()
337

    
338
        total_rows = history.count()
339
        items_per_page = int(config['vigiboard_items_per_page'])
340

    
341
        id_first_row = items_per_page * (page-1)
342
        id_last_row = min(id_first_row + items_per_page, total_rows)
343

    
344
        history_entries = history[id_first_row : id_last_row]
345

    
346
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
347
        if not total_rows:
348
            id_first_row = 0
349
        else:
350
            id_first_row += 1
351

    
352
        return dict(
353
            idevent = idevent,
354
            plugins = get_plugins_instances(),
355
            rows_info = {
356
                'id_first_row': id_first_row,
357
                'id_last_row': id_last_row,
358
                'total_rows': total_rows,
359
            },
360
            nb_pages = nb_pages,
361
            page = page,
362
            history = history_entries,
363
            search = {
364
                'host': '',
365
                'service': '',
366
                'output': '',
367
                'tt': '',
368
                'from_date': '',
369
                'to_date': '',
370
                'hostgroup': '',
371
                'servicegroup': '',
372
            },
373
           refresh_times=config['vigiboard_refresh_times'],
374
        )
375

    
376
    @validate(
377
        validators={
378
            'host': validators.NotEmpty(),
379
#            'service': validators.NotEmpty(),
380
            'page': validators.Int(min=1),
381
        }, 
382
        error_handler = process_form_errors)
383
    @expose('events_table.html')
384
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
385
    def item(self, page, host, service=None):
386
        """
387
        Affichage de l'historique de l'ensemble des événements corrélés
388
        jamais ouverts sur l'hôte / service demandé.
389
        Pour accéder à cette page, l'utilisateur doit être authentifié.
390

391
        @param host: Nom de l'hôte souhaité.
392
        @param service: Nom du service souhaité
393

394
        Cette méthode permet de satisfaire
395
        l'exigence VIGILO_EXIG_VIGILO_BAC_0080.
396
        """
397
        idsupitem = SupItem.get_supitem(host, service)
398

    
399
        username = request.environ['repoze.who.identity']['repoze.who.userid']
400
        aggregates = VigiboardRequest(User.by_user_name(username), False)
401
        aggregates.add_table(
402
            CorrEvent,
403
            aggregates.items.c.hostname,
404
            aggregates.items.c.servicename,
405
        )
406
        aggregates.add_join((Event, CorrEvent.idcause == Event.idevent))
407
        aggregates.add_join((aggregates.items, 
408
            Event.idsupitem == aggregates.items.c.idsupitem))
409
        aggregates.add_filter(aggregates.items.c.idsupitem == idsupitem)
410

    
411
        # Vérification qu'il y a au moins 1 événement qui correspond
412
        total_rows = aggregates.num_rows()
413
        if not total_rows:
414
            flash(_('No access to this host/service or no event yet'), 'error')
415
            redirect('/')
416

    
417
        items_per_page = int(config['vigiboard_items_per_page'])
418

    
419
        id_first_row = items_per_page * (page-1)
420
        id_last_row = min(id_first_row + items_per_page, total_rows)
421

    
422
        aggregates.format_events(id_first_row, id_last_row)
423
        aggregates.generate_tmpl_context()
424

    
425
        nb_pages = int(math.ceil(total_rows / (items_per_page + 0.0)))
426
        if not total_rows:
427
            id_first_row = 0
428
        else:
429
            id_first_row += 1
430
        
431
        return dict(
432
            hostname = host,
433
            servicename = service,
434
            events = aggregates.events,
435
            plugins = get_plugins_instances(),
436
            rows_info = {
437
                'id_first_row': id_first_row,
438
                'id_last_row': id_last_row,
439
                'total_rows': total_rows,
440
            },
441
            nb_pages = nb_pages,
442
            page = page,
443
            event_edit_status_options = edit_event_status_options,
444
            search = {
445
                'host': '',
446
                'service': '',
447
                'output': '',
448
                'tt': '',
449
                'from_date': '',
450
                'to_date': '',
451
                'hostgroup': '',
452
                'servicegroup': '',
453
            },
454
            refresh_times=config['vigiboard_refresh_times'],
455
        )
456

    
457
    @validate(validators={
458
        "id": validators.Regex(r'^[0-9]+(,[0-9]+)*,?$'),
459
#        "trouble_ticket": validators.Regex(r'^[0-9]*$'),
460
        "ack": validators.OneOf([
461
            u'NoChange',
462
            u'None',
463
            u'Acknowledged',
464
            u'AAClosed'
465
        ])}, error_handler=process_form_errors)
466
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
467
    def update(self, **krgv):
468
        """
469
        Mise à jour d'un événement suivant les arguments passés.
470
        Cela peut être un changement de ticket ou un changement de statut.
471
        
472
        @param krgv['id']: Le ou les identifiants des événements à traiter
473
        @param krgv['last_modification']: La date de la dernière modification
474
        dont l'utilisateur est au courant.
475
        @param krgv['tt']: Nouveau numéro du ticket associé.
476
        @param krgv['status']: Nouveau status de/des événements.
477

478
        Cette méthode permet de satisfaire les exigences suivantes : 
479
        - VIGILO_EXIG_VIGILO_BAC_0020,
480
        - VIGILO_EXIG_VIGILO_BAC_0060,
481
        - VIGILO_EXIG_VIGILO_BAC_0110.
482
        """
483

    
484
        # On vérifie que des identifiants ont bien été transmis via
485
        # le formulaire, et on informe l'utilisateur le cas échéant.
486
        if krgv['id'] is None:
487
            flash(_('No event has been selected'), 'warning')
488
            raise redirect(request.environ.get('HTTP_REFERER', url('/')))
489

    
490
        # Le filtre permet d'éliminer les chaines vides contenues dans le
491
        # tableau ('a,b,' -> split -> ['a','b',''] -> filter -> ['a','b']).
492
        ids = map(int, filter(len, krgv['id'].split(',')))
493

    
494
        # Si l'utilisateur édite plusieurs événements à la fois,
495
        # il nous faut chacun des identifiants
496
       
497
        username = request.environ['repoze.who.identity']['repoze.who.userid']
498
        events = VigiboardRequest(User.by_user_name(username))
499
        events.add_table(CorrEvent)
500
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
501
        events.add_join((events.items, 
502
            Event.idsupitem == events.items.c.idsupitem))
503
        events.add_filter(CorrEvent.idcorrevent.in_(ids))
504
        
505
        events.generate_request()
506
        idevents = [cause.idcause for cause in events.req]
507
        # Si des changements sont survenus depuis que la 
508
        # page est affichée, on en informe l'utilisateur.
509
        last_modification = get_last_modification_timestamp(idevents, None)
510
        if last_modification and datetime.fromtimestamp(\
511
            float(krgv['last_modification'])) < last_modification:
512
            flash(_('Changes have occurred since the page was last displayed, '
513
                    'your changes HAVE NOT been saved.'), 'warning')
514
            raise redirect(request.environ.get('HTTP_REFERER', url('/')))
515
        
516
        # Vérification que au moins un des identifiants existe et est éditable
517
        if not events.num_rows():
518
            flash(_('No access to this event'), 'error')
519
            redirect('/')
520
        
521
        # Modification des événements et création d'un historique
522
        # pour chacun d'eux.
523
        for req in events.req:
524
            if isinstance(req, CorrEvent):
525
                event = req
526
            else:
527
                event = req[0]
528

    
529
            if krgv['trouble_ticket'] != '' :
530
                history = EventHistory(
531
                        type_action="Ticket change",
532
                        idevent=event.idcause,
533
                        value=krgv['trouble_ticket'],
534
                        text="Changed trouble ticket from '%s' to '%s'" % (
535
                            event.trouble_ticket, krgv['trouble_ticket']
536
                        ),
537
                        username=username,
538
                        timestamp=datetime.now(),
539
                    )
540
                DBSession.add(history)   
541
                event.trouble_ticket = krgv['trouble_ticket']
542

    
543
            if krgv['ack'] != 'NoChange' :
544
                history = EventHistory(
545
                        type_action="Acknowledgement change state",
546
                        idevent=event.idcause,
547
                        value=krgv['ack'],
548
                        text="Changed acknowledgement status "
549
                            "from '%s' to '%s'" % (
550
                            event.status, krgv['ack']
551
                        ),
552
                        username=username,
553
                        timestamp=datetime.now(),
554
                    )
555
                DBSession.add(history)
556
                event.status = krgv['ack']
557

    
558
        DBSession.flush()
559
        flash(_('Updated successfully'))
560
        redirect(request.environ.get('HTTP_REFERER', url('/')))
561

    
562
    @validate(validators={
563
        "plugin_name": validators.OneOf([i[0] for i \
564
            in config.get('vigiboard_plugins', [])]),
565
        'idcorrevent': validators.Int(not_empty=True),
566
        }, error_handler=process_form_errors)
567
    @expose('json')
568
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
569
    def get_plugin_value(self, idcorrevent, plugin_name, *arg, **krgv):
570
        """
571
        Permet de récupérer la valeur d'un plugin associée à un CorrEvent
572
        donné via JSON.
573
        """
574
        plugins = config.get('vigiboard_plugins', {})
575

    
576
        # Permet de vérifier si l'utilisateur a bien les permissions
577
        # pour accéder à cet événement et si l'événement existe.
578
        username = request.environ['repoze.who.identity']['repoze.who.userid']
579
        events = VigiboardRequest(User.by_user_name(username), False)
580
        events.add_table(CorrEvent.idcorrevent)
581
        events.add_join((Event, CorrEvent.idcause == Event.idevent))
582
        events.add_join((events.items, 
583
            Event.idsupitem == events.items.c.idsupitem))
584
        events.add_filter(CorrEvent.idcorrevent == idcorrevent)
585

    
586
        # Pas d'événement ou permission refusée. On ne distingue pas
587
        # les 2 cas afin d'éviter la divulgation d'informations.
588
        if not events.num_rows():
589
            raise HTTPNotFound(_('No such incident or insufficient permissions'))
590

    
591
        plugin_class = [p[1] for p in plugins if p[0] == plugin_name]
592
        if not plugin_class:
593
            raise HTTPNotFound(_('No such plugin'))
594

    
595
        plugin_class = plugin_class[0]
596
        try:
597
            mypac = __import__(
598
                'vigiboard.controllers.plugins.' + plugin_name,
599
                globals(), locals(), [plugin_class], -1)
600
            plugin = getattr(mypac, plugin_class)
601
            if callable(plugin):
602
                return plugin().get_value(idcorrevent, *arg, **krgv)
603
            raise HTTPInternalServerError(_('Not a valid plugin'))
604
        except ImportError:
605
            raise HTTPInternalServerError(_('Plugin could not be loaded'))
606

    
607
    @validate(validators={
608
        "fontsize": validators.Regex(
609
            r'[0-9]+(pt|px|em|%)',
610
            regexOps = ('I',)
611
        )}, error_handler = process_form_errors)
612
    @expose('json')
613
    def set_fontsize(self, fontsize):
614
        """Enregistre la taille de la police dans les préférences."""
615
        session['fontsize'] = fontsize
616
        session.save()
617
        return dict()
618

    
619
    @validate(validators={"refresh": validators.Int()},
620
            error_handler=process_form_errors)
621
    @expose('json')
622
    def set_refresh(self, refresh):
623
        """Enregistre le temps de rafraichissement dans les préférences."""
624
        session['refresh'] = refresh
625
        session.save()
626
        return dict()
627

    
628
    @expose('json')
629
    def set_theme(self, theme):
630
        """Enregistre le thème à utiliser dans les préférences."""
631
        # On sauvegarde l'ID du thème sans vérifications
632
        # car les thèmes (styles CSS) sont définies dans
633
        # les packages de thèmes (ex: vigilo-themes-default).
634
        # La vérification de la valeur est faite dans les templates.
635
        session['theme'] = theme
636
        session.save()
637
        return dict()
638
    
639
def get_last_modification_timestamp(event_id_list, 
640
                                    value_if_none=datetime.now()):
641
    """
642
    Récupère le timestamp de la dernière modification 
643
    opérée sur l'un des événements dont l'identifiant
644
    fait partie de la liste passée en paramètre.
645
    """
646
    last_modification_timestamp = DBSession.query(
647
                                func.max(EventHistory.timestamp),
648
                         ).filter(EventHistory.idevent.in_(event_id_list)
649
                         ).scalar()
650
    if not last_modification_timestamp:
651
        if not value_if_none:
652
            return None
653
        else:
654
            last_modification_timestamp = value_if_none
655
    return datetime.fromtimestamp(mktime(
656
        last_modification_timestamp.timetuple()))
657

    
658
def get_plugins_instances():
659
    """
660
    Renvoie une liste d'instances de plugins pour VigiBoard.
661

662
    @return: Liste de tuples contenant le nom du plugin et l'instance associé.
663
    @rtype: C{list} of C{tuple}
664
    """
665
    plugins = config.get('vigiboard_plugins', [])
666
    plugins_instances = []
667
    for (plugin_name, plugin_class) in plugins:
668
        try:
669
            mypac = __import__(
670
                'vigiboard.controllers.plugins.' + plugin_name,
671
                globals(), locals(), [plugin_class], -1)
672
            plugin = getattr(mypac, plugin_class)
673
            if callable(plugin):
674
                plugins_instances.append((plugin_name, plugin()))
675
        except ImportError:
676
            pass
677
    return plugins_instances
678