def upload_new(cls, registry: Registry, local_quiz, args): canvas = registry.get_service(args.service, "canvas") quiz_data = cls._make_canvas_upload(registry, local_quiz, args) created_quiz = canvas.api.post('quizzes/', data=quiz_data) if 'errors' in created_quiz: pprint(created_quiz['errors']) raise WaltzException("Error loading data, see above.") print("Created quiz", local_quiz['title'], "on canvas") # Create the groups group_name_to_id = {} for group in local_quiz['groups'].values(): group_data = QuizGroup._make_canvas_upload(registry, group, args) created_group = canvas.api.post( 'quizzes/{quiz_id}/groups'.format(quiz_id=created_quiz['id']), data=group_data) created_group = created_group['quiz_groups'][ 0] # acbart: Weird response type # acbart: Okay because names are strings and IDs are ints group_name_to_id[created_group['name']] = created_group['id'] group_name_to_id[created_group['id']] = created_group['id'] if local_quiz['groups']: print("Created quiz", local_quiz['title'], "groups on canvas") # Create the questions for question in local_quiz['questions']: if 'quiz_group_id' in question and question[ 'quiz_group_id'] is not None: question['quiz_group_id'] = group_name_to_id[ question['quiz_group_id']] question_data = QuizQuestion._make_canvas_upload( registry, question, args) created_question = canvas.api.post( 'quizzes/{quiz_id}/questions'.format( quiz_id=created_quiz['id']), data=question_data) print("Created quiz", local_quiz['title'], "questions on canvas")
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) result['answers'] = [{ 'comments_html': m2h(answer.get('comment', "")), 'text': answer['text'] } for answer in data['answers']] return result
def _make_canvas_upload_raw(cls, registry: Registry, data, args): result = QuizQuestion._make_canvas_upload_common(registry, data, args) for index, answer in enumerate(data['answers']): base = 'question[answers][{index}]'.format(index=index) result[base + "[answer_text]"] = answer['text'] result[base + "[answer_comment_html]"] = answer['comments_html'] result[base + "[answer_weight]"] = 100 return result
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) result['answers'] = [{ 'comments_html': m2h(answer.get('comment', "")), 'text': answer['text'], 'blank_id': blank_id } for blank_id, answers in data['answers'].items() for answer in answers] return result
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) result['matching_answer_incorrect_matches'] = "\n".join( data.get('distractors', [])) result['answers'] = [{ 'comments_html': m2h(answer.get('comment', '')), 'left': answer['left'], 'right': answer['right'] } for answer in data['answers']] return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) if not args.hide_answers: result['answers'] = [] for answer in data['answers']: a = CommentedMap() a['text'] = answer['text'] if answer['comments_html']: a['comment'] = h2m(answer['comments_html']) result['answers'].append(a) return result
def _make_canvas_upload_raw(cls, registry: Registry, data, args): result = QuizQuestion._make_canvas_upload_common(registry, data, args) result['question[matching_answer_incorrect_matches]'] = data[ 'matching_answer_incorrect_matches'] for index, answer in enumerate(data['answers']): base = 'question[answers][{index}]'.format(index=index) result[base + "[answer_match_left]"] = answer['left'] result[base + "[answer_match_right]"] = answer['right'] result[base + "[answer_comment_html]"] = answer['comments_html'] result[base + "[answer_weight]"] = 100 # TODO: Unnecessary? result[base + "[answer_precision]"] = 10 # TODO: Unnecessary? return result
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) result['answers'] = [{ 'comments_html': m2h(answer.get('comment', "")), 'weight': 100 if 'correct' in answer else 0, 'text': answer['correct'] if 'correct' in answer else answer['wrong'], 'html': m2h(answer['correct'] if 'correct' in answer else answer['wrong']) } for answer in data['answers']] return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) if not args.hide_answers: result['answers'] = CommentedMap() for answer in data['answers']: blank_id = answer['blank_id'] if blank_id not in result['answers']: result['answers'][blank_id] = [] a = CommentedMap() a['text'] = answer['text'] if 'comments_html' in answer and answer['comments_html']: a['comment'] = h2m(answer['comments_html']) result['answers'][blank_id].append(a) return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) if not args.hide_answers: comments = CommentedMap() for answer in data['answers']: if answer['text'] == 'True': result['answer'] = True if answer['weight'] else False if answer.get('comments_html'): comments['if_true_chosen'] = h2m( answer['comments_html']) elif answer.get('comments_html'): comments['if_false_chosen'] = h2m(answer['comments_html']) if comments and any(comments.values()): result['comments'] = comments return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) result['answers'] = [] for answer in data['answers']: a = CommentedMap() html = h2m(answer['html']) if args.hide_answers: a['possible'] = html else: if answer['weight']: a['correct'] = html else: a['wrong'] = html if answer['comments_html']: a['comment'] = h2m(answer['comments_html']) result['answers'].append(a) return result
def _make_canvas_upload_raw(cls, registry: Registry, data, args): result = QuizQuestion._make_canvas_upload_common(registry, data, args) for index, answer in enumerate(data['answers']): base = 'question[answers][{index}]'.format(index=index) result[base + "[answer_comment_html]"] = answer['comments_html'] result[base + "[numerical_answer_type]"] = answer['numerical_answer_type'] if answer['numerical_answer_type'] == 'exact_answer': result[base + "[answer_exact]"] = answer['exact'] result[base + "[answer_error_margin]"] = answer.get( 'margin', 0) elif answer['numerical_answer_type'] == 'range_answer': result[base + "[answer_range_start]"] = answer['start'] result[base + "[answer_range_end]"] = answer['end'] elif answer['numerical_answer_type'] == 'precision_answer': result[base + "[answer_precision]"] = answer['precision'] result[base + "[answer_approximate]"] = answer['approximate'] return result
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) comments = data.get('comments', {}) result['answers'] = [{ 'comments_html': m2h(comments.get('if_true_chosen', "")), 'weight': 100 if data['answer'] else 0, 'text': 'True' }, { 'comments_html': m2h(comments.get('if_false_chosen', "")), 'weight': 100 if not data['answer'] else 0, 'text': 'False' }] return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) if not args.hide_answers: result['answers'] = [] for answer in data['answers']: a = CommentedMap() if answer['numerical_answer_type'] == 'exact_answer': a['exact'] = answer['exact'] a['margin'] = answer['margin'] elif answer['numerical_answer_type'] == 'range_answer': a['start'] = answer['start'] a['end'] = answer['end'] elif answer['numerical_answer_type'] == 'precision_answer': a['precision'] = answer['precision'] a['approximate'] = answer['approximate'] if answer.get('comments_html'): a['comment'] = h2m(answer['comments_html']) result['answers'].append(a) return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) result['answers'] = CommentedMap() for answer in data['answers']: blank_id = answer['blank_id'] if blank_id not in result['answers']: result['answers'][blank_id] = [] a = CommentedMap() text = answer['text'] if args.hide_answers: a['possible'] = text else: if answer['weight']: a['correct'] = text else: a['wrong'] = text if answer['comments_html']: a['comment'] = h2m(answer['comments_html']) result['answers'][blank_id].append(a) return result
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) text_mode = data['mode'] == 'text' if 'mode' in data else False result['answers'] = [] for answer in data['answers']: result_answer = { 'comments_html': m2h(answer.get('comment', "")), 'weight': 100 if 'correct' in answer else 0, 'text': answer['correct'] if 'correct' in answer else answer['wrong'], 'html': m2h(answer['correct'] if 'correct' in answer else answer['wrong']) } if text_mode: del result_answer['html'] result['answers'].append(result_answer) return result
def encode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.encode_question_common(registry, data, args) result['answers'] = [] for answer in data['answers']: numerical_answer_type = ( 'exact_answer' if 'exact' in answer else 'range_answer' if 'start' in answer else 'precision_answer') a = { 'comments_html': m2h(answer.get('comment', "")), 'numerical_answer_type': numerical_answer_type } if numerical_answer_type == 'exact_answer': a['exact'] = answer['exact'] a['margin'] = answer.get('margin', 0) elif numerical_answer_type == 'range_answer': a['start'] = answer['start'] a['end'] = answer['end'] elif numerical_answer_type == 'precision_answer': a['precision'] = answer['precision'] a['approximate'] = answer['approximate'] result['answers'].append(a) return result
def decode_json_raw(cls, registry: Registry, data, args): result = QuizQuestion.decode_question_common(registry, data, args) if args.hide_answers: result['answers'] = CommentedMap() result['answers']['lefts'] = list( sorted(set([answer['left'] for answer in data['answers']]))) result['answers']['rights'] = ( list( sorted(set([answer['right'] for answer in data['answers']]))) + data['matching_answer_incorrect_matches'].split("\n")) else: result['answers'] = [] for answer in data['answers']: a = CommentedMap() a['left'] = answer['left'] a['right'] = answer['right'] if answer.get('comments_html'): a['comment'] = h2m(answer['comments_html']) result['answers'].append(a) result["distractors"] = data[ 'matching_answer_incorrect_matches'].split("\n") return result
from waltz.resources.quizzes.essay_question import EssayQuestion from waltz.resources.quizzes.matching_question import MatchingQuestion from waltz.resources.quizzes.numerical_question import NumericalQuestion from waltz.resources.quizzes.quiz_question import QuizQuestion from waltz.resources.quizzes.fill_in_multiple_blanks_question import FillInMultipleBlanksQuestion from waltz.resources.quizzes.multiple_answers_question import MultipleAnswersQuestion from waltz.resources.quizzes.multiple_dropdowns_question import MultipleDropDownsQuestion from waltz.resources.quizzes.multiple_choice_question import MultipleChoiceQuestion from waltz.resources.quizzes.short_answer_question import ShortAnswerQuestion from waltz.resources.quizzes.text_only_question import TextOnlyQuestion from waltz.resources.quizzes.true_false_question import TrueFalseQuestion QuizQuestion.register_type(MultipleChoiceQuestion) QuizQuestion.register_type(TrueFalseQuestion) QuizQuestion.register_type(ShortAnswerQuestion) QuizQuestion.register_type(FillInMultipleBlanksQuestion) QuizQuestion.register_type(MultipleAnswersQuestion) QuizQuestion.register_type(MultipleDropDownsQuestion) QuizQuestion.register_type(MatchingQuestion) QuizQuestion.register_type(NumericalQuestion) QuizQuestion.register_type(EssayQuestion) QuizQuestion.register_type(TextOnlyQuestion)
def upload_edit(cls, registry: Registry, old_quiz, new_quiz, args): canvas = registry.get_service(args.service, "canvas") quiz_id = old_quiz['id'] # Edit the quiz on canvas quiz_data = cls._make_canvas_upload(registry, new_quiz, args) canvas.api.put('quizzes/{quiz_id}'.format(quiz_id=quiz_id), data=quiz_data) print("Updated quiz", old_quiz['title'], "on canvas") # Make a map of the old groups' names/ids to the groups old_group_map = {} for group in old_quiz['groups'].values(): old_group_map[group['name']] = group old_group_map[group['id']] = group # Update groups with the same name and create new ones used_groups = {} for group in new_quiz['groups'].values(): group_data = QuizGroup._make_canvas_upload(registry, group, args) if group['name'] in old_group_map: canvas_group = old_group_map[group['name']] canvas_group = canvas.api.put( 'quizzes/{quiz_id}/groups/{group_id}'.format( quiz_id=quiz_id, group_id=canvas_group['id']), data=group_data) else: canvas_group = canvas.api.post( 'quizzes/{quiz_id}/groups'.format(quiz_id=quiz_id), data=group_data) canvas_group = canvas_group['quiz_groups'][ 0] # acbart: Weird response type used_groups[canvas_group['name']] = canvas_group used_groups[canvas_group['id']] = canvas_group if new_quiz['groups']: print("Updated quiz", old_quiz['title'], "groups on canvas") # Delete any groups that no longer have a reference for old_group in old_quiz['groups'].values(): if old_group['id'] not in used_groups: canvas.api.delete('quizzes/{quiz_id}/groups/{group_id}'.format( quiz_id=quiz_id, group_id=old_group['id'])) print("Deleted question group", old_group['name'], " (ID: {})".format(old_group['id'])) # Push all the questions name_map = {q['question_name']: q for q in old_quiz['questions']} used_questions = {} for new_question in new_quiz['questions']: if new_question.get('quiz_group_id') is not None: new_question['quiz_group_id'] = used_groups[ new_question['quiz_group_id']]['id'] question_data = QuizQuestion._make_canvas_upload( registry, new_question, args) if new_question['question_name'] in name_map: canvas_question = name_map[new_question['question_name']] canvas_question = canvas.api.put( 'quizzes/{quiz_id}/questions/{question_id}'.format( quiz_id=quiz_id, question_id=canvas_question['id']), data=question_data) else: canvas_question = canvas.api.post( 'quizzes/{quiz_id}/questions'.format(quiz_id=quiz_id), data=question_data) used_questions[canvas_question['id']] = canvas_question print("Updated quiz", old_quiz['title'], "questions on canvas") # Delete any old questions for question in old_quiz['questions']: if question['id'] not in used_questions: canvas.api.delete( 'quizzes/{quiz_id}/questions/{question_id}'.format( quiz_id=quiz_id, question_id=question['id'])) print("Deleted question", question.get('name', "NO NAME"), " (ID: {})".format(question['id']))
def decode_json(cls, registry: Registry, data: str, args): raw_data = json.loads(data) result = CommentedMap() result['title'] = raw_data['title'] result['resource'] = 'quiz' result['url'] = raw_data['html_url'] result['published'] = raw_data['published'] result['settings'] = CommentedMap() result['settings']['quiz_type'] = raw_data['quiz_type'] if raw_data.get('points_possible') is not None: result['settings']['points_possible'] = raw_data['points_possible'] result['settings']['allowed_attempts'] = raw_data['allowed_attempts'] result['settings']['scoring_policy'] = raw_data['scoring_policy'] result['settings']['timing'] = CommentedMap() result['settings']['timing']['due_at'] = to_friendly_date( raw_data['due_at']) result['settings']['timing']['unlock_at'] = to_friendly_date( raw_data['unlock_at']) result['settings']['timing']['lock_at'] = to_friendly_date( raw_data['lock_at']) result['settings']['secrecy'] = CommentedMap() result['settings']['secrecy']['shuffle_answers'] = raw_data[ 'shuffle_answers'] result['settings']['secrecy']['time_limit'] = raw_data['time_limit'] result['settings']['secrecy']['one_question_at_a_time'] = raw_data[ 'one_question_at_a_time'] result['settings']['secrecy']['cant_go_back'] = raw_data[ 'cant_go_back'] result['settings']['secrecy']['show_correct_answers'] = raw_data[ 'show_correct_answers'] result['settings']['secrecy'][ 'show_correct_answers_last_attempt'] = raw_data[ 'show_correct_answers_last_attempt'] result['settings']['secrecy']['show_correct_answers_at'] = raw_data[ 'show_correct_answers_at'] result['settings']['secrecy']['hide_correct_answers_at'] = raw_data[ 'hide_correct_answers_at'] result['settings']['secrecy']['hide_results'] = raw_data[ 'hide_results'] result['settings']['secrecy']['one_time_results'] = raw_data[ 'one_time_results'] if raw_data['access_code']: result['settings']['secrecy']['access_code'] = raw_data[ 'access_code'] if raw_data['ip_filter']: result['settings']['secrecy']['ip_filter'] = raw_data['ip_filter'] # Handle questions and groups result['questions'] = [] available_groups = raw_data['groups'] used_groups = {} extra_files = [] for question in raw_data['questions']: quiz_question, destination_path, full_body = QuizQuestion.decode_question( registry, question, raw_data, args) if destination_path is not None: extra_files.append((destination_path, full_body)) quiz_group_id = question.get('quiz_group_id') if quiz_group_id is not None: quiz_group_id = str( quiz_group_id) # acbart: JSON only allows string keys if quiz_group_id not in used_groups: used_groups[quiz_group_id] = QuizGroup.decode_group( available_groups[quiz_group_id]) result['questions'].append(used_groups[quiz_group_id]) used_groups[quiz_group_id]['questions'].append(quiz_question) else: result['questions'].append(quiz_question) return h2m(raw_data['description'], result), extra_files
def encode_json(cls, registry: Registry, data, args): regular, waltz, body = extract_front_matter(data) settings = waltz.get('settings', {}) timing = settings.get('timing', {}) secrecy = settings.get('secrecy', {}) body = hide_data_in_html(regular, m2h(body)) questions = [] groups = {} for question in waltz.get('questions', []): if isinstance(question, str): # Look up quiz question name questions.append( QuizQuestion.encode_question_by_title( registry, question, args)) elif 'group' in question: # This is a question group group = QuizGroup.encode_group(registry, question, args) groups[group['name']] = group questions.extend( QuizGroup.encode_questions(registry, question, args)) else: # This is an embedded question questions.append( QuizQuestion.encode_question(registry, question, args)) # TODO: total_estimated_points from the questions return json.dumps({ 'title': waltz['title'], 'published': waltz.get('published', False), 'description': body, # Settings 'quiz_type': settings.get('quiz_type', 'assignment'), 'points_possible': settings.get('points_possible'), 'allowed_attempts': settings.get('allowed_attempts'), 'scoring_policy': settings.get('scoring_policy'), # Timing 'due_at': from_friendly_date(timing.get('due_at')), 'unlock_at': from_friendly_date(timing.get('unlock_at')), 'lock_at': from_friendly_date(timing.get('lock_at')), # Secrecy 'one_question_at_a_time': int(secrecy.get('one_question_at_a_time', 0)), 'shuffle_answers': int(secrecy.get('shuffle_answers', 0)), 'time_limit': secrecy.get('time_limit'), 'cant_go_back': int(secrecy.get('cant_go_back', 0)), 'show_correct_answers': int(secrecy.get('show_correct_answers', 1)), 'show_correct_answers_last_attempt': secrecy.get('show_correct_answers_last_attempt'), 'show_correct_answers_at': secrecy.get('show_correct_answers_at'), 'hide_correct_answers_at': secrecy.get('hide_correct_answers_at'), 'hide_results': secrecy.get('hide_results'), 'one_time_results': int(secrecy.get('one_time_results', 0)), 'access_code': secrecy.get('access_code'), 'ip_filter': secrecy.get('ip_filter'), # Questions and Groups 'questions': questions, 'groups': groups })
def encode_json_raw(cls, registry: Registry, data, args): return QuizQuestion.encode_question_common(registry, data, args)
def _make_canvas_upload_raw(cls, registry: Registry, data, args): return QuizQuestion._make_canvas_upload_common(registry, data, args)