def test_admin_may_bypass_min_period(resource_with_opening_hours, user): """ Admin users should be able to bypass min_period, and their minimum reservation time should be limited by slot_size """ activate('en') # min_period is bypassed respecting slot_size restriction resource_with_opening_hours.min_period = datetime.timedelta(hours=1) resource_with_opening_hours.slot_size = datetime.timedelta(minutes=30) resource_with_opening_hours.save() tz = timezone.get_current_timezone() begin = tz.localize(datetime.datetime(2115, 6, 1, 8, 0, 0)) end = begin + datetime.timedelta(hours=0, minutes=30) UnitAuthorization.objects.create( subject=resource_with_opening_hours.unit, level=UnitAuthorizationLevel.admin, authorized=user, ) reservation = Reservation(resource=resource_with_opening_hours, begin=begin, end=end, user=user) reservation.clean() # min_period is bypassed and slot_size restriction is violated resource_with_opening_hours.slot_size = datetime.timedelta(minutes=25) resource_with_opening_hours.save() with pytest.raises(ValidationError) as error: reservation.clean() assert error.value.code == 'invalid_time_slot'
def create_respa_outlook_reservation(self, appointment, reservation, email): if not appointment: return if not reservation: if not email: return reservation = Reservation(resource=self.resource, begin=appointment.start, end=appointment.end, reserver_email_address=email, state=Reservation.CONFIRMED) reservation.clean() reservation.save() ret = send_respa_mail(email_address=email, subject="Reservation created", body="Reservation via outlook created") print(ret[0], ret[1]) RespaOutlookReservation( name='%s (%s)' % (reservation.reserver_email_address, self.resource.name), exchange_id=appointment.id, exchange_changekey=appointment.changekey, reservation=reservation).save()
def test_valid_reservation_duration_with_slot_size(resource_with_opening_hours): resource_with_opening_hours.min_period = datetime.timedelta(hours=1) resource_with_opening_hours.slot_size = datetime.timedelta(minutes=30) resource_with_opening_hours.save() tz = timezone.get_current_timezone() begin = tz.localize(datetime.datetime(2115, 6, 1, 8, 0, 0)) end = begin + datetime.timedelta(hours=2, minutes=30) reservation = Reservation(resource=resource_with_opening_hours, begin=begin, end=end) reservation.clean()
def test_invalid_reservation_duration_with_slot_size(resource_with_opening_hours): activate('en') resource_with_opening_hours.min_period = datetime.timedelta(hours=1) resource_with_opening_hours.slot_size = datetime.timedelta(minutes=30) resource_with_opening_hours.save() tz = timezone.get_current_timezone() begin = tz.localize(datetime.datetime(2115, 6, 1, 8, 0, 0)) end = begin + datetime.timedelta(hours=2, minutes=45) reservation = Reservation(resource=resource_with_opening_hours, begin=begin, end=end) with pytest.raises(ValidationError) as error: reservation.clean() assert error.value.code == 'invalid_time_slot'
def validate(self, data): reservation = self.instance request_user = self.context['request'].user # this check is probably only needed for PATCH try: resource = data['resource'] except KeyError: resource = reservation.resource if not resource.can_make_reservations(request_user): raise PermissionDenied() if data['end'] < timezone.now(): raise ValidationError(_('You cannot make a reservation in the past')) # normal users cannot make reservations for other people if not resource.is_admin(request_user): data.pop('user', None) # Check user specific reservation restrictions relating to given period. resource.validate_reservation_period(reservation, request_user, data=data) if 'comments' in data: if not resource.is_admin(request_user): raise ValidationError(dict(comments=_('Only allowed to be set by staff members'))) # Mark begin of a critical section. Subsequent calls with this same resource will block here until the first # request is finished. This is needed so that the validations and possible reservation saving are # executed in one block and concurrent requests cannot be validated incorrectly. Resource.objects.select_for_update().get(pk=resource.pk) # Check maximum number of active reservations per user per resource. # Only new reservations are taken into account ie. a normal user can modify an existing reservation # even if it exceeds the limit. (one that was created via admin ui for example). if reservation is None: resource.validate_max_reservations_per_user(request_user) # Run model clean data.pop('staff_event', None) instance = Reservation(**data) instance.clean(original_reservation=reservation) return data
def validate(self, data): reservation = self.instance request_user = self.context['request'].user # this check is probably only needed for PATCH try: resource = data['resource'] except KeyError: resource = reservation.resource if not resource.can_make_reservations(request_user): raise PermissionDenied() if data['end'] < timezone.now(): raise ValidationError( _('You cannot make a reservation in the past')) if not resource.is_admin(request_user): reservable_before = resource.get_reservable_before() if reservable_before and data['begin'] >= reservable_before: raise ValidationError( _('The resource is reservable only before %(datetime)s' % {'datetime': reservable_before})) # normal users cannot make reservations for other people if not resource.is_admin(request_user): data.pop('user', None) # Check user specific reservation restrictions relating to given period. resource.validate_reservation_period(reservation, request_user, data=data) if 'comments' in data: if not resource.is_admin(request_user): raise ValidationError( dict( comments=_('Only allowed to be set by staff members'))) if 'access_code' in data: if data['access_code'] == None: data['access_code'] = '' access_code_enabled = resource.is_access_code_enabled() if not access_code_enabled and data['access_code']: raise ValidationError( dict(access_code=_( 'This field cannot have a value with this resource'))) if access_code_enabled and reservation and data[ 'access_code'] != reservation.access_code: raise ValidationError( dict(access_code=_('This field cannot be changed'))) # Mark begin of a critical section. Subsequent calls with this same resource will block here until the first # request is finished. This is needed so that the validations and possible reservation saving are # executed in one block and concurrent requests cannot be validated incorrectly. Resource.objects.select_for_update().get(pk=resource.pk) # Check maximum number of active reservations per user per resource. # Only new reservations are taken into account ie. a normal user can modify an existing reservation # even if it exceeds the limit. (one that was created via admin ui for example). if reservation is None: resource.validate_max_reservations_per_user(request_user) # Run model clean data.pop('staff_event', None) instance = Reservation(**data) try: instance.clean(original_reservation=reservation) except DjangoValidationError as exc: # Convert Django ValidationError to DRF ValidationError so that in the response # field specific error messages are added in the field instead of in non_field_messages. if not hasattr(exc, 'error_dict'): raise ValidationError(exc) error_dict = {} for key, value in exc.error_dict.items(): error_dict[key] = [error.message for error in value] raise ValidationError(error_dict) return data
def test_reservation(self): r1a = Resource.objects.get(id='r1a') r1b = Resource.objects.get(id='r1b') tz = timezone.get_current_timezone() begin = tz.localize(datetime.datetime(2116, 6, 1, 8, 0, 0)) end = begin + datetime.timedelta(hours=2) reservation = Reservation.objects.create(resource=r1a, begin=begin, end=end) reservation.clean() # Attempt overlapping reservation with self.assertRaises(ValidationError): reservation = Reservation(resource=r1a, begin=begin, end=end) reservation.clean() valid_begin = begin + datetime.timedelta(hours=3) valid_end = end + datetime.timedelta(hours=3) # Attempt incorrectly aligned begin time with self.assertRaises(ValidationError): reservation = Reservation(resource=r1a, begin=valid_begin + datetime.timedelta(minutes=1), end=valid_end) reservation.clean() # Attempt incorrectly aligned end time with self.assertRaises(ValidationError): reservation = Reservation(resource=r1a, begin=valid_begin, end=valid_end + datetime.timedelta(minutes=1)) reservation.clean() # Attempt reservation that starts before the resource opens # Should not raise an exception as this check isn't included in model clean reservation = Reservation( resource=r1a, begin=begin - datetime.timedelta(hours=1), end=begin ) reservation.clean() begin = tz.localize(datetime.datetime(2116, 6, 1, 16, 0, 0)) end = begin + datetime.timedelta(hours=2) # Make a reservation that ends when the resource closes reservation = Reservation(resource=r1a, begin=begin, end=end) reservation.clean() # Attempt reservation that ends after the resource closes # Should not raise an exception as this check isn't included in model clean reservation = Reservation(resource=r1a, begin=begin, end=end + datetime.timedelta(hours=1)) reservation.clean()
def validate(self, data): reservation = self.instance request_user = self.context['request'].user # this check is probably only needed for PATCH try: resource = data['resource'] except KeyError: resource = reservation.resource data.update({'resource': resource}) if not data.get('begin', None): data.update({'begin': reservation.begin}) if not data.get('end', None): data.update({'end': reservation.end}) if not resource.can_make_reservations(request_user): raise PermissionDenied( _('You are not allowed to make reservations in this resource.') ) if data['end'] < timezone.now(): raise ValidationError( _('You cannot make a reservation in the past')) if resource.min_age and is_underage(request_user, resource.min_age): raise PermissionDenied( _('You have to be over %s years old to reserve this resource' % (resource.min_age))) if resource.max_age and is_overage(request_user, resource.max_age): raise PermissionDenied( _('You have to be under %s years old to reserve this resource' % (resource.max_age))) is_resource_admin = resource.is_admin(request_user) is_resource_manager = resource.is_manager(request_user) if not isinstance(request_user, AnonymousUser): if request_user.preferred_language is None: request_user.preferred_language = settings.LANGUAGES[0][0] request_user.save() if not resource.can_ignore_opening_hours(request_user): reservable_before = resource.get_reservable_before() if reservable_before and data['begin'] >= reservable_before: raise ValidationError( _('The resource is reservable only before %(datetime)s' % {'datetime': reservable_before})) reservable_after = resource.get_reservable_after() if reservable_after and data['begin'] < reservable_after: raise ValidationError( _('The resource is reservable only after %(datetime)s' % {'datetime': reservable_after})) # Check given home municipality is included in resource's home municipality set if 'home_municipality' in data: included = resource.get_included_home_municipality_names() found = False for municipality in included: if data['home_municipality'].id == municipality['id']: found = True break if not found: raise ValidationError( _('Home municipality has to be one of the included home municipality options' )) # normal users cannot make reservations for other people if not resource.can_create_reservations_for_other_users(request_user): data.pop('user', None) # Check user specific reservation restrictions relating to given period. resource.validate_reservation_period(reservation, request_user, data=data) reserver_phone_number = data.get('reserver_phone_number', '') if reserver_phone_number.startswith('+'): if not region_code_for_country_code( phonenumbers.parse(reserver_phone_number).country_code): raise ValidationError( dict(reserver_phone_number=_('Invalid country code'))) if data.get('staff_event', False): if not resource.can_create_staff_event(request_user): raise ValidationError( dict(staff_event=_( 'Only allowed to be set by resource managers'))) if 'type' in data: if (data['type'] != Reservation.TYPE_NORMAL and not resource.can_create_special_type_reservation( request_user)): raise ValidationError({ 'type': _('You are not allowed to make a reservation of this type') }) if 'comments' in data: if not resource.can_comment_reservations(request_user): raise ValidationError( dict( comments=_('Only allowed to be set by staff members'))) if 'access_code' in data: if data['access_code'] is None: data['access_code'] = '' access_code_enabled = resource.is_access_code_enabled() if not access_code_enabled and data['access_code']: raise ValidationError( dict(access_code=_( 'This field cannot have a value with this resource'))) if access_code_enabled and reservation and data[ 'access_code'] != reservation.access_code: raise ValidationError( dict(access_code=_('This field cannot be changed'))) # Mark begin of a critical section. Subsequent calls with this same resource will block here until the first # request is finished. This is needed so that the validations and possible reservation saving are # executed in one block and concurrent requests cannot be validated incorrectly. Resource.objects.select_for_update().get(pk=resource.pk) # Check maximum number of active reservations per user per resource. # Only new reservations are taken into account ie. a normal user can modify an existing reservation # even if it exceeds the limit. (one that was created via admin ui for example). if reservation is None and not isinstance(request_user, AnonymousUser): resource.validate_max_reservations_per_user(request_user) # Run model clean instance = Reservation(**data) try: instance.clean(original_reservation=reservation, user=request_user) except DjangoValidationError as exc: # Convert Django ValidationError to DRF ValidationError so that in the response # field specific error messages are added in the field instead of in non_field_messages. if not hasattr(exc, 'error_dict'): raise ValidationError(exc) error_dict = {} for key, value in exc.error_dict.items(): error_dict[key] = [error.message for error in value] raise ValidationError(error_dict) return data
def validate(self, data): reservation = self.instance request_user = self.context['request'].user # this check is probably only needed for PATCH try: resource = data['resource'] except KeyError: resource = reservation.resource if not resource.can_make_reservations(request_user): raise PermissionDenied() if data['end'] < timezone.now(): raise ValidationError(_('You cannot make a reservation in the past')) if not resource.is_admin(request_user): reservable_before = resource.get_reservable_before() if reservable_before and data['begin'] >= reservable_before: raise ValidationError(_('The resource is reservable only before %(datetime)s' % {'datetime': reservable_before})) # normal users cannot make reservations for other people if not resource.is_admin(request_user): data.pop('user', None) # Check user specific reservation restrictions relating to given period. resource.validate_reservation_period(reservation, request_user, data=data) if 'comments' in data: if not resource.is_admin(request_user): raise ValidationError(dict(comments=_('Only allowed to be set by staff members'))) if 'access_code' in data: if data['access_code'] == None: data['access_code'] = '' access_code_enabled = resource.is_access_code_enabled() if not access_code_enabled and data['access_code']: raise ValidationError(dict(access_code=_('This field cannot have a value with this resource'))) if access_code_enabled and reservation and data['access_code'] != reservation.access_code: raise ValidationError(dict(access_code=_('This field cannot be changed'))) # Mark begin of a critical section. Subsequent calls with this same resource will block here until the first # request is finished. This is needed so that the validations and possible reservation saving are # executed in one block and concurrent requests cannot be validated incorrectly. Resource.objects.select_for_update().get(pk=resource.pk) # Check maximum number of active reservations per user per resource. # Only new reservations are taken into account ie. a normal user can modify an existing reservation # even if it exceeds the limit. (one that was created via admin ui for example). if reservation is None: resource.validate_max_reservations_per_user(request_user) # Run model clean data.pop('staff_event', None) instance = Reservation(**data) try: instance.clean(original_reservation=reservation) except DjangoValidationError as exc: # Convert Django ValidationError to DRF ValidationError so that in the response # field specific error messages are added in the field instead of in non_field_messages. if not hasattr(exc, 'error_dict'): raise ValidationError(exc) error_dict = {} for key, value in exc.error_dict.items(): error_dict[key] = [error.message for error in value] raise ValidationError(error_dict) return data