Example #1
0
	def clean(self):
		cleaned_data = super().clean()
		tool = cleaned_data.get("tool")
		tool_usage_question_name = cleaned_data.get("tool_usage_question")
		if tool and tool_usage_question_name:
			error = None
			if tool.post_usage_questions:
				post_usage_form = DynamicForm(tool.post_usage_questions, tool.id)
				tool_question = post_usage_form.filter_questions(
					lambda x: (isinstance(x, PostUsageNumberFieldQuestion) or isinstance(x, PostUsageFloatFieldQuestion)) and x.name == tool_usage_question_name
				)
				if not tool_question:
					candidates = [
						question.name
						for question in post_usage_form.filter_questions(
							lambda x: isinstance(x, PostUsageNumberFieldQuestion) or isinstance(x, PostUsageFloatFieldQuestion)
						)
					]
					error = "The tool has no post usage question of type Number or Float with this name."
					if candidates:
						error += f" Valid question names are: {', '.join(candidates)}"
			else:
				error = "The tool does not have any post usage questions."
			if error:
				self.add_error("tool_usage_question", error)
		return cleaned_data
Example #2
0
def disable_tool(request, tool_id):


	if not settings.ALLOW_CONDITIONAL_URLS:
		return HttpResponseBadRequest('Tool control is only available on campus.')

	tool = get_object_or_404(Tool, id=tool_id)
	if tool.get_current_usage_event() is None:
		return HttpResponse()
	current_usage_event = tool.get_current_usage_event()
	downtime = timedelta(minutes=quiet_int(request.POST.get('downtime')))
	response = check_policy_to_disable_tool(tool, request.user, downtime)
	if response.status_code != HTTPStatus.OK:
		return response
	try:
		current_reservation = Reservation.objects.get(start__lt=timezone.now(), end__gt=timezone.now(), cancelled=False, missed=False, shortened=False, user=current_usage_event.user, tool=tool)
		# Staff are exempt from mandatory reservation shortening when tool usage is complete.
		if request.user.is_staff is False:
			# Shorten the user's reservation to the current time because they're done using the tool.
			new_reservation = deepcopy(current_reservation)
			new_reservation.id = None
			new_reservation.pk = None
			new_reservation.end = timezone.now() + downtime
			new_reservation.save()
			current_reservation.shortened = True
			current_reservation.descendant = new_reservation
			current_reservation.save()
	except Reservation.DoesNotExist:
		pass

	# All policy checks passed so disable the tool for the user.
	if tool.interlock and not tool.interlock.lock():
		error_message = f"The interlock command for the {tool} failed. The error message returned: {tool.interlock.most_recent_reply}"
		logger.error(error_message)
		return HttpResponseServerError(error_message)

	# End the current usage event for the tool
	current_usage_event.end = timezone.now() + downtime

	# Collect post-usage questions
	dynamic_form = DynamicForm(tool.post_usage_questions)
	current_usage_event.run_data = dynamic_form.extract(request)
	dynamic_form.charge_for_consumable(current_usage_event.user, current_usage_event.operator, current_usage_event.project, current_usage_event.run_data)

	current_usage_event.save()
	if request.user.charging_staff_time():
		existing_staff_charge = request.user.get_staff_charge()
		if existing_staff_charge.customer == current_usage_event.user and existing_staff_charge.project == current_usage_event.project:
			response = render(request, 'staff_charges/reminder.html', {'tool': tool})

	return response
Example #3
0
def tool_information(request, tool_id, user_id, back):
    tool = Tool.objects.get(id=tool_id, visible=True)
    customer = User.objects.get(id=user_id)
    dictionary = {
        'customer': customer,
        'tool': tool,
        'rendered_configuration_html': tool.configuration_widget(customer),
        'post_usage_questions':
        DynamicForm(tool.post_usage_questions).render(),
        'back': back,
    }
    try:
        current_reservation = Reservation.objects.get(start__lt=timezone.now(),
                                                      end__gt=timezone.now(),
                                                      cancelled=False,
                                                      missed=False,
                                                      shortened=False,
                                                      user=customer,
                                                      tool=tool)
        remaining_reservation_duration = int(
            (current_reservation.end - timezone.now()).total_seconds() / 60)
        # We don't need to bother telling the user their reservation will be shortened if there's less than two minutes left.
        # Staff are exempt from reservation shortening.
        if remaining_reservation_duration > 2 and not customer.is_staff:
            dictionary[
                'remaining_reservation_duration'] = remaining_reservation_duration
    except Reservation.DoesNotExist:
        pass
    return render(request, 'kiosk/tool_information.html', dictionary)
Example #4
0
 def _post_usage_preview(self, obj):
     form_validity_div = '<div id="form_validity"></div>' if obj.post_usage_questions else ''
     return format_html(
         '<div class="post_usage_preview">{}{}</div><div class="help post_usage_preview_help">Save form to preview post usage questions</div>'
         .format(
             DynamicForm(obj.post_usage_questions).render(),
             form_validity_div))
Example #5
0
def tool_status(request, tool_id):
	""" Gets the current status of the tool (that is, whether it is currently in use or not). """
	tool = get_object_or_404(Tool, id=tool_id, visible=True)

	dictionary = {
		'tool': tool,
		'tool_rate': rates.rate_class.get_tool_rate(tool),
		'task_categories': TaskCategory.objects.filter(stage=TaskCategory.Stage.INITIAL_ASSESSMENT),
		'rendered_configuration_html': tool.configuration_widget(request.user),
		'mobile': request.device == 'mobile',
		'task_statuses': TaskStatus.objects.all(),
		'post_usage_questions': DynamicForm(tool.post_usage_questions).render(),
		'configs': get_tool_full_config_history(tool),
	}

	try:
		current_reservation = Reservation.objects.get(start__lt=timezone.now(), end__gt=timezone.now(), cancelled=False, missed=False, shortened=False, user=request.user, tool=tool)
		if request.user == current_reservation.user:
			dictionary['time_left'] = current_reservation.end
	except Reservation.DoesNotExist:
		pass

	# Staff need the user list to be able to qualify users for the tool.
	if request.user.is_staff:
		dictionary['users'] = User.objects.filter(is_active=True)

	return render(request, 'tool_control/tool_status.html', dictionary)
Example #6
0
def disable_tool(request, tool_id):
    if not settings.ALLOW_CONDITIONAL_URLS:
        return HttpResponseBadRequest(
            "Tool control is only available on campus.")

    tool = get_object_or_404(Tool, id=tool_id)
    if tool.get_current_usage_event() is None:
        return HttpResponse()
    downtime = timedelta(minutes=quiet_int(request.POST.get("downtime")))
    bypass_interlock = request.POST.get("bypass", 'False') == 'True'
    response = check_policy_to_disable_tool(tool, request.user, downtime)
    if response.status_code != HTTPStatus.OK:
        return response

    # All policy checks passed so disable the tool for the user.
    if tool.interlock and not tool.interlock.lock():
        if bypass_interlock and interlock_bypass_allowed(request.user):
            pass
        else:
            return interlock_error("Disable", request.user)

    # Shorten the user's tool reservation since we are now done using the tool
    shorten_reservation(user=request.user,
                        item=tool,
                        new_end=timezone.now() + downtime)

    # End the current usage event for the tool
    current_usage_event = tool.get_current_usage_event()
    current_usage_event.end = timezone.now() + downtime

    # Collect post-usage questions
    dynamic_form = DynamicForm(tool.post_usage_questions, tool.id)

    try:
        current_usage_event.run_data = dynamic_form.extract(request)
    except RequiredUnansweredQuestionsException as e:
        if request.user.is_staff and request.user != current_usage_event.operator and current_usage_event.user != request.user:
            # if a staff is forcing somebody off the tool and there are required questions, send an email and proceed
            current_usage_event.run_data = e.run_data
            email_managers_required_questions_disable_tool(
                current_usage_event.operator, request.user, tool, e.questions)
        else:
            return HttpResponseBadRequest(str(e))

    dynamic_form.charge_for_consumables(current_usage_event.user,
                                        current_usage_event.operator,
                                        current_usage_event.project,
                                        current_usage_event.run_data, request)
    dynamic_form.update_counters(current_usage_event.run_data)

    current_usage_event.save()
    user: User = request.user
    if user.charging_staff_time():
        existing_staff_charge = user.get_staff_charge()
        if (existing_staff_charge.customer == current_usage_event.user and
                existing_staff_charge.project == current_usage_event.project):
            response = render(request, "staff_charges/reminder.html",
                              {"tool": tool})

    return response
Example #7
0
def disable_tool(request, tool_id):

    if not settings.ALLOW_CONDITIONAL_URLS:
        return HttpResponseBadRequest(
            'Tool control is only available on campus.')

    tool = get_object_or_404(Tool, id=tool_id)
    if tool.get_current_usage_event() is None:
        return HttpResponse()
    downtime = timedelta(minutes=quiet_int(request.POST.get('downtime')))
    response = check_policy_to_disable_tool(tool, request.user, downtime)
    if response.status_code != HTTPStatus.OK:
        return response

    # Shorten the user's tool reservation since we are now done using the tool
    shorten_reservation(user=request.user,
                        item=tool,
                        new_end=timezone.now() + downtime)

    # All policy checks passed so disable the tool for the user.
    if tool.interlock and not tool.interlock.lock():
        error_message = f"The interlock command for the {tool} failed. The error message returned: {tool.interlock.most_recent_reply}"
        tool_control_logger.error(error_message)
        return HttpResponseServerError(error_message)

    # End the current usage event for the tool
    current_usage_event = tool.get_current_usage_event()
    current_usage_event.end = timezone.now() + downtime

    # Collect post-usage questions
    dynamic_form = DynamicForm(tool.post_usage_questions)
    current_usage_event.run_data = dynamic_form.extract(request)
    dynamic_form.charge_for_consumables(current_usage_event.user,
                                        current_usage_event.operator,
                                        current_usage_event.project,
                                        current_usage_event.run_data)

    current_usage_event.save()
    if request.user.charging_staff_time():
        existing_staff_charge = request.user.get_staff_charge()
        if existing_staff_charge.customer == current_usage_event.user and existing_staff_charge.project == current_usage_event.project:
            response = render(request, 'staff_charges/reminder.html',
                              {'tool': tool})

    return response
Example #8
0
def disable_tool(request):
    logger = getLogger(__name__)

    tool = Tool.objects.get(id=request.POST['tool_id'])
    customer = User.objects.get(id=request.POST['customer_id'])
    downtime = timedelta(minutes=quiet_int(request.POST.get('downtime')))
    response = check_policy_to_disable_tool(tool, customer, downtime)
    if response.status_code != HTTPStatus.OK:
        dictionary = {
            'message': response.content,
            'delay': 10,
        }
        return render(request, 'kiosk/acknowledgement.html', dictionary)
    try:
        current_reservation = Reservation.objects.get(start__lt=timezone.now(),
                                                      end__gt=timezone.now(),
                                                      cancelled=False,
                                                      missed=False,
                                                      shortened=False,
                                                      user=customer,
                                                      tool=tool)
        # Staff are exempt from mandatory reservation shortening when tool usage is complete.
        if customer.is_staff is False:
            # Shorten the user's reservation to the current time because they're done using the tool.
            new_reservation = deepcopy(current_reservation)
            new_reservation.id = None
            new_reservation.pk = None
            new_reservation.end = timezone.now()
            new_reservation.save()
            current_reservation.shortened = True
            current_reservation.descendant = new_reservation
            current_reservation.save()
    except Reservation.DoesNotExist:
        pass

    # All policy checks passed so disable the tool for the user.
    if tool.interlock and not tool.interlock.lock():
        logger.error(
            "The interlock command for this tool failed. The error message returned: "
            + str(tool.interlock.most_recent_reply))
        raise Exception(
            "The interlock command for this tool failed. The error message returned: "
            + str(tool.interlock.most_recent_reply))
    # End the current usage event for the tool and save it.
    current_usage_event = tool.get_current_usage_event()
    current_usage_event.end = timezone.now() + downtime

    # Collect post-usage questions
    current_usage_event.run_data = DynamicForm(
        tool.post_usage_questions).extract(request)
    current_usage_event.save()

    dictionary = {
        'message': 'You are no longer using the {}'.format(tool),
        'badge_number': customer.badge_number,
    }
    return render(request, 'kiosk/acknowledgement.html', dictionary)
Example #9
0
 def _post_usage_preview(self, obj):
     if obj.id:
         form_validity_div = '<div id="form_validity"></div>' if obj.post_usage_questions else ""
         return mark_safe(
             '<div class="questions_preview">{}{}</div><div class="help questions_preview_help">Save form to preview post usage questions</div>'
             .format(
                 DynamicForm(obj.post_usage_questions).render(
                     "tool_usage_group_question", obj.id),
                 form_validity_div))
Example #10
0
 def clean(self):
     cleaned_data = super().clean()
     reservation_questions = cleaned_data.get("questions")
     tool_reservations = cleaned_data.get("tool_reservations")
     only_tools = cleaned_data.get("only_for_tools")
     area_reservations = cleaned_data.get("area_reservations")
     only_areas = cleaned_data.get("only_for_areas")
     if not tool_reservations and not area_reservations:
         self.add_error(
             "tool_reservations",
             "Reservation questions have to apply to tool and/or area reservations"
         )
         self.add_error(
             "area_reservations",
             "Reservation questions have to apply to tool and/or area reservations"
         )
     if not tool_reservations and only_tools:
         self.add_error(
             "tool_reservations",
             "You cannot restrict tools these questions apply to without enabling it for tools"
         )
     if not area_reservations and only_areas:
         self.add_error(
             "area_reservations",
             "You cannot restrict areas these questions apply to without enabling it for areas"
         )
     # Validate reservation_questions JSON format
     if reservation_questions:
         try:
             loads(reservation_questions)
         except ValueError:
             self.add_error("questions",
                            "This field needs to be a valid JSON string")
         try:
             dynamic_form = DynamicForm(reservation_questions)
             dynamic_form.validate("reservation_group_question",
                                   self.instance.id)
         except KeyError as e:
             self.add_error("questions", f"{e} property is required")
         except Exception:
             error_info = sys.exc_info()
             self.add_error(
                 "questions",
                 error_info[0].__name__ + ": " + str(error_info[1]))
Example #11
0
	def clean(self):
		cleaned_data = super().clean()
		parent_tool = cleaned_data.get("parent_tool")
		category = cleaned_data.get("_category")
		location = cleaned_data.get("_location")
		phone_number = cleaned_data.get("_phone_number")
		primary_owner = cleaned_data.get("_primary_owner")
		image = cleaned_data.get("_image")

		# only resize if an image is present and  has changed
		if image and not isinstance(image, FieldFile):
			from NEMO.utilities import resize_image

			# resize image to 500x500 maximum
			cleaned_data["_image"] = resize_image(image, 500)

		if parent_tool:
			if parent_tool.id == self.instance.id:
				self.add_error("parent_tool", "You cannot select the parent to be the tool itself.")
			# in case of alternate tool, remove everything except parent_tool and name
			data = dict([(k, v) for k, v in self.cleaned_data.items() if k == "parent_tool" or k == "name"])
			# an alternate tool is never visible
			data["visible"] = False
			return data
		else:
			if not category:
				self.add_error("_category", "This field is required.")
			if not location:
				self.add_error("_location", "This field is required.")
			if not phone_number:
				self.add_error("_phone_number", "This field is required.")
			if not primary_owner:
				self.add_error("_primary_owner", "This field is required.")

			post_usage_questions = cleaned_data.get("_post_usage_questions")
			# Validate _post_usage_questions JSON format
			if post_usage_questions:
				try:
					loads(post_usage_questions)
				except ValueError:
					self.add_error("_post_usage_questions", "This field needs to be a valid JSON string")
				try:
					DynamicForm(post_usage_questions, self.instance.id).validate()
				except Exception:
					error_info = sys.exc_info()
					self.add_error("_post_usage_questions", error_info[0].__name__ + ": " + str(error_info[1]))

			policy_off_between_times = cleaned_data.get("_policy_off_between_times")
			policy_off_start_time = cleaned_data.get("_policy_off_start_time")
			policy_off_end_time = cleaned_data.get("_policy_off_end_time")
			if policy_off_between_times and (not policy_off_start_time or not policy_off_end_time):
				if not policy_off_start_time:
					self.add_error("_policy_off_start_time", "Start time must be specified")
				if not policy_off_end_time:
					self.add_error("_policy_off_end_time", "End time must be specified")
Example #12
0
def do_disable_tool(request, tool_id):
    tool = Tool.objects.get(id=tool_id)
    customer = User.objects.get(id=request.POST["customer_id"])
    downtime = timedelta(minutes=quiet_int(request.POST.get("downtime")))
    bypass_interlock = request.POST.get("bypass", "False") == "True"
    response = check_policy_to_disable_tool(tool, customer, downtime)
    if response.status_code != HTTPStatus.OK:
        dictionary = {"message": response.content, "delay": 10}
        return render(request, "kiosk/acknowledgement.html", dictionary)

    # All policy checks passed so try to disable the tool for the user.
    if tool.interlock and not tool.interlock.lock():
        if bypass_interlock and interlock_bypass_allowed(customer):
            pass
        else:
            return interlock_error("Disable", customer)

    # Shorten the user's tool reservation since we are now done using the tool
    shorten_reservation(user=customer,
                        item=tool,
                        new_end=timezone.now() + downtime)

    # End the current usage event for the tool and save it.
    current_usage_event = tool.get_current_usage_event()
    current_usage_event.end = timezone.now() + downtime

    # Collect post-usage questions
    dynamic_form = DynamicForm(tool.post_usage_questions)

    try:
        current_usage_event.run_data = dynamic_form.extract(request)
    except RequiredUnansweredQuestionsException as e:
        if customer.is_staff and customer != current_usage_event.operator and current_usage_event.user != customer:
            # if a staff is forcing somebody off the tool and there are required questions, send an email and proceed
            current_usage_event.run_data = e.run_data
            email_managers_required_questions_disable_tool(
                current_usage_event.operator, customer, tool, e.questions)
        else:
            dictionary = {"message": str(e), "delay": 10}
            return render(request, "kiosk/acknowledgement.html", dictionary)

    dynamic_form.charge_for_consumables(
        current_usage_event.user,
        current_usage_event.operator,
        current_usage_event.project,
        current_usage_event.run_data,
        request,
    )
    dynamic_form.update_tool_counters(current_usage_event.run_data, tool.id)

    current_usage_event.save()
    dictionary = {
        "message": "You are no longer using the {}".format(tool),
        "badge_number": customer.badge_number
    }
    return render(request, "kiosk/acknowledgement.html", dictionary)
Example #13
0
def disable_tool(request):
    tool = Tool.objects.get(id=request.POST['tool_id'])
    customer = User.objects.get(id=request.POST['customer_id'])
    downtime = timedelta(minutes=quiet_int(request.POST.get('downtime')))
    response = check_policy_to_disable_tool(tool, customer, downtime)
    if response.status_code != HTTPStatus.OK:
        dictionary = {
            'message': response.content,
            'delay': 10,
        }
        return render(request, 'kiosk/acknowledgement.html', dictionary)

    # Shorten the user's tool reservation since we are now done using the tool
    shorten_reservation(user=customer,
                        item=tool,
                        new_end=timezone.now() + downtime)

    # All policy checks passed so disable the tool for the user.
    if tool.interlock and not tool.interlock.lock():
        raise Exception(
            "The interlock command for this tool failed. The error message returned: "
            + str(tool.interlock.most_recent_reply))
    # End the current usage event for the tool and save it.
    current_usage_event = tool.get_current_usage_event()
    current_usage_event.end = timezone.now() + downtime

    # Collect post-usage questions
    dynamic_form = DynamicForm(tool.post_usage_questions)
    current_usage_event.run_data = dynamic_form.extract(request)
    dynamic_form.charge_for_consumables(current_usage_event.user,
                                        current_usage_event.operator,
                                        current_usage_event.project,
                                        current_usage_event.run_data)

    current_usage_event.save()
    dictionary = {
        'message': 'You are no longer using the {}'.format(tool),
        'badge_number': customer.badge_number,
    }
    return render(request, 'kiosk/acknowledgement.html', dictionary)
Example #14
0
 def questions_preview(self, obj):
     form_validity_div = ""
     rendered_form = ""
     try:
         rendered_form = DynamicForm(obj.questions).render(
             "reservation_group_question", obj.id)
         if obj.questions:
             form_validity_div = '<div id="form_validity"></div>'
     except:
         pass
     return mark_safe(
         '<div class="questions_preview">{}{}</div><div class="help questions_preview_help">Save form to preview reservation questions</div>'
         .format(rendered_form, form_validity_div))
Example #15
0
def tool_status(request, tool_id):
	""" Gets the current status of the tool (that is, whether it is currently in use or not). """
	tool = get_object_or_404(Tool, id=tool_id, visible=True)
	exclude=get_customization('exclude_from_usage')
	projects_to_exclude = []
	if exclude:
		projects_to_exclude = [int(s) for s in exclude.split() if s.isdigit()]
	dictionary = {
		'tool': tool,
		'task_categories': TaskCategory.objects.filter(stage=TaskCategory.Stage.INITIAL_ASSESSMENT),
		'rendered_configuration_html': tool.configuration_widget(request.user),
		'mobile': request.device == 'mobile',
		'task_statuses': TaskStatus.objects.exclude(name="default"),
		'post_usage_questions': DynamicForm(tool.post_usage_questions).render(),
		'active_projects_filtered': request.user.active_projects().exclude(id__in=projects_to_exclude)
	}

	try:
		current_reservation = Reservation.objects.get(start__lt=timezone.now(), end__gt=timezone.now(), cancelled=False, missed=False, shortened=False, user=request.user, tool=tool)
		if request.user == current_reservation.user:
			dictionary['time_left'] = current_reservation.end
	except Reservation.DoesNotExist:
		pass

	try:
		next_4hrs = Reservation.objects.filter(start__gt=timezone.now(), start__lte=timezone.now()+timedelta(hours=4), cancelled=False, missed=False, shortened=False, tool=tool).order_by('start')
		if next_4hrs:
			next_res = next_4hrs[0]
			dictionary['next_res'] = next_res
	except Reservation.DoesNotExist:
		pass
	# Staff need the user list to be able to qualify users for the tool.
	if request.user.is_staff:
		dictionary['users'] = User.objects.filter(is_active=True)

	return render(request, 'tool_control/tool_status.html', dictionary)