def test_instance_generate_multif_answer(): """ Convert some html templates + variables into resulting instance HTML and make sure it's doing it right. Focus on multif choice "ANSWER" No side effects. """ tmpl = "blah<ANSWER1 MULTIF f,g,h,i>blah" qvars = {'f': 7, 'g': "joe", "h": "3.4", "i": 33} html = """blah<table border=0><tr><td>Please choose one:</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='1' Oa_CHK_1_1>7</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='2' Oa_CHK_1_2>joe</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='3' Oa_CHK_1_3>3.4</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='4' Oa_CHK_1_4>33</td></tr></table><br /> blah""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "blah<ANSWER1 MULTIF f,g,h,i,j>blah" qvars = {'f': 7, 'g': "joe", "h": "3.4", "i": 33, "j": "&"} html = """blah<table border=0><tr><td>Please choose one:</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='1' Oa_CHK_1_1>7</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='2' Oa_CHK_1_2>joe</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='3' Oa_CHK_1_3>3.4</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='4' Oa_CHK_1_4>33</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='5' Oa_CHK_1_5>&</td></tr></table><br /> blah""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "blah<ANSWER1 MULTIF f,g,h," qvars = {'f': 7, 'g': "joe", "h": "3.4", "i": 33, "j": "&"} html = """blah<ANSWER1 MULTIF f,g,h,""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "blah<ANSWER1 MULTIF f,g,h,i,j>" qvars = {'f': 7, 'g': "joe"} html = """blah<table border=0><tr><td>Please choose one:</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='1' Oa_CHK_1_1>7</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='2' Oa_CHK_1_2>joe</td><FONT COLOR="red">ERROR IN QUESTION DATA</FONT><FONT COLOR="red">ERROR IN QUESTION DATA</FONT><FONT COLOR="red">ERROR IN QUESTION DATA</FONT></tr></table><br />\n""" res = General.gen_q_html(qvars, tmpl) assert res == html
def mark_q(user_id, topic_id, q_id, request): """Mark the question and return the results""" answers = {} for i in request.form.keys(): part = re.search(r"^Q_(\d+)_ANS_(\d+)$", i) if part: newqid = int(part.groups()[0]) part = int(part.groups()[1]) if newqid == q_id: value = request.form[i] answers["G%d" % part] = value DB.save_guess(newqid, part, value) else: L.warn("received guess for wrong question? (%d,%d,%d,%s)" % (user_id, topic_id, q_id, request.form)) 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 - (%d, %d, %d, %s)" % (user_id, topic_id, q_id, request.form)) marks = {} q_body = General.render_mark_results(q_id, marks) parts = [int(var[1:]) for var in marks.keys() if re.search(r"^A([0-9]+)$", var) > 0] parts.sort() total = 0.0 for part in parts: if 'M%d' % (part,) in marks: total += float(marks['M%d' % (part,)]) DB.update_q_score(q_id, total) # 3 = marked DB.set_q_status(q_id, 2) return q_body
def test_html_esc(): """ Check that our HTML escaping works ok. ( & -> & etc) """ assert "&" == General.htmlesc("&") assert "<" == General.htmlesc("<") assert ">" == General.htmlesc(">")
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 test_date_ops(): """ Test our various date operations """ a = datetime.date(2013, 12, 1) b = datetime.date(2013, 12, 2) c = datetime.date(2013, 11, 1) d = datetime.date(2012, 11, 1) assert General.is_between(a, c, b) is True assert General.is_between(a, b, c) is True assert General.is_between(a, c, d) is False
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 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 test_do_question(self): """ Do a question""" course_id = Courses.create("TEST102", "Test question logic", 1, 1) self.assertGreater(course_id, 0) topic1_id = Topics.create(course_id, "TESTQUESTIONS1", 1, 2) self.assertGreater(topic1_id, 0) qt1_id = DB.create_qt(1, "TESTQ9", "Test question 9", 0, 5.0, 1, topic_id=topic1_id) self.assertIsNotNone(qt1_id) ver = DB.get_qt_version(qt1_id) self.assertGreater(ver, 0) data = "2\n|1\n|2\n" qvars = [{'A1': "2"}, {'A1': "3"}] for row in range(0, len(qvars)): DB.add_qt_variation(qt1_id, row + 1, qvars[row], ver) DB.create_qt_att(qt1_id, "datfile.dat", "text/plain", data , ver) DB.create_qt_att(qt1_id, "qtemplate.html", "text/html", "What is <VAL A1>? <ANSWER 1>", ver) q_id = DB.get_q_by_qt_student(qt1_id, 1) self.assertFalse(q_id) # Not generated yet q_id = General.gen_q(qt1_id, 1) self.assertGreater(q_id, 0) q_id = DB.get_q_by_qt_student(qt1_id, 1) self.assertTrue(qt1_id) # Better be there now DB.update_qt_maxscore(qt1_id, 7.0) score = DB.get_qt_maxscore(qt1_id) self.assertEqual(score, 7.0) DB.set_q_viewtime(q_id) self.assertIsNotNone(DB.get_q_viewtime(q_id))
def get_q_list(topic): """ Return a list of questions, sorted by position. """ # TODO: Duplicated in General.get_q_list ? def cmp_question_position(a, b): """Order questions by the absolute value of their positions since we use -'ve to indicate hidden. """ return cmp(abs(a['position']), abs(b['position'])) questionlist = General.get_q_list(topic, None, False) if questionlist: # At the moment we use -'ve positions to indicate that a question is # hidden but when displaying them we want to maintain the sort order. for question in questionlist: # Usually questions with position 0 are broken or uninteresting # so put them at the bottom. if question['position'] == 0: question['position'] = -10000 questionlist.sort(cmp_question_position) else: questionlist = [] return questionlist
def get_q_list(topic_id): """ Return a list of questions, sorted by position. """ # TODO: Duplicated in General.get_q_list ? def cmp_question_position(a, b): """Order questions by the absolute value of their positions since we use -'ve to indicate hidden. """ return cmp(abs(a['position']), abs(b['position'])) questionlist = General.get_q_list(topic_id, None, False) if questionlist: # At the moment we use -'ve positions to indicate that a question is # hidden but when displaying them we want to maintain the sort order. for question in questionlist: # Usually questions with position 0 are broken or uninteresting # so put them at the bottom. if question['position'] == 0: question['position'] = -10000 questionlist.sort(cmp_question_position) else: questionlist = [] return questionlist
def get_sorted_questions(course_id, topic_id, user_id=None): """ Return a list of questions, sorted by position """ def cmp_question_position(a, b): """Order questions by the absolute value of their positions since we use -'ve to indicate hidden. """ return cmp(abs(a['position']), abs(b['position'])) questionlist = General.get_q_list(topic_id, user_id, numdone=False) if questionlist: # Filter out the questions without a positive position unless # the user has prevew permission. canpreview = check_perm(user_id, course_id, "questionpreview") if not canpreview: questionlist = [question for question in questionlist if question['position'] > 0] else: # At the moment we use -'ve positions to indicate that a question # is hidden but when displaying them we want to maintain the sort # order. for question in questionlist: # Usually questions with position 0 are broken or # uninteresting so put them at the bottom. if question['position'] == 0: question['position'] = -10000 questionlist.sort(cmp_question_position) else: questionlist = [] return questionlist
def get_next_prev(qt_id, topic_id): """ Find the "next" and "previous" qtemplates, by topic, position. """ if not topic_id: return None, None # This is very inefficient, but with the way questions are stored, # I didn't see a better way. Could maybe be revisited some time? questionlist = General.get_q_list(topic_id, numdone=False) if questionlist: # Filter out the questions without a positive position questionlist = [question for question in questionlist if question['position'] > 0] else: questionlist = [] # We need to step through the list finding the "next and previous" id's nextid = None foundprev = None previd = None foundcurrent = None for i in questionlist: if foundcurrent: nextid = int(i['qtid']) break if int(i['qtid']) == int(qt_id): foundprev = True foundcurrent = True if not foundprev: previd = int(i['qtid']) # previd and nextid should now contain the correct values # or None, if they are not valid (current_qtid is the first or # last question) return previd, nextid
def mark_q(user_id, qtid, request): """Mark the question and show a page containing marking results.""" if "OaQID" in request.form: qid = int(request.form["OaQID"]) else: qid = None out = u"" answers = {} for i in request.form.keys(): part = re.search(r"^Q_(\d+)_ANS_(\d+)$", i) if part: newqid = int(part.groups()[0]) part = int(part.groups()[1]) value = request.form[i] answers["G%d" % part] = value DB.save_guess(newqid, part, value) if qid: try: marks = General.mark_q(qid, answers) DB.set_q_status(qid, 3) # 3 = marked DB.set_q_marktime(qid) except OaMarkerError: log(INFO, "getMarkQuestionPage(%d, %d, %s) Marker ERROR" % (user_id, qtid, request.form)) marks = {} out += General.render_mark_results(qid, marks) parts = [int(var[1:]) for var in marks.keys() if re.search("^A([0-9]+)$", var) > 0] parts.sort() total = 0.0 for part in parts: if marks.has_key('M%d' % (part,)): total += float(marks['M%d' % (part,)]) return out
def mark_q(user_id, qtid, request): """Mark the question and show a page containing marking results.""" if "OaQID" in request.form: qid = int(request.form["OaQID"]) else: qid = None out = u"" answers = {} for i in request.form.keys(): part = re.search(r"^Q_(\d+)_ANS_(\d+)$", i) if part: newqid = int(part.groups()[0]) part = int(part.groups()[1]) value = request.form[i] answers["G%d" % part] = value DB.save_guess(newqid, part, value) if qid: try: marks = General.mark_q(qid, answers) DB.set_q_status(qid, 3) # 3 = marked DB.set_q_marktime(qid) except OaMarkerError: L.info("getMarkQuestionPage(%d, %d, %s) Marker ERROR" % (user_id, qtid, request.form)) marks = {} out += General.render_mark_results(qid, marks) parts = [ int(var[1:]) for var in marks.keys() if re.search("^A([0-9]+)$", var) > 0 ] parts.sort() total = 0.0 for part in parts: if ('M%d' % part) in marks: total += float(marks['M%d' % (part, )]) return out
def test_instance_generate_multi_answer(): """ Convert some html templates + variables into resulting instance HTML and make sure it's doing it right. Focus on multi "ANSWER MULTI" No side effects. """ tmpl = "blah<ANSWER1 MULTI f,g,h,i>blah" qvars = {'f': 7, 'g': "joe", "h": "3.4", "i": 33} html = """<table border=0><tr><th>Please choose one:</th><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='1' Oa_CHK_1_1> 7</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='2' Oa_CHK_1_2> joe</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='3' Oa_CHK_1_3> 3.4</td><td CLASS='multichoicecell'><INPUT class='auto_save' TYPE='radio' NAME='ANS_1' VALUE='4' Oa_CHK_1_4> 33</td></tr></table><br />\n""" res = General.handle_multi(tmpl, 1, qvars, shuffle=False)[1] assert res == html
def test_instance_generate_listbox_answer(): """ Convert some html templates + variables into resulting instance HTML and make sure it's doing it right. Focus on listbox "ANSWER SELECT" No side effects. """ tmpl = "blah<ANSWER1 SELECT f,g,h,i>blah" qvars = {'f': 7, 'g': "joe", "h": "3.4", "i": 33} html = """<SELECT class='auto_save' NAME='ANS_1'>Please choose:<OPTION VALUE='None'>--Choose--</OPTION><OPTION VALUE='1' Oa_SEL_1_1>7</OPTION><OPTION VALUE='2' Oa_SEL_1_2>joe</OPTION><OPTION VALUE='3' Oa_SEL_1_3>3.4</OPTION><OPTION VALUE='4' Oa_SEL_1_4>33</OPTION></SELECT>\n""" res = General.handle_listbox(tmpl, 1, qvars, shuffle=False)[1] assert res == html
def setup_useraudit(audit_id): """ Show all the audit entries 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')) user = Users2.get_user(audit_id) audits = get_records_by_user(audit_id) for aud in audits: aud['humantime'] = General.human_date(aud['time']) return render_template('setup_useraudit.html', user=user, audits=audits)
def test_instance_generate_variable(): """ Convert some html templates + variables into resulting instance HTML and make sure it's doing it right. Focus on variable subs. No side effects. """ tmpl = "The value is <VAL A>" qvars = {"A": 7, "a": 5, "Arthur": 3} html = """The value is 7""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "The value is <VAL A> <VAL Arthur>" qvars = {"A": 7, "a": 5, "Arthur": 3} html = """The value is 7 3""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "The value is <VAL A> <VAL Arthur>" qvars = {"A": "&", "a": 5, "Arthur": 3} html = """The value is & 3""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "The value is <VAL A> <VAL Arthur>" qvars = {"A": "<blink>annoying</blink>", "a": 5, "Arthur": 3} html = """The value is <blink>annoying</blink> 3""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "The value is <VAL A> <VAL Arthur>" qvars = {"A": u"\x9f", "a": 5, "Arthur": 3} html = u"""The value is \x9f 3""" res = General.gen_q_html(qvars, tmpl) assert res == html
def test_instance_generate_simple_answer(): """ Convert some html templates + variables into resulting instance HTML and make sure it's doing it right. Focus on simple "ANSWER" No side effects. """ tmpl = "blah<ANSWER1>blah" qvars = {} html = """blah<INPUT class='auto_save' TYPE='text' NAME='ANS_1' VALUE="VAL_1"/>blah""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "blah<ANSWER2>blah" qvars = {} html = """blah<INPUT class='auto_save' TYPE='text' NAME='ANS_2' VALUE="VAL_2"/>blah""" res = General.gen_q_html(qvars, tmpl) assert res == html tmpl = "foo<ANSWER1>blah<ANSWER2>blah" qvars = {} html = """foo<INPUT class='auto_save' TYPE='text' NAME='ANS_1' VALUE="VAL_1"/>blah<INPUT class='auto_save' TYPE='text' NAME='ANS_2' VALUE="VAL_2"/>blah""" res = General.gen_q_html(qvars, tmpl) assert res == html
def setup_useraudit(audit_id): """ Show all the audit entries 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')) user = Users2.get_user(audit_id) audits = get_records_by_user(audit_id) for aud in audits: aud['humantime'] = General.human_date(aud['time']) return render_template( 'setup_useraudit.html', user=user, audits=audits )
def get_practice_q(qt_id, user_id): """ Find an existing, or create a new, practice question for the given user.""" try: qt_id = int(qt_id) assert qt_id > 0 except (ValueError, TypeError, AssertionError): L.warn("Called with bad qtid %s?" % qt_id) qid = DB.get_q_by_qt_student(qt_id, user_id) if qid is not False: return int(qid) qid = General.gen_q(qt_id, user_id) try: qid = int(qid) except (ValueError, TypeError): L.warn("generateQuestion(%s,%s) Fail: returned %s" % (qt_id, user_id, qid)) else: DB.set_q_viewtime(qid) return qid
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 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 practice_do_question(topic_id, position): """ Show them a question and allow them to fill in some answers """ user_id = session['user_id'] try: course_id = Topics.get_course_id(topic_id) except KeyError: course_id = None abort(404) try: course = Courses2.get_course(course_id) except KeyError: course = None abort(404) topictitle = "UNKNOWN" try: topictitle = Topics.get_name(topic_id) except KeyError: abort(404) try: choices = DB.get_qtemplates_in_topic_position(topic_id, position) except KeyError: choices = None abort(404) if len(choices) == 1: qt_id = choices[0] elif len(choices) > 1: L.debug("Practice choosing random from: %s" % repr(choices)) qt_id = random.choice(choices) else: L.warn("Access to non existent practice topic %s question %s" % (topic_id, position)) return render_template( "practicequestionerror.html", mesg="Error accessing question.", topic_id=topic_id, course=course, q_pos=position ) qtemplate = DB.get_qtemplate(qt_id) questions = Practice.get_sorted_questions(course_id, topic_id, user_id) q_title = qtemplate['title'] q_pos = DB.get_qtemplate_topic_pos(qt_id, topic_id) blocked = Practice.is_q_blocked(user_id, course_id, topic_id, qt_id) if blocked: return render_template( "practicequestionblocked.html", mesg=blocked, topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, ) try: q_id = Practice.get_practice_q(qt_id, user_id) except (ValueError, TypeError) as err: L.error("ERROR 1001 (%s,%s) %s" % (qt_id, user_id, err)) return render_template( "practicequestionerror.html", mesg="Error generating question.", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, ) if not q_id > 0: L.error("ERROR 1002 (%s,%s) Question not generated" % (qt_id, user_id)) return render_template( "practicequestionerror.html", mesg="Error generating question.", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, ) q_body = General.render_q_html(q_id) q_body = q_body.replace(r"\240", u" ") # TODO: why is this here? return render_template( "practicedoquestion.html", q_body=q_body, topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, q_id=q_id, )
) if not q_id > 0: return render_template( "practicequestionerror.html", mesg="Error generating question.", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos="?", ) q_body = General.render_q_html(q_id) q_body = q_body.replace(r"\240", u" ") # TODO: why is this here? return render_template( "practicedoquestion.html", q_body=q_body, topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, q_id=q_id, )
def get_sorted_qlist_wstats(course_id, topic_id, user_id=None): """ Return a list of questions, sorted by position. With some statistics (may be expensive to calculate). """ def cmp_question_position(a, b): """Order questions by the absolute value of their positions since we use -'ve to indicate hidden. """ return cmp(abs(a['position']), abs(b['position'])) questionlist = General.get_q_list(topic_id, user_id, numdone=False) if not questionlist: return [] # Filter out the questions without a positive position unless # the user has prevew permission. questions = [question for question in questionlist if question['position'] > 0] questions.sort(cmp_question_position) for question in questions: try: question['maxscore'] = DB.get_qt_maxscore(question['qtid']) except KeyError: question['maxscore'] = 0 stats_1 = DB.get_student_q_practice_stats(user_id, question['qtid'], 3) if stats_1: # Last practices # Date of last practice question['age'] = stats_1[(len(stats_1) - 1)]['age'] question['ageseconds'] = stats_1[(len(stats_1) - 1)]['ageseconds'] # Fetch last three scores and rate them as good, average or poor for attempt in stats_1: if question['maxscore'] > 0: attempt['pscore'] = "%d%%" % ((attempt['score'] / question['maxscore']) * 100,) attempt['rating'] = 2 # average if attempt['score'] == question['maxscore']: attempt['rating'] = 3 # good if attempt['score'] == 0: attempt['rating'] = 1 # poor else: # don't have maxscore so don't make score a percentage attempt['pscore'] = "%2.1f " % (attempt['score'],) if attempt['score'] == 0: attempt['rating'] = 1 question['stats'] = stats_1 else: question['stats'] = None stats_2 = DB.get_q_stats_class(course_id, question['qtid']) if not stats_2: # no stats, make some up stats_2 = {'num': 0, 'max': 0, 'min': 0, 'avg': 0} percentage = 0 else: if stats_2['max'] == 0: percentage = 0 else: percentage = int(stats_2['avg'] / stats_2['max'] * 100) question['classpercent'] = str(percentage) + "%" user_stats = DB.get_prac_stats_user_qt(user_id, question['qtid']) if not user_stats: indivpercentage = 0 else: if stats_2['max'] == 0: indivpercentage = 0 else: indivpercentage = int(user_stats['avg'] / stats_2['max'] * 100) question['indivpercent'] = str(indivpercentage) + "%" return questions
def render_own_marked_exam(student, exam): """ Return a students instance of the exam, with HTML version of the question, their answers, and a marking summary. returns list of questions/marks [ {'pos': position, 'html': rendered (marked) question, 'marking': [ 'part': part number, 'guess': student guess, 'correct': correct answer, 'mark': (float) mark, 'tolerance': marking tolerance, 'comment': marking comment ] }, ... ] """ questions = General.get_exam_qs(student, exam) firstview, examsubmit = student_exam_duration(student, exam) results = [] if not examsubmit: return [{'pos': 1, 'html': "In Progress", 'marking': {} }, ], False examtotal = 0.0 for question in questions: qtemplate = DB.get_q_parent(question) answers = DB.get_q_guesses_before_time(question, examsubmit) pos = DB.get_qt_exam_pos(exam, qtemplate) marks = General.mark_q(question, answers) parts = [int(var[1:]) for var in marks.keys() if re.search("^A([0-9]+$)", var) > 0] parts.sort() marking = [] for part in parts: guess = marks['G%d' % (part,)] if guess == "None": guess = None answer = marks['A%d' % (part,)] score = marks['M%d' % (part,)] tolerance = marks['T%d' % (part,)] comment = marks['C%d' % (part,)] examtotal += score marking.append({ 'part': part, 'guess': guess, 'correct': answer, 'mark': score, 'tolerance': tolerance, 'comment': comment }) html = General.render_q_html(question) results.append({ 'pos': pos, 'html': html, 'marking': marking }) return results, examtotal
def render_own_marked_exam(student, exam): """ Return a students instance of the exam, with HTML version of the question, their answers, and a marking summary. returns list of questions/marks [ {'pos': position, 'html': rendered (marked) question, 'marking': [ 'part': part number, 'guess': student guess, 'correct': correct answer, 'mark': (float) mark, 'tolerance': marking tolerance, 'comment': marking comment ] }, ... ] """ questions = General.get_exam_qs(student, exam) firstview, examsubmit = student_exam_duration(student, exam) results = [] if not examsubmit: return [ { 'pos': 1, 'html': "In Progress", 'marking': {} }, ], False examtotal = 0.0 for question in questions: qtemplate = DB.get_q_parent(question) answers = DB.get_q_guesses_before_time(question, examsubmit) pos = DB.get_qt_exam_pos(exam, qtemplate) marks = General.mark_q(question, answers) parts = [ int(var[1:]) for var in marks.keys() if re.search("^A([0-9]+$)", var) > 0 ] parts.sort() marking = [] for part in parts: guess = marks['G%d' % (part, )] if guess == "None": guess = None answer = marks['A%d' % (part, )] score = marks['M%d' % (part, )] tolerance = marks['T%d' % (part, )] comment = marks['C%d' % (part, )] examtotal += score marking.append({ 'part': part, 'guess': guess, 'correct': answer, 'mark': score, 'tolerance': tolerance, 'comment': comment }) html = General.render_q_html(question) results.append({'pos': pos, 'html': html, 'marking': marking}) return results, examtotal
def practice_do_question_id(topic_id, qt_id): """ Show them a question and allow them to fill in some answers """ user_id = session['user_id'] try: course_id = Topics.get_course_id(topic_id) except KeyError: course_id = None abort(404) try: course = Courses2.get_course(course_id) except KeyError: course = None abort(404) topictitle = "UNKNOWN" try: topictitle = Topics.get_name(topic_id) except KeyError: abort(404) qtemplate = DB.get_qtemplate(qt_id) questions = Practice.get_sorted_questions(course_id, topic_id, user_id) q_title = qtemplate['title'] q_pos = DB.get_qtemplate_topic_pos(qt_id, topic_id) blocked = Practice.is_q_blocked(user_id, course_id, topic_id, qt_id) if blocked: return render_template( "practicequestionblocked.html", mesg=blocked, topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, ) try: q_id = Practice.get_practice_q(qt_id, user_id) except (ValueError, TypeError) as err: L.error("ERROR 1001 (%s,%s) %s" % (qt_id, user_id, err)) return render_template( "practicequestionerror.html", mesg="Error generating question.", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, ) if not q_id > 0: L.error("ERROR 1002 (%s,%s) Question not generated" % (qt_id, user_id)) return render_template( "practicequestionerror.html", mesg="Error generating question.", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, ) q_body = General.render_q_html(q_id) q_body = q_body.replace(r"\240", u" ") # TODO: why is this here? return render_template( "practicedoquestion.html", q_body=q_body, topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, q_id=q_id, )