def admin_add_userfeed(): """ Present page to add a user feed to the system """ try: scripts = External.feeds_available_user_scripts() except OSError, err: flash(err) scripts = []
def admin_add_feed(): """ Present page to add a feed to the system """ try: scripts = External.feeds_available_group_scripts() except OSError, err: flash(err) scripts = []
def cadmin_course_questions_import(course_id, topic_id): """ Take an OAQ file and import any questions in it into topic """ if 'importfile' in request.files: data = request.files['importfile'].read() else: flash("No file uploaded?") return redirect(url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) if len(data) > 52000000: # approx 50Mb flash("Upload is too large, 50MB Maximum.") return redirect(url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) num = External.import_qts_from_zip(data) if num is False: flash("Invalid OASISQE file? No data recognized.") return redirect(url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) if num is 0: flash("Empty OASISQE file? No questions found.") return redirect(url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) flash("%s questions imported!" % num) return redirect(url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id))
def admin_group_update_from_feed(group_id): """ Update group membership from feed """ group = None added = [] removed = [] unknown = [] error = None try: group = Groups.Group(g_id=group_id) except KeyError: abort(401) if not group.source == "feed": abort(401) try: (added, removed, unknown) = External.group_update_from_feed(group_id) except BaseException as err: error = err members = group.member_unames() return render_template( "admin_run_group_feed.html", added=added, removed=removed, unknown=unknown, error=error, members=members )
def group_test_feed_output(group_id): """ Run the feed script for the group and return the output or error message. """ error = None output = "" group = None try: group = Groups.Group(g_id=group_id) except KeyError: abort(401) if not group.source == "feed": abort(401) feed = Feeds.Feed(f_id=group.feed) period = Periods.Period(p_id=group.period) scriptrun = ' '.join([feed.script, group.feedargs, period.code]) try: output = External.feeds_run_group_script(feed.script, args=[group.feedargs, period.code]) except BaseException as err: error = err return render_template( "admin_test_group_feed.html", output=output, error=error, scriptrun=scriptrun )
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 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 admin_edit_userfeed(feed_id): """ Present page to edit a user feed in the system """ try: feed = UFeeds.UFeed(f_id=feed_id) except KeyError: return abort(404) try: scripts = External.feeds_available_user_scripts() except OSError, err: flash(err) scripts = []
def admin_add_userfeed(): """ Present page to add a user feed to the system """ try: scripts = External.feeds_available_user_scripts() except OSError as err: flash(err) scripts = [] return render_template( "admin_edit_user_feed.html", feed={'id': 0}, scripts=scripts )
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 admin_edit_userfeed(feed_id): """ Present page to edit a user feed in the system """ try: feed = UFeeds.UFeed(f_id=feed_id) except KeyError: return abort(404) try: scripts = External.feeds_available_user_scripts() except OSError as err: flash(err) scripts = [] return render_template( "admin_edit_user_feed.html", feed=feed, scripts=scripts )
def _import_questions_from_file(data, topic_id): """ Take a data string with a question export and import questions from it into the given topic. :returns [string,]: list of (string) messages """ mesg = [] if len(data) > 52000000: # approx 50Mb mesg.append("Upload is too large, 50MB Maximum.") num = External.import_qts_from_zip(data, topic_id=topic_id) if num is False: mesg.append("Invalid OASISQE file? No data recognized.") if num is 0: mesg.append("Empty OASISQE file? No questions found.") mesg.append("%s questions imported!" % num) return mesg
def admin_edit_feed(feed_id): """ Present page to edit a feed in the system """ try: feed = Feeds.Feed(f_id=feed_id) except KeyError: return abort(404) try: scripts = External.feeds_available_group_scripts() except OSError as err: flash(err) scripts = [] return render_template( "admin_edit_group_feed.html", feed=feed, scripts=scripts )
def admin_group_update_from_feed(group_id): """ Update group membership from feed """ group = None added = [] removed = [] unknown = [] error = None try: group = Groups.Group(g_id=group_id) except KeyError: abort(401) if not group.source == "feed": abort(401) try: (added, removed, unknown) = External.group_update_from_feed(group_id) except BaseException, err: error = err
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 cadmin_course_questions_import(course_id, topic_id): """ Take an OAQ file and import any questions in it into topic """ if 'importfile' in request.files: data = request.files['importfile'].read() else: flash("No file uploaded?") return redirect( url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) if len(data) > 52000000: # approx 50Mb flash("Upload is too large, 50MB Maximum.") return redirect( url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) num = External.import_qts_from_zip(data, topicid=topic_id) if num is False: flash("Invalid OASISQE file? No data recognized.") return redirect( url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) if num is 0: flash("Empty OASISQE file? No questions found.") return redirect( url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_id)) flash("%s questions imported!" % num) return redirect( url_for("cadmin_edit_topic", course_id=course_id, topic_id=topic_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) 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 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 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}