def getVars(self): wvars = WTemplated.getVars(self) wvars['standalone'] = self._standalone room = wvars['room'] wvars['attrs'] = {attr.attribute.name: attr for attr in room.attributes if not attr.attribute.is_hidden or rb_is_admin(session.user)} wvars['owner_name'] = room.owner.full_name wvars['bookable_hours'] = room.bookable_hours.all() wvars['nonbookable_periods'] = room.nonbookable_periods.all() # URLs wvars['stats_url'] = UH.UHRoomBookingRoomStats.getURL(room) wvars['delete_room_url'] = url_for('rooms_admin.delete_room', room) wvars['modify_room_url'] = url_for('rooms_admin.modify_room', room) if not self._standalone: wvars['conference'] = self._rh._conf room_mapper = RoomMapperHolder().match({'placeName': self._rh._location.name}, exact=True) if room_mapper: wvars['show_on_map'] = room_mapper[0].getMapURL(self._rh._room.name) else: wvars['show_on_map'] = UH.UHRoomBookingMapOfRooms.getURL(roomID=self._rh._room.id) return wvars
def can_be_modified(self, user): """ The following persons are authorized to modify a blocking: - owner (the one who created the blocking) - admin (of course) """ return user and (user == self.created_by_user or rb_is_admin(user))
def getVars(self): wvars = WTemplated.getVars(self) wvars["standalone"] = self._standalone room = wvars["room"] wvars["attrs"] = { attr.attribute.name: attr for attr in room.attributes if not attr.attribute.is_hidden or rb_is_admin(session.user) } wvars["owner_name"] = room.owner.full_name wvars["bookable_hours"] = room.bookable_hours.all() wvars["nonbookable_periods"] = room.nonbookable_periods.all() # URLs wvars["stats_url"] = UH.UHRoomBookingRoomStats.getURL(room) wvars["delete_room_url"] = url_for("rooms_admin.delete_room", room) wvars["modify_room_url"] = url_for("rooms_admin.modify_room", room) if not self._standalone: wvars["conference"] = self._rh._conf wvars["show_on_map"] = room.map_url if room.map_url else url_for("rooms.roomBooking-mapOfRooms", room) return wvars
def _get_permissions(self, blocking): methods = ('can_delete', 'can_edit') admin_permissions = None user_permissions = {x: getattr(blocking, x)(session.user, allow_admin=False) for x in methods} if rb_is_admin(session.user): admin_permissions = {x: getattr(blocking, x)(session.user) for x in methods} return {'user': user_permissions, 'admin': admin_permissions}
def _get_permissions(self, occurrence): methods = ('can_cancel', 'can_reject') admin_permissions = None user_permissions = {x: getattr(occurrence, x)(session.user, allow_admin=False) for x in methods} if rb_is_admin(session.user): admin_permissions = {x: getattr(occurrence, x)(session.user) for x in methods} return {'user': user_permissions, 'admin': admin_permissions}
def _sidemenu_items(sender, **kwargs): user_is_admin = session.user is not None and rb_is_admin(session.user) user_has_rooms = session.user is not None and Room.user_owns_rooms(session.user) map_available = Location.default_location is not None and Location.default_location.is_map_available yield SideMenuItem('book_room', _('Book a Room'), url_for('rooms.book'), 80, icon='checkmark') if map_available: yield SideMenuItem('map', _('Map of Rooms'), url_for('rooms.roomBooking-mapOfRooms'), 70, icon='location') yield SideMenuItem('calendar', _('Calendar'), url_for('rooms.calendar'), 60, icon='calendar') yield SideMenuItem('my_bookings', _('My Bookings'), url_for('rooms.my_bookings'), 50, icon='time') yield SideMenuItem('search_bookings', _('Search bookings'), url_for('rooms.roomBooking-search4Bookings'), section='search') yield SideMenuItem('search_rooms', _('Search rooms'), url_for('rooms.search_rooms'), section='search') if user_has_rooms: yield SideMenuItem('bookings_in_my_rooms', _('Bookings in my rooms'), url_for('rooms.bookings_my_rooms'), section='my_rooms') yield SideMenuItem('prebookings_in_my_rooms', _('Pre-bookings in my rooms'), url_for('rooms.pending_bookings_my_rooms'), section='my_rooms') yield SideMenuItem('room_list', _('Room list'), url_for('rooms.search_my_rooms'), section='my_rooms') yield SideMenuItem('my_blockings', _('My Blockings'), url_for('rooms.blocking_list', only_mine=True, timeframe='recent'), section='blocking') if user_has_rooms: yield SideMenuItem('blockings_my_rooms', _('Blockings for my rooms'), url_for('rooms.blocking_my_rooms'), section='blocking') yield SideMenuItem('blocking_create', _('Block rooms'), url_for('rooms.create_blocking'), section='blocking') if user_is_admin: yield SideMenuItem('admin', _('Administration'), url_for('rooms_admin.roomBooking-admin'), 10, icon='user-chairperson')
def _process_select_room(self): # Step 1: Room(s), dates, repetition selection form = self._make_select_room_form() if form.validate_on_submit(): flexible_days = form.flexible_dates_range.data day_start_dt = datetime.combine(form.start_dt.data.date(), time()) day_end_dt = datetime.combine(form.end_dt.data.date(), time(23, 59)) selected_rooms = [r for r in self._rooms if r.id in form.room_ids.data] occurrences, candidates = self._get_all_occurrences(form.room_ids.data, form, flexible_days) period_form_defaults = FormDefaults(repeat_interval=form.repeat_interval.data, repeat_frequency=form.repeat_frequency.data) period_form = self._make_select_period_form(period_form_defaults) # Show step 2 page return self._get_view('select_period', rooms=selected_rooms, occurrences=occurrences, candidates=candidates, start_dt=day_start_dt, end_dt=day_end_dt, period_form=period_form, form=form, repeat_frequency=form.repeat_frequency.data, repeat_interval=form.repeat_interval.data, flexible_days=flexible_days).display() # GET or form errors => show step 1 page return self._get_view('select_room', errors=form.error_list, rooms=self._rooms, form=form, my_rooms=[r.id for r in Room.get_owned_by(session.user)], max_room_capacity=Room.max_capacity, can_override=rb_is_admin(session.user), date_changed=not form.is_submitted() and self.date_changed, ).display()
def _process_select_room(self): # Step 1: Room(s), dates, repetition selection form = self._make_select_room_form() if form.validate_on_submit(): flexible_days = form.flexible_dates_range.data day_start_dt = datetime.combine(form.start_dt.data.date(), time()) day_end_dt = datetime.combine(form.end_dt.data.date(), time(23, 59)) selected_rooms = [r for r in self._rooms if r.id in form.room_ids.data] selected_period_days = (day_end_dt - day_start_dt).days for room in selected_rooms: booking_limit_days = room.booking_limit_days or rb_settings.get('booking_limit') if selected_period_days > booking_limit_days: flash(_(u'Bookings for the room "{}" may not be longer than {} days') .format(room.name, booking_limit_days), 'error') return redirect(url_for('rooms.book')) occurrences, candidates = self._get_all_occurrences(form.room_ids.data, form, flexible_days) period_form_defaults = FormDefaults(repeat_interval=form.repeat_interval.data, repeat_frequency=form.repeat_frequency.data) period_form = self._make_select_period_form(period_form_defaults) # Show step 2 page return self._get_view('select_period', rooms=selected_rooms, occurrences=occurrences, candidates=candidates, start_dt=day_start_dt, end_dt=day_end_dt, period_form=period_form, form=form, repeat_frequency=form.repeat_frequency.data, repeat_interval=form.repeat_interval.data, flexible_days=flexible_days).display() # GET or form errors => show step 1 page return self._get_view('select_room', errors=form.error_list, rooms=self._rooms, form=form, my_rooms=[r.id for r in Room.get_owned_by(session.user)], max_room_capacity=Room.max_capacity, can_override=rb_is_admin(session.user), date_changed=not form.is_submitted() and self.date_changed, ).display()
def can_be_modified(self, user): if user is None: return False if self.is_rejected or self.is_cancelled: return False if rb_is_admin(user): return True return self.created_by_user == user or self.is_booked_for(user) or self.room.is_owned_by(user)
def check_bookable_hours(self, start_time, end_time, user=None, quiet=False): if user and (rb_is_admin(user) or self.is_owned_by(user)): return True bookable_hours = self.bookable_hours.all() if not bookable_hours: return True for bt in bookable_hours: if bt.fits_period(start_time, end_time): return True if quiet: return False raise NoReportError(u'Room cannot be booked at this time')
def can_cancel(self, user, allow_admin=True): if user is None: return False if not self.is_valid or self.end_dt < datetime.now(): return False booking = self.reservation booked_or_owned_by_user = booking.is_owned_by(user) or booking.is_booked_for(user) if booking.is_rejected or booking.is_cancelled or booking.is_archived: return False if booked_or_owned_by_user and self.is_within_cancel_grace_period: return True return allow_admin and rb_is_admin(user)
def check_advance_days(self, end_date, user=None, quiet=False): if not self.max_advance_days: return True if user and (rb_is_admin(user) or self.is_owned_by(user)): return True advance_days = (end_date - date.today()).days ok = advance_days < self.max_advance_days if quiet or ok: return ok else: msg = _(u'You cannot book this room more than {} days in advance') raise NoReportError(msg.format(self.max_advance_days))
def _can_be_booked(self, user, prebook=False, ignore_admin=False): if not user or not rb_check_user_access(user): return False if (not ignore_admin and rb_is_admin(user)) or (self.is_owned_by(user) and self.is_active): return True if self.is_active and self.is_reservable and (prebook or not self.reservations_need_confirmation): group_name = self.get_attribute_value('allowed-booking-group') if not group_name or user in GroupProxy.get_named_default_group(group_name): return True return False
def create_occurrences(self, skip_conflicts, user=None): ReservationOccurrence.create_series_for_reservation(self) db.session.flush() if user is None: user = self.created_by_user # Check for conflicts with nonbookable periods if not rb_is_admin(user) and not self.room.is_owned_by(user): nonbookable_periods = self.room.nonbookable_periods.filter(NonBookablePeriod.end_dt > self.start_dt) for occurrence in self.occurrences: if not occurrence.is_valid: continue for nbd in nonbookable_periods: if nbd.overlaps(occurrence.start_dt, occurrence.end_dt): if not skip_conflicts: raise ConflictingOccurrences() occurrence.cancel(user, u'Skipped due to nonbookable date', silent=True, propagate=False) break # Check for conflicts with blockings blocked_rooms = self.room.get_blocked_rooms(*(occurrence.start_dt for occurrence in self.occurrences)) for br in blocked_rooms: blocking = br.blocking if blocking.can_be_overridden(user, self.room): continue for occurrence in self.occurrences: if occurrence.is_valid and blocking.is_active_at(occurrence.start_dt.date()): # Cancel OUR occurrence msg = u'Skipped due to collision with a blocking ({})' occurrence.cancel(user, msg.format(blocking.reason), silent=True, propagate=False) # Check for conflicts with other occurrences conflicting_occurrences = self.get_conflicting_occurrences() for occurrence, conflicts in conflicting_occurrences.iteritems(): if not occurrence.is_valid: continue if conflicts['confirmed']: if not skip_conflicts: raise ConflictingOccurrences() # Cancel OUR occurrence msg = u'Skipped due to collision with {} reservation(s)' occurrence.cancel(user, msg.format(len(conflicts['confirmed'])), silent=True, propagate=False) elif conflicts['pending'] and self.is_accepted: # Reject OTHER occurrences for conflict in conflicts['pending']: conflict.reject(user, u'Rejected due to collision with a confirmed reservation')
def get_rooms_conflicts(rooms, start_dt, end_dt, repeat_frequency, repeat_interval, blocked_rooms, nonbookable_periods, unbookable_hours, skip_conflicts_with=None, allow_admin=False): rooms_conflicts = defaultdict(set) rooms_pre_conflicts = defaultdict(set) rooms_conflicting_candidates = defaultdict(set) skip_conflicts_with = skip_conflicts_with or [] candidates = ReservationOccurrence.create_series(start_dt, end_dt, (repeat_frequency, repeat_interval)) room_ids = [room.id for room in rooms] query = (ReservationOccurrence.query .filter(Reservation.room_id.in_(room_ids), ReservationOccurrence.is_valid, ReservationOccurrence.filter_overlap(candidates)) .join(ReservationOccurrence.reservation) .options(ReservationOccurrence.NO_RESERVATION_USER_STRATEGY, contains_eager(ReservationOccurrence.reservation))) if skip_conflicts_with: query = query.filter(~Reservation.id.in_(skip_conflicts_with)) overlapping_occurrences = group_list(query, key=lambda obj: obj.reservation.room.id) for room_id, occurrences in overlapping_occurrences.iteritems(): conflicts = get_room_bookings_conflicts(candidates, occurrences, room_id, skip_conflicts_with) rooms_conflicts[room_id], rooms_pre_conflicts[room_id], rooms_conflicting_candidates[room_id] = conflicts for room_id, occurrences in blocked_rooms.iteritems(): conflicts, conflicting_candidates = get_room_blockings_conflicts(room_id, candidates, occurrences) rooms_conflicts[room_id] |= conflicts rooms_conflicting_candidates[room_id] |= conflicting_candidates if not (allow_admin and rb_is_admin(session.user)): for room_id, occurrences in nonbookable_periods.iteritems(): room = Room.get_one(room_id) if not room.can_override(session.user, allow_admin=allow_admin): conflicts, conflicting_candidates = get_room_nonbookable_periods_conflicts(candidates, occurrences) rooms_conflicts[room_id] |= conflicts rooms_conflicting_candidates[room_id] |= conflicting_candidates for room_id, occurrences in unbookable_hours.iteritems(): room = Room.get_one(room_id) if not room.can_override(session.user, allow_admin=allow_admin): conflicts, conflicting_candidates = get_room_unbookable_hours_conflicts(candidates, occurrences) rooms_conflicts[room_id] |= conflicts rooms_conflicting_candidates[room_id] |= conflicting_candidates rooms_conflicting_candidates = defaultdict(list, ((k, list(v)) for k, v in rooms_conflicting_candidates.items())) return rooms_conflicts, rooms_pre_conflicts, rooms_conflicting_candidates
def can_override(self, user, room=None, explicit_only=False, allow_admin=True): """Check if a user can override the blocking The following persons are authorized to override a blocking: - the creator of the blocking - anyone on the blocking's ACL - unless explicit_only is set: rb admins and room managers (if a room is given) """ if not user: return False if self.created_by_user == user: return True if not explicit_only: if allow_admin and rb_is_admin(user): return True if room and room.can_manage(user): return True return any(user in principal for principal in iter_acl(self.allowed))
def can_be_overridden(self, user, room=None, explicit_only=False): """Determines if a user can override the blocking The following persons are authorized to override a blocking: - owner (the one who created the blocking) - any users on the blocking's ACL - unless explicitOnly is set: admins and room owners (if a room is given) """ if not user: return False if self.created_by_user == user: return True if not explicit_only: if rb_is_admin(user): return True elif room and room.is_owned_by(user): return True return any(user in principal for principal in iter_acl(self.allowed))
def getVars(self): wvars = WTemplated.getVars(self) wvars['standalone'] = self._standalone room = wvars['room'] wvars['attrs'] = {attr.attribute.name: attr for attr in room.attributes if not attr.attribute.is_hidden or rb_is_admin(session.user)} wvars['owner_name'] = room.owner.full_name wvars['bookable_hours'] = room.bookable_hours.all() wvars['nonbookable_periods'] = room.nonbookable_periods.all() # URLs wvars['stats_url'] = url_for('rooms.roomBooking-roomStats', room) wvars['delete_room_url'] = url_for('rooms_admin.delete_room', room) wvars['modify_room_url'] = url_for('rooms_admin.modify_room', room) wvars['show_on_map'] = room.map_url if room.map_url else url_for('rooms.roomBooking-mapOfRooms', room) return wvars
def _sidemenu_items(sender, **kwargs): user_is_admin = session.user is not None and rb_is_admin(session.user) user_has_rooms = session.user is not None and Room.user_owns_rooms(session.user) map_available = Location.default_location is not None and Location.default_location.is_map_available yield SideMenuItem("book_room", _("Book a Room"), url_for("rooms.book"), 80, icon="checkmark") if map_available: yield SideMenuItem("map", _("Map of Rooms"), url_for("rooms.roomBooking-mapOfRooms"), 70, icon="location") yield SideMenuItem("calendar", _("Calendar"), url_for("rooms.calendar"), 60, icon="calendar") yield SideMenuItem("my_bookings", _("My Bookings"), url_for("rooms.my_bookings"), 50, icon="time") yield SideMenuItem( "search_bookings", _("Search bookings"), url_for("rooms.roomBooking-search4Bookings"), section="search" ) yield SideMenuItem("search_rooms", _("Search rooms"), url_for("rooms.search_rooms"), section="search") if user_has_rooms: yield SideMenuItem( "bookings_in_my_rooms", _("Bookings in my rooms"), url_for("rooms.bookings_my_rooms"), section="my_rooms" ) yield SideMenuItem( "prebookings_in_my_rooms", _("Pre-bookings in my rooms"), url_for("rooms.pending_bookings_my_rooms"), section="my_rooms", ) yield SideMenuItem("room_list", _("Room list"), url_for("rooms.search_my_rooms"), section="my_rooms") yield SideMenuItem( "my_blockings", _("My Blockings"), url_for("rooms.blocking_list", only_mine=True, timeframe="recent"), section="blocking", ) if user_has_rooms: yield SideMenuItem( "blockings_my_rooms", _("Blockings for my rooms"), url_for("rooms.blocking_my_rooms"), section="blocking" ) yield SideMenuItem("blocking_create", _("Block rooms"), url_for("rooms.create_blocking"), section="blocking") if user_is_admin: yield SideMenuItem( "admin", _("Administration"), url_for("rooms_admin.roomBooking-admin"), 10, icon="user-chairperson" )
def create_occurrences(self, skip_conflicts, user=None): ReservationOccurrence.create_series_for_reservation(self) db.session.flush() if user is None: user = self.created_by_user # Check for conflicts with nonbookable periods if not rb_is_admin(user) and not self.room.is_owned_by(user): nonbookable_periods = self.room.nonbookable_periods.filter( NonBookablePeriod.end_dt > self.start_dt) for occurrence in self.occurrences: if not occurrence.is_valid: continue for nbd in nonbookable_periods: if nbd.overlaps(occurrence.start_dt, occurrence.end_dt): if not skip_conflicts: raise ConflictingOccurrences() occurrence.cancel(user, u'Skipped due to nonbookable date', silent=True, propagate=False) break # Check for conflicts with blockings blocked_rooms = self.room.get_blocked_rooms( *(occurrence.start_dt for occurrence in self.occurrences)) for br in blocked_rooms: blocking = br.blocking if blocking.can_be_overridden(user, self.room): continue for occurrence in self.occurrences: if occurrence.is_valid and blocking.is_active_at( occurrence.start_dt.date()): # Cancel OUR occurrence msg = u'Skipped due to collision with a blocking ({})' occurrence.cancel(user, msg.format(blocking.reason), silent=True, propagate=False) # Check for conflicts with other occurrences conflicting_occurrences = self.get_conflicting_occurrences() for occurrence, conflicts in conflicting_occurrences.iteritems(): if not occurrence.is_valid: continue if conflicts['confirmed']: if not skip_conflicts: raise ConflictingOccurrences() # Cancel OUR occurrence msg = u'Skipped due to collision with {} reservation(s)' occurrence.cancel(user, msg.format(len(conflicts['confirmed'])), silent=True, propagate=False) elif conflicts['pending'] and self.is_accepted: # Reject OTHER occurrences for conflict in conflicts['pending']: conflict.reject( user, u'Rejected due to collision with a confirmed reservation' ) # Mark occurrences created within the notification window as notified for occurrence in self.occurrences: if occurrence.is_valid and occurrence.is_in_notification_window(): occurrence.notification_sent = True # Mark occurrences created within the digest window as notified if self.repeat_frequency == RepeatFrequency.WEEK: if self.room.is_in_digest_window(): digest_start = round_up_month(date.today()) else: digest_start = date.today() digest_end = get_month_end(digest_start) self.occurrences.filter( ReservationOccurrence.start_dt <= digest_end).update( {'notification_sent': True}, synchronize_session='fetch')
def can_delete(self, user): if not user: return False return rb_is_admin(user)
def can_be_modified(self, user): """Only admin can modify rooms.""" if not user: return False return rb_is_admin(user)
def _checkProtection(self): user = session.user if not user or (not rb_is_admin(user) and not self.blocked_room.room.is_owned_by(user)): raise ServiceError( _('You are not permitted to modify this blocking'))
def _checkProtection(self): if session.user is None: self._checkSessionUser() elif not rb_is_admin(session.user): raise AccessError("You are not authorized to take this action.")
def _check_access(self): user = session.user if not user or (not rb_is_admin(user) and not self.blocked_room.room.is_owned_by(user)): raise Forbidden(_('You are not permitted to modify this blocking'))
def _check_access(self): RHRoomBookingBase._check_access(self) if not rb_is_admin(session.user): raise Forbidden
def _sidemenu_items(sender, **kwargs): user_is_admin = session.user is not None and rb_is_admin(session.user) user_has_rooms = session.user is not None and Room.user_owns_rooms( session.user) map_available = Location.default_location is not None and Location.default_location.is_map_available yield SideMenuItem('book_room', _('Book a Room'), url_for('rooms.book'), 80, icon='checkmark') if map_available: yield SideMenuItem('map', _('Map of Rooms'), url_for('rooms.roomBooking-mapOfRooms'), 70, icon='location') yield SideMenuItem('calendar', _('Calendar'), url_for('rooms.calendar'), 60, icon='calendar') yield SideMenuItem('my_bookings', _('My Bookings'), url_for('rooms.my_bookings'), 50, icon='time') yield SideMenuItem('search_bookings', _('Search bookings'), url_for('rooms.roomBooking-search4Bookings'), section='search') yield SideMenuItem('search_rooms', _('Search rooms'), url_for('rooms.search_rooms'), section='search') if user_has_rooms: yield SideMenuItem('bookings_in_my_rooms', _('Bookings in my rooms'), url_for('rooms.bookings_my_rooms'), section='my_rooms') yield SideMenuItem('prebookings_in_my_rooms', _('Pre-bookings in my rooms'), url_for('rooms.pending_bookings_my_rooms'), section='my_rooms') yield SideMenuItem('room_list', _('Room list'), url_for('rooms.search_my_rooms'), section='my_rooms') yield SideMenuItem('my_blockings', _('My Blockings'), url_for('rooms.blocking_list', only_mine=True, timeframe='recent'), section='blocking') if user_has_rooms: yield SideMenuItem('blockings_my_rooms', _('Blockings for my rooms'), url_for('rooms.blocking_my_rooms'), section='blocking') yield SideMenuItem('blocking_create', _('Block rooms'), url_for('rooms.create_blocking'), section='blocking') if user_is_admin: yield SideMenuItem('admin', _('Administration'), url_for('rooms_admin.roomBooking-admin'), 10, icon='user-chairperson')
def can_be_cancelled(self, user): if user is None: return False return self.is_owned_by(user) or rb_is_admin(user) or self.is_booked_for(user)
def _process_DELETE(self): if not rb_is_admin(session.user): raise Forbidden logger.info('Room %r deleted by %r', self.room, session.user) self.room.is_deleted = True return '', 204
def can_be_overridden(self, user): if not user: return False return rb_is_admin(user) or self.is_owned_by(user)
def _check_access(self): RHRoomBookingBase._check_access(self) if not rb_is_admin(session.user) and not self._skip_admin_check(): raise Forbidden
def is_user_admin(user): return rb_is_admin(user)
def can_be_rejected(self, user): if user is None: return False return rb_is_admin(user) or self.room.is_owned_by(user)
def create_occurrences(self, skip_conflicts, user=None): ReservationOccurrence.create_series_for_reservation(self) db.session.flush() if user is None: user = self.created_by_user # Check for conflicts with nonbookable periods if not rb_is_admin(user) and not self.room.is_owned_by(user): nonbookable_periods = self.room.nonbookable_periods.filter(NonBookablePeriod.end_dt > self.start_dt) for occurrence in self.occurrences: if not occurrence.is_valid: continue for nbd in nonbookable_periods: if nbd.overlaps(occurrence.start_dt, occurrence.end_dt): if not skip_conflicts: raise ConflictingOccurrences() occurrence.cancel(user, u"Skipped due to nonbookable date", silent=True, propagate=False) break # Check for conflicts with blockings blocked_rooms = self.room.get_blocked_rooms(*(occurrence.start_dt for occurrence in self.occurrences)) for br in blocked_rooms: blocking = br.blocking if blocking.can_be_overridden(user, self.room): continue for occurrence in self.occurrences: if occurrence.is_valid and blocking.is_active_at(occurrence.start_dt.date()): # Cancel OUR occurrence msg = u"Skipped due to collision with a blocking ({})" occurrence.cancel(user, msg.format(blocking.reason), silent=True, propagate=False) # Check for conflicts with other occurrences conflicting_occurrences = self.get_conflicting_occurrences() for occurrence, conflicts in conflicting_occurrences.iteritems(): if not occurrence.is_valid: continue if conflicts["confirmed"]: if not skip_conflicts: raise ConflictingOccurrences() # Cancel OUR occurrence msg = u"Skipped due to collision with {} reservation(s)" occurrence.cancel(user, msg.format(len(conflicts["confirmed"])), silent=True, propagate=False) elif conflicts["pending"] and self.is_accepted: # Reject OTHER occurrences for conflict in conflicts["pending"]: conflict.reject(user, u"Rejected due to collision with a confirmed reservation") # Mark occurrences created within the notification window as notified for occurrence in self.occurrences: if occurrence.is_valid and occurrence.is_in_notification_window(): occurrence.notification_sent = True # Mark occurrences created within the digest window as notified if self.repeat_frequency == RepeatFrequency.WEEK: if self.room.is_in_digest_window(): digest_start = round_up_month(date.today()) else: digest_start = date.today() digest_end = get_month_end(digest_start) self.occurrences.filter(ReservationOccurrence.start_dt <= digest_end).update({"notification_sent": True})
def search_for_rooms(filters, allow_admin=False, availability=None): """Search for a room, using the provided filters. :param filters: The filters, provided as a dictionary :param allow_admin: A boolean specifying whether admins have override privileges :param availability: A boolean specifying whether (un)available rooms should be provided, or `None` in case all rooms should be returned. """ query = (Room.query .outerjoin(favorite_room_table, db.and_(favorite_room_table.c.user_id == session.user.id, favorite_room_table.c.room_id == Room.id)) .reset_joinpoint() # otherwise filter_by() would apply to the favorite table .options(joinedload('owner').load_only('id')) .filter(~Room.is_deleted) .order_by(favorite_room_table.c.user_id.is_(None), db.func.indico.natsort(Room.full_name))) criteria = {} if 'capacity' in filters: query = query.filter(Room.capacity >= filters['capacity']) if 'building' in filters: criteria['building'] = filters['building'] if 'division' in filters: criteria['division'] = filters['division'] query = query.filter_by(**criteria) if 'text' in filters: text = ' '.join(filters['text'].strip().split()) query = query.filter(_make_room_text_filter(text)) if filters.get('equipment'): subquery = (db.session.query(RoomEquipmentAssociation) .with_entities(db.func.count(RoomEquipmentAssociation.c.room_id)) .filter( RoomEquipmentAssociation.c.room_id == Room.id, EquipmentType.name.in_(filters['equipment']) ) .join(EquipmentType, RoomEquipmentAssociation.c.equipment_id == EquipmentType.id) .correlate(Room) .as_scalar()) query = query.filter(subquery == len(filters['equipment'])) if filters.get('features'): for feature in filters['features']: query = query.filter(Room.available_equipment.any(EquipmentType.features.any(RoomFeature.name == feature))) if filters.get('favorite'): query = query.filter(favorite_room_table.c.user_id.isnot(None)) if filters.get('mine'): ids = get_managed_room_ids(session.user) query = query.filter(Room.id.in_(ids)) query = _filter_coordinates(query, filters) if availability is None: return query start_dt, end_dt = filters['start_dt'], filters['end_dt'] repeatability = (filters['repeat_frequency'], filters['repeat_interval']) availability_filters = [Room.filter_available(start_dt, end_dt, repeatability, include_blockings=False, include_pre_bookings=True)] if not (allow_admin and rb_is_admin(session.user)): selected_period_days = (filters['end_dt'] - filters['start_dt']).days booking_limit_days = db.func.coalesce(Room.booking_limit_days, rb_settings.get('booking_limit')) criterion = db.and_(Room.filter_bookable_hours(start_dt.time(), end_dt.time()), Room.filter_nonbookable_periods(start_dt, end_dt), db.or_(booking_limit_days.is_(None), selected_period_days <= booking_limit_days)) unbookable_ids = [room.id for room in query.filter(db.and_(*availability_filters), ~criterion) if not room.can_override(session.user, allow_admin=False)] availability_filters.append(~Room.id.in_(unbookable_ids)) availability_criterion = db.and_(*availability_filters) if availability is False: availability_criterion = ~availability_criterion return query.filter(availability_criterion)
def test_rb_is_admin(create_user, is_admin, is_rb_admin, expected): user = create_user(1, admin=is_admin, rb_admin=is_rb_admin) assert rb_is_admin(user) == expected
def can_be_deleted(self, user): if user is None: return False return rb_is_admin(user)
def create_occurrences(self, skip_conflicts, user=None, allow_admin=True): ReservationOccurrence.create_series_for_reservation(self) db.session.flush() if user is None: user = self.created_by_user # Check for conflicts with nonbookable periods admin = allow_admin and rb_is_admin(user) if not admin and not self.room.can_manage(user, permission='override'): nonbookable_periods = self.room.nonbookable_periods.filter( NonBookablePeriod.end_dt > self.start_dt) for occurrence in self.occurrences: if not occurrence.is_valid: continue for nbd in nonbookable_periods: if nbd.overlaps(occurrence.start_dt, occurrence.end_dt): if not skip_conflicts: raise ConflictingOccurrences() occurrence.cancel(user, 'Skipped due to nonbookable date', silent=True, propagate=False) break # Check for conflicts with blockings blocked_rooms = self.room.get_blocked_rooms( *(occurrence.start_dt for occurrence in self.occurrences)) for br in blocked_rooms: blocking = br.blocking if blocking.can_override(user, room=self.room, allow_admin=allow_admin): continue for occurrence in self.occurrences: if occurrence.is_valid and blocking.is_active_at( occurrence.start_dt.date()): # Cancel OUR occurrence msg = 'Skipped due to collision with a blocking ({})' occurrence.cancel(user, msg.format(blocking.reason), silent=True, propagate=False) # Check for conflicts with other occurrences conflicting_occurrences = self.get_conflicting_occurrences() for occurrence, conflicts in conflicting_occurrences.items(): if not occurrence.is_valid: continue if conflicts['confirmed']: if not skip_conflicts: raise ConflictingOccurrences() # Cancel OUR occurrence msg = 'Skipped due to collision with {} reservation(s)' occurrence.cancel(user, msg.format(len(conflicts['confirmed'])), silent=True, propagate=False) elif conflicts['pending'] and self.is_accepted: # Reject OTHER occurrences for conflict in conflicts['pending']: conflict.reject( user, 'Rejected due to collision with a confirmed reservation' )
def can_be_cancelled(self, user): if user is None: return False return self.is_owned_by(user) or rb_is_admin( user) or self.is_booked_for(user)
def can_delete(self, user, allow_admin=True): if user is None: return False return allow_admin and rb_is_admin(user) and (self.is_cancelled or self.is_rejected)
def can_delete(self, user, allow_admin=True): if not user: return False return user == self.created_by_user or (allow_admin and rb_is_admin(user))
def can_edit(self, user): if not user: return False return rb_is_admin(user)
def get_rooms_conflicts(rooms, start_dt, end_dt, repeat_frequency, repeat_interval, blocked_rooms, nonbookable_periods, unbookable_hours, skip_conflicts_with=None, allow_admin=False, skip_past_conflicts=False): rooms_conflicts = defaultdict(set) rooms_pre_conflicts = defaultdict(set) rooms_conflicting_candidates = defaultdict(set) skip_conflicts_with = skip_conflicts_with or [] candidates = ReservationOccurrence.create_series( start_dt, end_dt, (repeat_frequency, repeat_interval)) room_ids = [room.id for room in rooms] query = (ReservationOccurrence.query.filter( Reservation.room_id.in_(room_ids), ReservationOccurrence.is_valid, ReservationOccurrence.filter_overlap(candidates)).join( ReservationOccurrence.reservation).options( ReservationOccurrence.NO_RESERVATION_USER_STRATEGY, contains_eager(ReservationOccurrence.reservation))) if skip_conflicts_with: query = query.filter(~Reservation.id.in_(skip_conflicts_with)) if skip_past_conflicts: query = query.filter(ReservationOccurrence.start_dt > datetime.now()) overlapping_occurrences = group_list( query, key=lambda obj: obj.reservation.room.id, sort_by=lambda obj: obj.reservation.room.id) for room_id, occurrences in overlapping_occurrences.items(): conflicts = get_room_bookings_conflicts(candidates, occurrences, skip_conflicts_with) rooms_conflicts[room_id], rooms_pre_conflicts[ room_id], rooms_conflicting_candidates[room_id] = conflicts for room_id, occurrences in blocked_rooms.items(): conflicts, conflicting_candidates = get_room_blockings_conflicts( room_id, candidates, occurrences, allow_admin=allow_admin) rooms_conflicts[room_id] |= conflicts rooms_conflicting_candidates[room_id] |= conflicting_candidates if not (allow_admin and rb_is_admin(session.user)): for room_id, occurrences in nonbookable_periods.items(): room = Room.get_or_404(room_id) if not room.can_override(session.user, allow_admin=allow_admin): conflicts, conflicting_candidates = get_room_nonbookable_periods_conflicts( candidates, occurrences) rooms_conflicts[room_id] |= conflicts rooms_conflicting_candidates[room_id] |= conflicting_candidates for room_id, occurrences in unbookable_hours.items(): room = Room.get_or_404(room_id) if not room.can_override(session.user, allow_admin=allow_admin): conflicts, conflicting_candidates = get_room_unbookable_hours_conflicts( candidates, occurrences) rooms_conflicts[room_id] |= conflicts rooms_conflicting_candidates[room_id] |= conflicting_candidates rooms_conflicting_candidates = defaultdict( list, ((k, list(v)) for k, v in rooms_conflicting_candidates.items())) return rooms_conflicts, rooms_pre_conflicts, rooms_conflicting_candidates
def _process(self): return jsonify( user=self._get_permissions(allow_admin=False), admin=(self._get_permissions( allow_admin=True) if rb_is_admin(session.user) else None))
def _check_access(self): if session.user is None: self._require_user() elif not rb_is_admin(session.user): raise Forbidden(_('You are not authorized to take this action.'))
def _jsonify_user_permissions(user): permissions = Room.get_permissions_for_user(user, allow_admin=False) return jsonify(user=permissions, admin=(Room.get_permissions_for_user(user) if rb_is_admin(user) else None))
def search_for_rooms(filters, allow_admin=False, availability=None): """Search for a room, using the provided filters. :param filters: The filters, provided as a dictionary :param allow_admin: A boolean specifying whether admins have override privileges :param availability: A boolean specifying whether (un)available rooms should be provided, or `None` in case all rooms should be returned. """ query = ( Room.query.outerjoin( favorite_room_table, db.and_( favorite_room_table.c.user_id == session.user.id, favorite_room_table.c.room_id == Room.id)).reset_joinpoint( ) # otherwise filter_by() would apply to the favorite table .options(raiseload('owner')).filter(Room.is_active).order_by( favorite_room_table.c.user_id.is_(None), db.func.indico.natsort(Room.full_name))) criteria = {} if 'capacity' in filters: query = query.filter(Room.capacity >= filters['capacity']) if 'building' in filters: criteria['building'] = filters['building'] if 'division' in filters: criteria['division'] = filters['division'] query = query.filter_by(**criteria) if 'text' in filters: query = query.filter(_make_room_text_filter(filters['text'])) if filters.get('equipment'): subquery = (db.session.query(RoomEquipmentAssociation).with_entities( db.func.count(RoomEquipmentAssociation.c.room_id)).filter( RoomEquipmentAssociation.c.room_id == Room.id, EquipmentType.name.in_(filters['equipment'])).join( EquipmentType, RoomEquipmentAssociation.c.equipment_id == EquipmentType.id).correlate(Room).as_scalar()) query = query.filter(subquery == len(filters['equipment'])) if filters.get('features'): for feature in filters['features']: query = query.filter( Room.available_equipment.any( EquipmentType.features.any(RoomFeature.name == feature))) if filters.get('favorite'): query = query.filter(favorite_room_table.c.user_id.isnot(None)) if filters.get('mine'): ids = get_managed_room_ids(session.user) query = query.filter(Room.id.in_(ids)) query = _filter_coordinates(query, filters) if availability is None: return query start_dt, end_dt = filters['start_dt'], filters['end_dt'] repeatability = (filters['repeat_frequency'], filters['repeat_interval']) include_blockings = not rb_is_admin(session.user) availability_filters = [ Room.filter_available(start_dt, end_dt, repeatability, include_blockings=include_blockings, include_pre_bookings=True) ] if not (allow_admin and rb_is_admin(session.user)): selected_period_days = (filters['end_dt'] - filters['start_dt']).days booking_limit_days = db.func.coalesce(Room.booking_limit_days, rb_settings.get('booking_limit')) criterion = db.and_( Room.filter_bookable_hours(start_dt.time(), end_dt.time()), Room.filter_nonbookable_periods(start_dt, end_dt), db.or_(booking_limit_days.is_(None), selected_period_days <= booking_limit_days)) unbookable_ids = [ room.id for room in query.filter(db.and_( *availability_filters), ~criterion) if not room.can_override(session.user, allow_admin=False) ] availability_filters.append(~Room.id.in_(unbookable_ids)) availability_criterion = db.and_(*availability_filters) if availability is False: availability_criterion = ~availability_criterion return query.filter(availability_criterion)
def _checkProtection(self): if not session.user: self._checkSessionUser() elif not rb_is_admin(session.user): raise Forbidden(_('You are not authorized to take this action.'))