def api_exam_qtemplates(course_id, exam_id): """ Return a JSON list of all the qtemplates used for the given exam. """ user_id = session['user_id'] if not satisfy_perms(user_id, course_id, ("examcreate", )): abort(401) if exam_id == 0: # New assessment may be being created return jsonify(result=[ [ { 'qtid': 0 }, ], ]) exam = Exams.get_exam_struct(exam_id) ecid = exam['cid'] if not ecid == course_id: # They may be trying to bypass permission check abort(401) qtemplates = [] try: qtemplates = Exams.get_qts_list(exam_id) except KeyError: abort(401) return jsonify(result=qtemplates)
def setup_usersummary(view_id): """ Show an account summary for the given user account. """ user_id = session['user_id'] if not check_perm(user_id, -1, "useradmin"): flash("You do not have User Administration access.") return redirect(url_for('setup_top')) is_sysadmin = check_perm(user_id, -1, 'sysadmin') user = Users2.get_user(view_id) examids = Exams.get_exams_done(view_id) exams = [] for examid in examids: exam = Exams.get_exam_struct(examid) started = General.human_date(exam['start']) exam['started'] = started exam['viewable'] = satisfy_perms(user_id, exam['cid'], ("viewmarks", )) exams.append(exam) exams.sort(key=lambda x: x['start_epoch'], reverse=True) course_ids = Users2.get_courses(view_id) courses = [] for course_id in course_ids: courses.append(Courses2.get_course(course_id)) user_is_admin = check_perm(view_id, 0, 'sysadmin') return render_template('setup_usersummary.html', user=user, exams=exams, courses=courses, is_sysadmin=is_sysadmin, user_is_admin=user_is_admin)
def mark_exam(user_id, exam_id): """ Submit the assessment and mark it. Returns True if it went well, or False if a problem. """ numquestions = Exams.get_num_questions(exam_id) status = Exams.get_user_status(user_id, exam_id) L.info("Marking assessment %s for %s, status is %s" % (exam_id, user_id, status)) examtotal = 0.0 errors = 0 for position in range(1, numquestions + 1): q_id = General.get_exam_q(exam_id, position, user_id) if not q_id: L.critical( "Unable to retrieve exam question page %s, exam %s, for user %s" % (position, exam_id, user_id)) errors += 1 continue answers = DB.get_q_guesses(q_id) # There's a small chance they got here without ever seeing a question, # make sure it exists. DB.add_exam_q(user_id, exam_id, q_id, position) # First, mark the question try: marks = General.mark_q(q_id, answers) DB.set_q_status(q_id, 3) # 3 = marked DB.set_q_marktime(q_id) except OaMarkerError: L.warn("Marker Error in question %s, exam %s, student %s!" % (q_id, exam_id, user_id)) return False parts = [ int(var[1:]) for var in marks.keys() if re.search("^A([0-9]+)$", var) > 0 ] parts.sort() # Then calculate the mark total = 0.0 for part in parts: try: mark = float(marks['M%d' % (part, )]) except (KeyError, ValueError): mark = 0 total += mark DB.update_q_score(q_id, total) examtotal += total if not errors: Exams.set_user_status(user_id, exam_id, 5) Exams.set_submit_time(user_id, exam_id) Exams.save_score(exam_id, user_id, examtotal) Exams.touchuserexam(exam_id, user_id) if errors: return False L.info("user %s scored %s total on exam %s" % (user_id, examtotal, exam_id)) return True
def mark_exam(user_id, exam_id): """ Submit the assessment and mark it. Returns True if it went well, or False if a problem. """ numquestions = Exams.get_num_questions(exam_id) status = Exams.get_user_status(user_id, exam_id) L.info("Marking assessment %s for %s, status is %s" % (exam_id, user_id, status)) examtotal = 0.0 errors = 0 for position in range(1, numquestions + 1): q_id = General.get_exam_q(exam_id, position, user_id) if not q_id: L.critical("Unable to retrieve exam question page %s, exam %s, for user %s" % (position, exam_id, user_id ) ) errors += 1 continue answers = DB.get_q_guesses(q_id) # There's a small chance they got here without ever seeing a question, # make sure it exists. DB.add_exam_q(user_id, exam_id, q_id, position) # First, mark the question try: marks = General.mark_q(q_id, answers) DB.set_q_status(q_id, 3) # 3 = marked DB.set_q_marktime(q_id) except OaMarkerError: L.warn("Marker Error in question %s, exam %s, student %s!" % (q_id, exam_id, user_id)) return False parts = [int(var[1:]) for var in marks.keys() if re.search("^A([0-9]+)$", var) > 0] parts.sort() # Then calculate the mark total = 0.0 for part in parts: try: mark = float(marks['M%d' % (part,)]) except (KeyError, ValueError): mark = 0 total += mark DB.update_q_score(q_id, total) examtotal += total if not errors: Exams.set_user_status(user_id, exam_id, 5) Exams.set_submit_time(user_id, exam_id) Exams.save_score(exam_id, user_id, examtotal) Exams.touchuserexam(exam_id, user_id) if errors: return False L.info("user %s scored %s total on exam %s" % (user_id, examtotal, exam_id)) return True
def cadmin_exam_viewmarked(course_id, exam_id, student_uid): """ Show a student's marked assessment results """ course = Courses2.get_course(course_id) try: exam = Exams.get_exam_struct(exam_id, course_id) except KeyError: exam = {} abort(404) results, examtotal = Assess.render_own_marked_exam(student_uid, exam_id) if examtotal is False: status = 0 else: status = 1 marktime = Exams.get_mark_time(exam_id, student_uid) firstview = Exams.get_student_start_time(exam_id, student_uid) submittime = Exams.get_submit_time(exam_id, student_uid) try: datemarked = General.human_date(marktime) except AttributeError: datemarked = None try: datefirstview = General.human_date(firstview) except AttributeError: datefirstview = None try: datesubmit = General.human_date(submittime) except AttributeError: datesubmit = None user = Users2.get_user(student_uid) if submittime and firstview: taken = submittime-firstview takenmins = (taken.seconds/60) else: takenmins = None return render_template( "cadmin_markedresult.html", course=course, exam=exam, results=results, examtotal=examtotal, datesubmit=datesubmit, datemarked=datemarked, datefirstview=datefirstview, taken=takenmins, user=user, status=status )
def cadmin_exam_viewmarked(course_id, exam_id, student_uid): """ Show a student's marked assessment results """ course = Courses2.get_course(course_id) try: exam = Exams.get_exam_struct(exam_id, course_id) except KeyError: exam = {} abort(404) results, examtotal = Assess.render_own_marked_exam(student_uid, exam_id) if examtotal is False: status = 0 else: status = 1 marktime = Exams.get_mark_time(exam_id, student_uid) firstview = Exams.get_student_start_time(exam_id, student_uid) submittime = Exams.get_submit_time(exam_id, student_uid) try: datemarked = General.human_date(marktime) except AttributeError: datemarked = None try: datefirstview = General.human_date(firstview) except AttributeError: datefirstview = None try: datesubmit = General.human_date(submittime) except AttributeError: datesubmit = None user = Users2.get_user(student_uid) if submittime and firstview: taken = submittime - firstview takenmins = (taken.seconds / 60) else: takenmins = None return render_template("cadmin_markedresult.html", course=course, exam=exam, results=results, examtotal=examtotal, datesubmit=datesubmit, datemarked=datemarked, datefirstview=datefirstview, taken=takenmins, user=user, status=status)
def cadmin_exam_results(course_id, exam_id): """ View the results of an assessment """ course = Courses2.get_course(course_id) if not course: abort(404) exam = Exams.get_exam_struct(exam_id, course_id) if not exam: abort(404) if not int(exam['cid']) == int(course_id): flash("Assessment %s does not belong to this course." % int(exam_id)) return redirect(url_for('cadmin_top', course_id=course_id)) exam['start_date'] = int(date_from_py2js(exam['start'])) exam['end_date'] = int(date_from_py2js(exam['end'])) exam['start_hour'] = int(exam['start'].hour) exam['end_hour'] = int(exam['end'].hour) exam['start_minute'] = int(exam['start'].minute) exam['end_minute'] = int(exam['end'].minute) groups = [Groups.Group(g_id=g_id) for g_id in Groups.active_by_course(course_id)] results = {} uids = set([]) totals = {} for group in groups: results[group.id] = Exams.get_marks(group, exam_id) for user_id in results[group.id]: uids.add(user_id) if user_id not in totals: totals[user_id] = 0.0 for qt, val in results[group.id][user_id].iteritems(): totals[user_id] += val['score'] questions = Exams.get_qts_list(exam_id) users = {} for uid in uids: users[uid] = Users2.get_user(uid) return render_template( "cadmin_examresults.html", course=course, exam=exam, results=results, groups=groups, users=users, questions=questions, when=datetime.now().strftime("%H:%m, %a %d %b %Y"), totals=totals )
def cadmin_exam_unsubmit(course_id, exam_id, student_uid): """ "unsubmit" the student's assessment and reset their timer so they can log back on and have another attempt. """ course = Courses2.get_course(course_id) try: exam = Exams.get_exam_struct(exam_id, course.id) except KeyError: exam = {} abort(404) Exams.unsubmit(exam_id, student_uid) user = Users2.get_user(student_uid) flash("""Assessment for %s unsubmitted and timer reset.""" % user["uname"]) return redirect(url_for("cadmin_exam_viewmarked", course_id=course.id, exam_id=exam["id"], student_uid=student_uid))
def cadmin_exam_results(course_id, exam_id): """ View the results of an assessment """ course = Courses2.get_course(course_id) if not course: abort(404) exam = Exams.get_exam_struct(exam_id, course_id) if not exam: abort(404) if not int(exam['cid']) == int(course_id): flash("Assessment %s does not belong to this course." % int(exam_id)) return redirect(url_for('cadmin_top', course_id=course_id)) exam['start_date'] = int(date_from_py2js(exam['start'])) exam['end_date'] = int(date_from_py2js(exam['end'])) exam['start_hour'] = int(exam['start'].hour) exam['end_hour'] = int(exam['end'].hour) exam['start_minute'] = int(exam['start'].minute) exam['end_minute'] = int(exam['end'].minute) groups = [ Groups.Group(g_id=g_id) for g_id in Groups.active_by_course(course_id) ] results = {} uids = set([]) totals = {} for group in groups: results[group.id] = Exams.get_marks(group, exam_id) for user_id in results[group.id]: uids.add(user_id) if user_id not in totals: totals[user_id] = 0.0 for qt, val in results[group.id][user_id].iteritems(): totals[user_id] += val['score'] questions = Exams.get_qts_list(exam_id) users = {} for uid in uids: users[uid] = Users2.get_user(uid) return render_template("cadmin_examresults.html", course=course, exam=exam, results=results, groups=groups, users=users, questions=questions, when=datetime.now().strftime("%H:%m, %a %d %b %Y"), totals=totals)
def cadmin_top(course_id): """ Present top level course admin page """ course = Courses2.get_course(course_id) if not course: abort(404) user_id = session['user_id'] is_sysadmin = check_perm(user_id, -1, 'sysadmin') topics = Courses2.get_topics_list(course_id) exams = [Exams.get_exam_struct(exam_id, course_id) for exam_id in Courses.get_exams(course_id, prev_years=False)] exams.sort(key=lambda y: y['start_epoch'], reverse=True) groups = Courses.get_groups(course_id) choosegroups = [group for group in Groups.all_groups() if group.id not in groups] return render_template( "courseadmin_top.html", course=course, topics=topics, exams=exams, choosegroups=choosegroups, groups=groups, is_sysadmin=is_sysadmin )
def test_assess_create(self): """ Create an empty assessment""" course_id = Courses.create("TESTCOURSE5", "unit tests for assessment", 1, 1) Courses.create_config(course_id, "casual", 1) Courses.set_active(course_id, True) Courses.set_prac_vis(course_id, "none") Courses.set_assess_vis(course_id, "none") title = "Test Assessment 1" atype = 2 # assignment duration = 60 code = "123456" instant = 1 instructions = "These are the instructions" astart = datetime.datetime.utcnow() aend = astart + datetime.timedelta(hours=2) exam_id = Exams.create(course_id, 1, title, atype, duration, astart, aend, instructions, code=code, instant=instant) self.assertGreater(exam_id, 0) topic1_id = Topics.create(course_id, "TESTASSESS1", 1, 1) self.assertGreater(topic1_id, 0) data = open(self.test_question_fname).read() numread = External.import_qts_from_zip(data, topic1_id) self.assertEqual(numread, 3)
def cadmin_edit_exam(course_id, exam_id): """ Provide a form to edit an assessment """ course = Courses2.get_course(course_id) if not course: abort(404) exam = Exams.get_exam_struct(exam_id, course_id) if not exam: abort(404) if not int(exam['cid']) == int(course_id): flash("Assessment %s does not belong to this course." % int(exam_id)) return redirect(url_for('cadmin_top', course_id=course_id)) exam['start_date'] = int(date_from_py2js(exam['start'])) exam['end_date'] = int(date_from_py2js(exam['end'])) exam['start_hour'] = int(exam['start'].hour) exam['end_hour'] = int(exam['end'].hour) exam['start_minute'] = int(exam['start'].minute) exam['end_minute'] = int(exam['end'].minute) return render_template( "exam_edit.html", course=course, exam=exam )
def cadmin_top(course_id): """ Present top level course admin page """ course = Courses2.get_course(course_id) if not course: abort(404) user_id = session['user_id'] is_sysadmin = check_perm(user_id, -1, 'sysadmin') topics = Courses2.get_topics_list(course_id) exams = [ Exams.get_exam_struct(exam_id, course_id) for exam_id in Courses.get_exams(course_id, prev_years=False) ] exams.sort(key=lambda y: y['start_epoch'], reverse=True) groups = Courses.get_groups(course_id) choosegroups = [ group for group in Groups.all_groups() if group.id not in groups ] return render_template("courseadmin_top.html", course=course, topics=topics, exams=exams, choosegroups=choosegroups, groups=groups, is_sysadmin=is_sysadmin)
def cadmin_export_csv(course_id, exam_id, group_id): """ Send the group results as a CSV file """ course = Courses2.get_course(course_id) if not course: abort(404) exam = Exams.get_exam_struct(exam_id, course_id) if not exam: abort(404) if not int(exam['cid']) == int(course_id): flash("Assessment %s does not belong to this course." % int(exam_id)) return redirect(url_for('cadmin_top', course_id=course_id)) group = Groups.Group(g_id=group_id) output = Spreadsheets.exam_results_as_spreadsheet(course_id, group, exam_id) response = make_response(output) response.headers.add( 'Content-Type', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8" ) response.headers.add( 'Content-Disposition', 'attachment; filename="OASIS_%s_%s_Results.xlsx"' % (course.title, exam.title)) return response
def cadmin_export_csv(course_id, exam_id, group_id): """ Send the group results as a CSV file """ course = Courses2.get_course(course_id) if not course: abort(404) exam = Exams.get_exam_struct(exam_id, course_id) if not exam: abort(404) if not int(exam["cid"]) == int(course_id): flash("Assessment %s does not belong to this course." % int(exam_id)) return redirect(url_for("cadmin_top", course_id=course_id)) group = Groups.Group(g_id=group_id) output = Spreadsheets.exam_results_as_spreadsheet(course_id, group, exam_id) response = make_response(output) response.headers.add( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8" ) response.headers.add( "Content-Disposition", 'attachment; filename="OASIS_%s_%s_Results.xlsx"' % (course["title"], exam["title"]) ) return response
def cadmin_exam_unsubmit(course_id, exam_id, student_uid): """ "unsubmit" the student's assessment and reset their timer so they can log back on and have another attempt. """ try: exam = Exams.get_exam_struct(exam_id, course_id) except KeyError: exam = {} abort(404) Exams.unsubmit(exam_id, student_uid) user = Users2.get_user(student_uid) flash("""Assessment for %s unsubmitted and timer reset.""" % user['uname']) return redirect( url_for("cadmin_exam_viewmarked", course_id=course_id, exam_id=exam['id'], student_uid=student_uid))
def get_exam_list_sorted(user_id, prev_years=False): """ Return a list of exams for the given user. """ courses = Courses.get_all() exams = [] for cid in courses: try: exams += [Exams.get_exam_struct(e, user_id) for e in Courses.get_exams(cid, prev_years=prev_years)] except KeyError, err: L.error("Failed fetching exam list for user %s: %s" % (user_id, err))
def get_exam_list_sorted(user_id, prev_years=False): """ Return a list of exams for the given user. """ courses = Courses.get_all() exams = [] for cid in courses: try: exams += [ Exams.get_exam_struct(e, user_id) for e in Courses.get_exams(cid, prev_years=prev_years) ] except KeyError, err: L.error("Failed fetching exam list for user %s: %s" % (user_id, err))
def api_exam_qtemplates(course_id, exam_id): """ Return a JSON list of all the qtemplates used for the given exam. """ user_id = session['user_id'] if not satisfy_perms(user_id, course_id, ("examcreate",)): abort(401) if exam_id == 0: # New assessment may be being created return jsonify(result=[[{'qtid': 0}, ], ]) exam = Exams.get_exam_struct(exam_id) ecid = exam['cid'] if not ecid == course_id: # They may be trying to bypass permission check abort(401) qtemplates = [] try: qtemplates = Exams.get_qts_list(exam_id) except KeyError: abort(401) return jsonify(result=qtemplates)
def setup_usersummary(view_id): """ Show an account summary for the given user account. """ user_id = session['user_id'] if not check_perm(user_id, -1, "useradmin"): flash("You do not have User Administration access.") return redirect(url_for('setup_top')) is_sysadmin = check_perm(user_id, -1, 'sysadmin') user = Users2.get_user(view_id) examids = Exams.get_exams_done(view_id) exams = [] for examid in examids: exam = Exams.get_exam_struct(examid) started = General.human_date(exam['start']) exam['started'] = started exam['viewable'] = satisfy_perms(user_id, exam['cid'], ("viewmarks", )) exams.append(exam) exams.sort(key=lambda x: x['start_epoch'], reverse=True) course_ids = Users2.get_courses(view_id) courses = [] for course_id in course_ids: courses.append(Courses.get_course(course_id)) user_is_admin = check_perm(view_id, 0, 'sysadmin') return render_template( 'setup_usersummary.html', user=user, exams=exams, courses=courses, is_sysadmin=is_sysadmin, user_is_admin=user_is_admin )
def cadmin_prev_assessments(course_id): """ Show a list of older assessments.""" course = Courses2.get_course(course_id) if not course: abort(404) exams = [ Exams.get_exam_struct(exam_id, course_id) for exam_id in DB.get_course_exam_all(course_id, prev_years=True) ] years = [exam["start"].year for exam in exams] years = list(set(years)) years.sort(reverse=True) exams.sort(key=lambda y: y["start_epoch"]) return render_template("courseadmin_previousassessments.html", course=course, exams=exams, years=years)
def cadmin_edit_exam_submit(course_id, exam_id): """ Provide a form to edit an assessment """ user_id = session["user_id"] course = Courses2.get_course(course_id) if not course: abort(404) if "exam_cancel" in request.form: flash("Assessment editing cancelled.") return redirect(url_for("cadmin_top", course_id=course_id)) exam_id = CourseAdmin.exam_edit_submit(request, user_id, course_id, exam_id) exam = Exams.get_exam_struct(exam_id, course_id) flash("Assessment saved.") return render_template("exam_edit_submit.html", course=course, exam=exam)
def cadmin_edit_exam_submit(course_id, exam_id): """ Provide a form to edit an assessment """ user_id = session['user_id'] course = Courses2.get_course(course_id) if not course: abort(404) if "exam_cancel" in request.form: flash("Assessment editing cancelled.") return redirect(url_for('cadmin_top', course_id=course_id)) exam_id = CourseAdmin.exam_edit_submit(request, user_id, course_id, exam_id) exam = Exams.get_exam_struct(exam_id, course_id) flash("Assessment saved.") return render_template("exam_edit_submit.html", course=course, exam=exam)
def cadmin_prev_assessments(course_id): """ Show a list of older assessments.""" course = Courses2.get_course(course_id) if not course: abort(404) exams = [ Exams.get_exam_struct(exam_id, course_id) for exam_id in DB.get_course_exam_all(course_id, prev_years=True) ] years = [exam['start'].year for exam in exams] years = list(set(years)) years.sort(reverse=True) exams.sort(key=lambda y: y['start_epoch']) return render_template("courseadmin_previousassessments.html", course=course, exams=exams, years=years)
def cadmin_edit_exam(course_id, exam_id): """ Provide a form to edit an assessment """ course = Courses2.get_course(course_id) if not course: abort(404) exam = Exams.get_exam_struct(exam_id, course_id) if not exam: abort(404) if not int(exam['cid']) == int(course_id): flash("Assessment %s does not belong to this course." % int(exam_id)) return redirect(url_for('cadmin_top', course_id=course_id)) exam['start_date'] = int(date_from_py2js(exam['start'])) exam['end_date'] = int(date_from_py2js(exam['end'])) exam['start_hour'] = int(exam['start'].hour) exam['end_hour'] = int(exam['end'].hour) exam['start_minute'] = int(exam['start'].minute) exam['end_minute'] = int(exam['end'].minute) return render_template("exam_edit.html", course=course, exam=exam)
def student_exam_duration(student, exam_id): """ How long did the assessment take. returns starttime, endtime either could be None if it hasn't been started/finished """ firstview = None examsubmit = Exams.get_submit_time(exam_id, student) questions = General.get_exam_qs(student, exam_id) # we're working out the first time the assessment was viewed is the # earliest time a question in it was viewed # It's possible (although unlikely) that they viewed a question # other than the first page, first. for question in questions: questionview = DB.get_q_viewtime(question) if firstview: if questionview < firstview: firstview = questionview else: firstview = questionview return firstview, examsubmit
def exam_edit_submit(request, user_id, cid, exam_id): """ Accept the submitted exam edit/create form. If exam_id is not provided, create a new one. """ # TODO: More validation. Currently we trust the client validation, # although the user is authenticated staff so probably not too high a # risk of shenanigans. title = str(request.form['assess_title']) atype = int(request.form['assess_type']) startdate = request.form['startdate'] starthour = int(request.form['examstart_hour']) startmin = int(request.form['examstart_minute']) enddate = request.form['enddate'] endhour = int(request.form['examend_hour']) endmin = int(request.form['examend_minute']) duration = int(request.form['duration']) code = request.form['assess_code'] instant = int(request.form['assess_instant']) if "instructions" in request.form: instructions = request.form['instructions'] else: instructions = "" astart = datetime.datetime.strptime(startdate, "%a %d %b %Y") astart = astart.replace(hour=starthour, minute=startmin) aend = datetime.datetime.strptime(enddate, "%a %d %b %Y") aend = aend.replace(hour=endhour, minute=endmin) qns = {} for k in request.form.keys(): v = request.form.getlist(k) if k.startswith("question_"): _, q, p = k.split("_") if not q in qns: qns[q] = [] if not v[0] == '---': qns[q].append(int(v[0])) if not exam_id: exam_id = Exams.create(cid, user_id, title, atype, duration, astart, aend, instructions, code=code, instant=instant) else: # update Exams.set_title(exam_id, title) Exams.set_duration(exam_id, duration) Exams.set_type(exam_id, atype) Exams.set_description(exam_id, instructions) Exams.set_start_time(exam_id, astart) Exams.set_end_time(exam_id, aend) Exams.set_code(exam_id, code) Exams.set_instant(exam_id, instant) for pos, qts in qns.iteritems(): if pos: DB.update_exam_qt_in_pos(exam_id, int(pos), qts) return exam_id
def exam_edit_submit(request, user_id, cid, exam_id): """ Accept the submitted exam edit/create form. If exam_id is not provided, create a new one. """ # TODO: More validation. Currently we trust the client validation, # although the user is authenticated staff so probably not too high a # risk of shenanigans. title = str(request.form['assess_title']) atype = int(request.form['assess_type']) startdate = request.form['startdate'] starthour = int(request.form['examstart_hour']) startmin = int(request.form['examstart_minute']) enddate = request.form['enddate'] endhour = int(request.form['examend_hour']) endmin = int(request.form['examend_minute']) duration = int(request.form['duration']) code = request.form['assess_code'] instant = int(request.form['assess_instant']) if "instructions" in request.form: instructions = request.form['instructions'] else: instructions = "" astart = datetime.datetime.strptime(startdate, "%a %d %b %Y") astart = astart.replace(hour=starthour, minute=startmin) aend = datetime.datetime.strptime(enddate, "%a %d %b %Y") aend = aend.replace(hour=endhour, minute=endmin) qns = {} for k in request.form.keys(): v = request.form.getlist(k) if k.startswith("question_"): _, q, p = k.split("_") if q not in qns: qns[q] = [] if not v[0] == '---': qns[q].append(int(v[0])) if not exam_id: exam_id = Exams.create(cid, user_id, title, atype, duration, astart, aend, instructions, code=code, instant=instant) else: # update Exams.set_title(exam_id, title) Exams.set_duration(exam_id, duration) Exams.set_type(exam_id, atype) Exams.set_description(exam_id, instructions) Exams.set_start_time(exam_id, astart) Exams.set_end_time(exam_id, aend) Exams.set_code(exam_id, code) Exams.set_instant(exam_id, instant) for pos, qts in qns.iteritems(): if pos: DB.update_exam_qt_in_pos(exam_id, int(pos), qts) return exam_id
def exam_results_as_spreadsheet(course_id, group, exam_id): """ Export the assessment results as a XLSX spreadsheet """ course = Courses2.get_course(course_id) exam = Exams.get_exam_struct(exam_id, course_id) uids = set([]) totals = {} results = Exams.get_marks(group, exam_id) for user_id in results: uids.add(user_id) if user_id not in totals: totals[user_id] = 0.0 for qt, val in results[user_id].iteritems(): totals[user_id] += val['score'] questions = Exams.get_qts_list(exam_id) users = {} for uid in uids: users[uid] = Users2.get_user(uid) wb = Workbook() ws = wb.get_active_sheet() ws.title = "Results" ws.cell(row=1, column=0).value = course['name'] ws.cell(row=1, column=1).value = course['title'] ws.cell(row=2, column=0).value = "Assessment:" ws.cell(row=2, column=1).value = exam['title'] ws.cell(row=3, column=0).value = "Group:" ws.cell(row=3, column=1).value = group.name col = 5 qcount = 1 for _ in questions: ws.cell(row=4, column=col).value = "Q%s" % qcount qcount += 1 col += 1 ws.cell(row=4, column=col).value = "Total" row = 5 sortusers = users.keys() sortusers.sort(key=lambda us: users[us]['familyname']) for user_id in sortusers: result = results[user_id] ws.cell(row=row, column=0).value = users[user_id]['uname'] ws.cell(row=row, column=1).value = users[user_id]['student_id'] ws.cell(row=row, column=2).value = users[user_id]['familyname'] ws.cell(row=row, column=3).value = users[user_id]['givenname'] ws.cell(row=row, column=4).value = users[user_id]['email'] col = 5 for pos in questions: for qt in pos: if qt['id'] in result: ws.cell(row=row, column=col).value = result[qt['id']]['score'] col += 1 ws.cell(row=row, column=col).value = totals[user_id] row += 1 return save_virtual_workbook(wb)