예제 #1
0
파일: policy.py 프로젝트: carl-dawson/NEMO
def check_policy_to_create_outage(outage: ScheduledOutage):
    # Outages may not have a start time that is earlier than the end time.
    if outage.start >= outage.end:
        return "Outage start time (" + format_datetime(
            outage.start
        ) + ") must be before the end time (" + format_datetime(
            outage.end) + ")."

    # The user may not create, move, or resize an outage to coincide with another user's reservation.
    coincident_events = Reservation.objects.filter(
        **outage.outage_item_filter).filter(cancelled=False,
                                            missed=False,
                                            shortened=False)
    # 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.
    coincident_events = coincident_events.exclude(start__lt=outage.start,
                                                  end__lte=outage.start)
    coincident_events = coincident_events.exclude(start__gte=outage.end,
                                                  end__gt=outage.end)
    if coincident_events.count() > 0:
        return "Your scheduled outage coincides with a reservation that already exists. Please choose a different time."

    # No policy issues! The outage can be created...
    return None
예제 #2
0
 def save(self, commit=True):
     instance = super(TaskForm, self).save(commit=False)
     action = self.cleaned_data['action']
     description = self.cleaned_data['description']
     instance.problem_category = self.cleaned_data['problem_category']
     if action == 'create':
         instance.problem_description = description
         instance.urgency = Task.Urgency.HIGH if self.cleaned_data[
             'force_shutdown'] or self.cleaned_data[
                 'safety_hazard'] else Task.Urgency.NORMAL
         instance.creator = self.user
     if action == 'update':
         instance.status = Task.Status.WORK_IN_PROGRESS
         instance.last_updated = timezone.now()
         instance.last_updated_by = self.user
         if description:
             preface = 'On ' + format_datetime(timezone.now(
             )) + ' ' + self.user.get_full_name() + ' updated this task:\n'
             if instance.progress_description is None:
                 instance.progress_description = preface + description
             else:
                 instance.progress_description += '\n\n' + preface + description
             instance.progress_description = instance.progress_description.strip(
             )
     if action == 'resolve':
         instance.status = Task.Status.COMPLETE
         instance.resolution_time = timezone.now()
         instance.resolver = self.user
         if 'resolution_category' in self.cleaned_data:
             instance.resolution_category = self.cleaned_data[
                 'resolution_category']
         if 'description' in self.cleaned_data:
             if instance.resolution_description:
                 preface = 'On ' + format_datetime(
                     timezone.now()) + ' ' + self.user.get_full_name(
                     ) + ' updated the resolution information:\n'
                 instance.resolution_description = (
                     instance.resolution_description + '\n\n' + preface +
                     self.cleaned_data['description']).strip()
             else:
                 instance.resolution_description = self.cleaned_data[
                     'description']
         if instance.first_response_time is None:
             instance.first_response_time = timezone.now()
         if instance.first_responder is None:
             instance.first_responder = self.user
     if action in ('update', 'resolve'):
         if instance.first_response_time is None:
             instance.first_response_time = timezone.now()
         if instance.first_responder is None:
             instance.first_responder = self.user
     return super(TaskForm, self).save(commit=commit)
예제 #3
0
파일: policy.py 프로젝트: carl-dawson/NEMO
def check_policy_to_cancel_reservation(reservation,
                                       user_cancelling_reservation):
    """
	Checks the reservation deletion policy.
	If all checks pass the function returns an HTTP "OK" response.
	Otherwise, the function returns an HTTP "Bad Request" with an error message.
	"""

    # Users may only cancel reservations that they own.
    # Staff may break this rule.
    if (reservation.user != user_cancelling_reservation
        ) and not user_cancelling_reservation.is_staff:
        return HttpResponseBadRequest(
            "You may not cancel reservations that you do not own.")

    # Users may not cancel reservations that have already ended.
    # Staff may break this rule.
    if reservation.end < timezone.now(
    ) and not user_cancelling_reservation.is_staff:
        return HttpResponseBadRequest(
            "You may not cancel reservations that have already ended.")

    if reservation.cancelled:
        return HttpResponseBadRequest(
            "This reservation has already been cancelled by " +
            str(reservation.cancelled_by) + " at " +
            format_datetime(reservation.cancellation_time) + ".")

    if reservation.missed:
        return HttpResponseBadRequest(
            "This reservation was missed and cannot be modified.")

    return HttpResponse()
예제 #4
0
파일: calendar.py 프로젝트: hb9kns/NEMO
def email_usage_reminders(request):
    projects_to_exclude = request.GET.getlist("projects_to_exclude[]")
    busy_users = AreaAccessRecord.objects.filter(
        end=None,
        staff_charge=None).exclude(project__id__in=projects_to_exclude)
    busy_tools = UsageEvent.objects.filter(end=None).exclude(
        project__id__in=projects_to_exclude)
    facility_name = get_customization('facility_name')
    if facility_name == '':
        facility_name = "Facility"
    # Make lists of all the things a user is logged in to.
    # We don't want to send 3 separate emails if a user is logged into three things.
    # Just send one email for all the things!
    aggregate = {}
    for access_record in busy_users:
        key = str(access_record.customer)
        aggregate[key] = {
            'email': access_record.customer.email,
            'first_name': access_record.customer.first_name,
            'resources_in_use': [str(access_record.area)],
        }
    for usage_event in busy_tools:
        key = str(usage_event.operator)
        if key in aggregate:
            aggregate[key]['resources_in_use'].append(usage_event.tool.name)
        else:
            aggregate[key] = {
                'email': usage_event.operator.email,
                'first_name': usage_event.operator.first_name,
                'resources_in_use': [usage_event.tool.name],
            }

    user_office_email = get_customization('user_office_email_address')

    message = get_media_file_contents('usage_reminder_email.html')
    if message:
        subject = f"{facility_name} usage"
        for user in aggregate.values():
            rendered_message = Template(message).render(Context({'user':
                                                                 user}))
            send_mail(subject,
                      '',
                      user_office_email, [user['email']],
                      html_message=rendered_message)

    message = get_media_file_contents('staff_charge_reminder_email.html')
    if message:
        busy_staff = StaffCharge.objects.filter(end=None)
        for staff_charge in busy_staff:
            subject = "Active staff charge since " + format_datetime(
                staff_charge.start)
            rendered_message = Template(message).render(
                Context({'staff_charge': staff_charge}))
            staff_charge.staff_member.email_user(subject, rendered_message,
                                                 user_office_email)

    return HttpResponse()
예제 #5
0
def reservation_details(request, reservation_id):
    reservation = get_object_or_404(Reservation, id=reservation_id)
    if reservation.cancelled:
        error_message = 'This reservation was cancelled by {0} at {1}.'.format(
            reservation.cancelled_by,
            format_datetime(reservation.cancellation_time))
        return HttpResponseNotFound(error_message)
    return render(request, 'calendar/reservation_details.html',
                  {'reservation': reservation})
예제 #6
0
	def save(self, commit=True):
		instance = super(SafetyIssueUpdateForm, self).save(commit=False)
		progress_type = 'resolved' if self.cleaned_data['resolved'] else 'updated'
		if progress_type == 'resolved':
			instance.resolution = self.cleaned_data['update']
			instance.resolution_time = timezone.now()
			instance.resolver = self.user
		if progress_type == 'updated' and self.cleaned_data['update']:
			progress = 'On ' + format_datetime(timezone.now()) + ' ' + self.user.get_full_name() + ' updated this issue:\n' + self.cleaned_data['update']
			if instance.progress:
				instance.progress += '\n\n' + progress
			else:
				instance.progress = progress
		return super(SafetyIssueUpdateForm, self).save(commit=commit)
예제 #7
0
파일: policy.py 프로젝트: gaybro8777/NEMO
def check_policy_to_cancel_reservation(reservation,
                                       user_cancelling_reservation):
    """
	Checks the reservation deletion policy.
	If all checks pass the function returns an HTTP "OK" response.
	Otherwise, the function returns an HTTP "Bad Request" with an error message.
	"""

    # Users may only cancel reservations that they own.
    # Staff may break this rule.
    if (reservation.user != user_cancelling_reservation
        ) and not user_cancelling_reservation.is_staff:
        return HttpResponseBadRequest(
            "You may not cancel reservations that you do not own.")

    # Users may not cancel reservations that have already ended.
    # Staff may break this rule.
    if reservation.end < timezone.now(
    ) and not user_cancelling_reservation.is_staff:
        return HttpResponseBadRequest(
            "You may not cancel reservations that have already ended.")

    # Users may not cancel ongoing area reservations when they are currently logged in that area
    # Staff may break this rule.
    if reservation.area and reservation.area.requires_reservation and reservation.start < timezone.now(
    ) < reservation.end and AreaAccessRecord.objects.filter(
            end=None,
            staff_charge=None,
            customer=reservation.user,
            area=reservation.area
    ) and not user_cancelling_reservation.is_staff:
        return HttpResponseBadRequest(
            "You may not cancel an area reservation while logged in that area."
        )

    if reservation.cancelled:
        return HttpResponseBadRequest(
            "This reservation has already been cancelled by " +
            str(reservation.cancelled_by) + " at " +
            format_datetime(reservation.cancellation_time) + ".")

    if reservation.missed:
        return HttpResponseBadRequest(
            "This reservation was missed and cannot be modified.")

    return HttpResponse()
예제 #8
0
파일: forms.py 프로젝트: krzywon/NEMO
 def save(self, commit=True):
     instance = super(SafetyIssueUpdateForm, self).save(commit=False)
     progress_type = "resolved" if self.cleaned_data[
         "resolved"] else "updated"
     if progress_type == "resolved":
         instance.resolution = self.cleaned_data["update"]
         instance.resolution_time = timezone.now()
         instance.resolver = self.user
     if progress_type == "updated" and self.cleaned_data["update"]:
         progress = ("On " + format_datetime(timezone.now()) + " " +
                     self.user.get_full_name() + " updated this issue:\n" +
                     self.cleaned_data["update"])
         if instance.progress:
             instance.progress += "\n\n" + progress
         else:
             instance.progress = progress
     return super(SafetyIssueUpdateForm, self).save(commit=commit)
예제 #9
0
파일: calendar.py 프로젝트: sbonaime/NEMO
def reservation_details(request, reservation_id):
    reservation = get_object_or_404(Reservation, id=reservation_id)
    if reservation.cancelled:
        error_message = 'This reservation was cancelled by {0} at {1}.'.format(
            reservation.cancelled_by,
            format_datetime(reservation.cancellation_time))
        return HttpResponseNotFound(error_message)
    reservation_project_can_be_changed = (
        request.user.is_staff or request.user == reservation.user
    ) and reservation.has_not_ended and reservation.has_not_started and reservation.user.active_project_count(
    ) > 1
    return render(
        request, 'calendar/reservation_details.html', {
            'reservation':
            reservation,
            'reservation_project_can_be_changed':
            reservation_project_can_be_changed
        })
예제 #10
0
파일: policy.py 프로젝트: SolitonMan/NEMO
def check_policy_to_create_outage(outage, request):
	# Outages may not have a start time that is earlier than the end time.
	if outage.start >= outage.end:
		return "Outage start time (" + format_datetime(outage.start) + ") must be before the end time (" + format_datetime(outage.end) + ")."

	# The user may not create, move, or resize an outage to coincide with another user's reservation.
	coincident_events = Reservation.objects.filter(tool=outage.tool, cancelled=False, missed=False, shortened=False)
	# 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.
	coincident_events = coincident_events.exclude(start__lt=outage.start, end__lte=outage.start)
	coincident_events = coincident_events.exclude(start__gte=outage.end, end__gt=outage.end)
	if coincident_events.count() > 0:
		return "Your scheduled outage coincides with a reservation that already exists. Please choose a different time."

	# prevent staff from creating outages on tools in different cores
	active_core_id = request.session.get("active_core_id")
	if str(active_core_id) != "0" and str(active_core_id) != "None":
		if str(outage.tool.core_id.id) not in str(active_core_id) and outage.tool not in user.qualifications.all() and not user.is_superuser:
			msg = "Your core is not the same as the core of " + str(outage.tool.core_id.name) + " to which the " + str(outage.tool.name) + " belongs.  You cannot create an outage for this tool."
			return HttpResponseBadRequest(msg)

	# No policy issues! The outage can be created...
	return None
예제 #11
0
파일: policy.py 프로젝트: hb9kns/NEMO
def check_policy_to_save_reservation(cancelled_reservation, new_reservation,
                                     user, explicit_policy_override):
    """ Check the reservation creation policy and return a list of policy problems """

    # The function will check all policies. Policy problems are placed in the policy_problems list. overridable is True if the policy problems can be overridden by a staff member.
    policy_problems = []
    overridable = False

    # Reservations may not have a start time that is earlier than the end time.
    if new_reservation.start >= new_reservation.end:
        policy_problems.append("Reservation start time (" +
                               format_datetime(new_reservation.start) +
                               ") must be before the end time (" +
                               format_datetime(new_reservation.end) + ").")

    # The user may not create, move, or resize a reservation to coincide with another user's reservation.
    coincident_events = Reservation.objects.filter(tool=new_reservation.tool,
                                                   cancelled=False,
                                                   missed=False,
                                                   shortened=False)
    # Exclude the reservation we're cancelling in order to create a new one:
    if cancelled_reservation and cancelled_reservation.id:
        coincident_events = coincident_events.exclude(
            id=cancelled_reservation.id)
    # 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.
    coincident_events = coincident_events.exclude(
        start__lt=new_reservation.start, end__lte=new_reservation.start)
    coincident_events = coincident_events.exclude(
        start__gte=new_reservation.end, end__gt=new_reservation.end)
    if coincident_events.count() > 0:
        policy_problems.append(
            "Your reservation coincides with another reservation that already exists. Please choose a different time."
        )

    # The user may not create, move, or resize a reservation to coincide with a scheduled outage.
    coincident_events = ScheduledOutage.objects.filter(
        Q(tool=new_reservation.tool)
        | Q(resource__fully_dependent_tools__in=[new_reservation.tool]))
    # 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.
    coincident_events = coincident_events.exclude(
        start__lt=new_reservation.start, end__lte=new_reservation.start)
    coincident_events = coincident_events.exclude(
        start__gte=new_reservation.end, end__gt=new_reservation.end)
    if coincident_events.count() > 0:
        policy_problems.append(
            "Your reservation coincides with a scheduled outage. Please choose a different time."
        )

    # Reservations that have been cancelled may not be changed.
    if new_reservation.cancelled:
        policy_problems.append(
            "This reservation has already been cancelled by " +
            str(new_reservation.cancelled_by) + " at " +
            format_datetime(new_reservation.cancellation_time) + ".")

    # The user must belong to at least one active project to make a reservation.
    if new_reservation.user.active_project_count() < 1:
        if new_reservation.user == user:
            policy_problems.append(
                "You do not belong to any active projects. Thus, you may not create any reservations."
            )
        else:
            policy_problems.append(
                str(new_reservation.user) +
                " does not belong to any active projects and cannot have reservations."
            )

    # The user must associate their reservation with a project they belong to.
    if new_reservation.project and new_reservation.project not in new_reservation.user.active_projects(
    ):
        if new_reservation.user == user:
            policy_problems.append(
                "You do not belong to the project associated with this reservation."
            )
        else:
            policy_problems.append(
                str(new_reservation.user) +
                " does not belong to the project named " +
                str(new_reservation.project) + ".")

    # If the user is a staff member or there's an explicit policy override then the policy check is finished.
    if user.is_staff or explicit_policy_override:
        return policy_problems, overridable

    # If there are no blocking policy conflicts at this point, the rest of the policies can be overridden.
    if not policy_problems:
        overridable = True

    # The user must complete NEMO training to create reservations.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if user.training_required:
        policy_problems.append(
            "You are blocked from making reservations for all tools in the facility. Please complete the rules tutorial in order to create new reservations."
        )

    # Users may only change their own reservations.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.user != user:
        policy_problems.append(
            "You may not change reservations that you do not own.")

    # The user may not create or move a reservation to have a start time that is earlier than the current time.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.start < timezone.now():
        policy_problems.append("Reservation start time (" +
                               format_datetime(new_reservation.start) +
                               ") is earlier than the current time (" +
                               format_datetime(timezone.now()) + ").")

    # The user may not move or resize a reservation to have an end time that is earlier than the current time.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.end < timezone.now():
        policy_problems.append("Reservation end time (" +
                               format_datetime(new_reservation.end) +
                               ") is earlier than the current time (" +
                               format_datetime(timezone.now()) + ").")

    # The user must be qualified on the tool in question in order to create, move, or resize a reservation.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool not in user.qualifications.all():
        policy_problems.append(
            "You are not qualified to use this tool. Creating, moving, and resizing reservations is forbidden."
        )

    # The reservation start time may not exceed the tool's reservation horizon.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool.reservation_horizon is not None:
        reservation_horizon = timedelta(
            days=new_reservation.tool.reservation_horizon)
        if new_reservation.start > timezone.now() + reservation_horizon:
            policy_problems.append(
                "You may not create reservations further than " +
                str(reservation_horizon.days) +
                " days from now for this tool.")

    # Calculate the duration of the reservation:
    duration = new_reservation.end - new_reservation.start

    # The reservation must be at least as long as the minimum block time for this tool.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool.minimum_usage_block_time:
        minimum_block_time = timedelta(
            minutes=new_reservation.tool.minimum_usage_block_time)
        if duration < minimum_block_time:
            policy_problems.append(
                "Your reservation has a duration of " +
                str(int(duration.total_seconds() / 60)) +
                " minutes. This tool requires a minimum reservation duration of "
                + str(int(minimum_block_time.total_seconds() / 60)) +
                " minutes.")

    # The reservation may not exceed the maximum block time for this tool.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool.maximum_usage_block_time:
        maximum_block_time = timedelta(
            minutes=new_reservation.tool.maximum_usage_block_time)
        if duration > maximum_block_time:
            policy_problems.append(
                "Your reservation has a duration of " +
                str(int(duration.total_seconds() / 60)) +
                " minutes. Reservations for this tool may not exceed " +
                str(int(maximum_block_time.total_seconds() / 60)) +
                " minutes.")

    # If there is a limit on number of reservations per user per day then verify that the user has not exceeded it.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool.maximum_reservations_per_day:
        start_of_day = new_reservation.start
        start_of_day = start_of_day.replace(hour=0,
                                            minute=0,
                                            second=0,
                                            microsecond=0)
        end_of_day = start_of_day + timedelta(days=1)
        reservations_for_that_day = Reservation.objects.filter(
            cancelled=False,
            shortened=False,
            start__gte=start_of_day,
            end__lte=end_of_day,
            user=user,
            tool=new_reservation.tool)
        # Exclude any reservation that is being cancelled.
        if cancelled_reservation and cancelled_reservation.id:
            reservations_for_that_day = reservations_for_that_day.exclude(
                id=cancelled_reservation.id)
        if reservations_for_that_day.count(
        ) >= new_reservation.tool.maximum_reservations_per_day:
            policy_problems.append(
                "You may only have " +
                str(new_reservation.tool.maximum_reservations_per_day) +
                " reservations for this tool per day. Missed reservations are included when counting the number of reservations per day."
            )

    # A minimum amount of time between reservations for the same user & same tool can be enforced.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool.minimum_time_between_reservations:
        buffer_time = timedelta(
            minutes=new_reservation.tool.minimum_time_between_reservations)
        must_end_before = new_reservation.start - buffer_time
        too_close = Reservation.objects.filter(cancelled=False,
                                               shortened=False,
                                               user=user,
                                               end__gt=must_end_before,
                                               start__lt=new_reservation.start,
                                               tool=new_reservation.tool)
        if cancelled_reservation and cancelled_reservation.id:
            too_close = too_close.exclude(id=cancelled_reservation.id)
        if too_close.exists():
            policy_problems.append(
                "Separate reservations for this tool that belong to you must be at least "
                + str(new_reservation.tool.minimum_time_between_reservations) +
                " minutes apart from each other. The proposed reservation ends too close to another reservation."
            )
        must_start_after = new_reservation.end + buffer_time
        too_close = Reservation.objects.filter(cancelled=False,
                                               shortened=False,
                                               user=user,
                                               start__lt=must_start_after,
                                               end__gt=new_reservation.start,
                                               tool=new_reservation.tool)
        if cancelled_reservation and cancelled_reservation.id:
            too_close = too_close.exclude(id=cancelled_reservation.id)
        if too_close.exists():
            policy_problems.append(
                "Separate reservations for this tool that belong to you must be at least "
                + str(new_reservation.tool.minimum_time_between_reservations) +
                " minutes apart from each other. The proposed reservation begins too close to another reservation."
            )

    # Check that the user is not exceeding the maximum amount of time they may reserve in the future.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool.maximum_future_reservation_time:
        reservations_after_now = Reservation.objects.filter(
            cancelled=False,
            user=user,
            tool=new_reservation.tool,
            start__gte=timezone.now())
        if cancelled_reservation and cancelled_reservation.id:
            reservations_after_now = reservations_after_now.exclude(
                id=cancelled_reservation.id)
        amount_reserved_in_the_future = new_reservation.duration()
        for r in reservations_after_now:
            amount_reserved_in_the_future += r.duration()
        if amount_reserved_in_the_future.total_seconds(
        ) / 60 > new_reservation.tool.maximum_future_reservation_time:
            policy_problems.append(
                "You may only reserve up to " +
                str(new_reservation.tool.maximum_future_reservation_time) +
                " minutes of time on this tool, starting from the current time onward."
            )

    # Return the list of all policies that are not met.
    return policy_problems, overridable
예제 #12
0
파일: policy.py 프로젝트: dsbarth/NEMO
def check_policy_to_cancel_reservation(user_cancelling_reservation: User, reservation_to_cancel: Reservation, new_reservation: Optional[Reservation] = None):
	"""
	Checks the reservation deletion policy.
	If all checks pass the function returns an HTTP "OK" response.
	Otherwise, the function returns an HTTP "Bad Request" with an error message.
	"""

	move = new_reservation and new_reservation.start != reservation_to_cancel.start
	resize = new_reservation and new_reservation.start == reservation_to_cancel.start
	action = 'move' if move else 'resize' if resize else 'cancel'

	# Users may only cancel reservations that they own.
	# Staff may break this rule.
	if (reservation_to_cancel.user != user_cancelling_reservation) and not user_cancelling_reservation.is_staff:
		return HttpResponseBadRequest(f"You may not {action} reservations that you do not own.")

	# Users may not cancel reservations that have already ended.
	# Staff may break this rule.
	if reservation_to_cancel.end < timezone.now() and not user_cancelling_reservation.is_staff:
		return HttpResponseBadRequest(f"You may not {action} reservations that have already ended.")

	# Users may not cancel ongoing area reservations when they are currently logged in that area (unless they are extending it)
	# Staff may break this rule.
	if reservation_to_cancel.area and reservation_to_cancel.area.requires_reservation and not resize and reservation_to_cancel.start < timezone.now() < reservation_to_cancel.end and AreaAccessRecord.objects.filter(end=None, staff_charge=None, customer=reservation_to_cancel.user, area=reservation_to_cancel.area) and not user_cancelling_reservation.is_staff:
		if move:
			return HttpResponseBadRequest("You may only resize an area reservation while logged in that area.")
		else:
			return HttpResponseBadRequest("You may not cancel an area reservation while logged in that area.")

	if reservation_to_cancel.cancelled:
		return HttpResponseBadRequest("This reservation has already been cancelled by " + str(reservation_to_cancel.cancelled_by) + " on " + format_datetime(reservation_to_cancel.cancellation_time) + ".")

	if reservation_to_cancel.missed:
		return HttpResponseBadRequest("This reservation was missed and cannot be modified.")

	return HttpResponse()
예제 #13
0
파일: policy.py 프로젝트: carl-dawson/NEMO
def check_policy_to_save_reservation(
        cancelled_reservation: Optional[Reservation],
        new_reservation: Reservation, user_creating_reservation: User,
        explicit_policy_override: bool):
    """
		Check the reservation creation policy and return a list of policy problems if any.
	"""
    user = new_reservation.user

    facility_name = get_customization('facility_name')

    # The function will check all policies. Policy problems are placed in the policy_problems list. overridable is True if the policy problems can be overridden by a staff member.
    policy_problems = []
    overridable = False

    item_type = new_reservation.reservation_item_type

    # Reservations may not have a start time that is earlier than the end time.
    if new_reservation.start >= new_reservation.end:
        policy_problems.append("Reservation start time (" +
                               format_datetime(new_reservation.start) +
                               ") must be before the end time (" +
                               format_datetime(new_reservation.end) + ").")

    check_coincident_item_reservation_policy(cancelled_reservation,
                                             new_reservation,
                                             user_creating_reservation,
                                             policy_problems)

    # Reservations that have been cancelled may not be changed.
    if new_reservation.cancelled:
        policy_problems.append(
            "This reservation has already been cancelled by " +
            str(new_reservation.cancelled_by) + " at " +
            format_datetime(new_reservation.cancellation_time) + ".")

    # The user must belong to at least one active project to make a reservation.
    if user.active_project_count() < 1:
        if user == user_creating_reservation:
            policy_problems.append(
                "You do not belong to any active projects. Thus, you may not create any reservations."
            )
        else:
            policy_problems.append(
                str(user) +
                " does not belong to any active projects and cannot have reservations."
            )

    # The user must associate their reservation with a project they belong to.
    if new_reservation.project and new_reservation.project not in user.active_projects(
    ):
        if user == user_creating_reservation:
            policy_problems.append(
                "You do not belong to the project associated with this reservation."
            )
        else:
            policy_problems.append(
                str(user) + " does not belong to the project named " +
                str(new_reservation.project) + ".")

    # If the user is a staff member or there's an explicit policy override then the policy check is finished.
    if user.is_staff or explicit_policy_override:
        return policy_problems, overridable

    # If there are no blocking policy conflicts at this point, the rest of the policies can be overridden.
    if not policy_problems:
        overridable = True

    # Some tool reservations require a prior area reservation
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if item_type == ReservationItemType.TOOL:
        if new_reservation.tool.requires_area_reservation():
            area: Area = new_reservation.tool.requires_area_access
            # Check that a reservation for the area has been made and contains the start time
            if not Reservation.objects.filter(
                    missed=False,
                    cancelled=False,
                    shortened=False,
                    user=user,
                    area=area,
                    start__lte=new_reservation.start,
                    end__gt=new_reservation.start).exists():
                if user == user_creating_reservation:
                    policy_problems.append(
                        f"This tool requires a {area} reservation. Please make a reservation in the {area} prior to reserving this tool."
                    )
                else:
                    policy_problems.append(
                        f"This tool requires a {area} reservation. Please make sure to also create a reservation in the {area} or {str(user)} will not be able to enter the area."
                    )

    # The user must complete training to create reservations.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if user.training_required:
        if user == user_creating_reservation:
            policy_problems.append(
                f"You are blocked from making reservations in the {facility_name}. Please complete the {facility_name} rules tutorial in order to create new reservations."
            )
        else:
            policy_problems.append(
                f"{str(user)} is blocked from making reservations in the {facility_name}. The user needs to complete the {facility_name} rules tutorial in order to create new reservations."
            )

    # Users may only change their own reservations.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if cancelled_reservation and user != user_creating_reservation:
        policy_problems.append(
            "You may not change reservations that you do not own.")

    # The user may not create or move a reservation to have a start time that is earlier than the current time.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.start < timezone.now():
        policy_problems.append("Reservation start time (" +
                               format_datetime(new_reservation.start) +
                               ") is earlier than the current time (" +
                               format_datetime(timezone.now()) + ").")

    # The user may not move or resize a reservation to have an end time that is earlier than the current time.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.end < timezone.now():
        policy_problems.append("Reservation end time (" +
                               format_datetime(new_reservation.end) +
                               ") is earlier than the current time (" +
                               format_datetime(timezone.now()) + ").")

    # The user must be qualified on the tool in question in order to create, move, or resize a reservation.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if new_reservation.tool and new_reservation.tool not in user.qualifications.all(
    ):
        if user == user_creating_reservation:
            policy_problems.append(
                "You are not qualified to use this tool. Creating, moving, and resizing reservations is forbidden."
            )
        else:
            policy_problems.append(
                f"{str(user)} is not qualified to use this tool. Creating, moving, and resizing reservations is forbidden."
            )

    # The user must be authorized on the area in question at the start and end times of the reservation in order to create, move, or resize a reservation.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    if item_type == ReservationItemType.AREA:
        user_access_levels = user.accessible_access_levels_for_area(
            new_reservation.area)
        if not any([
                access_level.accessible_at(new_reservation.start)
                for access_level in user_access_levels
        ]) or not any([
                access_level.accessible_at(new_reservation.end)
                for access_level in user_access_levels
        ]):
            details = f" (times allowed in this area are: {','.join([access.get_schedule_display_with_times() for access in user_access_levels])})" if user_access_levels else ''
            if user == user_creating_reservation:
                policy_problems.append(
                    f"You are not authorized to access this area at this time{details}. Creating, moving, and resizing reservations is forbidden."
                )
            else:
                policy_problems.append(
                    f"{str(user)} is not authorized to access this area at this time{details}. Creating, moving, and resizing reservations is forbidden."
                )

    # The reservation start time may not exceed the item's reservation horizon.
    # Staff may break this rule.
    # An explicit policy override allows this rule to be broken.
    item = new_reservation.reservation_item
    if item.reservation_horizon is not None:
        reservation_horizon = timedelta(days=item.reservation_horizon)
        if new_reservation.start > timezone.now() + reservation_horizon:
            policy_problems.append(
                "You may not create reservations further than " +
                str(reservation_horizon.days) +
                f" days from now for this {item_type.value}.")

    # Check item policy rules
    item_policy_problems = []
    if should_enforce_policy(new_reservation):
        item_policy_problems = check_policy_rules_for_item(
            cancelled_reservation, new_reservation, user_creating_reservation)

    # Return the list of all policies that are not met.
    return policy_problems + item_policy_problems, overridable
예제 #14
0
def email_reservation_reminders(request):
    # Exit early if the reservation reminder email template has not been customized for the organization yet.
    good_message = get_media_file_contents('reservation_reminder_email.html')
    problem_message = get_media_file_contents('reservation_warning_email.html')
    if not good_message or not problem_message:
        return HttpResponseNotFound(
            'The reservation reminder email template has not been customized for your organization yet. Please visit the NEMO customizable_key_values page to upload a template, then reservation reminder email notifications can be sent.'
        )

    # Find all reservations in the next day
    #preparation_time = 120  These were sending an email for each reservation 2 hrs in advance. I'm not using them
    #tolerance = 5
    earliest_start = timezone.now()
    latest_start = timezone.now() + timedelta(hours=24)
    upcoming_reservations = Reservation.objects.filter(
        cancelled=False, start__gt=earliest_start, start__lt=latest_start)
    # Email a reminder to each user with an upcoming reservation.
    goodAggregate = {}
    problemAggregate = {}
    for reservation in upcoming_reservations:
        key = str(reservation.user)
        if reservation.tool.operational and not reservation.tool.problematic(
        ) and reservation.tool.all_resources_available():
            if key in goodAggregate:
                goodAggregate[key]['Tools'].append(
                    reservation.tool.name + " starting " +
                    format_datetime(reservation.start))
            else:
                goodAggregate[key] = {
                    'email':
                    reservation.user.email,
                    'first_name':
                    reservation.user.first_name,
                    'Tools': [
                        reservation.tool.name + " starting " +
                        format_datetime(reservation.start)
                    ],
                }
        else:
            if key in problemAggregate:
                problemAggregate[key]['Tools'].append(
                    reservation.tool.name + " starting " +
                    format_datetime(reservation.start))
            else:
                problemAggregate[key] = {
                    'email':
                    reservation.user.email,
                    'first_name':
                    reservation.user.first_name,
                    'Tools': [
                        reservation.tool.name + " starting " +
                        format_datetime(reservation.start)
                    ],
                }
    user_office_email = get_customization('user_office_email_address')
    if good_message:
        subject = "Upcoming PRISM Cleanroom Reservations"
        for user in goodAggregate.values():
            rendered_message = Template(good_message).render(
                Context({
                    'user': user,
                    'template_color': bootstrap_primary_color('success')
                }))
            send_mail(subject,
                      '',
                      user_office_email, [user['email']],
                      html_message=rendered_message)

    if problem_message:
        subject = "Problem With Upcoming PRISM Cleanroom Reservations"
        for user in problemAggregate.values():
            rendered_message = Template(problem_message).render(
                Context({
                    'user': user,
                    'template_color': bootstrap_primary_color('danger')
                }))
            send_mail(subject,
                      '',
                      user_office_email, [user['email']],
                      html_message=rendered_message)

    return HttpResponse()
예제 #15
0
파일: policy.py 프로젝트: SolitonMan/NEMO
def check_policy_to_cancel_reservation(reservation, user, request):
	"""
	Checks the reservation deletion policy.
	If all checks pass the function returns an HTTP "OK" response.
	Otherwise, the function returns an HTTP "Bad Request" with an error message.
	"""

	# Users may only cancel reservations that they own.
	# Staff may break this rule.
	if (reservation.user != user) and not user.is_staff:
		return HttpResponseBadRequest("You may not cancel reservations that you do not own.")

	# Users may not cancel reservations that have already ended.
	# Staff may break this rule.
	if reservation.end.replace(tzinfo=None) < timezone.now().replace(tzinfo=None) and not user.is_staff:
		return HttpResponseBadRequest("You may not cancel reservations that have already ended.")

	if reservation.cancelled:
		return HttpResponseBadRequest("This reservation has already been cancelled by " + str(reservation.cancelled_by) + " at " + format_datetime(reservation.cancellation_time) + ".")

	if reservation.missed:
		return HttpResponseBadRequest("This reservation was missed and cannot be modified.")

	# Staff may only cancel reservations for tools in their core
	active_core_id = request.session.get("active_core_id")
	if str(active_core_id) != "0" and str(active_core_id) != "None":
		if str(reservation.tool.core_id.id) not in str(active_core_id) and reservation.tool not in user.qualifications.all() and not user.is_superuser:
			msg = "Your core is not the same as the core of " + str(reservation.tool.core_id.name) + " to which the " + str(reservation.tool.name) + " belongs.  You cannot cancel a reservation for this tool."
			return HttpResponseBadRequest(msg)

	return HttpResponse()
예제 #16
0
파일: policy.py 프로젝트: sbonaime/NEMO
def check_policy_to_save_reservation(cancelled_reservation, new_reservation, user, explicit_policy_override):
	""" Check the reservation creation policy and return a list of policy problems """

	# The function will check all policies. Policy problems are placed in the policy_problems list. overridable is True if the policy problems can be overridden by a staff member.
	policy_problems = []
	overridable = False

	# Reservations may not have a start time that is earlier than the end time.
	if new_reservation.start >= new_reservation.end:
		policy_problems.append("Reservation start time (" + format_datetime(new_reservation.start) + ") must be before the end time (" + format_datetime(new_reservation.end) + ").")

	# The user may not create, move, or resize a reservation to coincide with another user's reservation.
	coincident_events = Reservation.objects.filter(tool=new_reservation.tool, cancelled=False, missed=False, shortened=False)
	# Exclude the reservation we're cancelling in order to create a new one:
	if cancelled_reservation and cancelled_reservation.id:
		coincident_events = coincident_events.exclude(id=cancelled_reservation.id)
	# 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.
	coincident_events = coincident_events.exclude(start__lt=new_reservation.start, end__lte=new_reservation.start)
	coincident_events = coincident_events.exclude(start__gte=new_reservation.end, end__gt=new_reservation.end)
	if coincident_events.count() > 0:
		policy_problems.append("Your reservation coincides with another reservation that already exists. Please choose a different time.")

	# The user may not create, move, or resize a reservation to coincide with a scheduled outage.
	coincident_events = ScheduledOutage.objects.filter(Q(tool=new_reservation.tool) | Q(resource__fully_dependent_tools__in=[new_reservation.tool]))
	# 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.
	coincident_events = coincident_events.exclude(start__lt=new_reservation.start, end__lte=new_reservation.start)
	coincident_events = coincident_events.exclude(start__gte=new_reservation.end, end__gt=new_reservation.end)
	if coincident_events.count() > 0:
		policy_problems.append("Your reservation coincides with a scheduled outage. Please choose a different time.")

	# Reservations that have been cancelled may not be changed.
	if new_reservation.cancelled:
		policy_problems.append("This reservation has already been cancelled by " + str(new_reservation.cancelled_by) + " at " + format_datetime(new_reservation.cancellation_time) + ".")

	# The user must belong to at least one active project to make a reservation.
	if new_reservation.user.active_project_count() < 1:
		if new_reservation.user == user:
			policy_problems.append("You do not belong to any active projects. Thus, you may not create any reservations.")
		else:
			policy_problems.append(str(new_reservation.user) + " does not belong to any active projects and cannot have reservations.")

	# The user must associate their reservation with a project they belong to.
	if new_reservation.project and new_reservation.project not in new_reservation.user.active_projects():
		if new_reservation.user == user:
			policy_problems.append("You do not belong to the project associated with this reservation.")
		else:
			policy_problems.append(str(new_reservation.user) + " does not belong to the project named " + str(new_reservation.project) + ".")

	# If the user is a staff member or there's an explicit policy override then the policy check is finished.
	if user.is_staff or explicit_policy_override:
		return policy_problems, overridable

	# If there are no blocking policy conflicts at this point, the rest of the policies can be overridden.
	if not policy_problems:
		overridable = True

	# The user must complete NEMO training to create reservations.
	# Staff may break this rule.
	# An explicit policy override allows this rule to be broken.
	if user.training_required:
		policy_problems.append("You are blocked from making reservations for all tools in the NanoFab. Please complete the NanoFab rules tutorial in order to create new reservations.")

	# Users may only change their own reservations.
	# Staff may break this rule.
	# An explicit policy override allows this rule to be broken.
	if new_reservation.user != user:
		policy_problems.append("You may not change reservations that you do not own.")

	# The user may not create or move a reservation to have a start time that is earlier than the current time.
	# Staff may break this rule.
	# An explicit policy override allows this rule to be broken.
	if new_reservation.start < timezone.now():
		policy_problems.append("Reservation start time (" + format_datetime(new_reservation.start) + ") is earlier than the current time (" + format_datetime(timezone.now()) + ").")

	# The user may not move or resize a reservation to have an end time that is earlier than the current time.
	# Staff may break this rule.
	# An explicit policy override allows this rule to be broken.
	if new_reservation.end < timezone.now():
		policy_problems.append("Reservation end time (" + format_datetime(new_reservation.end) + ") is earlier than the current time (" + format_datetime(timezone.now()) + ").")

	# The user must be qualified on the tool in question in order to create, move, or resize a reservation.
	# Staff may break this rule.
	# An explicit policy override allows this rule to be broken.
	if new_reservation.tool not in user.qualifications.all():
		policy_problems.append("You are not qualified to use this tool. Creating, moving, and resizing reservations is forbidden.")

	# The reservation start time may not exceed the tool's reservation horizon.
	# Staff may break this rule.
	# An explicit policy override allows this rule to be broken.
	if new_reservation.tool.reservation_horizon is not None:
		reservation_horizon = timedelta(days=new_reservation.tool.reservation_horizon)
		if new_reservation.start > timezone.now() + reservation_horizon:
			policy_problems.append("You may not create reservations further than " + str(reservation_horizon.days) + " days from now for this tool.")


	# Check tool policy rules
	tool_policy_problems = []
	if new_reservation.tool.should_enforce_policy(new_reservation):
		tool_policy_problems = check_policy_rules_for_tool(cancelled_reservation, new_reservation, user)

	# Return the list of all policies that are not met.
	return policy_problems + tool_policy_problems, overridable
예제 #17
0
    def __issue_command(self, interlock: Interlock_model,
                        command_type: Interlock_model.State):
        interlocks_enabled = getattr(settings, 'INTERLOCKS_ENABLED', False)
        if not interlocks_enabled or not interlock.card.enabled:
            interlock.most_recent_reply = "Interlock interface mocked out because settings.INTERLOCKS_ENABLED = False or interlock card is disabled. Interlock last set on " + format_datetime(
                timezone.now()) + "."
            interlock.state = command_type
            interlock.save()
            return True

        state = Interlock_model.State.UNKNOWN
        error_message = ''
        # try to send the command to the interlock
        try:
            state = self._send_command(interlock, command_type)
        except InterlockError as error:
            interlocks_logger.error(error)
            error_message = error.message
        except Exception as error:
            interlocks_logger.error(error)
            error_message = str(error)

        # save interlock state
        interlock.state = state
        interlock.most_recent_reply = Interlock.__create_reply_message(
            command_type, state, error_message)
        interlock.save()

        # log some useful information
        if interlock.state == interlock.State.UNKNOWN:
            interlocks_logger.error(
                f"Interlock {interlock.id} is in an unknown state. {interlock.most_recent_reply}"
            )
        elif interlock.state == interlock.State.LOCKED:
            interlocks_logger.debug(
                f"Interlock {interlock.id} locked successfully at {format_datetime(timezone.now())}"
            )
        elif interlock.state == interlock.State.UNLOCKED:
            interlocks_logger.debug(
                f"Interlock {interlock.id} unlocked successfully at {format_datetime(timezone.now())}"
            )

        # If the command type equals the current state then the command worked which will return true:
        return interlock.state == command_type