def handle(self, *args, **options): """ Management command entry point, simply call into the signal firiing """ # pylint: disable=import-outside-toplevel from edx_proctoring.api import (update_attempt_status, get_exam_by_id) exam_id = options['exam_id'] user_id = options['user_id'] to_status = options['to_status'] msg = (u'Running management command to update user {user_id} ' u'attempt status on exam_id {exam_id} to {to_status}'.format( user_id=user_id, exam_id=exam_id, to_status=to_status)) self.stdout.write(msg) if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status): raise CommandError( u'{to_status} is not a valid attempt status!'.format( to_status=to_status)) # get exam, this will throw exception if does not exist, so let it bomb out get_exam_by_id(exam_id) update_attempt_status(exam_id, user_id, to_status) self.stdout.write('Completed!')
def handle(self, *args, **options): """ Management command entry point, simply call into the signal firiing """ from edx_proctoring.api import ( update_attempt_status, get_exam_by_id ) exam_id = options['exam_id'] user_id = options['user_id'] to_status = options['to_status'] msg = ( 'Running management command to update user {user_id} ' 'attempt status on exam_id {exam_id} to {to_status}'.format( user_id=user_id, exam_id=exam_id, to_status=to_status ) ) self.stdout.write(msg) if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status): raise CommandError('{to_status} is not a valid attempt status!'.format(to_status=to_status)) # get exam, this will throw exception if does not exist, so let it bomb out get_exam_by_id(exam_id) update_attempt_status(exam_id, user_id, to_status) self.stdout.write('Completed!')
def handle(self, *args, **options): """ Management command entry point, simply call into the signal firiing """ from edx_proctoring.api import (update_attempt_status, get_exam_by_id) exam_id = options['exam_id'] user_id = options['user_id'] to_status = options['to_status'] msg = ('Running management command to update user {user_id} ' 'attempt status on exam_id {exam_id} to {to_status}'.format( user_id=user_id, exam_id=exam_id, to_status=to_status)) print msg if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status): raise Exception( '{to_status} is not a valid attempt status!'.format( to_status=to_status)) # get exam, this will throw exception if does not exist, so let it bomb out get_exam_by_id(exam_id) update_attempt_status(exam_id, user_id, to_status) print 'Completed!'
def post(self, request): """ HTTP POST handler. To create an exam attempt. """ start_immediately = request.data.get('start_clock', 'false').lower() == 'true' exam_id = request.data.get('exam_id', None) attempt_proctored = request.data.get('attempt_proctored', 'false').lower() == 'true' exam = get_exam_by_id(exam_id) # Bypassing the due date check for practice exam # because student can attempt the practice after the due date if not exam.get("is_practice_exam") and is_exam_passed_due(exam, request.user): raise ProctoredExamPermissionDenied( 'Attempted to access expired exam with exam_id {exam_id}'.format(exam_id=exam_id) ) exam_attempt_id = create_exam_attempt( exam_id=exam_id, user_id=request.user.id, taking_as_proctored=attempt_proctored ) # if use elected not to take as proctored exam, then # use must take as open book, and loose credit eligibility if exam['is_proctored'] and not attempt_proctored: update_attempt_status( exam_id, request.user.id, ProctoredExamStudentAttemptStatus.declined ) elif start_immediately: start_exam_attempt(exam_id, request.user.id) data = {'exam_attempt_id': exam_attempt_id} return Response(data)
def post(self, request): """ HTTP POST handler. To create an exam attempt. """ start_immediately = request.DATA.get('start_clock', 'false').lower() == 'true' exam_id = request.DATA.get('exam_id', None) attempt_proctored = request.DATA.get('attempt_proctored', 'false').lower() == 'true' try: exam_attempt_id = create_exam_attempt( exam_id=exam_id, user_id=request.user.id, taking_as_proctored=attempt_proctored ) exam = get_exam_by_id(exam_id) # if use elected not to take as proctored exam, then # use must take as open book, and loose credit eligibility if exam['is_proctored'] and not attempt_proctored: update_attempt_status( exam_id, request.user.id, ProctoredExamStudentAttemptStatus.declined ) elif start_immediately: start_exam_attempt(exam_id, request.user.id) return Response({'exam_attempt_id': exam_attempt_id}) except ProctoredBaseException, ex: LOG.exception(ex) return Response( status=status.HTTP_400_BAD_REQUEST, data={"detail": unicode(ex)} )
def post(self, request): """ HTTP POST handler. To create an exam attempt. """ start_immediately = request.data.get('start_clock', 'false').lower() == 'true' exam_id = request.data.get('exam_id', None) attempt_proctored = request.data.get('attempt_proctored', 'false').lower() == 'true' try: exam_attempt_id = create_exam_attempt( exam_id=exam_id, user_id=request.user.id, taking_as_proctored=attempt_proctored) exam = get_exam_by_id(exam_id) # if use elected not to take as proctored exam, then # use must take as open book, and loose credit eligibility if exam['is_proctored'] and not attempt_proctored: update_attempt_status( exam_id, request.user.id, ProctoredExamStudentAttemptStatus.declined) elif start_immediately: start_exam_attempt(exam_id, request.user.id) return Response({'exam_attempt_id': exam_attempt_id}) except ProctoredBaseException, ex: LOG.exception(ex) return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": unicode(ex)})
def _render_exam(self, content_id, context_overrides=None): """ Renders a test exam. """ exam = get_exam_by_id(content_id) context = { 'is_proctored': True, 'allow_proctoring_opt_out': True, 'display_name': self.exam_name, 'default_time_limit_mins': 90, 'is_practice_exam': False, 'credit_state': { 'enrollment_mode': 'verified', 'credit_requirement_status': [], }, 'verification_status': 'approved', 'verification_url': '/reverify', } if context_overrides: context.update(context_overrides) return get_student_view( user_id=self.user_id, course_id=exam['course_id'], content_id=exam['content_id'], context=context, )
def post(self, request): """ HTTP POST handler. To create an exam attempt. """ start_immediately = request.data.get('start_clock', 'false').lower() == 'true' exam_id = request.data.get('exam_id', None) attempt_proctored = request.data.get('attempt_proctored', 'false').lower() == 'true' exam = get_exam_by_id(exam_id) # Bypassing the due date check for practice exam # because student can attempt the practice after the due date if not exam.get("is_practice_exam") and is_exam_passed_due(exam, request.user): raise ProctoredExamPermissionDenied( u'Attempted to access expired exam with exam_id {exam_id}'.format(exam_id=exam_id) ) exam_attempt_id = create_exam_attempt( exam_id=exam_id, user_id=request.user.id, taking_as_proctored=attempt_proctored ) # if use elected not to take as proctored exam, then # use must take as open book, and loose credit eligibility if exam['is_proctored'] and not attempt_proctored: update_attempt_status( exam_id, request.user.id, ProctoredExamStudentAttemptStatus.declined ) elif start_immediately: start_exam_attempt(exam_id, request.user.id) data = {'exam_attempt_id': exam_attempt_id} return Response(data)
def get(self, request, course_id, exam_id=None): """ Redirect to dashboard for a given course and optional exam_id """ exam = None attempt_id = None ext_exam_id = None show_configuration_dashboard = False if exam_id: exam = get_exam_by_id(exam_id) # the exam_id in the url is our database id (for ease of lookups) # but the backend needs its external id for the instructor dashboard ext_exam_id = exam['external_id'] attempt_id = request.GET.get('attempt', None) # only show the configuration dashboard if an exam_id is passed in show_configuration_dashboard = request.GET.get('config', '').lower() == 'true' else: found_backend = None for exam in get_all_exams_for_course(course_id, True): exam_backend = exam['backend'] or settings.PROCTORING_BACKENDS.get('DEFAULT', None) if found_backend and exam_backend != found_backend: # In this case, what are we supposed to do?! # It should not be possible to get in this state, because # course teams will be prevented from updating the backend after the course start date error_message = "Multiple backends for course %r %r != %r" % (course_id, found_backend, exam['backend']) return Response(data=error_message, status=400) else: found_backend = exam_backend if exam is None: error = _('No exams in course {course_id}.').format(course_id=course_id) else: backend = get_backend_provider(exam) if backend: user = { 'id': obscured_user_id(request.user.id, exam['backend']), 'full_name': request.user.get_full_name(), 'email': request.user.email } url = backend.get_instructor_url( exam['course_id'], user, exam_id=ext_exam_id, attempt_id=attempt_id, show_configuration_dashboard=show_configuration_dashboard ) if url: return redirect(url) else: error = _('No instructor dashboard for {proctor_service}').format( proctor_service=backend.verbose_name) else: error = _('No proctored exams in course {course_id}').format(course_id=course_id) return Response(data=error, status=404, headers={'X-Frame-Options': 'sameorigin'})
def get(self, request, exam_id=None, course_id=None, content_id=None): # pylint: disable=unused-argument """ HTTP GET handler. Scenarios: by exam_id: calls get_exam_by_id() by course_id, content_id: get_exam_by_content_id() """ if exam_id: try: return Response(data=get_exam_by_id(exam_id), status=status.HTTP_200_OK) except ProctoredExamNotFoundException, ex: LOG.exception(ex) return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": "The exam_id does not exist."})
def get(self, request, exam_id=None, course_id=None, content_id=None): # pylint: disable=unused-argument """ HTTP GET handler. Scenarios: by exam_id: calls get_exam_by_id() by course_id, content_id: get_exam_by_content_id() """ if exam_id: data = get_exam_by_id(exam_id) elif course_id is not None: if content_id is not None: # get by course_id & content_id data = get_exam_by_content_id(course_id, content_id) else: data = get_all_exams_for_course(course_id=course_id, active_only=True) return Response(data)
def post(self, request): """ HTTP POST handler. To create an exam attempt. """ start_immediately = request.data.get('start_clock', 'false').lower() == 'true' exam_id = request.data.get('exam_id', None) attempt_proctored = request.data.get('attempt_proctored', 'false').lower() == 'true' provider_name = request.data.get('provider_name', 'dummy') try: exam = get_exam_by_id(exam_id) # Bypassing the due date check for practice exam # because student can attempt the practice after the due date if not exam.get("is_practice_exam") and has_due_date_passed( exam.get('due_date')): raise ProctoredExamPermissionDenied( 'Attempted to access expired exam with exam_id {exam_id}'. format(exam_id=exam_id)) exam_attempt_id = create_exam_attempt( exam_id=exam_id, user_id=request.user.id, taking_as_proctored=attempt_proctored, provider_name=provider_name) # if use elected not to take as proctored exam, then # use must take as open book, and loose credit eligibility if exam['is_proctored'] and not attempt_proctored: update_attempt_status( exam_id, request.user.id, ProctoredExamStudentAttemptStatus.declined) elif start_immediately: start_exam_attempt(exam_id, request.user.id) return Response({'exam_attempt_id': exam_attempt_id}) except ProctoredBaseException, ex: LOG.exception(ex) return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": unicode(ex)})
def get(self, request, exam_id=None, course_id=None, content_id=None): # pylint: disable=unused-argument """ HTTP GET handler. Scenarios: by exam_id: calls get_exam_by_id() by course_id, content_id: get_exam_by_content_id() """ if exam_id: data = get_exam_by_id(exam_id) elif course_id is not None: if content_id is not None: # get by course_id & content_id data = get_exam_by_content_id(course_id, content_id) else: data = get_all_exams_for_course( course_id=course_id, active_only=True ) return Response(data)
def get(self, request, course_id, exam_id=None): """ Redirect to dashboard for a given course and optional exam_id """ exam = None backend = None ext_exam_id = None attempt_id = None show_configuration_dashboard = False if exam_id: exam = get_exam_by_id(exam_id) backend = get_backend_provider(exam=exam) # the exam_id in the url is our database id (for ease of lookups) # but the backend needs its external id for the instructor dashboard ext_exam_id = exam['external_id'] attempt_id = request.GET.get('attempt', None) # only show the configuration dashboard if an exam_id is passed in show_configuration_dashboard = request.GET.get('config', '').lower() == 'true' else: existing_backend_name = None for exam in get_all_exams_for_course(course_id, True): if not exam.get('is_proctored'): # We should only get backends of exams which are configured to be proctored continue exam_backend_name = exam.get('backend') backend = get_backend_provider(name=exam_backend_name) if existing_backend_name and exam_backend_name != existing_backend_name: # In this case, what are we supposed to do?! # It should not be possible to get in this state, because # course teams will be prevented from updating the backend after the course start date error_message = u"Multiple backends for course %r %r != %r" % ( course_id, existing_backend_name, exam_backend_name ) return Response(data=error_message, status=400) else: existing_backend_name = exam_backend_name if not exam: return Response( data=_(u'No exams in course {course_id}.').format(course_id=course_id), status=404, headers={'X-Frame-Options': 'sameorigin'} ) if not backend: return Response( data=_(u'No proctored exams in course {course_id}').format(course_id=course_id), status=404, headers={'X-Frame-Options': 'sameorigin'} ) user = { 'id': obscured_user_id(request.user.id, exam['backend']), 'full_name': request.user.profile.name, 'email': request.user.email } url = backend.get_instructor_url( exam['course_id'], user, exam_id=ext_exam_id, attempt_id=attempt_id, show_configuration_dashboard=show_configuration_dashboard ) if not url: return Response( data=_(u'No instructor dashboard for {proctor_service}').format( proctor_service=backend.verbose_name ), status=404, headers={'X-Frame-Options': 'sameorigin'} ) return redirect(url)