def new_reservation(request, item_type, item_id, date=None): # If the user has no active projects then they're not allowed to make reservations. user: User = request.user if user.active_project_count() == 0: return render(request, 'mobile/no_active_projects.html') item_type = ReservationItemType(item_type) item = get_object_or_404(item_type.get_object_class(), id=item_id) if item_type == ReservationItemType.TOOL: dictionary = item.get_configuration_information(user=request.user, start=None) else: dictionary = {} dictionary['item'] = item dictionary['item_type'] = item_type.value dictionary['date'] = date dictionary['item_reservation_times'] = list( Reservation.objects.filter(**{ item_type.value: item }).filter(cancelled=False, missed=False, shortened=False, start__gte=timezone.now())) # Reservation questions if applicable if not user.is_staff: reservation_question_dict = {} for project in user.active_projects(): reservation_questions = render_reservation_questions( item_type, item_id, project) if reservation_questions: reservation_question_dict[project.id] = reservation_questions dictionary['reservation_questions'] = reservation_question_dict return render(request, 'mobile/new_reservation.html', dictionary)
def make_reservation(request): """ Create a reservation for a user. """ try: date = parse_date(request.POST['date']) start = localize( datetime.combine(date, parse_time(request.POST['start']))) end = localize(datetime.combine(date, parse_time(request.POST['end']))) except: return render( request, 'mobile/error.html', { 'message': 'Please enter a valid date, start time, and end time for the reservation.' }) item_type = ReservationItemType(request.POST['item_type']) item = get_object_or_404(item_type.get_object_class(), id=request.POST.get('item_id')) # Create the new reservation: reservation = Reservation() reservation.user = request.user reservation.creator = request.user reservation.reservation_item = item reservation.start = start reservation.end = end if item_type == ReservationItemType.TOOL: reservation.short_notice = determine_insufficient_notice(item, start) else: reservation.short_notice = False policy_problems, overridable = check_policy_to_save_reservation( cancelled_reservation=None, new_reservation=reservation, user_creating_reservation=request.user, explicit_policy_override=False) # If there was a problem in saving the reservation then return the error... if policy_problems: return render(request, 'mobile/error.html', {'message': policy_problems[0]}) # All policy checks have passed. try: reservation.project = Project.objects.get( id=request.POST['project_id']) except: if not request.user.is_staff: return render( request, 'mobile/error.html', {'message': 'You must specify a project for your reservation'}) reservation.additional_information, reservation.self_configuration = extract_configuration( request) # Reservation can't be short notice if the user is configuring the tool themselves. if reservation.self_configuration: reservation.short_notice = False reservation.save_and_notify() return render(request, 'mobile/reservation_success.html', {'new_reservation': reservation})
def view_calendar(request, item_type, item_id, date=None): item_type = ReservationItemType(item_type) item = get_object_or_404(item_type.get_object_class(), id=item_id) if date: try: date = extract_date(date) except: render(request, 'mobile/error.html', {'message': 'Invalid date requested for tool calendar'}) return HttpResponseBadRequest() else: date = datetime.now() start = beginning_of_the_day(date, in_local_timezone=True) end = end_of_the_day(date, in_local_timezone=True) reservations = Reservation.objects.filter(**{ item_type.value: item }).filter(cancelled=False, missed=False, shortened=False).filter(**{}) # Exclude events for which the following is true: # The event starts and ends before the time-window, and... # The event starts and ends after the time-window. reservations = reservations.exclude(start__lt=start, end__lt=start) reservations = reservations.exclude(start__gt=end, end__gt=end) outages = ScheduledOutage.objects.none() if item_type == ReservationItemType.TOOL: outages = ScheduledOutage.objects.filter( Q(tool=item) | Q(resource__fully_dependent_tools__in=[item])) elif item_type == ReservationItemType.AREA: outages = item.scheduled_outage_queryset() # Exclude outages for which the following is true: # The outage starts and ends before the time-window, and... # The outage starts and ends after the time-window. outages = outages.exclude(start__lt=start, end__lt=start) outages = outages.exclude(start__gt=end, end__gt=end) events = list(chain(reservations, outages)) events.sort(key=lambda x: x.start) dictionary = { 'item': item, 'item_type': item_type.value, 'previous_day': start - timedelta(days=1), 'current_day': start, 'current_day_string': date.strftime('%Y-%m-%d'), 'next_day': start + timedelta(days=1), 'events': events, } return render(request, 'mobile/view_calendar.html', dictionary)
def create_reservation(request): """ Create a reservation for a user. """ try: start, end = extract_times(request.POST) item_type = request.POST['item_type'] item_id = request.POST.get('item_id') except Exception as e: return HttpResponseBadRequest(str(e)) return create_item_reservation(request, start, end, ReservationItemType(item_type), item_id)
def refresh_sidebar_icons(request, item_type=None): area_summary = [] tool_summary = [] item_type = ReservationItemType(item_type) if item_type == ReservationItemType.NONE: tool_summary = create_tool_summary() area_summary = create_area_summary() elif item_type == ReservationItemType.AREA: area_summary = create_area_summary() elif item_type == ReservationItemType.TOOL: tool_summary = create_tool_summary() return render(request, 'refresh_sidebar_icons.html', { 'tool_summary': tool_summary, 'area_summary': area_summary })
def shorten_reservation(user: User, item: Union[Area, Tool], new_end: datetime = None): try: if new_end is None: new_end = timezone.now() current_reservation = Reservation.objects.filter(start__lt=timezone.now(), end__gt=timezone.now(), cancelled=False, missed=False, shortened=False, user=user) current_reservation = current_reservation.get(**{ReservationItemType.from_item(item).value: item}) # Staff are exempt from mandatory reservation shortening. if user.is_staff is False: new_reservation = deepcopy(current_reservation) new_reservation.id = None new_reservation.pk = None new_reservation.end = new_end new_reservation.save() current_reservation.shortened = True current_reservation.descendant = new_reservation current_reservation.save() except Reservation.DoesNotExist: pass
def usage_event_feed(request, start, end): usage_events = UsageEvent.objects # Exclude events for which the following is true: # The event starts and ends before the time-window, and... # The event starts and ends after the time-window. usage_events = usage_events.exclude(start__lt=start, end__lt=start) usage_events = usage_events.exclude(start__gt=end, end__gt=end) # Filter events that only have to do with the relevant tool. item_id = request.GET.get('item_id') item_type = ReservationItemType(request.GET.get('item_type')) if request.GET.get('item_type') else None if item_id and item_type == ReservationItemType.TOOL: usage_events = usage_events.filter(tool__id__in=Tool.objects.get(pk=item_id).get_family_tool_ids()) area_access_events = None # Filter events that only have to do with the current user. personal_schedule = request.GET.get('personal_schedule') if personal_schedule: usage_events = usage_events.filter(user=request.user) # Display area access along side tool usage when 'personal schedule' is selected. area_access_events = AreaAccessRecord.objects.filter(customer__id=request.user.id) area_access_events = area_access_events.exclude(start__lt=start, end__lt=start) area_access_events = area_access_events.exclude(start__gt=end, end__gt=end) missed_reservations = None if personal_schedule: missed_reservations = Reservation.objects.filter(missed=True, user=request.user) elif item_type: reservation_filter = {item_type.value: item_id} missed_reservations = Reservation.objects.filter(missed=True).filter(**reservation_filter) if missed_reservations: missed_reservations = missed_reservations.exclude(start__lt=start, end__lt=start) missed_reservations = missed_reservations.exclude(start__gt=end, end__gt=end) dictionary = { 'usage_events': usage_events, 'area_access_events': area_access_events, 'personal_schedule': personal_schedule, 'missed_reservations': missed_reservations, } return render(request, 'calendar/usage_event_feed.html', dictionary)
def reservation_event_feed(request, start, end): events = Reservation.objects.filter(cancelled=False, missed=False, shortened=False) outages = ScheduledOutage.objects.none() # Exclude events for which the following is true: # The event starts and ends before the time-window, and... # The event starts and ends after the time-window. events = events.exclude(start__lt=start, end__lt=start) events = events.exclude(start__gt=end, end__gt=end) # Filter events that only have to do with the relevant tool/area. item_type = request.GET.get('item_type') if item_type: item_type = ReservationItemType(item_type) item_id = request.GET.get('item_id') if item_id: events = events.filter(**{f'{item_type.value}__id': item_id}) if item_type == ReservationItemType.TOOL: outages = ScheduledOutage.objects.filter(Q(tool=item_id) | Q(resource__fully_dependent_tools__in=[item_id])) elif item_type == ReservationItemType.AREA: outages = Area.objects.get(pk=item_id).scheduled_outage_queryset() # Exclude outages for which the following is true: # The outage starts and ends before the time-window, and... # The outage starts and ends after the time-window. outages = outages.exclude(start__lt=start, end__lt=start) outages = outages.exclude(start__gt=end, end__gt=end) # Filter events that only have to do with the current user. personal_schedule = request.GET.get('personal_schedule') if personal_schedule: events = events.filter(user=request.user) dictionary = { 'events': events, 'outages': outages, 'personal_schedule': personal_schedule, } return render(request, 'calendar/reservation_event_feed.html', dictionary)
def get_target(self): target = self.cleaned_data["target"].split("|", 1) return ReservationItemType(target[0]), int(target[1])
def create_outage(request): """ Create an outage. """ try: start, end = extract_times(request.POST) item_type = ReservationItemType(request.POST['item_type']) item_id = request.POST.get('item_id') except Exception as e: return HttpResponseBadRequest(str(e)) item = get_object_or_404(item_type.get_object_class(), id=item_id) # Create the new reservation: outage = ScheduledOutage() outage.creator = request.user outage.category = request.POST.get('category', '')[:200] outage.outage_item = item outage.start = start outage.end = end # If there is a policy problem for the outage then return the error... policy_problem = check_policy_to_create_outage(outage) if policy_problem: return HttpResponseBadRequest(policy_problem) # Make sure there is at least an outage title if not request.POST.get('title'): dictionary = { 'categories': ScheduledOutageCategory.objects.all(), 'recurrence_intervals': recurrence_frequency_display, 'recurrence_date_start': start.date(), } return render(request, 'calendar/scheduled_outage_information.html', dictionary) outage.title = request.POST['title'] outage.details = request.POST.get('details', '') if request.POST.get('recurring_outage') == 'on': # we have to remove tz before creating rules otherwise 8am would become 7am after DST change for example. start_no_tz = outage.start.replace(tzinfo=None) end_no_tz = outage.end.replace(tzinfo=None) submitted_frequency = request.POST.get('recurrence_frequency') submitted_date_until = request.POST.get('recurrence_until', None) date_until = end.replace(hour=0, minute=0, second=0) if submitted_date_until: date_until = localize(datetime.strptime(submitted_date_until, '%m/%d/%Y')) date_until += timedelta(days=1, seconds=-1) # set at the end of the day by_week_day = None if submitted_frequency == 'DAILY_WEEKDAYS': by_week_day = (rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR) elif submitted_frequency == 'DAILY_WEEKENDS': by_week_day = (rrule.SA, rrule.SU) frequency = recurrence_frequencies.get(submitted_frequency, rrule.DAILY) rules: Iterable[datetime] = rrule.rrule(dtstart=start, freq=frequency, interval=int(request.POST.get('recurrence_interval',1)), until=date_until, byweekday=by_week_day) for rule in list(rules): recurring_outage = ScheduledOutage() recurring_outage.creator = outage.creator recurring_outage.category = outage.category recurring_outage.outage_item = outage.outage_item recurring_outage.title = outage.title recurring_outage.details = outage.details recurring_outage.start = localize(start_no_tz.replace(year=rule.year, month=rule.month, day=rule.day)) recurring_outage.end = localize(end_no_tz.replace(year=rule.year, month=rule.month, day=rule.day)) recurring_outage.save() else: outage.save() return HttpResponse()
def create_item_reservation(request, start, end, item_type: ReservationItemType, item_id): item = get_object_or_404(item_type.get_object_class(), id=item_id) explicit_policy_override = False if request.user.is_staff: try: user = User.objects.get(id=request.POST['impersonate']) except: user = request.user try: explicit_policy_override = request.POST['explicit_policy_override'] == 'true' except: pass else: user = request.user # Create the new reservation: new_reservation = Reservation() new_reservation.user = user new_reservation.creator = request.user # set tool or area setattr(new_reservation, item_type.value, item) new_reservation.start = start new_reservation.end = end new_reservation.short_notice = determine_insufficient_notice(item, start) if item_type == ReservationItemType.TOOL else False policy_problems, overridable = check_policy_to_save_reservation(cancelled_reservation=None, new_reservation=new_reservation, user_creating_reservation=request.user, explicit_policy_override=explicit_policy_override) # If there was a problem in saving the reservation then return the error... if policy_problems: return render(request, 'calendar/policy_dialog.html', {'policy_problems': policy_problems, 'overridable': overridable and request.user.is_staff, 'reservation_action': 'create'}) # All policy checks have passed. # If the user only has one project then associate it with the reservation. # Otherwise, present a dialog box for the user to choose which project to associate. if not user.is_staff: active_projects = user.active_projects() if len(active_projects) == 1: new_reservation.project = active_projects[0] else: try: new_reservation.project = Project.objects.get(id=request.POST['project_id']) except: return render(request, 'calendar/project_choice.html', {'active_projects': active_projects}) # Make sure the user is actually enrolled on the project. We wouldn't want someone # forging a request to reserve against a project they don't belong to. if new_reservation.project not in new_reservation.user.active_projects(): return render(request, 'calendar/project_choice.html', {'active_projects': active_projects}) # Configuration rules only apply to tools if item_type == ReservationItemType.TOOL: configured = (request.POST.get('configured') == "true") # If a reservation is requested and the tool does not require configuration... if not item.is_configurable(): new_reservation.save_and_notify() return reservation_success(request, new_reservation) # If a reservation is requested and the tool requires configuration that has not been submitted... elif item.is_configurable() and not configured: configuration_information = item.get_configuration_information(user=user, start=start) return render(request, 'calendar/configuration.html', configuration_information) # If a reservation is requested and configuration information is present also... elif item.is_configurable() and configured: new_reservation.additional_information, new_reservation.self_configuration = extract_configuration(request) # Reservation can't be short notice if the user is configuring the tool themselves. if new_reservation.self_configuration: new_reservation.short_notice = False new_reservation.save_and_notify() return reservation_success(request, new_reservation) elif item_type == ReservationItemType.AREA: new_reservation.save_and_notify() return HttpResponse() return HttpResponseBadRequest("Reservation creation failed because invalid parameters were sent to the server.")
def get_target(self): target = self.cleaned_data['target'].split('|', 1) return ReservationItemType(target[0]), int(target[1])