source: indico/indico/MaKaC/rb_reservation.py @ c7361e

burotelhello-world-walkthroughipv6v0.97-seriesv0.98-seriesv0.98.2v0.98.3v0.98b1v0.98b2v0.99v1.0v1.1
Last change on this file since c7361e was c7361e, checked in by Jose Benito <jose.benito.gonzalez@…>, 3 years ago

[FIX] Reservation in conferences _p_changed

  • Property mode set to 100644
File size: 73.3 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.
20
21"""
22Part of Room Booking Module (rb_)
23Responsible: Piotr Wlodarek
24"""
25
26from datetime import datetime, timedelta, date
27from MaKaC.rb_tools import iterdays, weekNumber, doesPeriodsOverlap, overlap, Period, Impersistant, checkPresence, fromUTC, toUTC, formatDateTime, formatDate
28from MaKaC.rb_location import ReservationGUID, Location, CrossLocationQueries
29from MaKaC.accessControl import AccessWrapper
30from MaKaC.user import AvatarHolder, Avatar
31from MaKaC.common.Configuration import Config
32from MaKaC.common.info import HelperMaKaCInfo
33from MaKaC.conference import ConferenceHolder
34
35class RepeatabilityEnum( object ):
36    """
37    Enumeration - types of repetition.
38    """
39    daily, onceAWeek, onceEvery2Weeks, onceEvery3Weeks, onceAMonth = range( 5 )
40    # How many days are between consequent repeatings
41    rep2diff = {
42        daily: 1,
43        onceAWeek: 7,
44        onceEvery2Weeks: 14,
45        onceEvery3Weeks: 21
46    }
47
48    rep2description = {
49        None: "Single day",
50        daily: "Daily",
51        onceAWeek: "Once a week",
52        onceEvery2Weeks: "Once every 2 weeks",
53        onceEvery3Weeks: "Once every 3 weeks",
54        onceAMonth: "Once a month",
55    }
56
57class WeekDayEnum( object ):
58    """
59    Enumeration - days of the week.
60    """
61    monday, tuesday, wednesday, thursday, friday, saturday, sunday = range( 7 )
62
63    week2desc = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]
64
65# This text prepends every notification - in general it should be empty
66TEST_VERSION_WARNING = "" #"[This notification considers FAKE event in TEST version of new Indico::CRBS. You can ignore it.]"
67
68# Room booking module notifications will be send to this e-mail when debug is on
69EMAIL_FOR_DEBUG_NOTIFICATIONS = ""
70EMAIL_FROM_PREFIX = "noreply-"
71
72# Room booking module notifications will be send to this e-mail when debug is on
73NOTIFICATION_SUBJECT_PREFIX = "[CRBS] "
74
75# Carbon copy of ALL room booking notifications will be send to this email
76EMAIL_FOR_CATCH_ALL_NOTIFICATIONS = ""
77
78class ReservationBase( object ):
79    """
80    Generic reservation, Data Access Layer independant.
81    Represents physical room reservation.
82    """
83
84    # !=> Properties are in the end of class definition
85
86    # Management -------------------------------------------------------------
87
88    def __init__( self ):
89        """
90        Do NOT insert object into database in the constructor.
91        """
92        pass
93
94    def insert( self ):
95        """
96        Inserts reservation into database (SQL: INSERT).
97        """
98        if self.isCancelled == None: self.isCancelled = False
99        if self.isRejected == None: self.isRejected = False
100        if self.startDT.date() == self.endDT.date():
101            self.repeatability = None
102        self.checkIntegrity()
103
104    def update( self ):
105        """
106        Updates reservation in database (SQL: UPDATE)
107        """
108        if self.startDT.date() == self.endDT.date():
109            self.repeatability = None
110        self.checkIntegrity()
111
112    def remove( self ):
113        """
114        Removes reservation from database (SQL: DELETE)
115        """
116        pass
117
118    def cancel( self ):
119        """
120        FINAL (not intented to be overriden)
121        When user cancels reservation.
122        """
123        self.isCancelled = True
124
125    def reject( self ):
126        """
127        FINAL (not intented to be overriden)
128        When responsible rejects reservation.
129        """
130        self.isRejected = True
131
132    # Notifications ----------------------------------------------------------
133
134    def notifyAboutNewReservation( self ):
135        """
136        FINAL (not intented to be overriden)
137        Notifies (e-mails) user and responsible about creation of a new reservation.
138        Called after insert().
139        """
140        from MaKaC.webinterface.wcomponents import WTemplated
141        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
142        emails = []
143        # ---- Email creator and contact ----
144
145        if self.createdByUser(): # Imported bookings does not have creator
146            to = self.createdByUser().getEmail()
147            firstName = self.createdByUser().getFirstName()
148
149            to2 = self._getContactEmailList()
150
151            if self.isConfirmed:
152                subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] You have made a New Booking on " + formatDateTime(self.startDT)
153                wc = WTemplated( 'RoomBookingEmail_2UserAfterBookingInsertion' )
154            else:
155                subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Your PRE-Booking waits for Acceptance"
156                wc = WTemplated( 'RoomBookingEmail_2UserAfterPreBookingInsertion' )
157            text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'firstName':firstName } )
158            fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
159            addrs = []
160            if debug:
161                addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
162            else:
163                if to :
164                    addrs.append( to )
165                    if to in to2:
166                        to2.remove(to)
167                addrs.extend(to2)
168                if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
169                    addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
170            maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
171            emails.append(maildata)
172
173        # ---- Email responsible(s) ----
174
175        toMain = self.room.getResponsible().getEmail()
176        toCustom = self._getNotificationEmailList()
177
178        if self.isConfirmed:
179            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] New Booking on " + formatDateTime(self.startDT)
180            bookingMessage = "Book"
181        else:
182            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] New PRE-Booking on " + formatDateTime(self.startDT)
183            bookingMessage = "PRE-book"
184        wc = WTemplated( 'RoomBookingEmail_2ResponsibleAfterBookingInsertion' )
185        text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'bookingMessage': bookingMessage } )
186
187        fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
188        addrs = []
189        if debug:
190            addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
191        else:
192            addrs.append( toMain )
193            if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
194                addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
195            if toMain in toCustom :
196                toCustom.remove( toMain )
197            addrs.extend( toCustom )
198        maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
199        emails.append(maildata)
200
201        # ---- Email AVC Support ----
202
203        if self.isConfirmed  and  self.usesAVC: # Inform only about confirmed bookings
204            to = Location.parse( self.locationName ).getAVCSupportEmails()
205            if to:
206                subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] New Booking on " + formatDateTime(self.startDT)
207                wc = WTemplated( 'RoomBookingEmail_2AVCSupportAfterBookingInsertion' )
208                text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation': self } )
209                fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
210                addrs = []
211                if debug:
212                    addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
213                else:
214                    addrs += to
215                    if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
216                        addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
217                maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
218                emails.append(maildata)
219        return emails
220
221    def notifyAboutCancellation( self, date = None ):
222        """
223        FINAL (not intented to be overriden)
224        Notifies (e-mails) user and responsible about reservation cancellation.
225        Called after cancel().
226        """
227        from MaKaC.webinterface.wcomponents import WTemplated
228        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
229        emails = []
230
231        if date:
232            occurrenceText = " (SINGLE OCCURRENCE)"
233            startDate = formatDate(date)
234        else:
235            occurrenceText = ""
236            # Fix by David: include date in this mails too. I have put a try...except in case the date is not accessible in this method
237            try:
238                startDate = formatDateTime(self.startDT)
239            except:
240                startDate = ""
241
242        # ---- Email user ----
243
244        if self.createdByUser(): # Imported bookings does not have creator
245            to = self.createdByUser().getEmail()
246            firstName = self.createdByUser().getFirstName()
247
248            to2 = self._getContactEmailList()
249
250            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Cancellation Confirmation on " + startDate + " %s" % occurrenceText
251            wc = WTemplated( 'RoomBookingEmail_2UserAfterBookingCancellation' )
252            text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'date':date, 'firstName':firstName } )
253            fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
254            addrs = []
255            if debug:
256                addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
257            else:
258                if to :
259                    addrs.append( to )
260                    if to in to2:
261                        to2.remove(to)
262                addrs.extend(to2)
263                if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
264                    addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
265            maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
266            emails.append(maildata)
267
268        # ---- Email responsible ----
269
270        toMain = self.room.getResponsible().getEmail()
271        toCustom = self._getNotificationEmailList()
272
273        subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Cancelled Booking on " + startDate + " %s" % occurrenceText
274        wc = WTemplated( 'RoomBookingEmail_2ResponsibleAfterBookingCancellation' )
275        text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'date':date } )
276        fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
277        addrs = []
278        if debug:
279            addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
280        else:
281            addrs.append( toMain )
282            if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
283                addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
284            if toMain in toCustom :
285                toCustom.remove( toMain )
286            addrs.extend( toCustom )
287        maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
288        emails.append(maildata)
289
290        # ---- Email AVC Support ----
291
292        if self.isCancelled and self.isConfirmed  and  self.usesAVC: # Inform only about confirmed bookings
293            to = Location.parse( self.locationName ).getAVCSupportEmails()
294            if to:
295                subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Booking Cancelled on " + startDate
296                wc = WTemplated( 'RoomBookingEmail_2AVCSupportAfterBookingCancellation' )
297                text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation': self } )
298                fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
299                addrs = []
300                if debug:
301                    addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
302                else:
303                    addrs += to
304                    if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
305                        addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
306                maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
307                emails.append(maildata)
308        return emails
309
310    def notifyAboutRejection( self, date = None, reason = None ):
311        """
312        FINAL (not intented to be overriden)
313        Notifies (e-mails) user and responsible about reservation rejection.
314        Called after reject().
315        """
316        from MaKaC.webinterface.wcomponents import WTemplated
317        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
318        emails = []
319        reason = self.rejectionReason or reason
320
321        if date:
322            occurrenceText = " (SINGLE OCCURRENCE)"
323            startDate = formatDate(date)
324        else:
325            occurrenceText = ""
326            # Fix by David: include date in this mails too. I have put a try...except in case the date is not accessible in this method
327            try:
328                startDate = formatDateTime(self.startDT)
329            except:
330                startDate = ""
331
332        # ---- Email user ----
333
334        if self.createdByUser(): # Imported bookings does not have creator
335            to = self.createdByUser().getEmail()
336            firstName = self.createdByUser().getFirstName()
337
338            to2 = self._getContactEmailList()
339
340            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] REJECTED Booking on " + startDate + " %s" % occurrenceText
341            wc = WTemplated( 'RoomBookingEmail_2UserAfterBookingRejection' )
342            text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'firstName':firstName, 'reason':reason, 'date':date } )
343            fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
344            addrs = []
345            if debug:
346                addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
347            else:
348                if to :
349                    addrs.append(to)
350                    if to in to2:
351                        to2.remove(to)
352                addrs.extend(to2)
353                if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
354                    addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
355            maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
356            emails.append(maildata)
357
358        # ---- Email responsible ----
359
360        toCustom = self._getNotificationEmailList()
361
362        subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Rejected Booking on " + startDate + " %s" % occurrenceText
363        wc = WTemplated( 'RoomBookingEmail_2ResponsibleAfterBookingRejection' )
364        text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'date':date, 'reason':reason } )
365        fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
366        addrs = []
367        if debug:
368            addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
369        else:
370            addrs.extend( toCustom )
371            if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
372                addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
373        maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
374        emails.append(maildata)
375
376        return emails
377
378    def notifyAboutConfirmation( self ):
379        """
380        FINAL (not intented to be overriden)
381        Notifies (e-mails) user about reservation acceptance.
382        Called after reject().
383        """
384        from MaKaC.webinterface.wcomponents import WTemplated
385        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
386        emails = []
387
388        # Fix by David: include date in this mails too. I have put a try...except in case the date is not accessible in this method
389        try:
390            startDate = formatDateTime(self.startDT)
391        except:
392            startDate = ""
393
394        # ---- Email user ----
395
396        if self.createdByUser(): # Imported bookings does not have creator
397            to = self.createdByUser().getEmail()
398            firstName = self.createdByUser().getFirstName()
399
400            to2 = self._getContactEmailList()
401
402            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Confirmed Booking on " + startDate
403            wc = WTemplated( 'RoomBookingEmail_2UserAfterBookingConfirmation' )
404            text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'firstName':firstName } )
405            fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
406            addrs = []
407            if debug:
408                addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
409            else:
410                if to :
411                    addrs.append(to)
412                    if to in to2:
413                        to2.remove(to)
414                addrs.extend(to2)
415                if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
416                    addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
417            maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
418            emails.append(maildata)
419
420        # ---- Email responsible ----
421
422        toCustom = self._getNotificationEmailList()
423
424        subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Confirmed Booking on " + startDate
425        wc = WTemplated( 'RoomBookingEmail_2ResponsibleAfterBookingConfirmation' )
426        text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self } )
427        fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
428        addrs = []
429        if debug:
430            addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
431        else:
432            addrs.extend( toCustom )
433            if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
434                addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
435        maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
436        emails.append(maildata)
437
438        # ---- Email AVC Support ----
439
440        if self.isConfirmed  and  self.usesAVC: # Inform only about confirmed bookings
441            to = Location.parse( self.locationName ).getAVCSupportEmails()
442            if to:
443                subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] New Booking on " + startDate
444                wc = WTemplated( 'RoomBookingEmail_2AVCSupportAfterBookingInsertion' )
445                text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation': self } )
446                fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
447                addrs = []
448                if debug:
449                    addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
450                else:
451                    addrs += to
452                    if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
453                        addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
454                maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
455                emails.append(maildata)
456        return emails
457
458    def notifyAboutUpdate( self ):
459        """
460        FINAL (not intented to be overriden)
461        Notifies (e-mails) user and responsible about reservation update.
462        Called after update().
463        """
464        from MaKaC.webinterface.wcomponents import WTemplated
465        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
466        emails = []
467
468        # Fix by David: include date in this mails too. I have put a try...except in case the date is not accessible in this method
469        try:
470            startDate = formatDateTime(self.startDT)
471        except:
472            startDate = ""
473
474        # ---- Email user ----
475
476        if self.createdByUser(): # Imported bookings does not have creator
477            to = self.createdByUser().getEmail()
478            firstName = self.createdByUser().getFirstName()
479
480            to2 = self._getContactEmailList()
481
482            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Booking Modified on " + startDate
483            wc = WTemplated( 'RoomBookingEmail_2UserAfterBookingModification' )
484            text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'firstName':firstName } )
485            fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
486            addrs = []
487            if debug:
488                addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
489            else:
490                if to :
491                    addrs.append(to)
492                    if to in to2:
493                        to2.remove(to)
494                addrs.extend(to2)
495                if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
496                    addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
497            maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
498            emails.append(maildata)
499
500        # ---- Email responsible ----
501
502        toMain = self.room.getResponsible().getEmail()
503        toCustom = self._getNotificationEmailList()
504
505        subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Booking Modified on " + startDate
506        wc = WTemplated( 'RoomBookingEmail_2ResponsibleAfterBookingModification' )
507        text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self } )
508        fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
509        addrs = []
510        if debug:
511            addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
512        else:
513            addrs.append( toMain )
514            if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
515                addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
516            if toMain in toCustom :
517                toCustom.remove( toMain )
518            addrs.extend( toCustom )
519        maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
520        emails.append(maildata)
521
522        # ---- Email AVC Support ----
523
524        if self.isConfirmed  and  self.usesAVC: # Inform only about confirmed bookings
525            to = Location.parse( self.locationName ).getAVCSupportEmails()
526            if to:
527                subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Modified booking on " + startDate
528                wc = WTemplated( 'RoomBookingEmail_2AVCSupportAfterBookingModification' )
529                text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation': self } )
530                fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
531                addrs = []
532                if debug:
533                    addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
534                else:
535                    addrs += to
536                    if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
537                        addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
538                maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
539                emails.append(maildata)
540
541        return emails
542
543    def requestProlongation( self ):
544        """
545        FINAL (not intented to be overriden)
546        Heavy reservations require user confirmation every x weeks.
547        This method sends user an e-mail, asking him to confirm (prolong)
548        the reservation for the next x weeks.
549        """
550        from MaKaC.webinterface.wcomponents import WTemplated
551        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
552        emails = []
553
554        # Fix by David: include date in this mails too. I have put a try...except in case the date is not accessible in this method
555        try:
556            startDate = formatDateTime(self.startDT)
557        except:
558            startDate = ""
559
560        # ---- Email user ----
561
562        if self.createdByUser(): # Imported bookings does not have creator
563            to = self.createdByUser().getEmail()
564            firstName = self.createdByUser().getFirstName()
565
566            to2 = self._getContactEmailList()
567
568            subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Request for Booking Prolongation on " + startDate
569            wc = WTemplated( 'RoomBookingEmail_2UserRequestProlongation' )
570            text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self, 'firstName':firstName } )
571            fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
572            addrs = []
573            if debug:
574                addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
575            else:
576                if to :
577                    addrs.append(to)
578                    if to in to2:
579                        to2.remove(to)
580                addrs.extend(to2)
581                if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
582                    addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
583            maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
584            emails.append(maildata)
585        return emails
586
587    def notifyAboutLackOfProlongation( self ):
588        """
589        FINAL (not intented to be overriden)
590        Notifies (e-mails) responsible that user
591        did not prolong his HEAVY booking.
592        """
593        from MaKaC.webinterface.wcomponents import WTemplated
594        debug = HelperMaKaCInfo.getMaKaCInfoInstance().isDebugActive()
595        emails = []
596
597        # ---- Email responsible ----
598
599        toMain = self.room.getResponsible().getEmail()
600        toCustom = self._getNotificationEmailList()
601
602        subject = NOTIFICATION_SUBJECT_PREFIX + " [" + self.room.getFullName() + "] Consider Rejecting This Booking"
603        wc = WTemplated( 'RoomBookingEmail_2ResponsibleConsiderRejecting' )
604        text = TEST_VERSION_WARNING + wc.getHTML( { 'reservation':self } )
605        fromAddr = EMAIL_FROM_PREFIX+HelperMaKaCInfo.getMaKaCInfoInstance().getSupportEmail()
606        addrs = []
607        if debug:
608            addrs.append( EMAIL_FOR_DEBUG_NOTIFICATIONS )
609        else:
610            addrs.append( toMain )
611            if EMAIL_FOR_CATCH_ALL_NOTIFICATIONS:
612                addrs.append( EMAIL_FOR_CATCH_ALL_NOTIFICATIONS )
613            if toMain in toCustom :
614                toCustom.remove( toMain )
615            addrs.extend( toCustom )
616        maildata = { "fromAddr": fromAddr, "toList": addrs, "subject": subject, "body": text }
617        emails.append(maildata)
618        return emails
619
620    # Query ------------------------------------------------------------------
621
622    @staticmethod
623    def getReservations( *args, **kwargs ):
624        """
625        Generic, universal query. Returns reservations meeting specified conditions.
626
627        It is 'query by example'. You specify conditions by creating
628        the object and passing it to the method.
629
630        All arguments are optional:
631
632        resvID - reservation ID
633        resvExample - example ReservationBase object
634        rooms - _list_ of RoomBase objects
635
636        Examples:
637
638        # 1. Get all reservations for Dec 2006, booked for Jean
639
640        resvEx = ReservationBase()
641        resvEx.startDT = datetime( 2006, 12, 1, 0 )     # 2006-12-01 00:00
642        resvEx.endDT = datetime( 2006, 12, 31, 23, 59 ) # 2006-12-31 23:59
643        resvEx.bookedForName = "Jean"
644
645        reservations = ReservationBase.getReservations( resvExample = resvEx )
646
647        # 2. Get all reservations of the room "AT AMPHITHEATRE" in Dec 2006
648
649        # copy above, then:
650        room = RoomBase.getRooms( roomName = 'AT AMPHITHEATRE' )
651        reservations = ReservationBase.getReservations( resvExample = resvEx, rooms = [room] )
652        """
653        # Simply redirect to the plugin
654        from MaKaC.rb_factory import Factory
655        return Factory.newReservation().getReservations( **kwargs )
656
657    # Time-play: repeatings, overlapings, etc.
658
659    def getCollisions( self, sansID = None, rooms = None, boolResult = False ):
660        """
661        Returns all collisions with other reservations for the same room,
662        or empty list [] if none collisions were found.
663
664        Collisions are returned as list of Collision objects.
665
666        Reservation having id == sansID is omited (use it to
667        skip conflicts with self).
668        """
669        # IMPLEMENTATION:
670        #
671        # Reservation is possible if for the given room x,
672        # there are no overlaping reservations.
673
674        # Reservation may be seen as a group of 1-day periods.
675        # For non-repeating reservation, it is exactly one period.
676        # For repeating ones, every repeating creates small period (i.e. 4 hours).
677
678        # Two reservations does not overlap <=> there are no overlaping periods.
679
680        # Therefore every period of r1 must be checked against every period of r2
681
682        # 1) Get all reservations that may have impact on the candidate.
683        # 2) Split candidate and other reservations into 1-day periods.
684        # 3) Check every candidate period against every other period.
685        # 4) Remember and return collisions.
686
687        if ( rooms == None and self.room == None ) or self.startDT == None or self.endDT == None:
688            raise 'room, startDT, endDT fields must not be None'
689
690        if rooms == None:
691            rooms = [ self.room ]
692
693        resvEx = ReservationBase()
694        resvEx.startDT = self.startDT
695        resvEx.endDT = self.endDT
696        resvEx.repeatability = self.repeatability
697        resvEx.isRejected = False
698        resvEx.isCancelled = False
699        # In general attributes from self cannot be copied to
700        # searching example. Self is i.e. new booking someone tries
701        # to insert, so it has all attributes.
702        # However, it is safe for isConfirmed == None, because
703        # isConfirmed is None only when explitly set.
704        # Normally isConfirmed defaults to True.
705        if self.isConfirmed == None: # None here is very important; 'False' differs
706            resvEx.isConfirmed = None # Force to include not confirmed bookings
707
708        candidatePeriods = self.splitToPeriods()
709        days = ( period.startDT.date() for period in candidatePeriods )
710
711        from MaKaC.plugins.RoomBooking.CERN.reservationCERN import ReservationCERN
712        resvs = ReservationCERN.getReservations( location = self.locationName,
713                                                      resvExample = resvEx,
714                                                      rooms = rooms,
715                                                      #archival = False,
716                                                      days = days )
717
718        resvs = filter( lambda r: r.id != sansID, resvs )
719
720        collisions = []
721        if len( resvs ) == 0:
722            return [] # No collisions
723
724        potentialColliders = []
725        for resv in resvs:
726            potentialColliders.append( ( resv, resv.splitToPeriods(endDT=self.endDT) ) )
727
728        for r in potentialColliders:
729            resv = r[0]
730            colliderPeriods = r[1]
731            for candidatePeriod in candidatePeriods:
732                for colliderPeriod in colliderPeriods:
733                    if doesPeriodsOverlap( candidatePeriod, colliderPeriod ):
734                        if boolResult:
735                            # There is at least one collision
736                            return True
737                        else:
738                            # Collect collisions
739                            collisions.append( Collision( overlap( candidatePeriod, colliderPeriod ), resv ) )
740
741        return collisions
742
743    def getNextRepeating( self, afterDT = None ):
744        """
745        Returns Period of the next repeating (occurence).
746        For non-repeating reservations simply returns
747        the time of the reservation (also as Period object).
748        Returns None if reservation will never repeat after afterDT.
749        """
750        if not afterDT:
751            afterDT = datetime.now()
752        # Do not look in the specified date
753        afterDT = datetime( afterDT.year, afterDT.month, afterDT.day, 23, 59, 59 )
754
755        # No repeatings after afterDT
756        if afterDT > self.endDT:
757            return None
758
759        # Non-repeating reservation
760        if self.repeatability == None:
761            if afterDT < self.startDT:
762                return Period( self.startDT, self.endDT )
763            else:
764                return None
765
766        # Before first repeating
767        if afterDT < self.startDT:
768            retEndDT = datetime( self.startDT.year, self.startDT.month, self.startDT.day, self.endDT.hour, self.endDT.minute )
769
770            if self.dayIsExcluded( self.startDT.date() ):
771                return self.getNextRepeating( self.startDT )  # Recurrently ask for next
772            return Period( self.startDT, retEndDT )
773
774        # Constant period repeatings
775        if self.repeatability in ( RepeatabilityEnum.daily, RepeatabilityEnum.onceAWeek,
776                                   RepeatabilityEnum.onceEvery2Weeks,
777                                   RepeatabilityEnum.onceEvery3Weeks ):
778            # Number of days between repeatings
779            diff = RepeatabilityEnum.rep2diff[ self.repeatability ]
780            # Candidate for next repeating: next day
781            repCandidateDT = afterDT + timedelta( 1 )
782            # How much to early is the candidate? (days)
783            toEarly = (diff - (( repCandidateDT - self.startDT ).days % diff )) % diff
784            # Add differene
785            repCandidateDT = repCandidateDT + timedelta( toEarly )
786            # Now it should represent next repeating
787            retStartDT = datetime( repCandidateDT.year, repCandidateDT.month, repCandidateDT.day, self.startDT.hour, self.startDT.minute )
788            retEndDT = datetime( repCandidateDT.year, repCandidateDT.month, repCandidateDT.day, self.endDT.hour, self.endDT.minute )
789
790            if self.dayIsExcluded( retStartDT.date() ):
791                return self.getNextRepeating( retStartDT )  # Recurrently ask for next
792            return Period( retStartDT, retEndDT )
793
794        # Monthly repeatings
795        if self.repeatability == RepeatabilityEnum.onceAMonth:
796            # Candidate for next repeating: next day
797            repCandidateDT = afterDT + timedelta( 1 )
798
799            weekDayDiff = self.weekDay - repCandidateDT.weekday()
800            if weekDayDiff < 0:
801                weekDayDiff += 7
802            repCandidateDT += timedelta( weekDayDiff ) # Try next day...
803
804            while weekNumber( repCandidateDT ) != self.weekNumber:
805                repCandidateDT += timedelta( 7 )  # Try next week...
806
807            retStartDT = datetime( repCandidateDT.year, repCandidateDT.month, repCandidateDT.day, self.startDT.hour, self.startDT.minute )
808            retEndDT = datetime( repCandidateDT.year, repCandidateDT.month, repCandidateDT.day, self.endDT.hour, self.endDT.minute )
809
810            if self.dayIsExcluded( retStartDT.date() ):
811                return self.getNextRepeating( retStartDT )  # Recurrently ask for next
812            return Period( retStartDT, retEndDT )
813
814        raise 'Unknown repeatability type.'
815
816    def overlapsOn( self, startDT, endDT ):
817        """
818        Does the reservation overlap on the period (startDT, endDT)?
819
820        This method takes repeatings into consideration.
821
822        Please note that for repeating reservations,
823        startDate and endDate DOES NOT imply the overlaping.
824
825        It is necessary to compute whether specific repeating
826        will fall into the requested period. This computation
827        must include not only subsequent repeatings of the
828        reservation, but also exceptions to those repeatings.
829
830        (Reservation may repeat every week through the whole
831        year, except date1, date2 and date3...).
832        """
833
834        theyOverlap = doesPeriodsOverlap( self.startDT, self.endDT, startDT, endDT )
835        if not theyOverlap:
836            return False
837
838        # NO REPEATINGS
839        if self.repeatability == None:
840            return True    # We do not have to check further
841
842        overlapStartDT, overlapEndDT = overlap( self.startDT, self.endDT, startDT, endDT )
843
844        # DAILY REPEATINGS
845        if self.repeatability == RepeatabilityEnum.daily:
846            for day in iterdays( overlapStartDT, overlapEndDT ):
847                if not self.dayIsExcluded( day.date() ):
848                    return True
849            return False
850
851        weekdaysInPeriod = []
852        for day in iterdays( overlapStartDT, overlapEndDT ):
853            if day.weekday() == self.weekDay:
854                if not self.dayIsExcluded( day.date() ):
855                    weekdaysInPeriod.append( day )
856        if len( weekdaysInPeriod ) == 0:
857            return False
858
859        # ONCE A WEEK
860        if self.repeatability == RepeatabilityEnum.onceAWeek:
861            return True
862
863        # ONCE EVERY 2 OR 3 WEEKS
864        if self.repeatability in ( RepeatabilityEnum.onceEvery2Weeks, RepeatabilityEnum.onceEvery3Weeks ):
865            # Check if candidate weekday is in the overlaping period
866            for weekday in weekdaysInPeriod:
867                if self.repeatability == RepeatabilityEnum.onceEvery2Weeks:
868                    # Check if the weekday is in the SECOND week (not the following week)
869                    # => There must be exactly 14 days between previous repeating
870                    # and the present one. So, the difference between initial repeating
871                    # (startDT) and any next repeating must divide by 14.
872                    if (weekday - self.startDT).days % 14 == 0:
873                        return True
874                if self.repeatability == RepeatabilityEnum.onceEvery3Weeks:
875                    # By analogy to the above, difference must divide by 21.
876                    if (weekday - self.startDT).days % 21 == 0:
877                        return True
878            return False
879
880        # ONCE A MONTH
881        if self.repeatability == RepeatabilityEnum.onceAMonth:
882            requiredWeekNumber = self.weekNumber
883            # Does the period contain weekNumber?
884            for weekday in weekdaysInPeriod:
885                if weekNumber( weekday ) == requiredWeekNumber:
886                    return True
887            return False
888
889        raise "Unknown repeatability type"
890
891    # Excluded days management ----------------------------------------------
892
893    # Repeating reservations repeat in certain period with
894    # specific frequency. However, there may be *exceptions*
895    # to this general pattern. It is possible to *exclude*
896    # some days. It allows to create resvs like:
897    # "Repeat every week through the whole year, except
898    #  date1, date2 and date3".
899    #
900    # The following methods allow to manage 'excluded days' list
901    # for a reservation.
902
903    def getExcludedDays( self ):
904        """
905        Returns list of excluded dates in random order.
906        Example:  [ date1, date2, date3, ... ]
907        Dates are of date type (NOT datetime).
908        """
909        if self.repeatability == None:
910            return 'Not applicable to non-repeating reservations.'
911
912    def setExcludedDays( self, excludedDays ):
913        """
914        Sets list of excluded dates. Accepts:
915        [ date1, date2, date3, ... ]
916        Dates are of date type (NOT datetime).
917        """
918        if self.repeatability == None:
919            return 'Not applicable to non-repeating reservations.'
920        for d in excludedDays:
921            if not isinstance( d, date ):
922                raise 'excludedDays must contain only objects of date type (NOT datetime)'
923
924    def excludeDay( self, dayD ):
925        """
926        Inserts dayD into list of excluded days.
927        dayD should be of date type (NOT datetime).
928        """
929        if self.repeatability == None:
930            return 'Not applicable to non-repeating reservations.'
931        if not isinstance( dayD, date ):
932            raise 'dayD must be of date type (NOT datetime)'
933
934    def includeDay( self, dayD ):
935        """
936        Inserts dayD into list of excluded days.
937        dayD should be of date type (not datetime).
938        """
939        if self.repeatability == None:
940            return 'Not applicable to non-repeating reservations.'
941        if not isinstance( dayD, date ):
942            raise 'dayD must be of date type (NOT datetime)'
943
944    def dayIsExcluded( self, dayD ):
945        """
946        Returns true if dayD is among the excluded days. False otherwise.
947        """
948        if self.repeatability == None:
949            return 'Not applicable to non-repeating reservations.'
950        if not isinstance( dayD, date ):
951            raise 'dayD must be of date type (NOT datetime)'
952
953
954    # Statistical ------------------------------------------------------------
955
956    @staticmethod
957    def countReservations():
958        """
959        Counts reservations meeting specified conditions.
960        Usage: like getReservations(). Tip: for common statistics,
961        there are ready methods, i.e. getNumberOfLiveReservations().
962        """
963        # Simply redirect to the plugin
964        from MaKaC.rb_factory import Factory
965        return Factory.newReservation().countReservations()
966
967    @staticmethod
968    def getNumberOfReservations():
969        """
970        Returns total number of reservations in database.
971        """
972        # Simply redirect to the plugin
973        from MaKaC.rb_factory import Factory
974        return Factory.newReservation().getNumberOfReservations()
975
976    @staticmethod
977    def getNumberOfLiveReservations():
978        """
979        Returns number of live reservations in database.
980        Reservation is live if it has impact in the future.
981        Therefore it is:
982        - not archival
983        - neither cancelled nor rejected
984        """
985        # Simply redirect to the plugin
986        from MaKaC.rb_factory import Factory
987        return Factory.newReservation().getNumberOfLiveReservations()
988
989    @staticmethod
990    def getNumberOfArchivalReservations():
991        """
992        Returns number of archival reservations in database.
993        Reservation is archival if it has end date in the past.
994        Cancelled future reservations are not consider as archival.
995        """
996        # Simply redirect to the plugin
997        from MaKaC.rb_factory import Factory
998        return Factory.newReservation().getNumberOfArchivalReservations()
999
1000    @staticmethod
1001    def getReservationStats( **kwargs ):
1002        """
1003        Used to generate statistics like this:
1004
1005                    Valid      Cancelled      Rejected
1006        Live            x              x             x
1007        Archival        x              x             x
1008
1009        Returns dictionary with the following keys:
1010        liveValid, liveCancelled, liveRejected
1011        archivalValid, archivalCancelled, archivalRejected
1012        """
1013        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
1014        allResvs = ReservationBase.getReservations( location = location )     # Run Forest, run! :)
1015        stats = { \
1016                     'liveValid': 0,
1017                     'liveCancelled': 0,
1018                     'liveRejected': 0,
1019                     'archivalValid': 0,
1020                     'archivalCancelled': 0,
1021                     'archivalRejected': 0,
1022                 }
1023        for r in allResvs:
1024            if r.isArchival:
1025                if r.isCancelled:
1026                    stats['archivalCancelled'] += 1
1027                elif r.isRejected:
1028                    stats['archivalRejected'] += 1
1029                else: # r.isValid (must be)
1030                    stats['archivalValid'] += 1
1031            else: # live
1032                if r.isCancelled:
1033                    stats['liveCancelled'] += 1
1034                elif r.isRejected:
1035                    stats['liveRejected'] += 1
1036                else: # r.isValid (must be)
1037                    stats['liveValid'] += 1
1038
1039        return stats
1040
1041    @staticmethod
1042    def getRoomReservationStats(room):
1043        """
1044        Used to generate statistics like this:
1045
1046                    Valid      Cancelled      Rejected
1047        Live            x              x             x
1048        Archival        x              x             x
1049
1050        Returns dictionary with the following keys:
1051        liveValid, liveCancelled, liveRejected
1052        archivalValid, archivalCancelled, archivalRejected
1053        """
1054        location = Location.getDefaultLocation().friendlyName
1055        allResvs = ReservationBase.getReservations( rooms = [ room ] )     # Run Forest, run! :)
1056        stats = { \
1057                     'liveValid': 0,
1058                     'liveCancelled': 0,
1059                     'liveRejected': 0,
1060                     'archivalValid': 0,
1061                     'archivalCancelled': 0,
1062                     'archivalRejected': 0,
1063                 }
1064        for r in allResvs:
1065            if r.isArchival:
1066                if r.isCancelled:
1067                    stats['archivalCancelled'] += 1
1068                elif r.isRejected:
1069                    stats['archivalRejected'] += 1
1070                else: # r.isValid (must be)
1071                    stats['archivalValid'] += 1
1072            else: # live
1073                if r.isCancelled:
1074                    stats['liveCancelled'] += 1
1075                elif r.isRejected:
1076                    stats['liveRejected'] += 1
1077                else: # r.isValid (must be)
1078                    stats['liveValid'] += 1
1079        return stats
1080
1081    @staticmethod
1082    def findSoonest( resvs, afterDT = datetime.now() ):
1083        if not resvs: return None
1084
1085        nextRepeating = datetime( 2050, 01, 01 )
1086        ret = resvs[0]
1087        for r in resvs:
1088            nrep = r.getNextRepeating( afterDT = afterDT )
1089            if nrep and nrep.startDT < nextRepeating:
1090                nextRepeating = nrep.startDT
1091                ret = r
1092        return ret
1093
1094
1095    # "System" ---------------------------------------------------------------
1096
1097    def checkIntegrity( self ):
1098        """
1099        FINAL (not intented to be overriden)
1100        Checks whether:
1101        - all required attributes has values
1102        - values are of correct type
1103        - semantic coherence (i.e. star date <= end date)
1104        """
1105        from MaKaC.rb_room import RoomBase
1106        # list of errors
1107        errors = []
1108
1109        # locationName - derived
1110        # guid - derived
1111        # weekDay - derived
1112        # weekNumber - derived
1113
1114        # check presence and types of arguments
1115        # =====================================================
1116        if self.id != None:         # Only for existing objects
1117            checkPresence( self, errors, 'id', int )
1118        checkPresence( self, errors, 'room', RoomBase )
1119        checkPresence( self,errors, '_utcStartDT', datetime )
1120        checkPresence( self,errors, '_utcEndDT', datetime )
1121        checkPresence( self,errors, '_utcCreatedDT', datetime )
1122        if self.repeatability != None:
1123            checkPresence( self, errors, 'repeatability', int )
1124        checkPresence( self, errors, 'bookedForName', str )
1125        #checkPresence( self, errors, 'contactEmail', str )
1126        #checkPresence( self, errors, 'contactPhone', str )
1127        #checkPresence( self, errors, 'createdBy', int )
1128        checkPresence( self, errors, 'reason', str )
1129        checkPresence( self, errors, 'reason', str )
1130
1131        checkPresence( self, errors, 'isRejected', bool )
1132        checkPresence( self, errors, 'isCancelled', bool )
1133
1134        # check semantic integrity
1135        # =====================================================
1136        # start < end
1137        if self.startDT > self.endDT:
1138            errors.append( 'startDT must be before endDT' )
1139        if self.startDT.time() > self.endDT.time():
1140            errors.append( 'startT must be before endT' )
1141        # room exists
1142        roomID = self.room.id
1143        roomLocation = self.room.locationName
1144        rooms = CrossLocationQueries.getRooms( location = roomLocation, roomID = roomID )
1145        if not rooms:
1146            errors.append ( 'room ' + self.room.guid + ' not found in db' )
1147        # collisions (only for valid ones)
1148        if self.isValid:
1149            if self.id:
1150                col = self.getCollisions( sansID = self.id )
1151            else:
1152                col = self.getCollisions()
1153            if len( col ) > 0:
1154                #errors.append ( 'the reservation collides with existing ones' )
1155                print "Candidate: --------------------------------"
1156                print self
1157                print "Collision 1: ------------------------------"
1158                print col[0].withReservation
1159
1160        if errors:
1161            raise str( errors )
1162
1163    # Indico architecture  AND  authorization -----------------------
1164
1165    __owner = None
1166
1167    def getLocator( self ):
1168        """
1169        FINAL (not intented to be overriden)
1170        Returns a globaly unique identification
1171        encapsulated in a Locator object
1172        """
1173        owner = self.getOwner()
1174        if owner:
1175            loc = owner.getLocator()
1176        else:
1177            from MaKaC.common.Locators import Locator
1178            loc = Locator()
1179        # There is no something like "resvLocation" since
1180        # location of a reservation always equals location
1181        # of a room it concerns.
1182        loc["roomLocation"] = self.locationName
1183        loc["resvID"] = self.id
1184        return loc
1185
1186    def setOwner( self, owner ):
1187        """
1188        FINAL (not intented to be overriden)
1189        Owner in terms of "parent", i.e. conference
1190        """
1191        self.__owner = None
1192        if owner:
1193            self.__owner = owner.getId()#Impersistant( owner )
1194
1195    def getOwner( self ):
1196        """
1197        FINAL (not intented to be overriden)
1198        Owner in terms of "parent", i.e. conference
1199        """
1200        ####---FIXING THE USE OF IMPERSISTANT CLASS-----
1201        if isinstance(self.__owner, Impersistant):
1202            o = self.__owner.getObject()
1203            if o:
1204                self.__owner=o.getId()
1205            else:
1206                self.__owner=None
1207        ####---ENDO OF FIXING THE USE OF IMPERSISTANT CLASS-----
1208        if self.__owner:
1209            return ConferenceHolder().getById(self.__owner) #self.__owner.getObject() # Wrapped in Impersistant
1210
1211        return None
1212
1213    def isProtected( self ):
1214        """
1215        FINAL (not intented to be overriden)
1216        The one must be logged in to do anything in RB module.
1217        """
1218        return True
1219
1220    def canView( self, accessWrapper ):
1221        """
1222        FINAL (not intented to be overriden)
1223        Reservation details are public - anyone who is
1224        authenticated can view.
1225        """
1226        return True
1227
1228    def canModify( self, accessWrapper ):
1229        """
1230        FINAL (not intented to be overriden)
1231        The following persons are authorized to modify a booking:
1232        - owner (the one who created the booking)
1233        - responsible for a room
1234        - admin (of course)
1235        """
1236        if accessWrapper == None:
1237            return False
1238
1239        user = None
1240        if isinstance( accessWrapper, AccessWrapper ):
1241            user = accessWrapper.getUser()
1242        elif isinstance( accessWrapper, Avatar ):
1243            user = accessWrapper
1244        else:
1245            raise 'canModify requires either AccessWrapper or Avatar object'
1246        if not user:
1247            return False
1248        can = user.isAdmin() or \
1249            self.isOwnedBy( user ) or \
1250            self.room.isOwnedBy( user )
1251        return can
1252
1253    def canCancel( self, user ):
1254        """ Owner can cancel """
1255        if user == None:
1256            return False
1257        return self.isOwnedBy( user ) or user.isAdmin()
1258
1259    def canReject( self, user ):
1260        """ Responsible can reject """
1261        if user == None:
1262            return False
1263        return self.room.isOwnedBy( user ) or user.isAdmin()
1264
1265    def canDelete( self, user ):
1266        """ Only admin can delete """
1267        if user == None:
1268            return False
1269        return user.isAdmin()
1270
1271    def isOwnedBy( self, avatar ):
1272        """
1273        Returns True if avatar is the one who inserted this
1274        reservation. False otherwise.
1275        """
1276        if not self.createdBy:
1277            return None
1278        if self.createdBy == avatar.id:
1279            return True
1280        return False
1281
1282    # Required by Indico architecture
1283    def getAccessKey( self ): return ""
1284
1285    def createdByUser( self ):
1286        if self.createdBy == None:
1287            return None
1288        user = AvatarHolder().getById( self.createdBy )
1289        if user == None:
1290            return None
1291        return user
1292
1293
1294    def splitToPeriods( self, endDT = None, startDT = None ):
1295        """
1296        Returns the list of Periods that represent this reservation.
1297
1298        For non-repeating reservations it is just the reservation period.
1299        For repeating ones, the list will include all repeatings.
1300        """
1301        if startDT is None:
1302            lastDT = self.startDT - timedelta( 1 )   # One day before the beginning
1303        else:
1304            lastDT = startDT - timedelta( 1 )
1305
1306        periods = []
1307
1308        while True:
1309            period = self.getNextRepeating( lastDT )
1310            if period is None or (endDT != None and period.startDT > endDT):
1311                return periods
1312            lastDT = period.startDT
1313            periods.append( period )
1314
1315
1316    # ==== Private ===================================================
1317
1318    # datetime (DT) converters --------
1319
1320    def _getStartDT( self ):
1321        return fromUTC( self._utcStartDT )
1322
1323    def _setStartDT( self, localNaiveDT ):
1324        self._utcStartDT = toUTC( localNaiveDT )
1325
1326    def _getEndDT( self ):
1327        return fromUTC( self._utcEndDT )
1328
1329    def _setEndDT( self, localNaiveDT ):
1330        self._utcEndDT = toUTC( localNaiveDT )
1331
1332    def _getCreatedDT( self ):
1333        return fromUTC( self._utcCreatedDT )
1334
1335    def _setCreatedDT( self, localNaiveDT ):
1336        self._utcCreatedDT = toUTC( localNaiveDT )
1337
1338    # --------------------------------
1339
1340    def _getIsValid( self ):
1341        """
1342        FINAL (not intented to be overriden)
1343        """
1344        if self.isRejected == None or self.isCancelled == None or self.isConfirmed == None:
1345            return None
1346        if self.isConfirmed and not self.isRejected and not self.isCancelled:
1347            return True
1348        return False
1349
1350    def _isArchival( self ):
1351        """
1352        FINAL (not intented to be overriden)
1353        """
1354        if self.endDT == None:
1355            return None
1356        return self.endDT < datetime.now()
1357
1358    def _isHeavy( self ):
1359        """
1360        Defines when reservation is considered "heavy".
1361        """
1362        if self.endDT == None or self.startDT == None or self.room == None:
1363            return None
1364
1365        # Conditions of heavines - the booking:
1366        # 1. Is for room which is publically reservable AND
1367        # 2. Is repeating AND
1368        # 3. Lasts longer than one month AND
1369        # 4. Takes more than x hours monthly
1370
1371        if not self.room.isReservable:
1372            return False
1373        if self.repeatability == None:
1374            return False
1375        if ( self.endDT - self.startDT ).days < 30:
1376            return False
1377
1378        HOURS_MONTHLY_TO_CONSIDER_HEAVY = 15
1379        periods = self.splitToPeriods()
1380        totalHours = 0.0
1381        for p in periods:
1382            totalHours += ( p.endDT - p.startDT ).seconds / 3600.0
1383        hoursPerMonth = totalHours / ( self.endDT - self.startDT ).days * 30
1384        if hoursPerMonth < HOURS_MONTHLY_TO_CONSIDER_HEAVY:
1385            return False
1386
1387        return True
1388
1389    def _getWeekDay( self ):
1390        """
1391        FINAL (not intented to be overriden)
1392        """
1393        if self.startDT != None and self.repeatability in (
1394            RepeatabilityEnum.onceAWeek, RepeatabilityEnum.onceEvery2Weeks,
1395            RepeatabilityEnum.onceEvery3Weeks, RepeatabilityEnum.onceAMonth ):
1396            return self.startDT.weekday()
1397        return None
1398
1399    def _getWeekNumber( self ):
1400        if self.repeatability == RepeatabilityEnum.onceAMonth and self.startDT != None:
1401            return weekNumber( self.startDT )
1402        return None
1403
1404    def _getLocationName( self ):
1405        if self.__class__.__name__ == 'ReservationBase':
1406            return None #Location.getDefaultLocation().friendlyName
1407        return self._getLocationName() # Subclass
1408
1409    def _getGuid( self ):
1410        if self.locationName == None:
1411            return None
1412        return ReservationGUID( Location.parse( self.locationName ), self.id )
1413
1414    def _getContactEmailList( self ):
1415        """
1416        Util method used for returning the contact emails in a list in case
1417        the contact email string contains more than one address
1418        """
1419        if self.contactEmail != None and self.contactEmail != "":
1420            return self.contactEmail.split(",")
1421        else :
1422            return []
1423
1424    def _getNotificationEmailList( self ):
1425        """
1426        Util method used for returning the notification emails in a list in case
1427        the notification email custom attribute string contains more than one address
1428        """
1429        addrs = []
1430        addr = self.room.customAtts.get( 'notification email', "" ).strip()
1431        if addr:
1432                addrs = addr.split(',')
1433
1434        return addrs
1435
1436    def _eval_str( self, s ):
1437        ixPrv = 0
1438        ret = ""
1439
1440        while True:
1441            ix = s.find( "#{", ixPrv )
1442            if ix == -1:
1443                break
1444            ret += s[ixPrv:ix] # verbatim
1445            ixPrv = s.index( "}", ix + 2 ) + 1
1446            try:
1447                ret += str( eval( s[ix+2:ixPrv-1] ) )
1448            except: pass
1449
1450        return ret
1451
1452    def __str__( self ):
1453        return self._eval_str(
1454"""
1455         location: #{self.locationName}
1456               id: #{self.id}
1457
1458             room: #{self.room.name}
1459           starDT: #{self.startDT}
1460            endDT: #{self.endDT}
1461        createdDT: #{self.createdDT}
1462
1463    repeatability: #{self.repeatability}
1464          weekDay: #{self.weekDay}
1465       weekNumber: #{self.weekNumber}
1466
1467    bookedForName: #{self.bookedForName}
1468     contactEmail: #{self.contactEmail}
1469     contactPhone: #{self.contactPhone}
1470        createdBy: #{self.createdBy}
1471           reason: #{self.reason}
1472
1473       isRejected: #{self.isRejected}
1474      isCancelled: #{self.isCancelled}
1475
1476      isConfirmed: #{self.isConfirmed}
1477          isValid: #{self.isValid}
1478       isArchival: #{self.isArchival}
1479          isHeavy: #{self.isHeavy}
1480"""
1481        )
1482
1483    def _getVerboseRepetition( self ):
1484        s = RepeatabilityEnum.rep2description[ self.repeatability ]
1485        if self.weekDay != None:
1486            s += " on %s" % WeekDayEnum.week2desc[self.weekDay]
1487
1488        return s
1489
1490    def _getVerboseStatus( self ):
1491        s = ""
1492        if self.isValid:
1493            s += "Valid"
1494        else:
1495            if self.isCancelled:
1496                s += " Cancelled"
1497            if self.isRejected:
1498                s += " Rejected"
1499            if not self.isConfirmed:
1500                s += " Not&nbsp;confirmed"
1501        if self.isArchival:
1502            s += " Archival"
1503        else:
1504            s += " Live"
1505        if self.isHeavy:
1506            s += " Heavy"
1507        s = ', '.join( s.strip().split( ' ' ) )
1508
1509        return s
1510
1511    def _getVerboseCreatedBy( self ):
1512        user = self.createdByUser()
1513        if user == None:
1514            return ""
1515        return user.getFullName()
1516
1517
1518    def __cmp__( self, other ):
1519        if self.__class__.__name__ == 'NoneType' and other.__class__.__name__ == 'NoneType':
1520            return 0
1521        if self.__class__.__name__ == 'NoneType':
1522            return cmp( None, 1 )
1523        if other.__class__.__name__ == 'NoneType':
1524            return cmp( 1, None )
1525
1526        if self.id != None  and  other.id != None:
1527            if self.id == other.id:
1528                return 0
1529            else:
1530                return cmp(self.id, other.id)
1531
1532        c = cmp( self.room, other.room )
1533        if c == 0 and self.startDT and self.endDT and other.startDT and other.endDT:
1534            yesterday = datetime.now() - timedelta( 1 )
1535            nrSelf = self.getNextRepeating( afterDT = yesterday )
1536            nrOther = other.getNextRepeating( afterDT = yesterday )
1537            if nrSelf or nrOther:
1538                if nrSelf == None:
1539                    return cmp( 1, None )
1540                if nrOther == None:
1541                    return cmp( None, 1 )
1542                c = cmp( nrSelf.startDT, nrOther.startDT )
1543        return c
1544
1545    # ==== Properties ===================================================
1546
1547    # DO NOT set default values here, since query-by-example will change!!!
1548
1549    # Attributes that take part in similarity comparison
1550
1551    id = None             # int - artificial ID; initialy value from oracle db
1552    locationName = property( _getLocationName ) # location (plugin) name
1553    guid = property( _getGuid ) # ReservationGUID
1554
1555    # Basic
1556
1557    room = None           # RoomBase - room that is reserved
1558
1559    # DO NOT use these directly!
1560    # Use properties instead
1561    _utcStartDT = None
1562    _utcEndDT = None
1563    _utcCreatedDT = None
1564
1565    startDT = property( _getStartDT, _setStartDT )    # datetime - when the reservation starts; internally UTC, accepted and returned in local/DST
1566    endDT = property( _getEndDT, _setEndDT )      # datetime - when the reservation ends; internally UTC, accepted and returned in local/DST
1567    createdDT = property( _getCreatedDT, _setCreatedDT )  # datetime - when the booking was created; internally UTC, accepted and returned in local/DST
1568
1569    # Repeatability
1570
1571    repeatability = None  # int - one of the RepeatabilityEnum
1572    weekDay = property( _getWeekDay )   # int - one of the WeekDayEnum
1573    # weekNumber - int, 1-5, in which week of the month the repeating takes place.
1574    # Applicable only for repeatability == OnceAMonth:
1575    weekNumber = property( _getWeekNumber )
1576
1577    # Who and why
1578    bookedForName = None  # str - for whom it is booked; free text
1579    contactEmail = None   # str - contact; typically the person for whom the booking is done
1580    contactPhone = None   # str - contact; typically the person for whom the booking is done
1581    createdBy = None      # str/avatar_id - who has created the booking
1582    reason = None         # str - justification for booking
1583    rejectionReason = None# str - justification for rejection
1584
1585    # Status
1586    isConfirmed = True    # bool - whether the booking is confirmed  (defaults to True to keep semantic of existing code)
1587    isRejected = None     # bool - whether was rejected by responsible
1588    isCancelled = None    # bool - whether was cancelled by user
1589
1590    # Reservation is valid when neither rejected nor cancelled
1591    isValid = property( _getIsValid )
1592
1593    # Reservation is archival if endDT is in the past
1594    isArchival = property( _isArchival )
1595
1596    # Whether it uses Audio Visual Conferencing equipment
1597    usesAVC = None
1598    needsAVCSupport = None
1599
1600    # True if reservation requires repeating confirmations
1601    isHeavy = property( _isHeavy )
1602
1603    # ==== Verbose Properties ================================================
1604
1605    # These are read-only, "redundant" properties designated for end user (GUI)
1606    # Therefore their purpose is to return human-readable text
1607
1608    verboseRepetition = property( _getVerboseRepetition )
1609    verboseStatus = property( _getVerboseStatus )
1610    verboseCreatedBy = property( _getVerboseCreatedBy )
1611
1612class Collision( object ):
1613    """
1614    Simple helper class for storing information about collision:
1615    - with whom (which reservation) the collision was noticed
1616    - overlaping period (max 1 day!)
1617    """
1618    withReservation = None # With which reservation
1619    startDT         = None # Overlaping period - begin
1620    endDT           = None # Overlaping period - end
1621
1622    def __init__( self, periodTuple, resv ):
1623        self.startDT, self.endDT = periodTuple
1624        self.withReservation = resv
1625
1626# ============================================================================
1627# ================================== TEST ====================================
1628# ============================================================================
1629
1630class Test:
1631
1632    @staticmethod
1633    def weekNumber():
1634        resv = ReservationBase()
1635        resv.startDT = datetime( 2006, 9, 22 ) # Fourth Friday
1636        assert( resv.weekNumber == None )      # No repeating
1637        resv.repeatability = RepeatabilityEnum.onceAMonth
1638        assert( resv.weekNumber == 4 )
1639        resv.startDT = datetime( 2006, 9, 30 )
1640        assert( resv.weekNumber == 5 )
1641        resv.startDT = datetime( 2006, 9, 1 )
1642        assert( resv.weekNumber == 1 )
1643
1644    @staticmethod
1645    def overlapsOn():
1646        resv = ReservationBase()
1647
1648        resv.startDT = datetime( 2006, 1, 1, 10 )
1649        resv.endDT = datetime( 2006, 8, 31, 12 )
1650
1651        # Dates does not overlap
1652        assert( not resv.overlapsOn( datetime( 2006, 9, 1 ) , datetime( 2006, 9, 30 ) ) )
1653        # Hours does not overlap
1654        assert( not resv.overlapsOn( datetime( 2006, 8, 31, 12, 1 ) , datetime( 2006, 9, 30, 13 ) ) )
1655        # Overlap
1656        assert( resv.overlapsOn( datetime( 2006, 8, 31, 9 ) , datetime( 2006, 9, 30, 11 ) ) )
1657
1658        # ONCE A WEEK
1659        resv.repeatability = RepeatabilityEnum.onceAWeek
1660
1661        # Weekdays does not overlap (Sunday vs Monday:Saturday)
1662        assert( not resv.overlapsOn( datetime( 2006, 8, 7, 10 ), datetime( 2006, 8, 12, 12 ) ) )
1663        # Overlap
1664        assert( resv.overlapsOn( datetime( 2006, 8, 7, 10 ), datetime( 2006, 8, 13, 12 ) ) )
1665
1666        # ONCE EVERY 2 WEEKS
1667        resv.repeatability = RepeatabilityEnum.onceEvery2Weeks
1668
1669        # The following week
1670        assert( not resv.overlapsOn( datetime( 2006, 1, 8, 10 ), datetime( 2006, 1, 14, 12 ) ) )
1671        # Overlap
1672        assert( resv.overlapsOn( datetime( 2006, 1, 15, 10 ), datetime( 2006, 1, 15, 12 ) ) )
1673
1674        # ONCE EVERY 3 WEEKS
1675        resv.repeatability = RepeatabilityEnum.onceEvery3Weeks
1676
1677        # The following week
1678        assert( not resv.overlapsOn( datetime( 2006, 1, 8, 10 ), datetime( 2006, 1, 14, 12 ) ) )
1679        # The second week
1680        assert( not resv.overlapsOn( datetime( 2006, 1, 15, 10 ), datetime( 2006, 1, 15, 12 ) ) )
1681        # The 6'th week (OK)
1682        assert( resv.overlapsOn( datetime( 2006, 2, 10, 10 ), datetime( 2006, 2, 12, 12 ) ) )
1683
1684        # ONCE A MONTH
1685        resv.repeatability = RepeatabilityEnum.onceAMonth
1686
1687        # The following week
1688        assert( not resv.overlapsOn( datetime( 2006, 1, 8, 10 ), datetime( 2006, 1, 14, 12 ) ) )
1689        # The second week
1690        assert( not resv.overlapsOn( datetime( 2006, 1, 15, 10 ), datetime( 2006, 1, 15, 12 ) ) )
1691        # Next month, bad day
1692        assert( not resv.overlapsOn( datetime( 2006, 2, 6, 10 ), datetime( 2006, 2, 28, 12 ) ) )
1693        # 4 months later, OK
1694        assert( resv.overlapsOn( datetime( 2006, 5, 6, 10 ), datetime( 2006, 5, 8, 12 ) ) )
1695
1696        resv.startDT = datetime( 2006, 1, 29 )
1697        assert( not resv.overlapsOn( datetime( 2006, 2, 1, 10 ), datetime( 2006, 3, 31, 12 ) ) )
1698        assert( resv.overlapsOn( datetime( 2006, 4, 20, 10 ), datetime( 2006, 4, 30, 12 ) ) )
1699
1700    @staticmethod
1701    def statistics():
1702        from MaKaC.rb_factory import Factory
1703        Factory.getDALManager().connect()
1704
1705        print "All reservations: %d" % ReservationBase.getNumberOfReservations()
1706        print "Archival: %d" % ReservationBase.getNumberOfArchivalReservations()
1707        print "Live: %d" % ReservationBase.getNumberOfLiveReservations()
1708
1709        Factory.getDALManager().disconnect()
1710
1711    @staticmethod
1712    def getNextRepeating():
1713        from MaKaC.rb_factory import Factory
1714        Factory.getDALManager().connect()
1715
1716        # Every x days (daily or every 1-3 weeks)
1717        resv = ReservationBase.getReservations( resvID = 393966 )
1718
1719        period = resv.getNextRepeating( datetime( 2005, 2, 6 ) )
1720        okPeriod = Period( datetime( 2007, 1, 9, 8, 30 ), datetime( 2007, 1, 9, 20 ) )
1721        assert( period == okPeriod )
1722
1723        period = resv.getNextRepeating( datetime( 2007, 1, 9 ) )
1724        okPeriod = Period( datetime( 2007, 1, 16, 8, 30 ), datetime( 2007, 1, 16, 20 ) )
1725        assert( period == okPeriod )
1726
1727        period = resv.getNextRepeating( datetime( 2007, 1, 22 ) )
1728        okPeriod = Period( datetime( 2007, 1, 23, 8, 30 ), datetime( 2007, 1, 23, 20 ) )
1729        assert( period == okPeriod )
1730
1731        # Every month
1732        resv = ReservationBase.getReservations( resvID = 371163 )
1733
1734        period = resv.getNextRepeating( datetime( 2006, 2, 12 ) )
1735        okPeriod = Period( datetime( 2006, 2, 13, 10 ), datetime( 2006, 2, 13, 12 ) )
1736        assert( period == okPeriod )
1737
1738        period = resv.getNextRepeating( datetime( 2006, 2, 13 ) )
1739        okPeriod = Period( datetime( 2006, 3, 13, 10 ), datetime( 2006, 3, 13, 12 ) )
1740        assert( period == okPeriod )
1741
1742        period = resv.getNextRepeating( datetime( 2006, 4, 9 ) )
1743        okPeriod = Period( datetime( 2006, 4, 10, 10 ), datetime( 2006, 4, 10, 12 ) )
1744        assert( period == okPeriod )
1745
1746        Factory.getDALManager().disconnect()
1747
1748    @staticmethod
1749    def splitToPeriods():
1750        from MaKaC.rb_factory import Factory
1751        Factory.getDALManager().connect()
1752
1753        # Every x days (daily or every 1-3 weeks)
1754        resv = ReservationBase.getReservations( resvID = 393966 )
1755        periods = resv.splitToPeriods()
1756        assert( len( periods )  == 50 )
1757
1758        # Every month
1759        resv = ReservationBase.getReservations( resvID = 371163 )
1760        periods = resv.splitToPeriods()
1761        assert( len( periods ) == 11 )
1762
1763        Factory.getDALManager().disconnect()
1764
1765    @staticmethod
1766    def getCollisions():
1767        from MaKaC.rb_factory import Factory
1768        from MaKaC.rb_room import RoomBase
1769        Factory.getDALManager().connect()
1770
1771        # Every x days (daily or every 1-3 weeks)
1772        #resv = ReservationBase.getReservations( resvID = 393966 )
1773        #resv = Factory.newReservation()
1774        #resv = ReservationBase()
1775        #resv.startDT = datetime( 2006, 10, 2, 10 )
1776        #resv.endDT = datetime( 2006, 10, 4, 15 )
1777        #resv.room = RoomBase.getRooms( roomID = 4 )
1778        #resv.repeatability = RepeatabilityEnum.daily
1779
1780        resv = Factory.newReservation()
1781        resv.startDT = datetime( 2006, 12, 1, 8, 30 )
1782        resv.endDT = datetime( 2006, 12, 2, 9, 30 )
1783        resv.room = RoomBase.getRooms( roomID = 89 )
1784        resv.repeatability = RepeatabilityEnum.daily
1785
1786        collisions = resv.getCollisions()
1787        print "\n\nFound %d collisions.\n" % len( collisions )
1788        for col in collisions:
1789            print "Collision: ======================================================== "
1790            print "Period: ", col[0]
1791            print "With reservation: ", col[1].id
1792
1793        Factory.getDALManager().disconnect()
1794
1795    @staticmethod
1796    def tmp():
1797        # Available rooms
1798        from MaKaC.rb_factory import Factory
1799        from MaKaC.rb_room import RoomBase
1800        from MaKaC.common.db import DBMgr
1801        DBMgr.getInstance().startRequest()
1802        Factory.getDALManager().connect()
1803
1804        candResv = ReservationBase()
1805        candResv.startDT = datetime( 2007, 03, 2, 00, 01 )
1806        candResv.endDT = datetime( 2007, 06, 2, 23, 55 )
1807        candResv.room = RoomBase.getRooms( roomName = '40-4-C01' )
1808        candResv.repeatability = RepeatabilityEnum.onceAWeek
1809
1810        print "!"
1811        t0 = datetime.now()
1812        for i in xrange( 0, 140 ):
1813            candResv.getCollisions( boolResult = True )
1814        t1 = datetime.now()
1815        print "! " + str( t1 - t0 )
1816
1817        Factory.getDALManager().disconnect()
1818        DBMgr.getInstance().endRequest()
1819
1820    @staticmethod
1821    def addUsesAVC():
1822        # Available rooms
1823        from MaKaC.rb_factory import Factory
1824        from MaKaC.common.db import DBMgr
1825        DBMgr.getInstance().startRequest()
1826        Factory.getDALManager().connect()
1827
1828        for resv in CrossLocationQueries.getReservations():
1829            resv.usesAVC = False
1830
1831        Factory.getDALManager().commit()
1832        Factory.getDALManager().disconnect()
1833        DBMgr.getInstance().endRequest()
1834
1835    @staticmethod
1836    def addNeedsAVCSupport():
1837        # Available rooms
1838        from MaKaC.rb_factory import Factory
1839        from MaKaC.common.db import DBMgr
1840        DBMgr.getInstance().startRequest()
1841        Factory.getDALManager().connect()
1842
1843        for resv in CrossLocationQueries.getReservations():
1844            resv.needsAVCSupport = False
1845
1846        Factory.getDALManager().commit()
1847        Factory.getDALManager().disconnect()
1848        DBMgr.getInstance().endRequest()
1849
1850    @staticmethod
1851    def addLocationAVCSupportEmails():
1852        # Available rooms
1853        from MaKaC.rb_factory import Factory
1854        from MaKaC.common.db import DBMgr
1855        DBMgr.getInstance().startRequest()
1856        Factory.getDALManager().connect()
1857
1858        for location in Location.allLocations:
1859            location._avcSupportEmails = []
1860
1861        Location.parse( "CERN" ).setAVCSupportEmails( ['collaborative-service@cern.ch'] )
1862
1863        Factory.getDALManager().commit()
1864        Factory.getDALManager().disconnect()
1865        DBMgr.getInstance().endRequest()
1866
1867    @staticmethod
1868    def simba():
1869        from MaKaC.rb_factory import Factory
1870        from MaKaC.common.db import DBMgr
1871        DBMgr.getInstance().startRequest()
1872        Factory.getDALManager().connect()
1873
1874
1875        AvatarHolder().invalidateRoomManagerIdList()
1876
1877
1878        Factory.getDALManager().commit()
1879        Factory.getDALManager().disconnect()
1880        DBMgr.getInstance().endRequest()
1881
1882    @staticmethod
1883    def assignBookings2DanieleLajust():
1884        from MaKaC.rb_factory import Factory
1885        from MaKaC.common.db import DBMgr
1886        DBMgr.getInstance().startRequest()
1887        Factory.getDALManager().connect()
1888
1889        from MaKaC.rb_room import RoomBase
1890
1891        print "Connected..."
1892
1893        avatar = AvatarHolder().match( { "email": "Daniele.Lajust@cern.ch" } )[0]
1894
1895        resvEx = ReservationBase()
1896        resvEx.isConfirmed = None
1897        resvEx.bookedForName = "daniele lajust"
1898
1899        allResvs = CrossLocationQueries.getReservations( resvExample = resvEx )
1900        for r in allResvs:
1901            r.createdBy = avatar.id
1902        #print len( allResvs )
1903
1904        Factory.getDALManager().commit()
1905        Factory.getDALManager().disconnect()
1906        DBMgr.getInstance().endRequest()
1907
1908if __name__ == '__main__':
1909    #Test.getNextRepeating()
1910    #Test.splitToPeriods()
1911    #Test.weekNumber()
1912    #Test.overlapsOn()
1913    #Test.statistics()
1914    #Test.getCollisions()
1915    #Test.addUsesAVC()
1916    Test.addLocationAVCSupportEmails()
1917    #Test.addNeedsAVCSupport()
1918    #Test.problem()
1919    #Test.assignBookings2DanieleLajust()
1920    pass
Note: See TracBrowser for help on using the repository browser.