source: indico/indico/MaKaC/plugins/RoomBooking/notifications.py @ dc8ef7

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.98b2v0.99v1.0v1.1
Last change on this file since dc8ef7 was dc8ef7, checked in by Jose Benito <jose.benito.gonzalez@…>, 23 months ago

[MIX] Added migration for RBlocking and some REF

  • Property mode set to 100644
File size: 7.8 KB
Line 
1# -*- coding: utf-8 -*-
2##
3##
4## This file is part of CDS Indico.
5## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6##
7## CDS Indico is free software; you can redistribute it and/or
8## modify it under the terms of the GNU General Public License as
9## published by the Free Software Foundation; either version 2 of the
10## License, or (at your option) any later version.
11##
12## CDS Indico is distributed in the hope that it will be useful, but
13## WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15## General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20from indico.core.extpoint.reservation import IReservationListener, IReservationStartStopListener
21from indico.core.extpoint.base import Component
22from zope.interface import implements
23from datetime import datetime, timedelta
24from persistent import Persistent
25from indico.modules.scheduler.client import Client
26from indico.modules.scheduler import tasks
27from MaKaC.plugins.base import Observable, PluginsHolder
28from MaKaC.webinterface.wcomponents import WTemplated
29from MaKaC.common.utils import getEmailList, formatDateTime
30from MaKaC.common.info import HelperMaKaCInfo
31from MaKaC.common.mail import GenericMailer
32from MaKaC.webinterface.mail import GenericNotification
33from MaKaC.plugins.RoomBooking.common import getRoomBookingOption
34from MaKaC.webinterface import urlHandlers
35
36class ReservationStartEndNotificationListener(Component):
37    implements(IReservationListener)
38
39    def reservationCreated(self, resv):
40        if getRoomBookingOption('notificationEnabled'):
41            resv.getStartEndNotification().resvCreated()
42
43    def reservationUpdated(self, resv):
44        if getRoomBookingOption('notificationEnabled'):
45            resv.getStartEndNotification().resvUpdated()
46
47    def reservationDeleted(self, resv):
48        pass
49
50class ReservationStartEndEmailListener(Component):
51    implements(IReservationStartStopListener)
52
53    def reservationStarted(self, obj, resv):
54        sendReservationStartStopNotification(resv, 'start')
55
56    def reservationFinished(self, obj, resv):
57        sendReservationStartStopNotification(resv, 'end')
58
59def sendReservationStartStopNotification(resv, which):
60    if which == 'start' and resv.room.resvStartNotification:
61        msg = getRoomBookingOption('startNotificationEmail')
62        subject = getRoomBookingOption('startNotificationEmailSubject')
63    elif which == 'end' and resv.room.resvEndNotification:
64        msg = getRoomBookingOption('endNotificationEmail')
65        subject = getRoomBookingOption('endNotificationEmailSubject')
66    else:
67        return
68
69    av = resv.bookedForUser
70    if av:
71        bookedForDetails = '%s %s\n%s' % (av.getFullName(), av.getPersonId() or '', av.getEmail())
72    else:
73        bookedForDetails = '%s\n%s' % (resv.bookedForName, resv.contactEmail)
74
75    msgArgs = {
76        'bookedForUser': bookedForDetails,
77        'roomName': resv.room.getFullName(),
78        'roomAtts': resv.room.customAtts,
79        'bookingStart': formatDateTime(resv.startDT),
80        'bookingEnd': formatDateTime(resv.endDT),
81        'detailsLink': urlHandlers.UHRoomBookingBookingDetails.getURL(resv)
82    }
83
84    recipients = []
85    recipients += getRoomBookingOption('notificationEmails')
86    if getRoomBookingOption('notificationEmailsToBookedFor'):
87        recipients += getEmailList(resv.contactEmail)
88    maildata = {
89        'fromAddr': HelperMaKaCInfo.getMaKaCInfoInstance().getNoReplyEmail(returnSupport=True),
90        'toList': recipients,
91        'subject': subject.format(**msgArgs),
92        'body': msg.format(**msgArgs)
93    }
94    GenericMailer.send(GenericNotification(maildata))
95
96class ReservationStartEndNotification(Persistent, Observable):
97
98    def __init__(self, resv):
99        minutes = getRoomBookingOption('notificationBefore')
100        self._notificationBefore = timedelta(minutes=minutes)
101        self._startActionTriggered = False
102        self._endActionTriggered = False
103        self._resv = resv
104        self._startDT = self._resv.getLocalizedStartDT() - self._notificationBefore
105        self._endDT = self._resv.getLocalizedEndDT()
106        self._hasTasks = False
107
108    def resvCreated(self):
109        if self._resv.endDT > datetime.now():
110            self.createTasks()
111
112    def resvUpdated(self):
113        #self._startActionTriggered = self._endActionTriggered = False # XXX REMOVE THIS LATER XXX
114        # Event was created in the past and is still in the past -> don't do anything
115        if not self._hasTasks and self._resv.endDT <= datetime.now():
116            return
117        if self._startDT != self._resv.getLocalizedStartDT() - self._notificationBefore:
118            self._startDT = self._resv.getLocalizedStartDT() - self._notificationBefore
119            if self._endActionTriggered:
120                if self._resv.startDT > datetime.now():
121                    # If the booking had finished but it was changed to start in the future, we can safely re-execute both actions
122                    self._startActionTriggered = False
123                    self._endActionTriggered = False
124                # In any other case we don't need to change flags
125            if not self._startActionTriggered:
126                self.createTasks('start')
127        if self._endDT != self._resv.getLocalizedEndDT() or not self._hasTasks:
128            self._endDT = self._resv.getLocalizedEndDT()
129            if not self._endActionTriggered:
130                self.createTasks('end')
131
132    def createTasks(self, which=None):
133        # We cannot get an ID for the task here and since RB can use a separate DB we also cannot store the task itself in the DB.
134        # So we have no way of modifying/removing our task when the booking is updated/cancelled/rejected.
135        # Instead we always add a new task and the task checks if it's valid when being executed
136        # If a notification has been sent before, we don't send a new one (e.g. if someone changes the end time of an expired event)
137        self._hasTasks = True
138        cl = Client()
139        if not which or which == 'start':
140            cl.enqueue(tasks.RoomReservationStartedTask(self._resv, self._startDT))
141        if not which or which == 'end':
142            cl.enqueue(tasks.RoomReservationFinishedTask(self._resv, self._endDT))
143
144    def taskTriggered(self, which, task):
145        if not self._shouldTriggerActions(task.getStartOn(), which, task.getLogger()):
146            return
147        if which == 'start':
148            self._notify('reservationStarted', self._resv)
149        elif which == 'end' and self.isActionTriggered('start'):
150            self._notify('reservationFinished', self._resv)
151        self.actionTriggered(which)
152
153    def _shouldTriggerActions(self, now, which, logger):
154        if which == 'start' and self._startDT != now:
155            logger.info('Reservation %s did not start yet (%s != %s), not triggering actions' % (self._resv.guid, self._startDT, now))
156            return False
157        elif which == 'end' and self._endDT != now:
158            logger.info('Reservation %s did not finish yet (%s != %s), not triggering actions' % (self._resv.guid, self._endDT, now))
159            return False
160        elif self.isActionTriggered(which):
161            logger.info('Reservation %s already had its %s actions sent, not triggering actions' % (self._resv.guid, which))
162            return False
163        return True
164
165    def actionTriggered(self, which):
166        if which == 'start':
167            self._startActionTriggered = True
168        elif which == 'end':
169            self._endActionTriggered = True
170
171    def isActionTriggered(self, which):
172        if which == 'start':
173            return self._startActionTriggered
174        elif which == 'end':
175            return self._endActionTriggered
176
177
178
179
180
Note: See TracBrowser for help on using the repository browser.