[FIX] Do not change room.isReservable DB filed
[cds-indico.git] / indico / MaKaC / rb_room.py
blob5a4866aa85ecb4f75cbd063dec73599b748d73d2
1 # -*- coding: utf-8 -*-
2 ##
3 ##
4 ## This file is part of Indico.
5 ## Copyright (C) 2002 - 2012 European Organization for Nuclear Research (CERN).
6 ##
7 ## 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 3 of the
10 ## License, or (at your option) any later version.
12 ## 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.
17 ## You should have received a copy of the GNU General Public License
18 ## along with Indico;if not, see <http://www.gnu.org/licenses/>.
19 from MaKaC.conference import ConferenceHolder
21 """
22 Part of Room Booking Module (rb_)
23 """
25 from MaKaC.plugins.RoomBooking.common import rb_check_user_access
26 from MaKaC.rb_tools import Impersistant, checkPresence, iterdays
27 from MaKaC.rb_location import Location, RoomGUID, CrossLocationQueries
28 from MaKaC.user import Avatar, AvatarHolder
29 from MaKaC.accessControl import AccessWrapper
30 from MaKaC.errors import MaKaCError
31 from datetime import datetime, timedelta
33 from zope.interface import implements
36 class RoomBase( object ):
37 """
38 Generic room, Data Access Layer independant.
39 Represents physical room suitable for meetings and/or conferences.
40 """
42 def __init__( self ):
43 """
44 Do NOT insert object into database in the constructor.
45 """
46 self._name = None
47 self._photoId = None
48 self._locationName = None
50 def insert( self ):
51 """
52 Inserts room into database
53 """
54 self.checkIntegrity()
56 def update( self ):
57 """
58 Updates room in database
59 """
60 self.checkIntegrity()
62 def remove( self ):
63 """
64 Removes room from database
65 """
66 pass
68 def notifyAboutResponsibility( self ):
69 """
70 FINAL (not intented to be overriden)
71 Notifies (e-mails) previous and new responsible about
72 responsibility change. Called after creating/updating the room.
73 """
74 pass
76 # Query ------------------------------------------------------------------
78 @staticmethod
79 def getRooms( *args, **kwargs ):
80 """
81 Returns list of rooms meeting specified conditions.
83 It is 'query by example'. You specify conditions by creating
84 the object and passing it to the method.
86 All arguments are optional:
88 roomID - just a shortcut. Will return ONE room (not a list) or None.
89 roomName - just a shortcut. Will return ONE room (not a list) or None.
90 roomExample - example RoomBase object.
91 reservationExample - example ReservationBase object. Represents reservation period.
92 available - Bool, true if room must be available, false if must be booked, None if do not care
93 freeText - str, room will be found if this string will be found anywhere in the object
94 i.e. in equipment list, comments, responsible etc.
95 minCapacity - Bool, defaults to False. If True, then rooms of capacity >= will be found.
96 Otherwise capacity it looks for rooms with capacity within 20% range.
97 allFast - Bool, defaults to False. If True, ALL active rooms will be returned
98 in ultra fast way, REGARDLESS of all other options.
99 ownedBy - Avatar
100 customAtts - for rooms with custom attributes.
101 rooms with no .customAtts attributes will be filtered out if this parameter is present
102 The customAtts attribute should be a list of dictionaries with the attributes "name", "allowEmpty", "filter".
103 "name" -> the name of the custom attribute
104 "allowEmpty" -> if we allow the custom attribute to be empty or not (empty = "" or string with only whitespaces)
105 "filter" -> a function to which we will pass the value of the custom attribute and has to return True or False.
106 If there is more than 1 dictionary in the list, it will be like doing an AND of the conditions they represent.
107 (see example 6)
109 Examples:
111 # 1. Get all rooms
112 rooms = RoomBase.getRooms()
114 # 2. Get all rooms with capacity about 30
115 r = Factory.newRoom()
116 r.capacity = 30
117 rooms = RoomBase.getRooms( roomExample = r )
119 # 3. Get all rooms reserved on the New Year 2007,
120 # which have capacity about 30, are at Meyrin site and have 'jean' in comments.
122 r = Factory.newRoom()
123 r.capacity = 30
124 r.site = 'Meyrin'
125 r.comments = 'jean'
126 p = ReservationBase()
127 p.startDT = datetime.datetime( 2007, 01, 01 )
128 p.endDT = datetime.datetime( 2007, 01, 01 )
129 p.repeatability = None
131 rooms = RoomBase.getRooms( roomExample = r, reservationExample = p, available = False )
133 # 4. Get all rooms containing "sex" in their attributes
135 rooms = RoomBase.getRooms( freeText = 'sex' )
137 # 5. Get room 'AT AMPHITHEATRE'
139 oneRoom = RoomBase.getRooms( roomName = 'AT AMPHITHEATRE' )
141 #6. Get rooms with a H.323 IP defined
142 rooms = RoomBase.getRooms ( customAtts = [{"name":'H323 IP', "allowEmpty":False,
143 "filter": (lambda ip: validIP(ip))}])
145 # Simply redirect to the plugin
146 from MaKaC.rb_factory import Factory
147 return Factory.newRoom().getRooms( **kwargs )
149 def getReservations( self, resvExample = None, archival = None ):
151 FINAL (not intented to be overriden)
152 Returns reservations of this room, meeting specified criteria.
153 Look ReservationBase.getReservations for details.
155 # Simply redirect to the plugin
156 from MaKaC.rb_factory import Factory
157 from MaKaC.rb_reservation import ReservationBase
159 return ReservationBase.getReservations( resvExample = resvExample, rooms = [self], archival = archival )
161 def getLiveReservations( self, resvExample = None ):
163 FINAL (not intented to be overriden)
164 Returns valid, non archival reservations of this room,
165 meeting specified criteria. Look ReservationBase.getReservations for details.
167 from MaKaC.rb_factory import Factory
168 from MaKaC.rb_reservation import ReservationBase
170 if resvExample == None:
171 resvExample = Factory.newReservation()
172 resvExample.isCancelled = False
173 resvExample.isRejected = False
175 return ReservationBase.getReservations( resvExample = resvExample,
176 rooms = [self], archival = False )
178 def isAvailable( self, potentialReservation ):
180 FINAL (not intented to be overriden)
181 Checks whether the room is available for the potentialReservation.
182 potentialReservation is of type ReservationBase. It specifies the period.
184 from MaKaC.rb_reservation import ReservationBase
185 if potentialReservation.getCollisions( boolResult = True ):
186 return False
187 return True
189 def getResponsible( self ):
191 FINAL (not intented to be overriden)
192 Returns responsible person (Avatar object).
194 avatar = AvatarHolder().getById( self.responsibleId )#match( { 'id': self.responsibleId } )[0]
196 return avatar
198 # Statistical ------------------------------------------------------------
200 @staticmethod
201 def getNumberOfRooms( **kwargs ):
203 FINAL (not intented to be overriden)
204 Returns total number of rooms in database.
206 name = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
207 location = Location.parse(name)
208 return location.factory.newRoom().getNumberOfRooms(location=name)
210 @staticmethod
211 def getNumberOfActiveRooms( **kwargs ):
213 FINAL (not intented to be overriden)
214 Returns number of rooms that are active (not logicaly deleted).
216 name = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
217 location = Location.parse(name)
218 return location.factory.newRoom().getNumberOfActiveRooms(location=name)
220 @staticmethod
221 def getNumberOfReservableRooms( **kwargs ):
223 FINAL (not intented to be overriden)
224 Returns number of rooms which can be reserved.
226 name = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
227 location = Location.parse(name)
228 return location.factory.newRoom().getNumberOfReservableRooms(location=name)
230 @staticmethod
231 def getTotalSurfaceAndCapacity( **kwargs ):
233 FINAL (not intented to be overriden)
234 Returns (total_surface, total_capacity) of all Active rooms.
236 name = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
237 location = Location.parse(name)
238 roomEx = location.factory.newRoom()
239 roomEx.isActive = True
240 roomEx.isReservable = True
241 rooms = CrossLocationQueries.getRooms( roomExample = roomEx, location = name )
242 totalSurface, totalCapacity = 0, 0
243 for r in rooms:
244 if r.surfaceArea:
245 totalSurface += r.surfaceArea
246 if r.capacity:
247 totalCapacity += r.capacity
248 return ( totalSurface, totalCapacity )
250 @staticmethod
251 def getAverageOccupation( **kwargs ):
253 FINAL (not intented to be overriden)
254 Returns float <0, 1> representing how often - on the avarage -
255 the rooms are booked during the working hours. (1 == all the time, 0 == never).
258 name = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
260 # Get active, publically reservable rooms
261 from MaKaC.rb_factory import Factory
262 roomEx = Factory.newRoom()
263 roomEx.isActive = True
264 roomEx.isReservable = True
266 rooms = CrossLocationQueries.getRooms( roomExample = roomEx, location = name )
268 # Find collisions with last month period
269 from MaKaC.rb_reservation import ReservationBase, RepeatabilityEnum
270 resvEx = ReservationBase()
271 now = datetime.now()
272 resvEx.endDT = datetime( now.year, now.month, now.day, 17, 30 )
273 resvEx.startDT = resvEx.endDT - timedelta( 30, 9 * 3600 ) # - 30 days and 9 hours
274 resvEx.repeatability = RepeatabilityEnum.daily
275 collisions = resvEx.getCollisions( rooms = rooms )
277 totalWorkingDays = 0
278 weekends = 0
279 for day in iterdays( resvEx.startDT, resvEx.endDT ):
280 if day.weekday() in [5,6]: # Skip Saturday and Sunday
281 weekends += 1
282 continue
283 # if c.startDT is CERN Holiday: continue
284 totalWorkingDays += 1
286 booked = timedelta( 0 )
287 for c in collisions:
288 if c.startDT.weekday() in [5,6]: # Skip Saturday and Sunday
289 continue
290 # if c.startDT is CERN Holiday: continue
291 booked = booked + ( c.endDT - c.startDT )
292 totalBookableTime = totalWorkingDays * 9 * len( rooms ) # Hours
293 bookedTime = booked.days * 24 + 1.0 * booked.seconds / 3600 # Hours
294 if totalBookableTime > 0:
295 return bookedTime / totalBookableTime
296 else:
297 return 0 # Error (no rooms in db)
300 def getMyAverageOccupation( self, period="pastmonth" ):
302 FINAL (not intented to be overriden)
303 Returns float <0, 1> representing how often - on the avarage -
304 the room is booked during the working hours. (1 == all the time, 0 == never).
306 # Find collisions with last month period
307 from MaKaC.rb_reservation import ReservationBase, RepeatabilityEnum
308 resvEx = ReservationBase()
309 now = datetime.now()
310 if period == "pastmonth":
311 resvEx.endDT = datetime( now.year, now.month, now.day, 17, 30 )
312 resvEx.startDT = resvEx.endDT - timedelta( 30, 9 * 3600 ) # - 30 days and 9 hours
313 elif period == "thisyear":
314 resvEx.endDT = datetime( now.year, now.month, now.day, 17, 30 )
315 resvEx.startDT = datetime( now.year, 1, 1, 0, 0 )
316 resvEx.repeatability = RepeatabilityEnum.daily
317 collisions = resvEx.getCollisions( rooms = [self] )
319 totalWorkingDays = 0
320 weekends = 0
321 for day in iterdays( resvEx.startDT, resvEx.endDT ):
322 if day.weekday() in [5,6]: # Skip Saturday and Sunday
323 weekends += 1
324 continue
325 # if c.startDT is CERN Holiday: continue
326 totalWorkingDays += 1
328 booked = timedelta( 0 )
329 for c in collisions:
330 if c.startDT.weekday() in [5,6]: # Skip Saturday and Sunday
331 continue
332 # if c.startDT is CERN Holiday: continue
333 booked = booked + ( c.endDT - c.startDT )
334 totalBookableTime = totalWorkingDays * 9 # Hours
335 bookedTime = booked.days * 24 + 1.0 * booked.seconds / 3600 # Hours
336 if totalBookableTime > 0:
337 return bookedTime / totalBookableTime
338 else:
339 return 0
342 # Equipment ------------------------------------------------------------
344 def setEquipment( self, eq ):
346 Sets (replaces) the equipment list with the new one.
347 It may be list ['eq1', 'eq2', ...] or str 'eq1`eq2`eq3`...'
349 if isinstance( eq, list ):
350 self._equipment = '`'.join( eq )
351 return
352 elif isinstance( eq, str ):
353 self._equipment = eq
354 return
355 raise MaKaCError('Invalid equipment list')
357 def getEquipment( self ):
359 Returns the room's equipment list.
361 return self._equipment.split( '`' )
363 def insertEquipment( self, equipmentName ):
364 """ Adds new equipment to the room. """
365 if len( self._equipment ) > 0:
366 self._equipment += '`'
367 self._equipment += equipmentName
369 def removeEquipment( self, equipmentName ):
370 """ Removes equipment from the room. """
371 e = self.getEquipment()
372 e.remove( equipmentName )
373 self.setEquipment( e )
375 def hasEquipment( self, equipmentName ):
376 return equipmentName in self._equipment
378 def isCloseToBuilding( self, buildingNr ):
379 """ Returns true if room is close to the specified building """
380 raise NotImplementedError('Not implemented')
382 def belongsTo( self, user ):
383 """ Returns true if current CrbsUser is responsible for this room """
384 raise NotImplementedError('Not implemented')
386 # "System" ---------------------------------------------------------------
388 def checkIntegrity( self ):
390 FINAL (not intented to be overriden)
391 Checks whether:
392 - all required attributes has values
393 - values are of correct type
394 - semantic coherence (i.e. star date <= end date)
397 # list of errors
398 errors = []
400 # check presence and types of arguments
401 # =====================================================
402 if self.id != None: # Only for existing objects
403 checkPresence( self, errors, 'id', int )
404 checkPresence( self, errors, '_locationName', str )
405 # check semantic integrity
406 # =====================================================
408 if errors:
409 raise str( errors )
411 # Photos -----------------------------------------------------------------
413 # NOTE: In general, URL generation should be in urlHandlers.
414 # This exception is because we want to allow other room booking systems
415 # to override room photos.
417 def getPhotoURL( self ):
418 # Used to send photos via Python script
419 #from MaKaC.webinterface.urlHandlers import UHSendRoomPhoto
420 #return UHSendRoomPhoto.getURL( self.photoId, small = False )
421 from MaKaC.webinterface.urlHandlers import UHRoomPhoto
422 return UHRoomPhoto.getURL( self.photoId )
424 def getSmallPhotoURL( self ):
425 # Used to send photos via Python script
426 #from MaKaC.webinterface.urlHandlers import UHSendRoomPhoto
427 #return UHSendRoomPhoto.getURL( self.photoId, small = True )
428 from MaKaC.webinterface.urlHandlers import UHRoomPhotoSmall
429 return UHRoomPhotoSmall.getURL( self.photoId )
431 def savePhoto( self, photoPath ):
433 Saves room's photo on the server.
435 pass
437 def saveSmallPhoto( self, photoPath ):
439 Saves room's small photo on the server.
441 pass
443 # Indico architecture ----------------------------------------------------
445 __owner = None
447 def getLocator( self ):
449 FINAL (not intented to be overriden)
450 Returns a globaly unique identification encapsulated in a Locator object
452 owner = self.getOwner()
453 if owner:
454 loc = owner.getLocator()
455 else:
456 from MaKaC.common.Locators import Locator
457 loc = Locator()
458 loc["roomLocation"] = self.locationName
459 loc["roomID"] = self.id
460 return loc
462 def setOwner( self, owner ):
464 FINAL (not intented to be overriden)
466 oryg = self._p_changed
467 self.__owner = None
468 if owner:
469 self.__owner = owner.getId()#Impersistant( owner )
470 self._p_changed = oryg
472 def getOwner( self ):
474 FINAL (not intented to be overriden)
475 Owner in terms of "parent", i.e. conference
477 ####---FIXING THE USE OF IMPERSISTANT CLASS-----
478 if isinstance(self.__owner, Impersistant):
479 o = self.__owner.getObject()
480 if o:
481 self.__owner=o.getId()
482 else:
483 self.__owner=None
484 ####---ENDO OF FIXING THE USE OF IMPERSISTANT CLASS-----
485 ch = ConferenceHolder()
486 if self.__owner and self.__owner in ch._getIdx():
487 return ch.getById(self.__owner)
489 return None
491 def isProtected( self ):
493 FINAL (not intented to be overriden)
494 The one must be logged in to do anything in RB module.
496 return True
498 def canView( self, accessWrapper ):
500 FINAL (not intented to be overriden)
501 Room details are public - anyone can view.
503 return True
505 def canBook(self, user):
507 FINAL (not intented to be overriden)
508 Reservable rooms which does not require pre-booking can be booked by anyone.
509 Other rooms - only by their responsibles.
512 # if the user has no access to the RB module, let's end it here
513 if not rb_check_user_access(user):
514 return False
516 if self.isActive and self.isReservable and not self.resvsNeedConfirmation:
517 simbaList = self.customAtts.get('Booking Simba List')
518 if simbaList and simbaList != "Error: unknown mailing list" and simbaList != "":
519 if user.isMemberOfSimbaList(simbaList):
520 return True
521 else:
522 return True
523 if user == None:
524 return False
526 if (self.isOwnedBy( user ) and self.isActive) \
527 or user.isRBAdmin():
528 return True
529 return False
531 def canPrebook(self, user):
533 FINAL (not intented to be overriden)
534 Reservable rooms can be pre-booked by anyone.
535 Other rooms - only by their responsibles.
538 # if the user has no access to the RB module, let's end it here
539 if not rb_check_user_access(user):
540 return False
542 if self.isActive and self.isReservable:
543 simbaList = self.customAtts.get('Booking Simba List')
544 if simbaList and simbaList != "Error: unknown mailing list" and simbaList != "":
545 if user.isMemberOfSimbaList(simbaList):
546 return True
547 else:
548 return True
549 if user == None:
550 return False
551 if (self.isOwnedBy( user ) and self.isActive) \
552 or user.isRBAdmin():
553 return True
554 return False
556 def canModify( self, accessWrapper ):
558 FINAL (not intented to be overriden)
559 Only admin can modify rooms.
561 if accessWrapper == None:
562 return False
563 if isinstance( accessWrapper, AccessWrapper ):
564 if accessWrapper.getUser():
565 return accessWrapper.getUser().isRBAdmin()
566 else:
567 return False
568 elif isinstance( accessWrapper, Avatar ):
569 return accessWrapper.isRBAdmin()
571 raise MaKaCError('canModify requires either AccessWrapper or Avatar object')
573 def canDelete( self, user ):
574 return self.canModify( user )
576 def isOwnedBy( self, user ):
578 Returns True if user is responsible for this room. False otherwise.
580 if not self.responsibleId:
581 return None
582 if self.responsibleId == user.id:
583 return True
584 try:
585 if user in self._v_isOwnedBy.keys():
586 return self._v_isOwnedBy[user]
587 except:
588 self._v_isOwnedBy = {}
589 if self.customAtts.get( 'Simba List' ):
590 list = self.customAtts.get( 'Simba List' )
591 if list != "Error: unknown mailing list" and list != "":
592 if user.isMemberOfSimbaList( list ):
593 self._v_isOwnedBy[user] = True
594 return True
595 self._v_isOwnedBy[user] = False
596 return False
598 def getLocationName( self ):
599 if self.__class__.__name__ == 'RoomBase':
600 return Location.getDefaultLocation().friendlyName
601 #raise 'This method is purely virtual. Call it only on derived objects.'
602 return self.getLocationName() # Subclass
604 def setLocationName( self, locationName ):
605 if self.__class__.__name__ == 'RoomBase':
606 raise MaKaCError('This method is purely virtual. Call it only on derived objects.')
607 return self.setLocationName( locationName ) # Subclass
609 def getAccessKey( self ): return ""
611 def getFullName( self ):
612 name = ""
613 if self.building != None and self.floor != None and self.building != None:
614 s = str( self.building ) + '-' + str( self.floor ) + '-' + str( self.roomNr )
615 if s != '--':
616 name = s
617 if self._name != None and len( self._name.strip() ) > 0:
618 name += " - %s" % self._name
619 return name
621 # ==== Private ===================================================
623 _name = None
624 _equipment = '' # str, 'eq1`eq2`eq3' - list of room's equipment, joined by '`'
626 def _getGuid( self ):
627 if self.id == None or self.locationName == None:
628 return None
629 if Location.parse( self.locationName ):
630 return RoomGUID( Location.parse( self.locationName ), self.id )
631 return None
633 def _getName( self ):
634 if self._name != None and len( self._name.strip() ) > 0:
635 return self._name
636 if self.building != None and self.floor != None and self.building != None:
637 s = str( self.building ) + '-' + str( self.floor ) + '-' + str( self.roomNr )
638 if s != '--':
639 return s
640 return ''
641 return None
643 def _setName( self, s ):
644 # Try to parse the name
645 if s == None:
646 self._name = None
647 return
648 parts = s.split( '-' )
649 if len( parts ) == 3:
650 try:
651 self.building = int( parts[0] )
652 self.floor = parts[1]
653 self.roomNr = parts[2]
654 return
655 except:
656 pass
657 # Parsing failed, that means it is real name
658 self._name = s
660 # CERN specific; don't bother
661 def _getNeedsAVCSetup( self ):
662 eq = self.getEquipment()
663 if not self.locationName or not eq:
664 return None
665 return 'Video conference' in ' '.join( eq )
667 # CERN specific; don't bother
668 def hasWebcastRecording( self ):
669 eq = self.getEquipment()
670 if not self.locationName or not eq:
671 return None
672 return 'Webcast/Recording' in ' '.join( eq )
674 def _eval_str( self, s ):
675 ixPrv = 0
676 ret = ""
678 while True:
679 ix = s.find( "#{", ixPrv )
680 if ix == -1:
681 break
682 ret += s[ixPrv:ix] # verbatim
683 ixPrv = s.index( "}", ix + 2 ) + 1
684 ret += str( eval( s[ix+2:ixPrv-1] ) )
685 ret += s[ixPrv:len(s)]
687 return ret
689 def _getVerboseEquipment( self ):
690 s = ""
691 eqList = self.getEquipment()
692 for eq in eqList:
693 s = s + eq + ", "
694 if len( eqList ) > 0: s = s[0:len(s)-2] # Cut off last ','
695 return s
697 def _getPhotoId( self ):
699 Feel free to override this in your inherited class.
701 return self._doGetPhotoId()
703 def _setPhotoId( self, value ):
704 self._photoId = value
706 def _doGetPhotoId( self ):
707 if '_photoId' in dir( self ): return self._photoId
708 return None
710 def __str__( self ):
711 s = self._eval_str(
713 id: #{self.id}
714 isActive: #{self.isActive}
716 room: #{self.name}
718 building: #{self.building}
719 floor: #{self.floor}
720 roomNr: #{self.roomNr}
721 isReservable: #{self.isReservable}
722 rNeedConfirmation: #{self.resvsNeedConfirmation}
723 startNotification: #{self.resvStartNotification}
724 endNotification: #{self.resvEndNotification}
725 startNotificationBefore: #{self.resvStartNotificationBefore}
726 notificationToResponsible: #{self.resvNotificationToResponsible}
727 notificationAssistance: #{self.resvNotificationAssistance}
729 site: #{self.site}
730 capacity: #{self.capacity}
731 surfaceArea: #{self.surfaceArea}
732 division: #{self.division}
733 photoId: #{self.photoId}
734 externalId: #{self.externalId}
736 telephone: #{self.telephone}
737 whereIsKey: #{self.whereIsKey}
738 comments: #{self.comments}
739 responsibleId: #{self.responsibleId}
740 equipment: """
742 s += self.verboseEquipment + "\n"
743 return s
745 def __cmp__( self, other ):
746 if self.__class__.__name__ == 'NoneType' and other.__class__.__name__ == 'NoneType':
747 return 0
748 if self.__class__.__name__ == 'NoneType':
749 return cmp( None, 1 )
750 if other.__class__.__name__ == 'NoneType':
751 return cmp( 1, None )
753 if self.id != None and other.id != None:
754 if self.id == other.id:
755 return 0
757 c = cmp( self.locationName, other.locationName )
758 if c == 0:
759 c = cmp( self.building, other.building )
760 if c == 0:
761 c = cmp( self.floor, other.floor )
762 if c == 0:
763 c = cmp( self.roomNr, other.roomNr )
764 if c == 0:
765 c = cmp( self.name, other.name )
767 return c
769 # ==== Properties ===================================================
771 # DO NOT set default values here, since query-by-example will change!!!
773 id = None # int - artificial ID; initialy value from oracle db
774 locationName = property( getLocationName, setLocationName ) # location (plugin) name
775 guid = property( _getGuid ) # RoomGUID
776 isActive = None # bool - whether the room is active (not logicaly removed) [STSCRBOK]
777 resvsNeedConfirmation = None # bool - whether reservations for this room must be confirmed by responsible
779 building = None # int, positive
780 floor = None # str, alphanumeric
781 roomNr = None # str
782 latitude = None # str
783 longitude = None # str
785 name = property( _getName, _setName ) # str - room name
787 capacity = None # int, positive
788 site = None # str - global room localisation, i.e. city
789 division = None # str, TODO
790 isReservable = None # bool - whether the room is reservable
791 photoId = property( _getPhotoId, _setPhotoId ) # str - room picture id
792 externalId = None # str - custom external room id, i.e. for locating on the map
794 resvStartNotification = False # bool - whether to send notifications on booking start
795 resvStartNotificationBefore = None # bool - whether to send notifications on booking start
796 resvEndNotification = False # bool - whether to send notifications on booking end
797 resvNotificationToResponsible = False # bool - whether to send notifications to the room responsible, too
798 resvNotificationAssistance = False # bool - whether to send notifications on assistance
800 telephone = None # str
801 surfaceArea = None # int, positive - in meters^2
802 whereIsKey = None # str, typically telephone number
803 comments = None # str
804 responsibleId = None # str, responsible person id (avatar.id)
805 maxAdvanceDays = 0 # maximum number of days a booking can be done in advance
807 #customAtts = {} # Must behave like name-value dictionary of
808 # custom attributes. Must be put in derived classes.
810 verboseEquipment = property( _getVerboseEquipment )
811 needsAVCSetup = property( _getNeedsAVCSetup )
813 # ============================================================================
814 # ================================== TEST ====================================
815 # ============================================================================
817 class Test:
818 from MaKaC.rb_factory import Factory
820 @staticmethod
821 def getReservations():
822 from MaKaC.rb_factory import Factory
823 from datetime import datetime
825 dalManager = Factory.getDALManager()
826 dalManager.connect()
828 amphitheatre = RoomBase.getRooms( roomName = 'IT AMPHITHEATRE' )
829 print "All reservations for IT AMPHITHEATRE: %d" % len( amphitheatre.getReservations() )
831 resvEx = Factory.newReservation()
832 resvEx.startDT = datetime( 2006, 9, 23, 0 )
833 resvEx.endDT = datetime( 2006, 9, 30, 23, 59 )
834 reservations = amphitheatre.getLiveReservations( resvExample = resvEx )
836 dalManager.disconnect()
839 if __name__ == '__main__':
840 Test.getReservations()