De-avatarify most of room booking (except room owner)
[cds-indico.git] / indico / modules / rb / models / blocked_rooms.py
blob76496d70a8f70fcc2ccbeceff3a7e55a7977403e
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__ import unicode_literals
19 from datetime import datetime, time
20 from operator import attrgetter
22 from indico.core.db import db
23 from indico.core.db.sqlalchemy import PyIntEnum
24 from indico.modules.rb.models.blockings import Blocking
25 from indico.modules.rb.models.reservation_occurrences import ReservationOccurrence
26 from indico.modules.rb.models.reservations import Reservation
27 from indico.modules.rb.notifications.blockings import notify_request_response
28 from indico.util.string import return_ascii
29 from indico.util.struct.enum import TitledIntEnum
32 class BlockedRoomState(TitledIntEnum):
33 __titles__ = ['Pending', 'Accepted', 'Rejected']
34 pending = 0
35 accepted = 1
36 rejected = 2
39 class BlockedRoom(db.Model):
40 __tablename__ = 'blocked_rooms'
41 __table_args__ = {'schema': 'roombooking'}
43 State = BlockedRoomState # make it available here for convenience
45 id = db.Column(
46 db.Integer,
47 primary_key=True
49 state = db.Column(
50 PyIntEnum(BlockedRoomState),
51 nullable=False,
52 default=BlockedRoomState.pending
54 rejected_by = db.Column(
55 db.String
57 rejection_reason = db.Column(
58 db.String
60 blocking_id = db.Column(
61 db.Integer,
62 db.ForeignKey('roombooking.blockings.id'),
63 nullable=False
65 room_id = db.Column(
66 db.Integer,
67 db.ForeignKey('roombooking.rooms.id'),
68 nullable=False,
69 index=True
72 @property
73 def state_name(self):
74 return BlockedRoomState(self.state).title
76 @classmethod
77 def find_with_filters(cls, filters):
78 q = cls.find(_eager=BlockedRoom.blocking, _join=Blocking)
79 if filters.get('room_ids'):
80 q = q.filter(BlockedRoom.room_id.in_(filters['room_ids']))
81 if filters.get('start_date') and filters.get('end_date'):
82 q = q.filter(Blocking.start_date <= filters['end_date'],
83 Blocking.end_date >= filters['start_date'])
84 if 'state' in filters:
85 q = q.filter(BlockedRoom.state == filters['state'])
86 return q
88 def reject(self, user=None, reason=None):
89 """Reject the room blocking."""
90 self.state = BlockedRoomState.rejected
91 if reason:
92 self.rejection_reason = reason
93 if user:
94 self.rejected_by = user.full_name
95 notify_request_response(self)
97 def approve(self, notify_blocker=True):
98 """Approve the room blocking, rejecting all colliding reservations/occurrences."""
99 self.state = BlockedRoomState.accepted
101 # Get colliding reservations
102 start_dt = datetime.combine(self.blocking.start_date, time())
103 end_dt = datetime.combine(self.blocking.end_date, time(23, 59, 59))
105 reservation_criteria = [
106 Reservation.room_id == self.room_id,
107 ~Reservation.is_rejected,
108 ~Reservation.is_cancelled
111 # Whole reservations to reject
112 reservations = Reservation.find_all(
113 Reservation.start_dt >= start_dt,
114 Reservation.end_dt <= end_dt,
115 *reservation_criteria
118 # Single occurrences to reject
119 occurrences = ReservationOccurrence.find_all(
120 ReservationOccurrence.start_dt >= start_dt,
121 ReservationOccurrence.end_dt <= end_dt,
122 ReservationOccurrence.is_valid,
123 ~ReservationOccurrence.reservation_id.in_(map(attrgetter('id'), reservations)) if reservations else True,
124 *reservation_criteria,
125 _join=Reservation
128 reason = 'Conflict with blocking {}: {}'.format(self.blocking.id, self.blocking.reason)
130 for reservation in reservations:
131 if self.blocking.can_be_overridden(reservation.created_by_user, reservation.room):
132 continue
133 reservation.reject(self.blocking.created_by_user, reason)
135 for occurrence in occurrences:
136 reservation = occurrence.reservation
137 if self.blocking.can_be_overridden(reservation.created_by_user, reservation.room):
138 continue
139 occurrence.reject(self.blocking.created_by_user, reason)
141 if notify_blocker:
142 # We only need to notify the blocking creator if the blocked room wasn't approved yet.
143 # This is the case if it's a new blocking for a room managed by the creator
144 notify_request_response(self)
146 @return_ascii
147 def __repr__(self):
148 return '<BlockedRoom({0}, {1}, {2})>'.format(
149 self.blocking_id,
150 self.room_id,
151 self.state_name