def exam_feedback(request, course_prefix, course_suffix, exam_slug): """ This is the endpoint that will be hit from an AJAX call from the front-end when the users pushes the "Check Answer" button on an interactive or in-video exercise. For all problems submitted, do three things: 1. instantiate and call the autograder 2. write a log entry storing the attempt 3. update the score table with the latest score While this will score and report results back for multiple problems, that isn't typical. """ course = request.common_page_data['course'] try: exam = Exam.objects.get(course = course, is_deleted=0, slug=exam_slug) except Exam.DoesNotExist: raise Http404 submissions = json.loads(request.POST['json_data']) autograder = None if exam.autograde: try: autograder = AutoGrader(exam.xml_metadata) except Exception as e: return HttpResponseBadRequest(unicode(e)) else: autograder = AutoGrader("<null></null>", default_return=True) #create a null autograder that always returns the "True" object if not autograder: return Http500("Could not create autograder") feedback = {} for prob, v in submissions.iteritems(): if prob == "__metadata__": # shouldn't happen, being careful next try: if isinstance(v,list): # multiple choice case student_input = map(lambda li: li['value'], v) feedback[prob] = autograder.grade(prob, student_input) else: # single answer case student_input = v['value'] feedback[prob] = autograder.grade(prob, student_input) except AutoGraderGradingException as e: logger.error(e) return HttpResponse(e, status=500) if 'questionreport' in v: human_name = v['questionreport'] else: human_name = "" log_attempt(course, exam, request.user, student_input, human_name, prob, feedback[prob]) update_score(course, exam, request.user, request.POST.get('json_data','{}'), prob, feedback[prob]) feedback['__metadata__'] = exam.xml_metadata if exam.xml_metadata else "<empty></empty>" return HttpResponse(json.dumps(feedback))
def check_metadata_xml(request, course_prefix, course_suffix): xml = request.POST.get('metaXMLContent') if not xml: return HttpResponseBadRequest("No metaXMLContent provided") try: grader = AutoGrader(xml) except Exception as e: #Since this is just a validator, pass back all the exceptions return HttpResponseBadRequest(unicode(e)) return HttpResponse("Metadata XML is OK.\n" + unicode(grader))
def collect_data(request, course_prefix, course_suffix, exam_slug): course = request.common_page_data['course'] try: exam = Exam.objects.get(course = course, is_deleted=0, slug=exam_slug) except Exam.DoesNotExist: raise Http404 postdata = request.POST['json_data'] #will return an error code to the user if either of these fail (throws 500) json_obj=json.loads(postdata) record = ExamRecord(course=course, exam=exam, student=request.user, json_data=postdata) record.save() if exam.autograde: try: autograder = AutoGrader(exam.xml_metadata) except Exception as e: #Pass back all the exceptions so user can see return HttpResponseBadRequest(unicode(e)) feedback = {} for prob,v in json_obj.iteritems(): try: if isinstance(v,list): submission = map(lambda li: li['value'], v) print(submission) feedback[prob] = autograder.grade(prob, submission) else: submission = float(v['value']) print(submission) feedback[prob] = autograder.grade(prob, submission) except ValueError: feedback[prob] = False except AutoGraderGradingException: pass return HttpResponse(json.dumps(feedback)) else: return HttpResponse("Submission has been saved.")
def collect_data(request, course_prefix, course_suffix, exam_slug): course = request.common_page_data['course'] user = request.user try: exam = Exam.objects.get(course = course, is_deleted=0, slug=exam_slug) except Exam.DoesNotExist: raise Http404 postdata = request.POST['json_data'] #will return an error code to the user if either of these fail (throws 500) json_obj=json.loads(postdata) if exam.mode == "ready" and exam.past_all_deadlines(): return HttpResponseBadRequest("Sorry! This submission is past the last deadline of %s" % \ datetime.datetime.strftime(exam.partial_credit_deadline, "%m/%d/%Y %H:%M PST")); attempt_number = exam.num_of_student_records(user)+1 onpage = request.POST.get('onpage','') record = ExamRecord(course=course, exam=exam, student=user, json_data=postdata, onpage=onpage, attempt_number=attempt_number, late=exam.past_due()) record.save() autograder = None if exam.exam_type == "survey": autograder = AutoGrader("<null></null>", default_return=True) #create a null autograder that always returns the "True" object elif exam.autograde: try: autograder = AutoGrader(exam.xml_metadata) except Exception as e: #Pass back all the exceptions so user can see return HttpResponseBadRequest(unicode(e)) if autograder: record_score = ExamRecordScore(record = record) record_score.save() feedback = {} total_score = 0 for prob,v in json_obj.iteritems(): #prob is the "input" id, v is the associated value, #which can be an object (input box) or a list of objects (multiple-choice) try: if isinstance(v,list): #multiple choice case submission = map(lambda li: li['value'], v) feedback[prob] = autograder.grade(prob, submission) field_obj = ExamRecordScoreField(parent=record_score, field_name = prob, human_name=v[0].get('questionreport', "") if len(v)>0 else "", subscore = feedback[prob]['score'], value = map(lambda li:li.encode('utf-8'),submission), correct = feedback[prob]['correct'], comments="", associated_text = v[0].get('associatedText', "") if len(v)>0 else "", ) field_obj.save() for li in v: if 'correct_choices' not in feedback[prob]: is_correct = None else: is_correct = li['value'] in feedback[prob]['correct_choices'] fc = ExamRecordScoreFieldChoice(parent=field_obj, choice_value=li['value'], correct=is_correct, human_name=li.get('report',""), associated_text=li.get('associatedText',"")) fc.save() else: #single answer submission = v['value'] feedback[prob] = autograder.grade(prob, submission) field_obj = ExamRecordScoreField(parent=record_score, field_name = prob, human_name=v.get('report', ""), subscore = feedback[prob]['score'], value = submission, correct = feedback[prob]['correct'], comments="", associated_text = v.get('associatedText', "")) field_obj.save() except AutoGraderGradingException as e: feedback[prob]={'correct':False, 'score':0} field_obj = ExamRecordScoreField(parent=record_score, field_name = prob, human_name=v.get('report', ""), subscore = 0, correct = feedback[prob]['correct'], comments = unicode(e), associated_text = v.get('associatedText', "")) field_obj.save() #This is when using code indents to denote blocks is a bit hairy #supposed to be at the same level as try...except. Run once per prob,v total_score += feedback[prob]['score'] #Set raw score for ExamRecordScore record_score.raw_score = total_score record_score.save() #Set penalty inclusive score for ExamRecord record.json_score_data = json.dumps(feedback) #apply resubmission penalty resubmission_penalty_percent = pow(((100 - exam.resubmission_penalty)/100), (attempt_number -1)) total_score = max(total_score * resubmission_penalty_percent, 0) #apply the late penalty if exam.grace_period and exam.late_penalty > 0 and datetime.datetime.now() > exam.grace_period: total_score = max(total_score * ((100 - exam.late_penalty)/100), 0) record.score = total_score record.save() #Set ExamScore.score to max of ExamRecord.score for that student, exam. exam_score, created = ExamScore.objects.get_or_create(course=course, exam=exam, student=user) exam_score.setScore() return HttpResponse(reverse(exam.record_view, args=[course.prefix, course.suffix, exam.slug, record.id])) else: return HttpResponse("Submission has been saved.")
def collect_data(request, course_prefix, course_suffix, exam_slug): course = request.common_page_data['course'] try: exam = Exam.objects.get(course = course, is_deleted=0, slug=exam_slug) except Exam.DoesNotExist: raise Http404 postdata = request.POST['json_data'] #will return an error code to the user if either of these fail (throws 500) json_obj=json.loads(postdata) record = ExamRecord(course=course, exam=exam, student=request.user, json_data=postdata) record.save() autograder = None if exam.exam_type == "survey": autograder = AutoGrader("<null></null>", default_return=True) #create a null autograder that always returns the "True" object elif exam.autograde: try: autograder = AutoGrader(exam.xml_metadata) except Exception as e: #Pass back all the exceptions so user can see return HttpResponseBadRequest(unicode(e)) if autograder: record_score = ExamRecordScore(record = record) record_score.save() feedback = {} total_score = 0 for prob,v in json_obj.iteritems(): try: if isinstance(v,list): #multiple choice case submission = map(lambda li: li['value'], v) feedback[prob] = autograder.grade(prob, submission) field_obj = ExamRecordScoreField(parent=record_score, field_name = prob, human_name=v[0].get('questiontag4humans', "") if len(v)>0 else "", subscore = feedback[prob]['score'], value = submission, correct = feedback[prob]['correct'], comments="", associated_text = v[0].get('associatedText', "") if len(v)>0 else "", ) field_obj.save() for li in v: fc = ExamRecordScoreFieldChoice(parent=field_obj, choice_value=li['value'], human_name=li.get('tag4humans',""), associated_text=li.get('associatedText',"")) fc.save() else: #single answer submission = v['value'] feedback[prob] = autograder.grade(prob, submission) field_obj = ExamRecordScoreField(parent=record_score, field_name = prob, human_name=v.get('questiontag4humans', ""), subscore = feedback[prob]['score'], value = submission, correct = feedback[prob]['correct'], comments="", associated_text = v.get('associatedText', "")) field_obj.save() except AutoGraderGradingException as e: feedback[prob]={'correct':False, 'score':0} field_obj = ExamRecordScoreField(parent=record_score, field_name = prob, human_name=v.get('questiontag4humans', ""), subscore = 0, correct = feedback[prob]['correct'], comments = unicode(e), associated_text = v.get('associatedText', "")) field_obj.save() #This is when using code indents to denote blocks is a bit hairy #supposed to be at the same level as try...except. Run once per prob,v total_score += feedback[prob]['score'] record_score.score = total_score record_score.save() record_score.copyToExamScore() #Make this score the current ExamScore record.json_score_data = json.dumps(feedback) record.score = total_score record.save() return HttpResponse(json.dumps(feedback)) else: return HttpResponse("Submission has been saved.")
def save_exam_ajax(request, course_prefix, course_suffix, create_or_edit="create", old_slug=""): course = request.common_page_data['course'] if course.mode == "ready": course = course.image slug = request.POST.get('slug', '') title = request.POST.get('title', '') description = request.POST.get('description', '') metaXMLContent = request.POST.get('metaXMLContent', '') htmlContent = request.POST.get('htmlContent', '') xmlImported = request.POST.get('xmlImported', '') due_date = request.POST.get('due_date', '') grace_period = request.POST.get('grace_period', '') partial_credit_deadline = request.POST.get('partial_credit_deadline', '') late_penalty = request.POST.get('late_penalty', '') num_subs_permitted = request.POST.get('num_subs_permitted', '') resubmission_penalty = request.POST.get('resubmission_penalty', '') assessment_type = request.POST.get('assessment_type', '') section = request.POST.get('section', '') invideo_val = request.POST.get('invideo', '') parent = request.POST.get('parent', 'none,none') if invideo_val and invideo_val == "true": invideo = True else: invideo = False #########Validation, lots of validation####### if not slug: return HttpResponseBadRequest("No URL identifier value provided") try: validate_slug(slug) except ValidationError as ve: return HttpResponseBadRequest(unicode(ve)) if not title: return HttpResponseBadRequest("No Title value provided") if not metaXMLContent: return HttpResponseBadRequest("No metadataXML provided") try: grader = AutoGrader(metaXMLContent) except Exception as e: #Since this is just a validator, pass back all the exceptions return HttpResponseBadRequest(unicode(e)) total_score = grader.points_possible if not htmlContent: return HttpResponseBadRequest("No Exam HTML provided") if not due_date: return HttpResponseBadRequest("No due date provided") if not grace_period: return HttpResponseBadRequest("No grace period provided") if not partial_credit_deadline: return HttpResponseBadRequest("No hard deadline provided") if not section: return HttpResponseBadRequest("Bad section provided!") try: contentsection = ContentSection.objects.get(id=section, course=course, is_deleted=False) except ContentSection.DoesNotExist: return HttpResponseBadRequest("Bad section provided!") dd = datetime.datetime.strptime(due_date, "%m/%d/%Y %H:%M") gp = datetime.datetime.strptime(grace_period, "%m/%d/%Y %H:%M") pcd = datetime.datetime.strptime(partial_credit_deadline, "%m/%d/%Y %H:%M") if assessment_type == "summative": autograde = True display_single = False grade_single = False exam_type = "problemset" elif assessment_type == "formative": autograde = True display_single = True grade_single = False #We will eventually want this to be True exam_type = "problemset" elif assessment_type == "interactive": autograde = True display_single = True grade_single = False exam_type = "interactive_exercise" elif assessment_type == "exam-autograde": autograde = True display_single = False grade_single = False exam_type = "exam" elif assessment_type == "exam-csv": autograde = False display_single = False grade_single = False exam_type = "exam" elif assessment_type == "survey": autograde = False display_single = False grade_single = False exam_type = "survey" else: return HttpResponseBadRequest("A bad assessment type (" + assessment_type + ") was provided") if not late_penalty: lp = 0 else: try: lp = int(late_penalty) except ValueError: return HttpResponseBadRequest("A non-numeric late penalty (" + late_penalty + ") was provided") if not num_subs_permitted: sp = 999 else: try: sp = int(num_subs_permitted) except ValueError: return HttpResponseBadRequest( "A non-numeric number of submissions permitted (" + sp + ") was provided") if not resubmission_penalty: rp = 0 else: try: rp = int(resubmission_penalty) except ValueError: return HttpResponseBadRequest( "A non-numeric resubmission penalty (" + resubmission_penalty + ") was provided") if parent and parent[:4] != 'none': parent_type, parent = parent.split(',') else: parent_type, parent = None, None #create or edit the Exam if create_or_edit == "create": if Exam.objects.filter(course=course, slug=slug, is_deleted=False).exists(): return HttpResponseBadRequest( "An exam with this URL identifier already exists in this course" ) exam_obj = Exam(course=course, slug=slug, title=title, description=description, html_content=htmlContent, xml_metadata=metaXMLContent, due_date=dd, assessment_type=assessment_type, mode="draft", total_score=total_score, grade_single=grade_single, grace_period=gp, partial_credit_deadline=pcd, late_penalty=lp, submissions_permitted=sp, resubmission_penalty=rp, exam_type=exam_type, autograde=autograde, display_single=display_single, invideo=invideo, section=contentsection, xml_imported=xmlImported) exam_obj.save() exam_obj.create_ready_instance() if parent_type: parent_ref = ContentGroup.groupable_types[parent_type].objects.get( id=long(parent)).image content_group_groupid = ContentGroup.add_parent( exam_obj.image.course, parent_type, parent_ref.image) ContentGroup.add_child(content_group_groupid, 'exam', exam_obj.image, display_style='list') return HttpResponse("Exam " + title + " created. \n" + unicode(grader)) else: try: #this is nasty code, I know. It should at least be moved into the model somehow exam_obj = Exam.objects.get(course=course, is_deleted=0, slug=old_slug) exam_obj.slug = slug exam_obj.title = title exam_obj.description = description exam_obj.html_content = htmlContent exam_obj.xml_metadata = metaXMLContent exam_obj.xml_imported = xmlImported exam_obj.due_date = dd exam_obj.total_score = total_score exam_obj.assessment_type = assessment_type exam_obj.grace_period = gp exam_obj.partial_credit_deadline = pcd exam_obj.late_penalty = lp exam_obj.submissions_permitted = sp exam_obj.resubmission_penalty = rp exam_obj.exam_type = exam_type exam_obj.autograde = autograde exam_obj.display_single = display_single exam_obj.grade_single = grade_single exam_obj.invideo = invideo exam_obj.section = contentsection exam_obj.save() exam_obj.commit() if parent_type: parent_ref = ContentGroup.groupable_types[ parent_type].objects.get(id=long(parent)).image content_group_parent = parent_ref.contentgroup_set.all() if content_group_parent: content_group_groupid = content_group_parent[0].group_id else: content_group_groupid = ContentGroup.add_parent( exam_obj.image.course, parent_type, parent_ref.image) ContentGroup.add_child(content_group_groupid, 'exam', exam_obj.image, display_style='list') return HttpResponse("Exam " + title + " saved. \n" + unicode(grader)) except Exam.DoesNotExist: return HttpResponseBadRequest( "No exam exists with URL identifier %s" % old_slug)
def collect_data(request, course_prefix, course_suffix, exam_slug): course = request.common_page_data['course'] user = request.user try: exam = Exam.objects.get(course=course, is_deleted=0, slug=exam_slug) except Exam.DoesNotExist: raise Http404 postdata = request.POST[ 'json_data'] #will return an error code to the user if either of these fail (throws 500) json_obj = json.loads(postdata) if exam.past_all_deadlines(): return HttpResponseBadRequest("Sorry! This submission is past the last deadline of %s" % \ datetime.datetime.strftime(exam.partial_credit_deadline, "%m/%d/%Y %H:%M PST")) attempt_number = exam.num_of_student_records(user) + 1 onpage = request.POST.get('onpage', '') record = ExamRecord(course=course, exam=exam, student=user, json_data=postdata, onpage=onpage, attempt_number=attempt_number, late=exam.past_due()) record.save() autograder = None if exam.exam_type == "survey": autograder = AutoGrader( "<null></null>", default_return=True ) #create a null autograder that always returns the "True" object elif exam.autograde: try: autograder = AutoGrader(exam.xml_metadata) except Exception as e: #Pass back all the exceptions so user can see return HttpResponseBadRequest(unicode(e)) if autograder: record_score = ExamRecordScore(record=record) record_score.save() feedback = {} total_score = 0 for prob, v in json_obj.iteritems( ): #prob is the "input" id, v is the associated value, #which can be an object (input box) or a list of objects (multiple-choice) try: if isinstance(v, list): #multiple choice case submission = map(lambda li: li['value'], v) feedback[prob] = autograder.grade(prob, submission) field_obj = ExamRecordScoreField( parent=record_score, field_name=prob, human_name=v[0].get('questionreport', "") if len(v) > 0 else "", subscore=feedback[prob]['score'], value=submission, correct=feedback[prob]['correct'], comments="", associated_text=v[0].get('associatedText', "") if len(v) > 0 else "", ) field_obj.save() for li in v: if 'correct_choices' not in feedback[prob]: is_correct = None else: is_correct = li['value'] in feedback[prob][ 'correct_choices'] fc = ExamRecordScoreFieldChoice( parent=field_obj, choice_value=li['value'], correct=is_correct, human_name=li.get('report', ""), associated_text=li.get('associatedText', "")) fc.save() else: #single answer submission = v['value'] feedback[prob] = autograder.grade(prob, submission) field_obj = ExamRecordScoreField( parent=record_score, field_name=prob, human_name=v.get('report', ""), subscore=feedback[prob]['score'], value=submission, correct=feedback[prob]['correct'], comments="", associated_text=v.get('associatedText', "")) field_obj.save() except AutoGraderGradingException as e: feedback[prob] = {'correct': False, 'score': 0} field_obj = ExamRecordScoreField( parent=record_score, field_name=prob, human_name=v.get('report', ""), subscore=0, correct=feedback[prob]['correct'], comments=unicode(e), associated_text=v.get('associatedText', "")) field_obj.save() #This is when using code indents to denote blocks is a bit hairy #supposed to be at the same level as try...except. Run once per prob,v total_score += feedback[prob]['score'] #Set raw score for ExamRecordScore record_score.raw_score = total_score record_score.save() #Set penalty inclusive score for ExamRecord record.json_score_data = json.dumps(feedback) #apply resubmission penalty resubmission_penalty_percent = pow( ((100 - exam.resubmission_penalty) / 100), (attempt_number - 1)) total_score = max(total_score * resubmission_penalty_percent, 0) #apply the late penalty if exam.grace_period and exam.late_penalty > 0 and datetime.datetime.now( ) > exam.grace_period: total_score = max(total_score * ((100 - exam.late_penalty) / 100), 0) record.score = total_score record.save() #Set ExamScore.score to max of ExamRecord.score for that student, exam. exam_score, created = ExamScore.objects.get_or_create(course=course, exam=exam, student=user) exam_score.setScore() return HttpResponse( reverse(exam.record_view, args=[course.prefix, course.suffix, exam.slug, record.id])) else: return HttpResponse("Submission has been saved.")