def test_extend_quiz_no_time_limit(self, m): from utils import extend_quiz response = extend_quiz( course_id=1, quiz={'id': 2, 'title': 'A Quiz'}, percent=200, user_id_list=[1, 2, 3] ) self.assertIsInstance(response, dict) self.assertTrue(response['success']) self.assertEqual( response['message'], "Quiz #2 has no time limit, so there is no time to add." ) self.assertEqual(response['added_time'], None)
def test_extend_quiz(self, m): from utils import extend_quiz m.register_uri('POST', '/api/v1/courses/1/quizzes/2/extensions', status_code=200) response = extend_quiz(course_id=1, quiz={ 'id': 2, 'title': 'A Quiz', 'time_limit': 10 }, percent=200, user_id_list=[1, 2, 3]) self.assertIsInstance(response, dict) self.assertTrue(response['success']) self.assertEqual(response['message'], "Successfully added 10 minutes to quiz #2") self.assertEqual(response['added_time'], 10)
def test_extend_quiz_invalid_response(self, m): from utils import extend_quiz m.register_uri('POST', '/api/v1/courses/1/quizzes/99/extensions', status_code=404) response = extend_quiz(course_id=1, quiz={ 'id': 99, 'title': 'A Quiz', 'time_limit': 10 }, percent=200, user_id_list=[1, 2, 3]) self.assertIsInstance(response, dict) self.assertFalse(response['success']) self.assertEqual( response['message'], "Error creating extension for quiz #99. Canvas status code: 404") self.assertEqual(response['added_time'], None)
def refresh_background(course_id): """ Look up existing extensions and apply them to new quizzes. :param course_id: The Canvas ID of the Course. :type course_id: int :rtype: dict :returns: A dictionary containing two parts: - success `bool` False if there was an error, True otherwise. - message `str` A long description of success or failure. """ job = get_current_job() update_job(job, 0, 'Starting...', 'started') with app.app_context(): course, created = get_or_create( db.session, Course, canvas_id=course_id ) try: course_name = get_course(course_id).get( 'name', '<UNNAMED COURSE>' ) course.course_name = course_name db.session.commit() except requests.exceptions.HTTPError: update_job( job, 0, 'Course not found.', 'failed', error=True ) logger.exception('Unable to find course #{}'.format(course_id)) return job.meta # quiz stuff quizzes = missing_quizzes(course_id) num_quizzes = len(quizzes) if num_quizzes < 1: update_job( job, 100, 'Complete. No quizzes required updates.', 'complete', error=False ) return job.meta percent_user_map = defaultdict(list) inactive_list = [] update_job(job, 0, 'Getting past extensions.', 'processing', False) for extension in course.extensions: # If extension is inactive, ignore. if not extension.active: inactive_list.append(extension.user.sortable_name) logger.debug('Extension #{} is inactive.'.format( extension.id )) continue user_canvas_id = User.query.filter_by( id=extension.user_id ).first().canvas_id # Check if user is in course. If not, deactivate extension. try: canvas_user = get_user(course_id, user_canvas_id) # Skip user if not a student. Fixes an edge case where a # student that previously recieved an extension changes roles. enrolls = canvas_user.get('enrollments', []) type_list = [e['type'] for e in enrolls if e['enrollment_state'] == 'active'] if not any(t == 'StudentEnrollment' for t in type_list): logger.info(( "User #{} was found in course #{}, but is not an " "active student. Deactivating extension #{}. Roles " "found: {}" ).format( user_canvas_id, course_id, extension.id, ", ".join(type_list) if len(enrolls) > 0 else None )) extension.active = False db.session.commit() inactive_list.append(extension.user.sortable_name) continue except requests.exceptions.HTTPError: log_str = ( 'User #{} not in course #{}. Deactivating extension #{}.' ) logger.info( log_str.format(user_canvas_id, course_id, extension.id) ) extension.active = False db.session.commit() inactive_list.append(extension.user.sortable_name) continue percent_user_map[extension.percent].append(user_canvas_id) if len(percent_user_map) < 1: msg_str = 'No active extensions were found.<br>' if len(inactive_list) > 0: msg_str += ' Extensions for the following students are inactive:<br>{}' msg_str = msg_str.format("<br>".join(inactive_list)) update_job( job, 100, msg_str, 'complete', error=False ) return job.meta for index, quiz in enumerate(quizzes): quiz_id = quiz.get('id', None) quiz_title = quiz.get('title', '[UNTITLED QUIZ]') comp_perc = int(((float(index)) / float(num_quizzes)) * 100) refreshing_str = 'Refreshing quiz #{} - {} [{} of {}]' update_job( job, comp_perc, refreshing_str.format( quiz_id, quiz_title, index + 1, num_quizzes ), 'processing', error=False ) for percent, user_list in percent_user_map.iteritems(): extension_response = extend_quiz( course_id, quiz, percent, user_list ) if extension_response.get('success', False) is True: # add/update quiz quiz_obj, created = get_or_create( db.session, Quiz, canvas_id=quiz_id, course_id=course.id ) quiz_obj.title = quiz_title db.session.commit() else: error_message = 'Some quizzes couldn\'t be updated. ' error_message += extension_response.get('message', '') update_job( job, comp_perc, error_message, 'failed', error=True, ) return job.meta msg = '{} quizzes have been updated.'.format(len(quizzes)) update_job(job, 100, msg, 'complete', error=False) return job.meta
def update_background(course_id, extension_dict): """ Update time on selected students' quizzes to a specified percentage. :param course_id: The Canvas ID of the Course to update in :type course_id: int :param extension_dict: A dictionary that includes the percent of time and a list of canvas user ids. Example: { 'percent': '300', 'user_ids': [ '0123456', '1234567', '9867543', '5555555' ] } :type extension_dict: dict """ job = get_current_job() update_job(job, 0, 'Starting...', 'started') with app.app_context(): if not extension_dict: update_job( job, 0, 'Invalid Request', 'failed', error=True ) logger.warning('Invalid Request: {}'.format(extension_dict)) return job.meta try: course_json = get_course(course_id) except requests.exceptions.HTTPError: update_job( job, 0, 'Course not found.', 'failed', error=True ) logger.exception('Unable to find course #{}'.format(course_id)) return job.meta course_name = course_json.get('name', '<UNNAMED COURSE>') user_ids = extension_dict.get('user_ids', []) percent = extension_dict.get('percent', None) if not percent: update_job( job, 0, '`percent` field required.', 'failed', error=True ) logger.warning('Percent field not provided. Request: {}'.format( extension_dict )) return job.meta course, created = get_or_create(db.session, Course, canvas_id=course_id) course.course_name = course_name db.session.commit() for user_id in user_ids: try: canvas_user = get_user(course_id, user_id) sortable_name = canvas_user.get('sortable_name', '<MISSING NAME>') sis_id = canvas_user.get('sis_user_id') except requests.exceptions.HTTPError: # Unable to find user. Log and skip them. logger.warning( "Unable to find user #{} in course #{}".format( user_id, course_id ) ) continue user, created = get_or_create(db.session, User, canvas_id=user_id) user.sortable_name = sortable_name user.sis_id = sis_id db.session.commit() # create/update extension extension, created = get_or_create( db.session, Extension, course_id=course.id, user_id=user.id ) extension.percent = percent db.session.commit() quizzes = get_quizzes(course_id) num_quizzes = len(quizzes) quiz_time_list = [] unchanged_quiz_time_list = [] if num_quizzes < 1: update_job( job, 0, 'Sorry, there are no quizzes for this course.', 'failed', error=True ) logger.warning( "No quizzes found for course {}. Unable to update.".format( course_id ) ) return job.meta for index, quiz in enumerate(quizzes): quiz_id = quiz.get('id', None) quiz_title = quiz.get('title', "[UNTITLED QUIZ]") comp_perc = int(((float(index)) / float(num_quizzes)) * 100) updating_str = 'Updating quiz #{} - {} [{} of {}]' update_job( job, comp_perc, updating_str.format(quiz_id, quiz_title, index + 1, num_quizzes), 'processing', error=False ) extension_response = extend_quiz(course_id, quiz, percent, user_ids) if extension_response.get('success', False) is True: # add/update quiz quiz_obj, created = get_or_create( db.session, Quiz, canvas_id=quiz_id, course_id=course.id ) quiz_obj.title = quiz_title db.session.commit() added_time = extension_response.get('added_time', None) if added_time is not None: quiz_time_list.append({ "title": quiz_title, "added_time": added_time }) else: unchanged_quiz_time_list.append({"title": quiz_title}) else: update_job( job, comp_perc, extension_response.get( 'message', 'An unknown error occured.' ), 'failed', error=True ) logger.error("Extension failed: {}".format(extension_response)) return job.meta msg_str = ( 'Success! {} {} been updated for {} student(s) to have {}% time. ' '{} {} no time limit and were left unchanged.' ) message = msg_str.format( len(quiz_time_list), "quizzes have" if len(quiz_time_list) != 1 else "quiz has", len(user_ids), percent, len(unchanged_quiz_time_list), "quizzes have" if len(unchanged_quiz_time_list) != 1 else "quiz has" ) update_job(job, 100, message, 'complete', error=False) job.meta['quiz_list'] = quiz_time_list job.meta['unchanged_list'] = unchanged_quiz_time_list job.save() return job.meta