def view(request, course_prefix, course_suffix, slug): common_page_data = request.common_page_data try: #getByCourse takes care of checking for draft vs live, is_deleted and live times video = Video.objects.getByCourse(course=common_page_data['course']).get(slug=slug) except Video.DoesNotExist: raise Http404 if not common_page_data['is_course_admin']: visit_log = PageVisitLog( course = common_page_data['ready_course'], user = request.user, page_type= 'video', object_id = str(video.id), ) visit_log.save() if not 'video_quiz_mode' in request.session: #Default to include quizzes in viewing videos request.session['video_quiz_mode'] = "quizzes included" videos = Video.objects.getByCourse(course=common_page_data['course']) #Get index of current video cur_index = None #just code safety for index, item in enumerate(videos): if item == video: cur_index = index break ready_section = video.section if ready_section and ready_section.mode == "draft": ready_section = ready_section.image #code safety next_slug = None prev_slug = None if cur_index is not None: if cur_index > 0: prev_slug = videos[cur_index-1].slug else: prev_slug = None if cur_index < videos.count() - 1: next_slug = videos[cur_index+1].slug else: next_slug = None video_rec = request.user.videoactivity_set.filter(video=video) if video_rec: video_rec = video_rec[0] else: #note student field to be renamed to user, VideoActivity for all users now video_rec = VideoActivity(student=request.user, course=common_page_data['course'], video=video) video_rec.save() course = common_page_data['course'] key = ('video', video.id) l1items, l2items = get_contentgroup_data(course=course) downloadable_content = get_children(key, l1items, l2items) if video.exam: try: exam = video.exam video_obj = videos_in_exam_metadata(exam.xml_metadata, times_for_video_slug=video.slug) question_times = video_obj['question_times'] except Exam.DoesNotExist: raise Http404 else: sections = ContentSection.objects.getByCourse(course) section = sections[0] # create fake exam as exam template (which is what displays all videos) needs exam data to function # correctly (TODO: Refactor this) exam = Exam(course=course, slug=slug, title=video.title, description="Empty Exam", html_content="", xml_metadata="", due_date='', assessment_type="survey", mode="draft", total_score=0, grade_single=0, grace_period='', partial_credit_deadline='', late_penalty=0, submissions_permitted=0, resubmission_penalty=0, exam_type="survey", autograde=0, display_single=0, invideo=1, section=section,) exam.live_datetime = video.live_datetime # needed so video shows up question_times = "" return render_to_response('exams/view_exam.html', { 'common_page_data': common_page_data, 'video': video, 'ready_section': ready_section, 'video_rec': video_rec, 'prev_slug': prev_slug, 'next_slug': next_slug, 'downloadable_content':downloadable_content, 'json_pre_pop':"{}", 'scores':"{}", 'editable':True, 'single_question':exam.display_single, 'videotest':exam.invideo, 'question_times':json.dumps(question_times), 'allow_submit':True, 'children': downloadable_content, 'exam':exam }, context_instance=RequestContext(request))
def test_num_questions(self): """ Tests the num_questions function in the Exam() class. """ badxml1 = "<" badxml2 = "<abc>" badxml3 = "<def />" badxml4 = """<exam_metadata choosenquestions="" />""" badxml5 = """<exam_metadata choosenquestions="baby" />""" xml1 = "<exam_metadata />" xml2 = """<exam_metadata choosenquestions="3" />""" xml3 = """<exam_metadata choosenquestions="542" />""" exam = Exam() exam.xml_metadata = badxml1 self.assertEqual(exam.num_random_questions(), 0) exam.xml_metadata = badxml2 self.assertEqual(exam.num_random_questions(), 0) exam.xml_metadata = badxml3 self.assertEqual(exam.num_random_questions(), 0) exam.xml_metadata = badxml4 self.assertEqual(exam.num_random_questions(), 0) exam.xml_metadata = badxml5 self.assertEqual(exam.num_random_questions(), 0) exam.xml_metadata = xml1 self.assertEqual(exam.num_random_questions(), 0) exam.xml_metadata = xml2 self.assertEqual(exam.num_random_questions(), 3) exam.xml_metadata = xml3 self.assertEqual(exam.num_random_questions(), 542)
def view(request, course_prefix, course_suffix, slug): common_page_data = request.common_page_data try: #getByCourse takes care of checking for draft vs live, is_deleted and live times video = Video.objects.getByCourse(course=common_page_data['course']).get(slug=slug) except Video.DoesNotExist: raise Http404 if not common_page_data['is_course_admin']: visit_log = PageVisitLog( course = common_page_data['ready_course'], user = request.user, page_type= 'video', object_id = str(video.id), ) visit_log.save() if not 'video_quiz_mode' in request.session: #Default to include quizzes in viewing videos request.session['video_quiz_mode'] = "quizzes included" videos = Video.objects.getByCourse(course=common_page_data['course']) #Get index of current video cur_index = None #just code safety for index, item in enumerate(videos): if item == video: cur_index = index break #code safety next_slug = None prev_slug = None if cur_index is not None: if cur_index > 0: prev_slug = videos[cur_index-1].slug else: prev_slug = None if cur_index < videos.count() - 1: next_slug = videos[cur_index+1].slug else: next_slug = None video_rec = request.user.videoactivity_set.filter(video=video) if video_rec: video_rec = video_rec[0] else: #note student field to be renamed to user, VideoActivity for all users now video_rec = VideoActivity(student=request.user, course=common_page_data['course'], video=video) video_rec.save() course = common_page_data['course'] full_contentsection_list, full_index_list = get_full_contentsection_list(course, filter_children=True) if request.user.is_authenticated(): is_logged_in = 1 else: is_logged_in = 0 key = ('video', video.id) l1items, l2items = get_contentgroup_data(course=course) downloadable_content = get_children(key, l1items, l2items) if video.exam: try: #exam = Exam.objects.get(course=course, is_deleted=0, slug=exam_slug) exam = video.exam display_single = exam.display_single invideo = exam.invideo metadata_dom = parseString(exam.xml_metadata) #The DOM corresponding to the XML metadata video_questions = metadata_dom.getElementsByTagName('video') question_times = {} for video_node in video_questions: video_slug = video_node.getAttribute("url-identifier") if video_slug == "": video_slug = video_node.getAttribute("url_identifier") if video_slug == video.slug: question_children = video_node.getElementsByTagName("question") times = [] for question in question_children: time = "sec_%s" % question.getAttribute("time") if time not in question_times: question_times[time] = [] question_times[time].append(question.getAttribute("id")) print json.dumps(question_times) except Exam.DoesNotExist: raise Http404 else: sections = ContentSection.objects.getByCourse(course) section = sections[0] # create fake exam as exam template (which is what displays all videos) needs exam data to function # correctly (TODO: Refactor this) exam = Exam(course=course, slug=slug, title=video.title, description="Empty Exam", html_content="", xml_metadata="", due_date='', assessment_type="survey", mode="draft", total_score=0, grade_single=0, grace_period='', partial_credit_deadline='', late_penalty=0, submissions_permitted=0, resubmission_penalty=0, exam_type="survey", autograde=0, display_single=0, invideo=1, section=section,) exam.live_datetime = video.live_datetime # needed so video shows up question_times = "" videoURL = None thumbnailPath = None if is_storage_local(): videoURL = local_file_server_root() + "/" + str(video.file) thumbnailPath = local_file_server_root() + "/" + course.prefix + "/" + course.suffix + "/videos/" + str(video.id if video.mode == 'draft' else video.image.id) + "/jpegs/" elif video.url: videoURL = "http://www.youtube.com/embed/" + (video.url if video.mode == 'draft' else video.image.url) + "?autoplay=0&wmode=transparent&fs=0&rel=0&modestbranding=1&showinfo=0&start=0&enablejsapi=1&disablekb=1&" thumbnailPath = "http://" + settings.AWS_STORAGE_BUCKET_NAME + ".s3-website-us-west-2.amazonaws.com/" + course.prefix + "/" + course.suffix + "/videos/" + str(video.id if video.mode == 'draft' else video.image.id) + "/jpegs/" # change from 'videos/view.html' to 'exams/view_exam.html' return render_to_response('exams/view_exam.html', { 'common_page_data': common_page_data, 'video': video, 'video_rec': video_rec, 'videoURL': videoURL, 'thumbnailPath': thumbnailPath, 'prev_slug': prev_slug, 'next_slug': next_slug, 'contentsection_list': full_contentsection_list, 'full_index_list': full_index_list, 'is_logged_in': is_logged_in, 'downloadable_content':downloadable_content, 'json_pre_pop':"{}", 'scores':"{}", 'editable':True, 'single_question':exam.display_single, 'videotest':exam.invideo, 'question_times':json.dumps(question_times), 'allow_submit':True, 'children': downloadable_content, 'exam':exam }, context_instance=RequestContext(request))
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','') 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") #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() # Set parent/child relationships create_contentgroup_entries_from_post(request, 'parent', exam_obj.image, 'exam', display_style='list') #Now set the video associations exam_obj.sync_videos_foreignkeys_with_metadata() vid_status_obj = exam_obj.image.sync_videos_foreignkeys_with_metadata() vid_status_string = "" if vid_status_obj['video_slugs_set']: exam_obj.invideo=True exam_obj.image.invideo=True exam_obj.save() exam_obj.image.save() vid_status_string = "This exam was successfully associated with the following videos:\n" + \ ", ".join(vid_status_obj['video_slugs_set']) + "\n" if vid_status_obj['video_slugs_not_set']: vid_status_string += "The following videos WERE NOT automatically associated with this exam:\n" + \ ", ".join(vid_status_obj['video_slugs_not_set']) + "\n\n" + \ "You may have provided the wrong url-identifier or have not yet uploaded the video" return HttpResponse("Exam " + title + " created. \n" + unicode(grader) + "\n\n" + vid_status_string) 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() # Set parent/chlid relationships for this exam create_contentgroup_entries_from_post(request, 'parent', exam_obj.image, 'exam', display_style='list') #Now set the video associations exam_obj.sync_videos_foreignkeys_with_metadata() vid_status_obj = exam_obj.image.sync_videos_foreignkeys_with_metadata() vid_status_string = "" if vid_status_obj['video_slugs_set']: exam_obj.invideo=True exam_obj.image.invideo=True exam_obj.save() exam_obj.image.save() vid_status_string = "This exam was successfully associated with the following videos:\n" + \ ", ".join(vid_status_obj['video_slugs_set']) + "\n\n" if vid_status_obj['video_slugs_not_set']: vid_status_string += "The following videos WERE NOT automatically associated with this exam:\n" + \ ", ".join(vid_status_obj['video_slugs_not_set']) + "\n" + \ "You may have provided the wrong url-identifier or have not yet uploaded the video" return HttpResponse("Exam " + title + " saved. \n" + unicode(grader) + "\n\n" + vid_status_string) except Exam.DoesNotExist: return HttpResponseBadRequest("No exam exists with URL identifier %s" % old_slug)
def test_get_html_basic(self): """ Some basic unit tests of randomizing question divs. """ preamble = "<p>This is a <b>preamble</b></p>" question_template = \ """ <div class="question" id="problem_%d"> <p>Q1</p> </div> """ badxml1 = "<abc>" xml_none = "<exam_metadata />" xml_template = '<exam_metadata choosenquestions="%d" />' exam = Exam() #test with bad xml and xml_none, which should yield verbatim html for xml in [badxml1, xml_none]: exam.xml_metadata = xml for i in range(10): #10 tests #build html with random number of questions j = random.randint(0, 10) html = preamble for k in range(j): html += question_template % k exam.html_content = html self.assertEqual(exam.getHTML(), { 'html': html, 'subset': False, 'question_ids': [] }) #random test with good xml and good html. these cases should result in a subset for i in range(10): #10 tests n = random.randint(3, 10) # number of actual questions exam.xml_metadata = xml_template % ( n - 2) #number of desired questions is n-2 html = preamble for k in range(n): html += question_template % k exam.html_content = html result = exam.getHTML() self.assertTrue(result['subset']) self.assertEqual(len(result['question_ids']), n - 2) #parse out which random numbers were chosen chosen_nums = map(lambda id: int(id[-1]), result['question_ids']) #now reconstuct html to test equivalence reconstructed_html = preamble for num in chosen_nums: reconstructed_html += question_template % num self.assertEqual(re.sub(r"\s", "", result['html']), re.sub(r"\s", "", reconstructed_html)) #random test with good xml and good html with requesting more than we have. these cases should not result in a subset for i in range(10): #10 tests n = random.randint(3, 10) # number of actual questions exam.xml_metadata = xml_template % ( n + 2) #number of desired questions is n+2 html = preamble for k in range(n): html += question_template % k exam.html_content = html result = exam.getHTML() self.assertFalse(result['subset']) self.assertEqual(len(result['question_ids']), n) self.assertEqual(re.sub(r"\s", "", result['html']), re.sub(r"\s", "", html)) #test specifying question_ids for i in range(50): #50 tests n = 10 #10 questions #setup the html html = preamble for k in range(n): html += question_template % k exam.html_content = html exam.xml_metadata = xml_template % 20 #to show that the choosenquestions attr doesn't matter in this case if i == 0: #no removal here chosen_nums = range(n) else: m = random.randint( 0, 9 ) # number of questions in question_ids, at most 9 so there will also be a removal t = range(2 * n) #specify some qids that are not in the html chosen_nums = [] while m != 0: c = random.choice(t) t.remove(c) chosen_nums.append(c) m -= 1 chosen_ids = map(lambda n: "problem_%d" % n, chosen_nums) result = exam.getHTML(question_ids=chosen_ids) if i == 0: self.assertFalse(result['subset']) else: self.assertTrue(result['subset']) correct_chosen_nums = filter(lambda num: num < n, chosen_nums) correct_chosen_nums.sort() correct_chosen_ids = map(lambda n: "problem_%d" % n, correct_chosen_nums) self.assertEqual(correct_chosen_ids, result['question_ids']) #reconstruct html to test html output reconstructed_html = preamble for num in correct_chosen_nums: reconstructed_html += question_template % num self.assertEqual(re.sub(r"\s", "", result['html']), re.sub(r"\s", "", reconstructed_html))
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 view(request, course_prefix, course_suffix, slug): common_page_data = request.common_page_data try: # getByCourse takes care of checking for draft vs live, is_deleted and live times video = Video.objects.getByCourse(course=common_page_data["course"]).get(slug=slug) except Video.DoesNotExist: raise Http404 if not common_page_data["is_course_admin"]: visit_log = PageVisitLog( course=common_page_data["ready_course"], user=request.user, page_type="video", object_id=str(video.id) ) visit_log.save() if not "video_quiz_mode" in request.session: # Default to include quizzes in viewing videos request.session["video_quiz_mode"] = "quizzes included" videos = Video.objects.getByCourse(course=common_page_data["course"]) # Get index of current video cur_index = None # just code safety for index, item in enumerate(videos): if item == video: cur_index = index break # code safety next_slug = None prev_slug = None if cur_index is not None: if cur_index > 0: prev_slug = videos[cur_index - 1].slug else: prev_slug = None if cur_index < videos.count() - 1: next_slug = videos[cur_index + 1].slug else: next_slug = None video_rec = request.user.videoactivity_set.filter(video=video) if video_rec: video_rec = video_rec[0] else: # note student field to be renamed to user, VideoActivity for all users now video_rec = VideoActivity(student=request.user, course=common_page_data["course"], video=video) video_rec.save() course = common_page_data["course"] full_contentsection_list, full_index_list = get_full_contentsection_list(course, filter_children=True) if request.user.is_authenticated(): is_logged_in = 1 else: is_logged_in = 0 key = ("video", video.id) l1items, l2items = get_contentgroup_data(course=course) downloadable_content = get_children(key, l1items, l2items) if video.exam: try: # exam = Exam.objects.get(course=course, is_deleted=0, slug=exam_slug) exam = video.exam display_single = exam.display_single invideo = exam.invideo metadata_dom = parseString(exam.xml_metadata) # The DOM corresponding to the XML metadata video_questions = metadata_dom.getElementsByTagName("video") question_times = {} for video_node in video_questions: video_slug = video_node.getAttribute("url-identifier") if video_slug == "": video_slug = video_node.getAttribute("url_identifier") if video_slug == video.slug: question_children = video_node.getElementsByTagName("question") times = [] for question in question_children: time = "sec_%s" % question.getAttribute("time") if time not in question_times: question_times[time] = [] question_times[time].append(question.getAttribute("id")) print json.dumps(question_times) except Exam.DoesNotExist: raise Http404 else: sections = ContentSection.objects.getByCourse(course) section = sections[0] # create fake exam as exam template (which is what displays all videos) needs exam data to function # correctly (TODO: Refactor this) exam = Exam( course=course, slug=slug, title=video.title, description="Empty Exam", html_content="", xml_metadata="", due_date="", assessment_type="survey", mode="draft", total_score=0, grade_single=0, grace_period="", partial_credit_deadline="", late_penalty=0, submissions_permitted=0, resubmission_penalty=0, exam_type="survey", autograde=0, display_single=0, invideo=1, section=section, ) exam.live_datetime = video.live_datetime # needed so video shows up question_times = "" # change from 'videos/view.html' to 'exams/view_exam.html' return render_to_response( "exams/view_exam.html", { "common_page_data": common_page_data, "video": video, "video_rec": video_rec, "prev_slug": prev_slug, "next_slug": next_slug, "contentsection_list": full_contentsection_list, "full_index_list": full_index_list, "is_logged_in": is_logged_in, "downloadable_content": downloadable_content, "json_pre_pop": "{}", "scores": "{}", "editable": True, "single_question": exam.display_single, "videotest": exam.invideo, "question_times": json.dumps(question_times), "allow_submit": True, "children": downloadable_content, "exam": exam, }, context_instance=RequestContext(request), )
def test_num_questions(self): """ Tests the num_questions function in the Exam() class. """ badxml1="<" badxml2="<abc>" badxml3="<def />" badxml4="""<exam_metadata choosenquestions="" />""" badxml5="""<exam_metadata choosenquestions="baby" />""" xml1="<exam_metadata />" xml2="""<exam_metadata choosenquestions="3" />""" xml3="""<exam_metadata choosenquestions="542" />""" exam = Exam() exam.xml_metadata=badxml1 self.assertEqual(exam.num_random_questions(),0) exam.xml_metadata=badxml2 self.assertEqual(exam.num_random_questions(),0) exam.xml_metadata=badxml3 self.assertEqual(exam.num_random_questions(),0) exam.xml_metadata=badxml4 self.assertEqual(exam.num_random_questions(),0) exam.xml_metadata=badxml5 self.assertEqual(exam.num_random_questions(),0) exam.xml_metadata=xml1 self.assertEqual(exam.num_random_questions(),0) exam.xml_metadata=xml2 self.assertEqual(exam.num_random_questions(),3) exam.xml_metadata=xml3 self.assertEqual(exam.num_random_questions(),542)
def view(request, course_prefix, course_suffix, slug): common_page_data = request.common_page_data try: #getByCourse takes care of checking for draft vs live, is_deleted and live times video = Video.objects.getByCourse( course=common_page_data['course']).get(slug=slug) except Video.DoesNotExist: raise Http404 if not common_page_data['is_course_admin']: visit_log = PageVisitLog( course=common_page_data['ready_course'], user=request.user, page_type='video', object_id=str(video.id), ) visit_log.save() if not 'video_quiz_mode' in request.session: #Default to include quizzes in viewing videos request.session['video_quiz_mode'] = "quizzes included" videos = Video.objects.getByCourse(course=common_page_data['course']) #Get index of current video cur_index = None #just code safety for index, item in enumerate(videos): if item == video: cur_index = index break #code safety next_slug = None prev_slug = None if cur_index is not None: if cur_index > 0: prev_slug = videos[cur_index - 1].slug else: prev_slug = None if cur_index < videos.count() - 1: next_slug = videos[cur_index + 1].slug else: next_slug = None video_rec = request.user.videoactivity_set.filter(video=video) if video_rec: video_rec = video_rec[0] else: #note student field to be renamed to user, VideoActivity for all users now video_rec = VideoActivity(student=request.user, course=common_page_data['course'], video=video) video_rec.save() course = common_page_data['course'] full_contentsection_list, full_index_list = get_full_contentsection_list( course, filter_children=True) if request.user.is_authenticated(): is_logged_in = 1 else: is_logged_in = 0 key = ('video', video.id) l1items, l2items = get_contentgroup_data(course=course) downloadable_content = get_children(key, l1items, l2items) if video.exam: try: #exam = Exam.objects.get(course=course, is_deleted=0, slug=exam_slug) exam = video.exam display_single = exam.display_single invideo = exam.invideo metadata_dom = parseString( exam.xml_metadata) #The DOM corresponding to the XML metadata video_questions = metadata_dom.getElementsByTagName('video') question_times = {} for video_node in video_questions: video_slug = video_node.getAttribute("url-identifier") if video_slug == "": video_slug = video_node.getAttribute("url_identifier") if video_slug == video.slug: question_children = video_node.getElementsByTagName( "question") times = [] for question in question_children: time = "sec_%s" % question.getAttribute("time") if time not in question_times: question_times[time] = [] question_times[time].append( question.getAttribute("id")) print json.dumps(question_times) except Exam.DoesNotExist: raise Http404 else: sections = ContentSection.objects.getByCourse(course) section = sections[0] # create fake exam as exam template (which is what displays all videos) needs exam data to function # correctly (TODO: Refactor this) exam = Exam( course=course, slug=slug, title=video.title, description="Empty Exam", html_content="", xml_metadata="", due_date='', assessment_type="survey", mode="draft", total_score=0, grade_single=0, grace_period='', partial_credit_deadline='', late_penalty=0, submissions_permitted=0, resubmission_penalty=0, exam_type="survey", autograde=0, display_single=0, invideo=1, section=section, ) exam.live_datetime = video.live_datetime # needed so video shows up question_times = "" videoURL = None thumbnailPath = None if is_storage_local(): videoURL = local_file_server_root() + "/" + str(video.file) thumbnailPath = local_file_server_root( ) + "/" + course.prefix + "/" + course.suffix + "/videos/" + str( video.id if video.mode == 'draft' else video.image.id) + "/jpegs/" elif video.url: videoURL = "http://www.youtube.com/embed/" + ( video.url if video.mode == 'draft' else video.image.url ) + "?autoplay=0&wmode=transparent&fs=0&rel=0&modestbranding=1&showinfo=0&start=0&enablejsapi=1&disablekb=1&" thumbnailPath = "http://" + settings.AWS_STORAGE_BUCKET_NAME + ".s3-website-us-west-2.amazonaws.com/" + course.prefix + "/" + course.suffix + "/videos/" + str( video.id if video.mode == 'draft' else video.image.id) + "/jpegs/" # change from 'videos/view.html' to 'exams/view_exam.html' return render_to_response('exams/view_exam.html', { 'common_page_data': common_page_data, 'video': video, 'video_rec': video_rec, 'videoURL': videoURL, 'thumbnailPath': thumbnailPath, 'prev_slug': prev_slug, 'next_slug': next_slug, 'contentsection_list': full_contentsection_list, 'full_index_list': full_index_list, 'is_logged_in': is_logged_in, 'downloadable_content': downloadable_content, 'json_pre_pop': "{}", 'scores': "{}", 'editable': True, 'single_question': exam.display_single, 'videotest': exam.invideo, 'question_times': json.dumps(question_times), 'allow_submit': True, 'children': downloadable_content, 'exam': exam }, context_instance=RequestContext(request))
def test_get_html_basic(self): """ Some basic unit tests of randomizing question divs. """ preamble = "<p>This is a <b>preamble</b></p>" question_template = \ """ <div class="question" id="problem_%d"> <p>Q1</p> </div> """ badxml1="<abc>" xml_none = "<exam_metadata />" xml_template = '<exam_metadata choosenquestions="%d" />' exam = Exam() #test with bad xml and xml_none, which should yield verbatim html for xml in [badxml1, xml_none]: exam.xml_metadata = xml for i in range(10): #10 tests #build html with random number of questions j = random.randint(0,10) html = preamble for k in range(j): html += question_template % k exam.html_content = html self.assertEqual(exam.getHTML(), {'html':html, 'subset':False, 'question_ids':[]}) #random test with good xml and good html. these cases should result in a subset for i in range(10): #10 tests n = random.randint(3,10) # number of actual questions exam.xml_metadata = xml_template % (n-2) #number of desired questions is n-2 html = preamble for k in range(n): html += question_template % k exam.html_content = html result = exam.getHTML() self.assertTrue(result['subset']) self.assertEqual(len(result['question_ids']), n-2) #parse out which random numbers were chosen chosen_nums = map(lambda id: int(id[-1]), result['question_ids']) #now reconstuct html to test equivalence reconstructed_html = preamble for num in chosen_nums: reconstructed_html += question_template % num self.assertEqual(re.sub(r"\s","", result['html']), re.sub(r"\s","", reconstructed_html)) #random test with good xml and good html with requesting more than we have. these cases should not result in a subset for i in range(10): #10 tests n = random.randint(3,10) # number of actual questions exam.xml_metadata = xml_template % (n+2) #number of desired questions is n+2 html = preamble for k in range(n): html += question_template % k exam.html_content = html result = exam.getHTML() self.assertFalse(result['subset']) self.assertEqual(len(result['question_ids']), n) self.assertEqual(re.sub(r"\s","", result['html']), re.sub(r"\s","", html)) #test specifying question_ids for i in range(50): #50 tests n = 10 #10 questions #setup the html html = preamble for k in range(n): html += question_template % k exam.html_content = html exam.xml_metadata = xml_template % 20 #to show that the choosenquestions attr doesn't matter in this case if i==0: #no removal here chosen_nums = range(n) else: m = random.randint(0,9) # number of questions in question_ids, at most 9 so there will also be a removal t = range(2*n) #specify some qids that are not in the html chosen_nums = [] while m != 0: c = random.choice(t) t.remove(c) chosen_nums.append(c) m -= 1 chosen_ids = map(lambda n: "problem_%d" % n, chosen_nums) result = exam.getHTML(question_ids=chosen_ids) if i==0: self.assertFalse(result['subset']) else: self.assertTrue(result['subset']) correct_chosen_nums = filter(lambda num: num < n, chosen_nums) correct_chosen_nums.sort() correct_chosen_ids = map(lambda n: "problem_%d" % n, correct_chosen_nums) self.assertEqual(correct_chosen_ids, result['question_ids']) #reconstruct html to test html output reconstructed_html = preamble for num in correct_chosen_nums: reconstructed_html += question_template % num self.assertEqual(re.sub(r"\s","", result['html']), re.sub(r"\s","", reconstructed_html))
def save_exam_ajax(request, course_prefix, course_suffix): course = request.common_page_data['course'] 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', '') 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','') #########Validation, lots of validation####### if not slug: return HttpResponseBadRequest("No URL identifier value provided") if Exam.objects.filter(course=course, slug=slug).exists(): return HttpResponseBadRequest("An exam with this URL identifier already exists in this course") 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)) 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") 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") print(assessment_type) if assessment_type == "summative": autograde = True invideo = False display_single = False exam_type = "exam" elif assessment_type == "invideo": autograde = True invideo = True display_single = True exam_type = "exam" elif assessment_type == "exam-autograde": autograde = True invideo = False display_single = False exam_type = "exam" elif assessment_type == "exam-csv": autograde = False invideo = False display_single = False exam_type = "exam" elif assessment_type == "survey": autograde = False invideo = False display_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(late_penalty) 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") #create Exam exam_obj = Exam(course=course, slug=slug, title=title, description=description, html_content=htmlContent, xml_metadata=metaXMLContent, due_date=dd, 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) exam_obj.save() return HttpResponse("Exam " + title + " created")
def view(request, course_prefix, course_suffix, slug): common_page_data = request.common_page_data try: #getByCourse takes care of checking for draft vs live, is_deleted and live times video = Video.objects.getByCourse( course=common_page_data['course']).get(slug=slug) except Video.DoesNotExist: raise Http404 if not common_page_data['is_course_admin']: visit_log = PageVisitLog( course=common_page_data['ready_course'], user=request.user, page_type='video', object_id=str(video.id), ) visit_log.save() if not 'video_quiz_mode' in request.session: #Default to include quizzes in viewing videos request.session['video_quiz_mode'] = "quizzes included" videos = Video.objects.getByCourse(course=common_page_data['course']) #Get index of current video cur_index = None #just code safety for index, item in enumerate(videos): if item == video: cur_index = index break ready_section = video.section if ready_section and ready_section.mode == "draft": ready_section = ready_section.image #code safety next_slug = None prev_slug = None if cur_index is not None: if cur_index > 0: prev_slug = videos[cur_index - 1].slug else: prev_slug = None if cur_index < videos.count() - 1: next_slug = videos[cur_index + 1].slug else: next_slug = None video_rec = request.user.videoactivity_set.filter(video=video) if video_rec: video_rec = video_rec[0] else: #note student field to be renamed to user, VideoActivity for all users now video_rec = VideoActivity(student=request.user, course=common_page_data['course'], video=video) video_rec.save() course = common_page_data['course'] key = ('video', video.id) l1items, l2items = get_contentgroup_data(course=course) downloadable_content = get_children(key, l1items, l2items) if video.exam: try: exam = video.exam video_obj = videos_in_exam_metadata( exam.xml_metadata, times_for_video_slug=video.slug) question_times = video_obj['question_times'] except Exam.DoesNotExist: raise Http404 else: sections = ContentSection.objects.getByCourse(course) section = sections[0] # create fake exam as exam template (which is what displays all videos) needs exam data to function # correctly (TODO: Refactor this) exam = Exam( course=course, slug=slug, title=video.title, description="Empty Exam", html_content="", xml_metadata="", due_date='', assessment_type="survey", mode="draft", total_score=0, grade_single=0, grace_period='', partial_credit_deadline='', late_penalty=0, submissions_permitted=0, resubmission_penalty=0, exam_type="survey", autograde=0, display_single=0, invideo=1, section=section, ) exam.live_datetime = video.live_datetime # needed so video shows up question_times = "" return render_to_response('exams/view_exam.html', { 'common_page_data': common_page_data, 'video': video, 'ready_section': ready_section, 'video_rec': video_rec, 'prev_slug': prev_slug, 'next_slug': next_slug, 'downloadable_content': downloadable_content, 'json_pre_pop': "{}", 'scores': "{}", 'editable': True, 'single_question': exam.display_single, 'videotest': exam.invideo, 'question_times': json.dumps(question_times), 'allow_submit': True, 'children': downloadable_content, 'exam': exam, }, context_instance=RequestContext(request))
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)
class Command(BaseCommand): """ Define the edit_exam manamagement command: bulk updates of exam settings. """ # instantiate a dummy exam so we can inspect it testexam=Exam() exam_attrs = [a for a in vars(testexam) \ if not callable(getattr(testexam,a)) \ and not a.startswith('_')] exam_types = [t[0] for t in Exam.EXAM_TYPE_CHOICES] help = "Make bulk exam changes. With the -u option update the database. " \ " PLEASE BE CAREFUL." \ "\n\nSelect which exams to change with one or more of " \ "-e, -c, and -t. At least one of -e or -c must be used." \ "\n\nThe list of Exam columns are:\n%s" % "\n".join(wrap(", ".join(sorted(exam_attrs)))) option_list = ( # Select make_option("-e", "--examids", dest="exam_ids", type="string", help="Select by comma-separated list of exam ID's"), make_option("-c", "--courseid", dest="course_id", type="int", help="Select by course. If only this option is chosen, all exams " \ "for that course will be selected."), make_option("-t", "--type", dest="examtype", type="string", help="Select by type, valid values are: %s" \ % ", ".join(sorted(exam_types))), # Change make_option("-s", "--set", action="append", dest="setlist", default=[], metavar="NAME=\"VAL\"", help="Set this to that for every exam that matches your search. " \ "Specify this multiple times to update multiple columns. " \ "The quotes around the value are optional."), # Do It! make_option("-u", "--update", action="store_false", dest="dryrun", default=True, help="actually update database (default is dry run)."), ) + BaseCommand.option_list def validate_selector(self, options): """ Make sure we have a valid set of things to select on, and if we do, return a named tuple like this: Selector(exam_ids=[840, 841], course_id=11, type='survey') """ if not (options['exam_ids'] or options['course_id']): raise CommandError("At least one of exam_ids (-e) or course_id (-c) is required.") Selector = namedtuple('Selector', 'exam_ids, course_id, examtype') result_exam_id_list = [] if options['exam_ids']: exid_strings = options['exam_ids'].split(',') for exid_str in exid_strings: errstr = None try: exid = int(exid_str) if exid == 0: errstr = "exam id \"%s\" is invalid" except ValueError as e: errstr = e if errstr: raiseCommandError("Exam ID parsing error, %s" % errstr) result_exam_id_list.append(exid) if options['examtype']: if options['examtype'] not in self.exam_types: raise CommandError("Invalid exam type \"%s\" given, allowed types are: %s" % (options['examtype'], ", ".join(sorted(self.exam_types)))) return Selector(exam_ids=result_exam_id_list, course_id=options['course_id'], examtype=options['examtype']) def validate_setters(self, options): """ Decide what we're going to set for each of the exams we select. Returns a dict with columns and settings for each. """ resultdict = {} if not options['setlist']: raise CommandError("you must specify at least one set (-s) command") for cmd in options['setlist']: splitcmd = cmd.split('=') if len(splitcmd) != 2: raise CommandError("cannot parse \"%s\", commands must be of the form NAME=VAL" % cmd) (name, val) = splitcmd if name not in self.exam_attrs: raise CommandError("value \"%s\" isn't a valid property of Exam, valid values are %s" % (splitcmd[0], self.exam_attrs)) resultdict[name] = val return resultdict def handle(self, *args, **options): """The actual exam_edit command""" selector = self.validate_selector(options) pprint(selector) setter_dict = self.validate_setters(options) sys.stdout.write("Setters = ") pprint(setter_dict) exams = Exam.objects.all() if selector.course_id: exams = exams.filter(course=selector.course_id) if selector.exam_ids: exams = exams.filter(id__in=selector.exam_ids) if selector.examtype: exams = exams.filter(exam_type=selector.examtype) if options['dryrun']: matches = len(exams) print "dryrun matches = %d" % matches else: updates = exams.update(**setter_dict) print "updated = %d" % updates for exam in exams: sys.stdout.write("%d: " % exam.id) pprint(exam)