def get_topic_list(cid, numq=True): """ Return a list of dicts with topic information for the given course. [{ tid: int Topic ID name: string Name of Topic num: int Number of questions (if numq is false, then None) visibility: int Who can see the topic. 0 = Noone, 1 = Staff, 2 = Course, 3 = Student, 4 = Guest },] """ # TODO: magic numbers! tlist = [] topics = Courses.get_topics(int(cid)) for topic in topics: if numq: num = Topics.get_num_qs(topic) else: num = None tlist.append({ 'tid': topic, 'name': Topics.get_name(topic), 'num': num, 'visibility': Topics.get_vis(topic) }) return tlist
def import_qts_from_zip(data, topic_id): """ Open the given OAQ file and import any qtemplates found. Return False if it's not valid Return 0 if it's valid but has no qtemplates Return NUM of templates imported. """ # TODO: How do we protect against malicious uploads? # At the moment they're allowed for trusted people only, # but we'll eventually want this in the UI for end users. # eg. unzip to huge size # add digital signatures? sdata = StringIO(data) tmpd = tempfile.mkdtemp(prefix="oa") qdir = os.path.join(tmpd, "oasisqe") os.mkdir(qdir) num = 0 try: with zipfile.ZipFile(sdata, "r") as zfile: zfile.extractall(qdir) data = open("%s/info.json" % qdir, "r").read() info = json.loads(data) qtids = info['qtemplates'].keys() qtids.sort() for qtid in qtids: qtemplate = info['qtemplates'][qtid]['qtemplate'] attachments = info['qtemplates'][qtid]['attachments'] if 'position' in info['qtemplates'][qtid]: position = info['qtemplates'][qtid]['position'] else: position = 0 newid = DB.create_qt(owner=1, # ownerid title=qtemplate['title'], desc=qtemplate['description'], marker=qtemplate['marker'], score_max=qtemplate['scoremax'], status=qtemplate['status'], topic_id=topic_id) DB.update_qt_practice_pos(newid, position) num += 1 # print "%s attachments" % len(attachments) for att in attachments: (att_name, att_type, att_size) = att data = open("%s/%s/attach/%s" % (qdir, qtemplate['id'], att_name)).read() DB.create_qt_att(newid, att_name, att_type, data, 1) if att_name == "datfile.txt" or att_name == "datfile.dat" or att_name == "datfile" or att_name == "_datfile" or att_name == "__datfile": qvars = QEditor.parse_datfile(data) for row in range(0, len(qvars)): DB.add_qt_variation(newid, row + 1, qvars[row], 1) except zipfile.BadZipfile: return False Topics.flush_num_qs(topic_id) return num
def get_topics_all(course, archived=2, numq=True): """ Return a summary of all topics in the course. if archived=0, only return non archived courses if archived=1, only return archived courses if archived=2, return all courses if numq is true then include the number of questions in the topic """ ret = None if archived == 0: ret = run_sql("""SELECT topic, title, position, visibility, archived FROM topics WHERE course=%s AND (archived='0' OR archived IS NULL) ORDER BY position, topic;""", (course,)) elif archived == 1: ret = run_sql("""SELECT topic, title, position, visibility, archived FROM topics WHERE course=%s AND archived='1' ORDER BY position, topic;""", (course,)) elif archived == 2: ret = run_sql("""SELECT topic, title, position, visibility, 0 FROM topics WHERE course=%s ORDER BY position, topic;""", (course,)) info = {} if ret: count = 0 for row in ret: info[count] = {'id': int(row[0]), 'title': row[1], 'position': row[2], 'visibility': row[3], 'archived': row[4]} if info[count]['position'] is None or info[count]['position'] is "None": info[count]['position'] = 0 if numq: info[count]['numquestions'] = Topics.get_num_qs(int(row[0])) count += 1 else: # we probably don't have the archived flag in the Db yet ret = run_sql( """SELECT topic, title, position, visibility FROM topics WHERE course=%s ORDER BY position, topic;""", (course,)) if ret: count = 0 for row in ret: info[count] = {'id': int(row[0]), 'title': row[1], 'position': row[2], 'visibility': row[3]} if info[count]['position'] is None or info[count]['position'] is "None": info[count]['position'] = 0 if numq: info[count]['numquestions'] = Topics.get_num_qs(int(row[0])) count += 1 return info
def practice_do_question(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) try: qtemplate = DB.get_qtemplate(qt_id) except KeyError: qtemplate = None abort(404) 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), err: log(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="?", )
def topic_to_zip(topic_id): """ :param topic_id: ID of the topic to export :return: binary string containing ZIPped data """ topic = Topics.get_topic(topic_id) qts = Topics.get_qts(topic_id) return qts_to_zip(qts, extra_info={'topic': topic})
def practice_mark_question(topic_id, question_id): """ Mark the submitted question answersjust wa """ user_id = session['user_id'] course_id = Topics.get_course_id(topic_id) if not course_id: abort(404) course = Courses2.get_course(course_id) if not course: abort(404) topictitle = "UNKNOWN" try: topictitle = Topics.get_name(topic_id) except KeyError: abort(404) qt_id = DB.get_q_parent(question_id) q_title = DB.get_qt_name(qt_id) questions = Practice.get_sorted_questions(course_id, topic_id, user_id) 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, ) marking = Practice.mark_q(user_id, topic_id, question_id, request) prev_id, next_id = Practice.get_next_prev(qt_id, topic_id) return render_template( "practicemarkquestion.html", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, q_id=question_id, marking=marking, next_id=next_id, prev_id=prev_id )
def practice_mark_question(topic_id, question_id): """ Mark the submitted question answersjust wa """ user_id = session['user_id'] course_id = Topics.get_course_id(topic_id) if not course_id: abort(404) course = Courses2.get_course(course_id) if not course: abort(404) topictitle = "UNKNOWN" try: topictitle = Topics.get_name(topic_id) except KeyError: abort(404) qt_id = DB.get_q_parent(question_id) q_title = DB.get_qt_name(qt_id) questions = Practice.get_sorted_questions(course_id, topic_id, user_id) 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, ) marking = Practice.mark_q(user_id, topic_id, question_id, request) prev_id, next_id = Practice.get_next_prev(qt_id, topic_id) # TODO: need next_pos and prev_pos return render_template( "practicemarkquestion.html", topictitle=topictitle, topic_id=topic_id, qt_id=qt_id, course=course, q_title=q_title, questions=questions, q_pos=q_pos, q_id=question_id, marking=marking, next_id=next_id, prev_id=prev_id )
def topic_to_zip(topic_id, fname='oa_export', suffix='oaq'): """ :param topic_id: ID of the topic to export :param fname: filename to create :param suffix: suffix :return: binary string containing ZIPped data """ topic = Topics.get_topic(topic_id) qts = Topics.get_qts(topic_id) return qts_to_zip(qts, fname=fname, extra_info={'topic': topic})
def import_qts_from_zip(data, topicid): """ Open the given OAQ file and import any qtemplates found. Return False if it's not valid Return 0 if it's valid but has no qtemplates Return NUM of templates imported. """ # TODO: How do we protect against malicious uploads? # At the moment they're allowed for trusted people only, # but we'll eventually want this in the UI for end users. # eg. unzip to huge size # add digital signatures? sdata = StringIO(data) tmpd = tempfile.mkdtemp(prefix="oa") qdir = os.path.join(tmpd, "oasisqe") os.mkdir(qdir) with zipfile.ZipFile(sdata, "r") as zfile: zfile.extractall(qdir) data = open("%s/info.json" % qdir, "r").read() info = json.loads(data) print "%s questions found" % (len(info['qtemplates'])) position = 1 for qtid in info['qtemplates'].keys(): qtemplate = info['qtemplates'][qtid]['qtemplate'] print "%s" % qtemplate['title'] newid = DB.create_qt(1, qtemplate['title'], qtemplate['description'], qtemplate['marker'], qtemplate['scoremax'], qtemplate['status']) DB.update_qt_pos(newid, topicid, position) position += 1 attachments = info['qtemplates'][qtid]['attachments'] print "%s attachments" % len(attachments) for att in attachments: (att_name, att_type, att_size) = att data = open("%s/%s/attach/%s" % (qdir, qtemplate['id'], att_name)).read() DB.create_qt_att(newid, att_name, att_type, data, 1) if att_name == "datfile.txt" or att_name == "datfile.dat" or att_name == "datfile" or att_name == "_datfile" or att_name == "__datfile": qvars = QEditor.parse_datfile(data) print "generating variations..." for row in range(0, len(qvars)): DB.add_qt_variation(newid, row + 1, qvars[row], 1) Topics.flush_num_qs(topicid) return 0
def cadmin_view_qtemplate_history(course_id, topic_id, qt_id): """ Show the practice history of the question template. """ if not course_id: abort(404) course = Courses2.get_course(course_id) topic = {"id": topic_id, "position": Topics.get_pos(topic_id), "name": Topics.get_name(topic_id)} qtemplate = DB.get_qtemplate(qt_id) year = datetime.now().year years = range(year, year - 6, -1) return render_template( "courseadmin_viewqtemplate.html", course=course, topic=topic, qtemplate=qtemplate, years=years )
def cadmin_edit_topic(course_id, topic_id): """ Present a page to view and edit a topic, including adding/editing questions and setting some parameters. """ user_id = session['user_id'] if not course_id: abort(404) course = Courses2.get_course(course_id) topic = { 'id': topic_id, 'position': Topics.get_pos(topic_id), 'name': Topics.get_name(topic_id) } questions = [question for question in Topics.get_qts(topic_id).values()] for question in questions: question['embed_id'] = DB.get_qt_embedid(question['id']) if question['embed_id']: question['embed_url'] = "%s/embed/question/%s/question.html" % \ (OaConfig.parentURL, question['embed_id']) else: question['embed_url'] = None question['editor'] = DB.get_qt_editor(question['id']) all_courses = Courses2.get_course_list() all_courses = [crse for crse in all_courses if satisfy_perms(user_id, int(crse['id']), ("questionedit", "courseadmin", "sysadmin"))] all_courses.sort(lambda f, s: cmp(f['name'], s['name'])) all_course_topics = [] for crse in all_courses: topics = Courses.get_topics_all(crse['id'], numq=False) if topics: all_course_topics.append({'course': crse['name'], 'topics': topics}) questions.sort(key=lambda k: k['position']) return render_template( "courseadmin_edittopic.html", course=course, topic=topic, questions=questions, all_course_topics=all_course_topics )
def test_topic_position(self): """ Test putting qtemplates into topics and moving them around """ course_id = Courses.create("TEST101", "Test topic position logic", 1, 1) topic1_id = Topics.create(course_id, "TESTTOPIC1", 1, 2) topic2_id = Topics.create(course_id, "TESTTOPIC2", 3, 3) qt1_id = DB.create_qt(1, "TESTQ1", "Test question 1", 0, 5.0, 1) qt2_id = DB.create_qt(1, "TESTQ2", "Test question 2", 0, 4.1, 2, topic1_id) DB.move_qt_to_topic(qt1_id, topic1_id) self.assertEqual(DB.get_qtemplate_practice_pos(qt1_id), 0) self.assertEqual(DB.get_qtemplate_practice_pos(qt2_id), 0) self.assertEqual(DB.get_topic_for_qtemplate(qt1_id), topic1_id) self.assertEqual(DB.get_topic_for_qtemplate(qt2_id), topic1_id) DB.update_qt_practice_pos(qt1_id, 3) DB.update_qt_practice_pos(qt2_id, 2) self.assertEqual(DB.get_qtemplate_practice_pos(qt1_id), 3) self.assertEqual(DB.get_qtemplate_practice_pos(qt2_id), 2) self.assertEqual(DB.get_qtemplate_practice_pos(qt1_id), 3, "Broken cache?") self.assertEqual(DB.get_qtemplate_practice_pos(qt2_id), 2, "Broken cache?") DB.update_qt_practice_pos(qt1_id, 0) DB.update_qt_practice_pos(qt2_id, -1) self.assertEqual(DB.get_qtemplate_practice_pos(qt1_id), 0) self.assertEqual(DB.get_qtemplate_practice_pos(qt2_id), -1) self.assertEqual(DB.get_qtemplate_practice_pos(qt1_id), 0, "Broken cache?") self.assertEqual(DB.get_qtemplate_practice_pos(qt2_id), -1, "Broken cache?") qts = Topics.get_qts(topic1_id) self.assertIn(qt1_id, qts) self.assertIn(qt2_id, qts) self.assertEqual(len(qts), 2) DB.move_qt_to_topic(qt1_id, topic2_id) qts = Topics.get_qts(topic1_id) self.assertNotIn(qt1_id, qts) self.assertIn(qt2_id, qts) self.assertEqual(len(qts), 1)
def api_qedit2_get_qtemplate_json(qt_id): """ Present a list of qtemplates that are available for use in the exam.""" if 'user_id' not in session: abort(401) user_id = session['user_id'] topic_id = DB.get_topic_for_qtemplate(qt_id) course_id = Topics.get_course_id(topic_id) if not satisfy_perms(user_id, course_id, ("questionedit",)): abort(401) # test data while we're building the frontend return jsonify(result={ 'type': "qtemplate data", 'title': "Test QE2 Question", 'embed_id': "aaaaaaaaa3", 'maxscore': 3, 'pre_vars': [ {'id': 1, 'name': "a", 'type': 'List', 'value': "2,3,4,5,6,7"}, {'id': 2, 'name': "b", 'type': 'Range', 'value': "1-10"}, {'id': 3, 'name': "a1", 'type': 'Calculation', 'value': "$a+$b"}, {'id': 4, 'name': "a2", 'type': 'Calculation', 'value': "$a*$b"}, ], 'qtext': "What is $a + $b ? <answer1>\nWhat is $a * $b? <answer2> ", 'answers': [ {'id': 1, 'source': 'Variable', 'value': '$a1', 'tolerance': 0, 'score': 1}, {'id': 2, 'source': 'Variable', 'value': '$a2', 'tolerance': 0, 'score': 1} ] })
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 get_q_list(tid, uid=None, numdone=True): """ Return a list of dicts with question template information for the topic. [{ qtid: int QTemplate ID name: string Name of Question position: int Position of Question in topic done: Number of times the given user has submitted a question for practice },] """ qlist = [] qtemplates = Topics.get_qts(int(tid)) for qtid in qtemplates: if uid and numdone: num = DB.get_student_q_practice_num(uid, qtid) else: num = 0 qlist.append({ 'qtid': qtid, 'name': qtemplates[qtid]['name'], 'position': qtemplates[qtid]['position'], 'done': num }) # Sort them by position qlist.sort(lambda f, s: cmp(f["position"], s["position"])) return qlist
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 test_create_qtemplate(self): """ Test qtemplates creation """ qt1_id = DB.create_qt(1, "TESTQ1", "Test question 1", 0, 5.0, 1) qt2_id = DB.create_qt(1, "TESTQ2", "Test question 2", 0, 4.1, 2) self.assertIsInstance(qt1_id, int) self.assertIsInstance(qt2_id, int) qt1 = DB.get_qtemplate(qt1_id) qt2 = DB.get_qtemplate(qt2_id) self.assertEqual(qt1['title'], "TESTQ1") self.assertEqual(qt2['title'], "TESTQ2") self.assertEqual(qt1['description'], "Test question 1") self.assertEqual(qt2['description'], "Test question 2") course_id = Courses.create("TEST107", "Test create qtemplate", 1, 1) topic1_id = Topics.create(course_id, "TESTTOPIC9", 1, 2) qt3_id = DB.create_qt(1, "TESTQ3", "Test question 3", 0, 5.0, 1, topic1_id) self.assertIsInstance(qt3_id, int) qt3 = DB.get_qtemplate(qt3_id) self.assertEqual(qt3['title'], "TESTQ3") self.assertEqual(qt3['description'], "Test question 3") self.assertEqual(DB.get_topic_for_qtemplate(qt3_id), topic1_id)
def cadmin_view_topic(course_id, topic_id): """ Present a page to view a topic, including basic stats """ user_id = session['user_id'] if not course_id: abort(404) course = Courses2.get_course(course_id) topic = { 'id': topic_id, 'position': Topics.get_pos(topic_id), 'name': Topics.get_name(topic_id) } questions = [question for question in Topics.get_qts(topic_id).values()] for question in questions: question['embed_id'] = DB.get_qt_embedid(question['id']) if question['embed_id']: question['embed_url'] = "%s/embed/question/%s/question.html" % \ (OaConfig.parentURL, question['embed_id']) else: question['embed_url'] = None question['editor'] = DB.get_qt_editor(question['id']) all_courses = [ crse for crse in Courses2.get_course_list() if satisfy_perms(user_id, int(crse['id']), ("questionedit", "courseadmin", "sysadmin")) ] all_courses.sort(lambda f, s: cmp(f['name'], s['name'])) all_course_topics = [] for crse in all_courses: topics = Courses.get_topics_all(crse['id'], numq=False) if topics: all_course_topics.append({ 'course': crse['name'], 'topics': topics }) questions.sort(key=lambda k: k['position']) return render_template( "courseadmin_viewtopic.html", course=course, topic=topic, questions=questions, all_course_topics=all_course_topics, )
def test_topic_nextprev(self): """ Do the "next/previous" options in practice work? """ course_id = Courses.create("TEST101", "Test topic next/prev logic", 1, 1) topic1_id = Topics.create(course_id, "TESTTOPIC1", 1, 2) qt1_id = DB.create_qt(1, "TESTQ1", "Test question 1", 0, 5.0, 1) qt2_id = DB.create_qt(1, "TESTQ2", "Test question 2", 0, 4.1, 2) qt3_id = DB.create_qt(1, "TESTQ3", "Test question 3", 0, 0.0, 2) qt4_id = DB.create_qt(1, "TESTQ4", "Test question 4", 0, 2.0, 2) DB.move_qt_to_topic(qt1_id, topic1_id) DB.move_qt_to_topic(qt2_id, topic1_id) DB.move_qt_to_topic(qt3_id, topic1_id) DB.move_qt_to_topic(qt4_id, topic1_id) DB.update_qt_practice_pos(qt1_id, 1) DB.update_qt_practice_pos(qt2_id, 2) DB.update_qt_practice_pos(qt3_id, 3) DB.update_qt_practice_pos(qt4_id, 4) qts = Topics.get_qts(topic1_id) self.assertIn(qt1_id, qts) self.assertIn(qt2_id, qts) self.assertIn(qt3_id, qts) self.assertIn(qt4_id, qts) self.assertEqual(len(qts), 4) self.assertTupleEqual(Practice.get_next_prev_pos(qt1_id, topic1_id), (None, 2)) self.assertTupleEqual(Practice.get_next_prev_pos(qt2_id, topic1_id), (1, 3)) self.assertTupleEqual(Practice.get_next_prev_pos(qt3_id, topic1_id), (2, 4)) self.assertTupleEqual(Practice.get_next_prev_pos(qt4_id, topic1_id), (3, None)) DB.update_qt_practice_pos(qt2_id, 3) self.assertEqual(DB.get_qtemplate_practice_pos(qt1_id), 1) self.assertEqual(DB.get_qtemplate_practice_pos(qt2_id), 3) self.assertEqual(DB.get_qtemplate_practice_pos(qt3_id), 3) self.assertEqual(DB.get_qtemplate_practice_pos(qt4_id), 4) self.assertTupleEqual(Practice.get_next_prev_pos(qt1_id, topic1_id), (None, 3)) self.assertTupleEqual(Practice.get_next_prev_pos(qt2_id, topic1_id), (1, 4)) self.assertTupleEqual(Practice.get_next_prev_pos(qt3_id, topic1_id), (1, 4)) self.assertTupleEqual(Practice.get_next_prev_pos(qt4_id, topic1_id), (3, None)) self.assertTupleEqual(Practice.get_next_prev_pos(qt4_id, None), (None, None))
def practice_choose_question(topic_id): """ Present a list of questions for them to choose from the given topic """ user_id = session['user_id'] try: course_id = Topics.get_course_id(topic_id) except KeyError: course_id = None abort(404) topics = [] try: topics = Courses2.get_topics_list(course_id) except KeyError: abort(404) try: course = Courses2.get_course(course_id) except KeyError: course = None abort(404) topictitle = Topics.get_name(topic_id) questions = Practice.get_sorted_questions(course_id, topic_id, user_id) thistopic = Topics.get_topic(topic_id) members = [] if thistopic['visibility'] == 2: # course only if not members: members = Courses.get_users(course_id) if not user_id in members: abort(404) for topic in topics: if topic['visibility'] == 2: # course only if not members: members = Courses.get_users(course_id) if not user_id in members: topics.remove(topic) return render_template( "practicetopic.html", canpreview=check_perm(user_id, course_id, "questionpreview"), topics=topics, topic_id=topic_id, course=course, topictitle=topictitle, questions=questions )
def practice_choose_question(topic_id): """ Present a list of questions for them to choose from the given topic """ user_id = session['user_id'] try: course_id = Topics.get_course_id(topic_id) except KeyError: course_id = None abort(404) topics = [] try: topics = Courses2.get_topics_list(course_id) except KeyError: abort(404) try: course = Courses2.get_course(course_id) except KeyError: course = None abort(404) topictitle = Topics.get_name(topic_id) questions = Practice.get_sorted_questions(course_id, topic_id, user_id) thistopic = Topics.get_topic(topic_id) members = [] if thistopic['visibility'] == 2: # course only if not members: members = Courses.get_users(course_id) if user_id not in members: abort(404) for topic in topics: if topic['visibility'] == 2: # course only if not members: members = Courses.get_users(course_id) if user_id not in members: topics.remove(topic) return render_template( "practicetopic.html", canpreview=check_perm(user_id, course_id, "questionpreview"), topics=topics, topic_id=topic_id, course=course, topictitle=topictitle, questions=questions )
def test_import_questions(self): """ Import the questions made in export_questions""" course_id = Courses.create("TEST103", "Test import questions", 1, 1) self.assertGreater(course_id, 0) topic1_id = Topics.create(course_id, "TESTQUESTIONS1", 1, 2) 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_view_qtemplate_history(course_id, topic_id, qt_id): """ Show the practice history of the question template. """ if not course_id: abort(404) course = Courses2.get_course(course_id) topic = { 'id': topic_id, 'position': Topics.get_pos(topic_id), 'name': Topics.get_name(topic_id) } qtemplate = DB.get_qtemplate(qt_id) year = datetime.now().year years = range(year, year - 6, -1) return render_template("courseadmin_viewqtemplate.html", course=course, topic=topic, qtemplate=qtemplate, years=years)
def cadmin_view_topic(course_id, topic_id): """ Present a page to view a topic, including basic stats """ user_id = session["user_id"] if not course_id: abort(404) course = Courses2.get_course(course_id) topic = {"id": topic_id, "position": Topics.get_pos(topic_id), "name": Topics.get_name(topic_id)} questions = [question for question in Topics.get_qts(topic_id).values()] for question in questions: question["embed_id"] = DB.get_qt_embedid(question["id"]) if question["embed_id"]: question["embed_url"] = "%s/embed/question/%s/question.html" % (OaConfig.parentURL, question["embed_id"]) else: question["embed_url"] = None question["editor"] = DB.get_qt_editor(question["id"]) all_courses = Courses2.get_course_list() all_courses = [ crse for crse in all_courses if satisfy_perms(user_id, int(crse["id"]), ("questionedit", "courseadmin", "sysadmin")) ] all_courses.sort(lambda f, s: cmp(f["name"], s["name"])) all_course_topics = [] for crse in all_courses: topics = Courses.get_topics_all(crse["id"], numq=False) if topics: all_course_topics.append({"course": crse["name"], "topics": topics}) questions.sort(key=lambda k: k["position"]) return render_template( "courseadmin_viewtopic.html", course=course, topic=topic, questions=questions, all_course_topics=all_course_topics, )
def get_topic_list(cid, numq=True): """ Return a list of dicts with topic information for the given course. [{ tid: int Topic ID name: string Name of Topic num: int Number of questions (if numq is false, then None) visibility: int Who can see the topic. 0 = Noone, 1 = Staff, 2 = Course, 3 = Student, 4 = Guest },] """ # TODO: magic numbers! tlist = [] topics = Courses.get_topics(int(cid)) for topic in topics: if numq: num = Topics.get_num_qs(topic) else: num = None tlist.append({'tid': topic, 'name': Topics.get_name(topic), 'num': num, 'visibility': Topics.get_vis(topic)}) return tlist
def test_export_questions(self): """ Make some questions and export them.""" course_id = Courses.create("TEST106", "Test Question Export", 1, 1) self.assertGreater(course_id, 0) topic1_id = Topics.create(course_id, "TESTEXPORT1", 1, 2) self.assertGreater(topic1_id, 0) qt1_id = DB.create_qt(1, "TESTQ1", "Test question 1", 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) qt2_id = DB.create_qt(1, "TESTQ2", "Test question 2", 0, 5.0, 1, topic_id=topic1_id) self.assertIsNotNone(qt2_id) ver = DB.get_qt_version(qt2_id) self.assertGreater(ver, 0) data = "2\n|6\n|7\n" qvars = [{'A1': "6"}, {'A1': "7"}] for row in range(0, len(qvars)): DB.add_qt_variation(qt2_id, row + 1, qvars[row], ver) DB.create_qt_att(qt2_id, "datfile.dat", "text/plain", data, ver) DB.create_qt_att(qt2_id, "qtemplate.html", "text/html", "Question 2: What is <VAL A1>? <ANSWER 1>", ver) qt3_id = DB.create_qt(1, "TESTQ3", "Test question 3", 0, 5.0, 1, topic_id=topic1_id) self.assertIsNotNone(qt3_id) ver = DB.get_qt_version(qt3_id) self.assertGreater(ver, 0) data = "3\n|9\n|10\n|11\n" qvars = [{'A1': "9"}, {'A1': "10"}, {'A1': "11"}] for row in range(0, len(qvars)): DB.add_qt_variation(qt3_id, row + 1, qvars[row], ver) DB.create_qt_att(qt3_id, "datfile.dat", "text/plain", data, ver) DB.create_qt_att(qt3_id, "qtemplate.html", "text/html", "Question 3: What is <VAL A1>? <ANSWER 1>", ver) data = External.topic_to_zip(topic1_id) f = open("%s" % self.test_question_fname, mode='w') f.write(data) f.close()
def practice_choose_question_stats(topic_id): """ Present a list of questions for them to choose from the given topic, and show some statistics on how they're doing. """ user_id = session['user_id'] course_id = Topics.get_course_id(topic_id) if not course_id: abort(404) topics = Courses2.get_topics_list(course_id) course = Courses2.get_course(course_id) topictitle = Topics.get_name(topic_id) questions = Practice.get_sorted_qlist_wstats(course_id, topic_id, user_id) return render_template( "practicetopicstats.html", canpreview=check_perm(user_id, course_id, "questionpreview"), topics=topics, topic_id=topic_id, course=course, topictitle=topictitle, questions=questions )
def do_topic_update(course, request): """Read the submitted form and make relevant changes to Topic information """ categories = [] topics = [] form = request.form if form: for i in form.keys(): parts = i.split('_') if len(parts) > 1: catid = parts[0] if catid not in categories: categories = categories + [catid] for c in categories: topics = topics + [{'id': int(c), 'position': form['%s_position' % c], 'name': form['%s_name' % c], 'visibility': form['%s_visibility' % c] }] for i in topics: if not i['id'] == 0: Topics.set_pos(i['id'], i['position']) Topics.set_name(int(i['id']), i['name']) Topics.set_vis(i['id'], i['visibility']) Courses.incr_version() else: if not i['name'] == "[Name of new topic]": Topics.create(course['id'], i['name'], int(i['visibility']), i['position']) Courses.incr_version() return True return False
def do_topic_update(course, request): """Read the submitted form and make relevant changes to Topic information """ categories = [] topics = [] form = request.form if form: for i in form.keys(): parts = i.split('_') if len(parts) > 1: catid = parts[0] if catid not in categories: categories = categories + [catid] for c in categories: topics = topics + [{ 'id': int(c), 'position': form['%s_position' % c], 'name': form['%s_name' % c], 'visibility': form['%s_visibility' % c] }] for i in topics: if not i['id'] == 0: Topics.set_pos(i['id'], i['position']) Topics.set_name(int(i['id']), i['name']) Topics.set_vis(i['id'], i['visibility']) Courses.incr_version() else: if not i['name'] == "[Name of new topic]": Topics.create(course['id'], i['name'], int(i['visibility']), i['position']) Courses.incr_version() return True return False
def get_q_list(tid, uid=None, numdone=True): """ Return a list of dicts with question template information for the topic. [{ qtid: int QTemplate ID name: string Name of Question position: int Position of Question in topic done: Number of times the given user has submitted a question for practice },] """ qlist = [] qtemplates = Topics.get_qts(int(tid)) for qtid in qtemplates: if uid and numdone: num = DB.get_student_q_practice_num(uid, qtid) else: num = 0 qlist.append({'qtid': qtid, 'name': qtemplates[qtid]['name'], 'position': qtemplates[qtid]['position'], 'done': num}) # Sort them by position qlist.sort(lambda f, s: cmp(f["position"], s["position"])) return qlist
def create_exported_questions(fname): """ Make some questions and export them.""" # Not really related to assessment, but can use this to create some questions to import and use multiple times course_id = Courses.create("TEST106", "Test Question Export", 1, 1) topic1_id = Topics.create(course_id, "TESTEXPORT1", 1, 2) qt1_id = DB.create_qt(1, "TESTQ1", "Test question 1", 0, 5.0, 1, topic_id=topic1_id) ver = DB.get_qt_version(qt1_id) 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) qt2_id = DB.create_qt(1, "TESTQ2", "Test question 2", 0, 5.0, 1, topic_id=topic1_id) ver = DB.get_qt_version(qt2_id) data = "2\n|6\n|7\n" qvars = [{'A1': "6"}, {'A1': "7"}] for row in range(0, len(qvars)): DB.add_qt_variation(qt2_id, row + 1, qvars[row], ver) DB.create_qt_att(qt2_id, "datfile.dat", "text/plain", data, ver) DB.create_qt_att(qt2_id, "qtemplate.html", "text/html", "Question 2: What is <VAL A1>? <ANSWER 1>", ver) qt3_id = DB.create_qt(1, "TESTQ3", "Test question 3", 0, 5.0, 1, topic_id=topic1_id) ver = DB.get_qt_version(qt3_id) data = "3\n|9\n|10\n|11\n" qvars = [{'A1': "9"}, {'A1': "10"}, {'A1': "11"}] for row in range(0, len(qvars)): DB.add_qt_variation(qt3_id, row + 1, qvars[row], ver) DB.create_qt_att(qt3_id, "datfile.dat", "text/plain", data, ver) DB.create_qt_att(qt3_id, "qtemplate.html", "text/html", "Question 3: What is <VAL A1>? <ANSWER 1>", ver) data = External.topic_to_zip(topic1_id) f = open("%s" % fname, mode='w') f.write(data) f.close()
def do_topic_page_commands(request, topic_id, user_id): """We've been asked to perform some operations on the Topic page. Expecting form fields: selected_QTID position_QTID name_QTID where QTID is a question template id. May receive many. new_position new_name new_type select_cmd = 'copy' | 'move' select_target = TOPICID of target topic """ form = request.form mesg = [] # Make a list of all the commands to run cmdlist = [] for command in request.form.keys(): (cmd, data) = command.split('_', 2) value = form[command] if not value == "none": cmdlist += [{'cmd': cmd, 'data': data, 'value': value}] # Now run them: # Titles first for command in [cmd for cmd in cmdlist if cmd['cmd'] == 'name']: qid = int(command['data']) title = command['value'] DB.update_qt_title(qid, title) # Then positions for command in [cmd for cmd in cmdlist if cmd['cmd'] == 'position']: qtid = int(command['data']) try: position = int(command['value']) except ValueError: position = 0 DB.update_qt_pos(qtid, topic_id, position) # Then commands on selected questions target_cmd = form.get('target_cmd', None) if target_cmd: qtids = [int(cmd['data']) for cmd in cmdlist if cmd['cmd'] == 'select'] try: target_topic = int(form.get('target_topic', 0)) except ValueError: target_topic = None if target_cmd == 'move': if target_topic: for qtid in qtids: qt_title = DB.get_qt_name(qtid) topic_title = Topics.get_name(target_topic) flash("Moving %s to %s" % (qt_title, topic_title)) DB.move_qt_to_topic(qtid, target_topic) Topics.flush_num_qs(topic_id) Topics.flush_num_qs(target_topic) if target_cmd == 'copy': if target_topic: for qtid in qtids: qt_title = DB.get_qt_name(qtid) topic_title = Topics.get_name(target_topic) flash("Copying %s to %s" % (qt_title, topic_title)) newid = DB.copy_qt_all(qtid) DB.add_qt_to_topic(newid, target_topic) Topics.flush_num_qs(target_topic) if target_cmd == 'hide': for qtid in qtids: position = DB.get_qtemplate_topic_pos(qtid, topic_id) if position > 0: # If visible, make it hidden DB.update_qt_pos(qtid, topic_id, -position) title = DB.get_qt_name(qtid) flash("Made '%s' Hidden" % title) Topics.flush_num_qs(topic_id) if target_cmd == 'show': for qtid in qtids: position = DB.get_qtemplate_topic_pos(qtid, topic_id) if position == 0: # If hidden, make it visible newpos = DB.get_qt_max_pos_in_topic(topic_id) DB.update_qt_pos(qtid, topic_id, newpos + 1) Topics.flush_num_qs(topic_id) title = DB.get_qt_name(qtid) flash("Made '%s' Visible" % title) if position < 0: # If hidden, make it visible DB.update_qt_pos(qtid, topic_id, -position) Topics.flush_num_qs(topic_id) title = DB.get_qt_name(qtid) flash("Made '%s' Visible" % title) if target_cmd == "export": if len(qtids) < 1: flash("No questions selected to export") else: data = External.qts_to_zip(qtids) if not data: abort(401) sio = StringIO.StringIO(data) return 2, send_file(sio, "application/oasisqe", as_attachment=True, attachment_filename="oa_export.zip") # Then new questions new_title = form.get('new_title', None) if new_title: if not (new_title == "[New Question]" or new_title == ""): new_position = form.get('new_position', 0) try: new_position = int(new_position) except ValueError: new_position = 0 new_qtype = form.get('new_qtype', 'raw') try: new_max_score = float(form.get('new_maxscore', 0)) except ValueError: new_max_score = 0 newid = DB.create_qt(user_id, new_title, "No Description", 1, new_max_score, 0) if newid: mesg.append("Created new question, id %s" % newid) DB.update_qt_pos(newid, topic_id, new_position) DB.create_qt_att(newid, "qtemplate.html", "application/oasis-html", "empty", 1) DB.create_qt_att(newid, "qtemplate.html", "application/oasis-html", "empty", 1) if new_qtype == "oqe": mesg.append("Creating new question, id %s as OQE" % newid) DB.create_qt_att(newid, "_editor.oqe", "application/oasis-oqe", "", 1) if new_qtype == "raw": mesg.append("Creating new question, id %s as RAW (%s)" % (newid, new_qtype)) DB.create_qt_att(newid, "datfile.txt", "application/oasis-dat", "0", 1) else: mesg.append("Error creating new question, id %s" % newid) L.error("Unable to create new question (%s) (%s)" % (new_title, new_position)) Topics.flush_num_qs(topic_id) return 1, {'mesg': mesg}
def test_create_topic(self): """ Fetch a topic back and check it """ course_id = Courses.create("TEST101", "Test topic position logic", 1, 1) self.assertDictContainsSubset( {course_id: {'active': 1, 'assess_visibility': 'enrol', 'id': course_id, 'name': 'TEST101', 'owner': 1, 'practice_visibility': 'all', 'title': 'Test topic position logic', 'type': 1 } }, Courses.get_courses_dict(), ) topic1_id = Topics.create(course_id, "TESTTOPIC1", 1, 2) topic2_id = Topics.create(course_id, "TESTTOPIC2", 3, 3) self.assertGreater(topic1_id, 0) self.assertIsInstance(topic1_id, int) self.assertGreater(topic2_id, 0) self.assertIsInstance(topic2_id, int) self.assertNotEqual(topic1_id, topic2_id) topic1 = Topics.get_topic(topic1_id) topic2 = Topics.get_topic(topic2_id) self.assertEqual(topic1['id'], topic1_id) self.assertEqual(topic2['id'], topic2_id) self.assertEqual(topic1['title'], "TESTTOPIC1") self.assertEqual(topic2['title'], "TESTTOPIC2") self.assertEqual(topic1['visibility'], 1) self.assertEqual(topic2['visibility'], 3) self.assertEqual(Topics.get_name(topic1_id), topic1['title']) Topics.set_name(topic1_id, "NEWNAME1") self.assertEqual(Topics.get_name(topic1_id), "NEWNAME1") self.assertEqual(Topics.get_num_qs(topic1_id), 0) self.assertEqual(Topics.get_pos(topic1_id), 2) Topics.set_pos(topic1_id, 8) self.assertEqual(Topics.get_pos(topic1_id), 8)
def get_topics_all(course, archived=2, numq=True): """ Return a summary of all topics in the course. if archived=0, only return non archived courses if archived=1, only return archived courses if archived=2, return all courses if numq is true then include the number of questions in the topic """ ret = None if archived == 0: ret = run_sql( """SELECT topic, title, position, visibility, archived FROM topics WHERE course=%s AND (archived='0' OR archived IS NULL) ORDER BY position, topic;""", (course, )) elif archived == 1: ret = run_sql( """SELECT topic, title, position, visibility, archived FROM topics WHERE course=%s AND archived='1' ORDER BY position, topic;""", (course, )) elif archived == 2: ret = run_sql( """SELECT topic, title, position, visibility, 0 FROM topics WHERE course=%s ORDER BY position, topic;""", (course, )) info = {} if ret: count = 0 for row in ret: info[count] = { 'id': int(row[0]), 'title': row[1], 'position': row[2], 'visibility': row[3], 'archived': row[4] } if info[count]['position'] is None or info[count][ 'position'] is "None": info[count]['position'] = 0 if numq: info[count]['numquestions'] = Topics.get_num_qs(int(row[0])) count += 1 else: # we probably don't have the archived flag in the Db yet ret = run_sql( """SELECT topic, title, position, visibility FROM topics WHERE course=%s ORDER BY position, topic;""", (course, )) if ret: count = 0 for row in ret: info[count] = { 'id': int(row[0]), 'title': row[1], 'position': row[2], 'visibility': row[3] } if info[count]['position'] is None or info[count][ 'position'] is "None": info[count]['position'] = 0 if numq: info[count]['numquestions'] = Topics.get_num_qs(int( row[0])) count += 1 return info
def do_topic_page_commands(request, topic_id, user_id): """We've been asked to perform some operations on the Topic page. Expecting form fields: selected_QTID position_QTID name_QTID where QTID is a question template id. May receive many. new_position new_name new_type select_cmd = 'copy' | 'move' select_target = TOPICID of target topic """ form = request.form files = request.files mesg = [] # Make a list of all the commands to run cmdlist = [] for command in request.form.keys(): if '_' in command: (cmd, data) = command.split('_', 2) value = form[command] if not value == "none": cmdlist += [{'cmd': cmd, 'data': data, 'value': value}] # Now run them: # Titles first for command in [cmd for cmd in cmdlist if cmd['cmd'] == 'name']: qid = int(command['data']) title = command['value'] DB.update_qt_title(qid, title) # Then positions for command in [cmd for cmd in cmdlist if cmd['cmd'] == 'position']: qtid = int(command['data']) try: position = int(command['value']) except ValueError: position = 0 DB.update_qt_practice_pos(qtid, position) # Then commands on selected questions target_cmd = form.get('target_cmd', None) if target_cmd: qtids = [int(cmd['data']) for cmd in cmdlist if cmd['cmd'] == 'select'] try: target_topic = int(form.get('target_topic', 0)) except ValueError: target_topic = None if target_cmd == 'move': if target_topic: for qtid in qtids: qt_title = DB.get_qt_name(qtid) topic_title = Topics.get_name(target_topic) flash("Moving %s to %s" % (qt_title, topic_title)) DB.move_qt_to_topic(qtid, target_topic) Topics.flush_num_qs(target_topic) if target_cmd == 'copy': if target_topic: for qtid in qtids: qt_title = DB.get_qt_name(qtid) topic_title = Topics.get_name(target_topic) flash("Copying %s to %s" % (qt_title, topic_title)) position = DB.get_qtemplate_practice_pos(qtid) newid = DB.copy_qt_all(qtid) DB.move_qt_to_topic(newid, target_topic, position) Topics.flush_num_qs(target_topic) if target_cmd == 'hide': for qtid in qtids: position = DB.get_qtemplate_practice_pos(qtid) if position > 0: # If visible, make it hidden DB.update_qt_practice_pos(qtid, -position) title = DB.get_qt_name(qtid) flash("Made '%s' Hidden" % title) if target_cmd == 'show': for qtid in qtids: position = DB.get_qtemplate_practice_pos(qtid) if position == 0: # If hidden, make it visible newpos = DB.get_qt_max_pos_in_topic(topic_id) DB.update_qt_practice_pos(qtid, newpos + 1) title = DB.get_qt_name(qtid) flash("Made '%s' Visible" % title) if position < 0: # If hidden, make it visible DB.update_qt_practice_pos(qtid, -position) title = DB.get_qt_name(qtid) flash("Made '%s' Visible" % title) if target_cmd == "export": if len(qtids) < 1: flash("No questions selected to export") else: data = External.qts_to_zip(qtids) if not data: abort(401) sio = StringIO.StringIO(data) return 2, send_file(sio, "application/oasisqe", as_attachment=True, attachment_filename="oa_export.zip") # Then new questions new_title = form.get('new_title', None) if new_title: if not (new_title == "[New Question]" or new_title == ""): new_position = form.get('new_position', 0) try: new_position = int(new_position) except ValueError: new_position = 0 new_qtype = form.get('new_qtype', 'raw') try: new_max_score = float(form.get('new_maxscore', 0)) except ValueError: new_max_score = 0 new_id = DB.create_qt(owner=user_id, title=new_title, desc="No Description", marker=1, score_max=new_max_score, status=0, topic_id=topic_id) if new_id: mesg.append("Created new question, id %s" % new_id) if new_position and new_position >= 1: DB.update_qt_practice_pos(new_id, new_position) if new_qtype == "qe2": mesg.append("Creating new question, id %s as QE2" % new_id) QEditor2.create_new(new_id, new_title) if new_qtype == "raw": mesg.append("Creating new question, id %s as RAW (%s)" % (new_id, new_qtype)) QEditor.create_new(new_id, new_title) else: mesg.append("Error creating new question, id %s" % new_id) L.error("Unable to create new question (%s) (%s)" % (new_title, new_position)) L.info("request.files = %s" % (repr(request.files.keys()))) # Did they upload a file to import? if 'import_file' in request.files: L.info("File upload to topic %s by user %s" % (topic_id, user_id)) data = files['import_file'].read() if len(data) > 1: for msg in _import_questions_from_file(data, topic_id): mesg.append(msg) Topics.flush_num_qs(topic_id) return 1, {'mesg': mesg}
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, )
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, )
def do_topic_page_commands(request, topic_id, user_id): """We've been asked to perform some operations on the Topic page. Expecting form fields: selected_QTID position_QTID name_QTID where QTID is a question template id. May receive many. new_position new_name new_type select_cmd = 'copy' | 'move' select_target = TOPICID of target topic """ form = request.form mesg = [] # Make a list of all the commands to run cmdlist = [] for command in request.form.keys(): (cmd, data) = command.split('_', 2) value = form[command] if not value == "none": cmdlist += [{'cmd': cmd, 'data': data, 'value': value}] # Now run them: # Titles first for command in [cmd for cmd in cmdlist if cmd['cmd'] == 'name']: qid = int(command['data']) title = command['value'] DB.update_qt_title(qid, title) # Then positions for command in [cmd for cmd in cmdlist if cmd['cmd'] == 'position']: qtid = int(command['data']) try: position = int(command['value']) except ValueError: position = 0 DB.update_qt_pos(qtid, topic_id, position) # Then commands on selected questions target_cmd = form.get('target_cmd', None) if target_cmd: qtids = [int(cmd['data']) for cmd in cmdlist if cmd['cmd'] == 'select'] try: target_topic = int(form.get('target_topic', 0)) except ValueError: target_topic = None if target_cmd == 'move': if target_topic: for qtid in qtids: qt_title = DB.get_qt_name(qtid) topic_title = Topics.get_name(target_topic) flash("Moving %s to %s" % (qt_title, topic_title)) DB.move_qt_to_topic(qtid, target_topic) Topics.flush_num_qs(topic_id) Topics.flush_num_qs(target_topic) if target_cmd == 'copy': if target_topic: for qtid in qtids: qt_title = DB.get_qt_name(qtid) topic_title = Topics.get_name(target_topic) flash("Copying %s to %s" % (qt_title, topic_title)) newid = DB.copy_qt_all(qtid) DB.add_qt_to_topic(newid, target_topic) Topics.flush_num_qs(target_topic) if target_cmd == 'hide': for qtid in qtids: position = DB.get_qtemplate_topic_pos(qtid, topic_id) if position > 0: # If visible, make it hidden DB.update_qt_pos(qtid, topic_id, -position) title = DB.get_qt_name(qtid) flash("Made '%s' Hidden" % title) Topics.flush_num_qs(topic_id) if target_cmd == 'show': for qtid in qtids: position = DB.get_qtemplate_topic_pos(qtid, topic_id) if position == 0: # If hidden, make it visible newpos = DB.get_qt_max_pos_in_topic(topic_id) DB.update_qt_pos(qtid, topic_id, newpos + 1) Topics.flush_num_qs(topic_id) title = DB.get_qt_name(qtid) flash("Made '%s' Visible" % title) if position < 0: # If hidden, make it visible DB.update_qt_pos(qtid, topic_id, -position) Topics.flush_num_qs(topic_id) title = DB.get_qt_name(qtid) flash("Made '%s' Visible" % title) if target_cmd == "export": if len(qtids) < 1: flash("No questions selected to export") else: data = External.qts_to_zip(qtids, fname="oa_export", suffix="oaq") if not data: abort(401) sio = StringIO.StringIO(data) return 2, send_file(sio, "application/oasisqe", as_attachment=True, attachment_filename="oa_export.zip") # Then new questions new_title = form.get('new_title', None) if new_title: if not (new_title == "[New Question]" or new_title == ""): new_position = form.get('new_position', 0) try: new_position = int(new_position) except ValueError: new_position = 0 new_qtype = form.get('new_qtype', 'raw') try: new_maxscore = float(form.get('new_maxscore', 0)) except ValueError: new_maxscore = 0 newid = DB.create_qt(user_id, new_title, "No Description", 1, new_maxscore, 0) if newid: mesg.append("Created new question, id %s" % newid) DB.update_qt_pos(newid, topic_id, new_position) DB.create_qt_att(newid, "qtemplate.html", "application/oasis-html", "empty", 1) DB.create_qt_att(newid, "qtemplate.html", "application/oasis-html", "empty", 1) if new_qtype == "oqe": mesg.append("Creating new question, id %s as OQE" % newid) DB.create_qt_att(newid, "_editor.oqe", "application/oasis-oqe", "", 1) if new_qtype == "raw": mesg.append("Creating new question, id %s as RAW (%s)" % (newid, new_qtype)) DB.create_qt_att(newid, "datfile.txt", "application/oasis-dat", "0", 1) else: mesg.append("Error creating new question, id %s" % newid) L.error("Unable to create new question (%s) (%s)" % (new_title, new_position)) Topics.flush_num_qs(topic_id) return 1, {'mesg': mesg}