Пример #1
0
def gen_q(qtid, student=0, exam=0, position=0):
    """ Given a qtemplate, will generate a question instance.
        If student and/or exam is supplied it will be assigned appropriately.
        If exam is supplied, position must also be supplied.
        Will return the ID of the created instance.
    """
    # Pick a variation randomly
    version = DB.get_qt_version(qtid)
    numvars = DB.get_qt_num_variations(qtid, version)
    if numvars > 0:
        variation = random.randint(1, numvars)
    else:
        L.warn("No question variations (qtid=%d)" % qtid)
        Audit.audit(
            3, student, qtid, "General",
            "Failed to generate question %s for %s, exam %s" %
            (qtid, student, exam))

        return False
    q_id = gen_q_from_var(qtid, student, exam, position, version, variation)
    if not q_id:
        Audit.audit(
            3, student, qtid, "General",
            "Failed to generate instance of %s for %s, exam %s" %
            (qtid, student, exam))
    return q_id
Пример #2
0
def render_q_html(q_id, readonly=False):
    """ Fetch the question html and get it ready for display - replacing
        links with appropriate targets and filling in form details."""
    try:
        q_id = int(q_id)
        assert q_id > 0
    except (ValueError, TypeError, AssertionError):
        log(WARN,
            "renderQuestionHTML(%s,%s) called with bad qid?" % (q_id, readonly))
    qt_id = DB.get_q_parent(q_id)
    try:
        qt_id = int(qt_id)
        assert qt_id > 0
    except (ValueError, TypeError, AssertionError):
        log(WARN,
            "renderQuestionHTML(%s,%s), getparent failed? " % (q_id, readonly))
    variation = DB.get_q_variation(q_id)
    version = DB.get_q_version(q_id)
    data = DB.get_q_att(qt_id, "qtemplate.html", variation, version)
    if not data:
        log(WARN,
            "Unable to retrieve qtemplate for q_id: %s" % q_id)
        return "QuestionError"
    try:
        out = unicode(data, "utf-8")
    except UnicodeDecodeError:
        try:
            out = unicode(DB.get_q_att(qt_id, "qtemplate.html", variation, version),
                          "latin-1")
        except UnicodeDecodeError, err:
            log(ERROR,
                "unicode error decoding qtemplate for q_id %s: %s" % (q_id, err))
            raise
Пример #3
0
def remark_exam(exam, student):
    """Re-mark the exam using the latest marking. """
    qtemplates = Exams.get_qts(exam)
    examtotal = 0.0
    end = Exams.get_mark_time(exam, student)
    for qtemplate in qtemplates:
        question = DB.get_exam_q_by_qt_student(exam, qtemplate, student)
        answers = DB.get_q_guesses_before_time(question, end)
        try:
            marks = mark_q(question, answers)
        except OaMarkerError:
            L.warn("Marker Error, question %d while re-marking exam %s for student %s!" % (question, exam, student))
            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['C%d' % part] == 'Correct':
                marks['C%d' % part] = "<b><font color='darkgreen'>Correct</font></b>"
            try:
                mark = float(marks['M%d' % part])
            except (ValueError, TypeError, KeyError):
                mark = 0
            total += mark
        DB.update_q_score(question, total)
        #        OaDB.setQuestionStatus(question, 3)    # 3 = marked
        examtotal += total
    Exams.save_score(exam, student, examtotal)
    return examtotal
Пример #4
0
def remark_exam(exam, student):
    """Re-mark the exam using the latest marking. """
    qtemplates = Exams.get_qts(exam)
    examtotal = 0.0
    end = Exams.get_mark_time(exam, student)
    for qtemplate in qtemplates:
        question = DB.get_exam_q_by_qt_student(exam, qtemplate, student)
        answers = DB.get_q_guesses_before_time(question, end)
        try:
            marks = mark_q(question, answers)
        except OaMarkerError:
            L.warn(
                "Marker Error, question %d while re-marking exam %s for student %s!"
                % (question, exam, student))
            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['C%d' % part] == 'Correct':
                marks['C%d' %
                      part] = "<b><font color='darkgreen'>Correct</font></b>"
            try:
                mark = float(marks['M%d' % part])
            except (ValueError, TypeError, KeyError):
                mark = 0
            total += mark
        DB.update_q_score(question, total)
        #        OaDB.setQuestionStatus(question, 3)    # 3 = marked
        examtotal += total
    Exams.save_score(exam, student, examtotal)
    return examtotal
Пример #5
0
def exam_edit_submit(request, user_id, cid, exam_id):
    """ Accept the submitted exam edit/create form.
        If exam_id is not provided, create a new one.
    """

    # TODO: More validation. Currently we trust the client validation,
    # although the user is authenticated staff so probably not too high a
    # risk of shenanigans.

    title = str(request.form['assess_title'])
    atype = int(request.form['assess_type'])
    startdate = request.form['startdate']
    starthour = int(request.form['examstart_hour'])
    startmin = int(request.form['examstart_minute'])
    enddate = request.form['enddate']
    endhour = int(request.form['examend_hour'])
    endmin = int(request.form['examend_minute'])
    duration = int(request.form['duration'])
    code = request.form['assess_code']
    instant = int(request.form['assess_instant'])
    if "instructions" in request.form:
        instructions = request.form['instructions']
    else:
        instructions = ""

    astart = datetime.datetime.strptime(startdate, "%a %d %b %Y")
    astart = astart.replace(hour=starthour, minute=startmin)
    aend = datetime.datetime.strptime(enddate, "%a %d %b %Y")
    aend = aend.replace(hour=endhour, minute=endmin)

    qns = {}
    for k in request.form.keys():

        v = request.form.getlist(k)
        if k.startswith("question_"):
            _, q, p = k.split("_")
            if not q in qns:
                qns[q] = []
            if not v[0] == '---':
                qns[q].append(int(v[0]))

    if not exam_id:
        exam_id = Exams.create(cid, user_id, title, atype, duration, astart,
                               aend, instructions, code=code, instant=instant)
    else:  # update
        Exams.set_title(exam_id, title)
        Exams.set_duration(exam_id, duration)
        Exams.set_type(exam_id, atype)
        Exams.set_description(exam_id, instructions)
        Exams.set_start_time(exam_id, astart)
        Exams.set_end_time(exam_id, aend)
        Exams.set_code(exam_id, code)
        Exams.set_instant(exam_id, instant)

    for pos, qts in qns.iteritems():
        if pos:
            DB.update_exam_qt_in_pos(exam_id, int(pos), qts)

    return exam_id
Пример #6
0
def teardown():
    """
        Remove testing configuration file and otherwise clean up.
    """
    with open(os.path.join(OaConfig.homedir, "sql", "eraseexisting.sql")) as f:
        sql = f.read()
    print "Removing tables."
    DB.run_sql(sql)
Пример #7
0
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="?",
        )
Пример #8
0
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
    )
Пример #9
0
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
Пример #10
0
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
Пример #11
0
def gen_q(qtid, student=0, exam=0, position=0):
    """ Given a qtemplate, will generate a question instance.
        If student and/or exam is supplied it will be assigned appropriately.
        If exam is supplied, position must also be supplied.
        Will return the ID of the created instance.
    """
    # Pick a variation randomly
    version = DB.get_qt_version(qtid)
    numvars = DB.get_qt_num_variations(qtid, version)
    if numvars > 0:
        variation = random.randint(1, numvars)
    else:
        L.warn("No question variations (qtid=%d)" % qtid)
        return False
    return gen_q_from_var(qtid, student, exam, position, version, variation)
Пример #12
0
def render_mark_results(qid, marks):
    """Take the marking results and display something for the student
       that tells them what they got right and wrong.
       If the question has an attachment "_rendermarks.py", it will be called,
       otherwise a default HTML table will be returned. _rendermarks.py should
       set variable "resultsHTML" to contain a suitable string for putting
       in an HTML page.
    """
    qtid = DB.get_q_parent(qid)
    renderscript = DB.get_qt_att(qtid, "__results.py")
    if not renderscript:
        resultshtml = render_mark_results_standard(qid, marks)
    else:
        resultshtml = render_mark_results_script(qtid, qid, marks, renderscript)
    return resultshtml
Пример #13
0
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
    )
Пример #14
0
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}
        ]
    })
Пример #15
0
def render_mark_results(qid, marks):
    """Take the marking results and display something for the student
       that tells them what they got right and wrong.
       If the question has an attachment "_rendermarks.py", it will be called,
       otherwise a default HTML table will be returned. _rendermarks.py should
       set variable "resultsHTML" to contain a suitable string for putting
       in an HTML page.
    """
    qtid = DB.get_q_parent(qid)
    renderscript = DB.get_qt_att(qtid, "__results.py")
    if not renderscript:
        resultshtml = render_mark_results_standard(qid, marks)
    else:
        resultshtml = render_mark_results_script(qtid, qid, marks,
                                                 renderscript)
    return resultshtml
Пример #16
0
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
Пример #17
0
def setup():
    """ Prepare database for testing.
    """
    if not DB.check_safe():
        print "Attempt to erase database with data."
        sys.exit(-1)
    with open(os.path.join(OaConfig.homedir, "sql", "eraseexisting.sql")) as f:
        sql = f.read()
    print "Removing existing tables."
    DB.run_sql(sql)

    with open(os.path.join(OaConfig.homedir, "sql", "emptyschema_396.sql")) as f:
        sql = f.read()

    DB.run_sql(sql)
    print "Installed v3.9.6 table structure."
Пример #18
0
def get_exam_q(exam, page, user_id):
    """ Find the appropriate exam question for the user.
        Generate it if there isn't one already.
    """
    qid = DB.get_exam_q_by_pos_student(exam, page, user_id)
    if qid is not False:
        return int(qid)
    qid = int(gen_exam_q(exam, page, user_id))
    try:
        qid = int(qid)
        assert qid > 0
    except (ValueError, TypeError, AssertionError):
        L.warn("generateExamQuestion(%s,%s, %s) Failed (returned %s)" %
            (exam, page, user_id, qid))
    DB.set_q_viewtime(qid)
    return qid
Пример #19
0
    def setUpClass(cls):

        cls.app = app
        cls.app.testing = True
        cls.app.config["TESTING"] = True
        cls.adminpass = DB.generate_admin_passwd()
        (fptr, cls.test_question_fname) = tempfile.mkstemp()
        os.close(fptr)
Пример #20
0
    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)
Пример #21
0
def mark_q(qid, answers):
    """ Mark the question according to the answers given in a dictionary and
        return the result in a dictionary:
        input:    {"A1":"0.345", "A2":"fred", "A3":"-26" }
        return:   {"M1": Mark One, "C1": Comment One, "M2": Mark Two..... }
    """
    qtid = DB.get_q_parent(qid)
    version = DB.get_q_version(qid)
    variation = DB.get_q_variation(qid)
    qvars = DB.get_qt_variation(qtid, variation, version)
    if not qvars:
        qvars = {}
        L.warn("markQuestion(%s, %s) unable to retrieve variables." %
            (qid, answers))
    qvars['OaQID'] = int(qid)
    marktype = DB.get_qt_marker(qtid)
    if marktype == 1:    # standard
        marks = mark_q_standard(qvars, answers)
    else:
        # We want the latest version of the marker, so no version given
        markerscript = DB.get_qt_att(qtid, "__marker.py")
        if not markerscript:
            markerscript = DB.get_qt_att(qtid, "marker.py")
            L.info("'marker.py' should now be called '__marker.py' (qtid=%s)" % qtid)
        if not markerscript:
            L.info("Unable to retrieve marker script for smart marker question (qtid=%s)!" % qtid)
            marks = mark_q_standard(qvars, answers)
        else:
            marks = mark_q_script(qvars, markerscript, answers)
    return marks
Пример #22
0
def mark_q(qid, answers):
    """ Mark the question according to the answers given in a dictionary and
        return the result in a dictionary:
        input:    {"A1":"0.345", "A2":"fred", "A3":"-26" }
        return:   {"M1": Mark One, "C1": Comment One, "M2": Mark Two..... }
    """
    qtid = DB.get_q_parent(qid)
    version = DB.get_q_version(qid)
    variation = DB.get_q_variation(qid)
    qvars = DB.get_qt_variation(qtid, variation, version)
    if not qvars:
        qvars = {}
        L.warn("markQuestion(%s, %s) unable to retrieve variables." %
               (qid, answers))
    qvars['OaQID'] = int(qid)
    marktype = DB.get_qt_marker(qtid)
    if marktype == 1:  # standard
        marks = mark_q_standard(qvars, answers)
    else:
        # We want the latest version of the marker, so no version given
        markerscript = DB.get_qt_att(qtid, "__marker.py")
        if not markerscript:
            markerscript = DB.get_qt_att(qtid, "marker.py")
            L.info("'marker.py' should now be called '__marker.py' (qtid=%s)" %
                   qtid)
        if not markerscript:
            L.info(
                "Unable to retrieve marker script for smart marker question (qtid=%s)!"
                % qtid)
            marks = mark_q_standard(qvars, answers)
        else:
            marks = mark_q_script(qvars, markerscript, answers)
    return marks
Пример #23
0
def setup():
    """ Prepare database  and configuration file for testing
    """
    # Switch us to use a test database
    test_config = """



    """

    f = open(TESTINI, "w")
    f.write(test_config)
    f.close()
    global DB

    from oasis.lib import DB  # Do this *after* writing the test ini file.

    DB.run_sql("SELECT name, value FROM config WHERE name='test_status';")
Пример #24
0
def setup():
    """ Prepare database  and configuration file for testing
    """
    # Switch us to use a test database
    test_config = """



    """

    f = open(TESTINI, "w")
    f.write(test_config)
    f.close()
    global DB

    from oasis.lib import DB  # Do this *after* writing the test ini file.

    DB.run_sql("SELECT name, value FROM config WHERE name='test_status';")
Пример #25
0
def get_exam_q(exam, page, user_id):
    """ Find the appropriate exam question for the user.
        Generate it if there isn't one already.
    """
    qid = DB.get_exam_q_by_pos_student(exam, page, user_id)
    if qid is not False:
        return int(qid)
    qid = int(gen_exam_q(exam, page, user_id))
    try:
        qid = int(qid)
        assert qid > 0
    except (ValueError, TypeError, AssertionError):
        L.warn("generateExamQuestion(%s,%s, %s) Failed (returned %s)" %
               (exam, page, user_id, qid))
        qid = None
    if qid:
        DB.set_q_viewtime(qid)
    return qid
Пример #26
0
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
Пример #27
0
def gen_q(qtid, student=0, exam=0, position=0, variation=None):
    """ Given a qtemplate, will generate a question instance.
        If student and/or exam is supplied it will be assigned appropriately.
        If exam is supplied, position must also be supplied.
        Will return the ID of the created instance.
    """
    # Pick a variation randomly if not supplied
    version = DB.get_qt_version(qtid)
    numvars = DB.get_qt_num_variations(qtid, version)
    if variation is None and numvars > 0:
        variation = random.randint(1, numvars)
    if numvars == 0:
        L.warn("No question variations (qtid=%d)" % qtid)
        Audit.audit(3, student, qtid, "General", "Failed to generate question %s for %s, exam %s" % (qtid, student, exam))
        return False
    q_id = gen_q_from_var(qtid, student, exam, position, version, variation)
    if not q_id:
        Audit.audit(3, student, qtid, "General", "Failed to generate instance of %s for %s, exam %s" % (qtid, student, exam))
    return q_id
Пример #28
0
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,
    )
Пример #29
0
def create_new(qt_id, name):
    """
    Set the given qtemplate up as a new (default) question. Makes sure
    the appropriate things the editor needs are configured and in place.
    Assumes the QT has not previously been set up.

    :param qt_id: The ID of the qtemplate to set up.
    :param name: Name of the question.
    :return:
    """

    # The _editor.qe2 file contains a json object with most of the structural
    # information about the question.
    default_ = {
        'name': name,
        'qe_version': 0.1
    }
    DB.create_qt_att(qt_id,
                     "__editor.qe2",
                     "application/oasis-qe2",
                     json.dumps(default_),
                     1)
    return
Пример #30
0
def remark_prac(question):
    """ Re-mark the practice question and store the score back
        in the questions table.
    """
    answers = DB.get_q_guesses(question)
    try:
        marks = mark_q(question, answers)
    except OaMarkerError:
        return None
    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:
        try:
            mark = float(marks['M%d' % part])
        except (ValueError, TypeError, KeyError):
            mark = 0
        total += mark
    DB.update_q_score(question, total)
    DB.set_q_status(question, 3)    # 3 = marked
    return total
Пример #31
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
    )
Пример #32
0
def cadmin_prev_assessments(course_id):
    """ Show a list of older assessments."""
    course = Courses2.get_course(course_id)
    if not course:
        abort(404)

    exams = [
        Exams.get_exam_struct(exam_id, course_id) for exam_id in DB.get_course_exam_all(course_id, prev_years=True)
    ]

    years = [exam["start"].year for exam in exams]
    years = list(set(years))
    years.sort(reverse=True)
    exams.sort(key=lambda y: y["start_epoch"])
    return render_template("courseadmin_previousassessments.html", course=course, exams=exams, years=years)
Пример #33
0
def remark_prac(question):
    """ Re-mark the practice question and store the score back
        in the questions table.
    """
    answers = DB.get_q_guesses(question)
    try:
        marks = mark_q(question, answers)
    except OaMarkerError:
        return None
    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:
        try:
            mark = float(marks['M%d' % part])
        except (ValueError, TypeError, KeyError):
            mark = 0
        total += mark
    DB.update_q_score(question, total)
    DB.set_q_status(question, 3)  # 3 = marked
    return total
Пример #34
0
def process_save(qt_id, topic_id, request, session, version):
    """ Have received a web form POST to save the current changes.

    :param qt_id: ID of the question template being edited
    :param topic_id: Topic the question template is in
    :param request: The POST request.
    :param session: The web session object
    :param version: (int) the new version of the qt being saved
    :return: None
    """

    form = request.form

    if 'blocks' in form:
        blocks = form['blocks'].encode("utf8")
        DB.create_qt_att(qt_id,
                         "__editor.qe2",
                         "text/json",
                         blocks,
                         version)
    else:
        raise KeyError("blocks")

    return
Пример #35
0
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
Пример #36
0
def gen_exam_q(exam, position, student):
    """ Generate an exam question instance for the given student and exam.
        If there are multiple qtemplates listed in a given position, one will
        be chosen at random.
    """
    qtemplates = DB.get_exam_qts_in_pos(exam, position)
    if not qtemplates:
        L.warn("DB.get_exam_qts_in_pos(%s,%s) returned a non list." %
               (exam, position))
        return False
    if len(qtemplates) < 1:
        L.warn("DB.get_exam_qts_in_pos(%s,%s) returned an empty list." %
               (exam, position))
        return False
    whichqtemplate = random.randint(1, len(qtemplates))
    qtid = qtemplates[whichqtemplate - 1]  # lists count from 0
    return gen_q(qtid, student, exam, position)
Пример #37
0
def gen_exam_q(exam, position, student):
    """ Generate an exam question instance for the given student and exam.
        If there are multiple qtemplates listed in a given position, one will
        be chosen at random.
    """
    qtemplates = DB.get_exam_qts_in_pos(exam, position)
    if not qtemplates:
        L.warn("DB.get_exam_qts_in_pos(%s,%s) returned a non list." %
            (exam, position))
        return False
    if len(qtemplates) < 1:
        L.warn("DB.get_exam_qts_in_pos(%s,%s) returned an empty list." %
            (exam, position))
        return False
    whichqtemplate = random.randint(1, len(qtemplates))
    qtid = qtemplates[whichqtemplate - 1]   # lists count from 0
    return gen_q(qtid, student, exam, position)
Пример #38
0
def q_att_details(qtid, version, variation, name):
    """ Find a question attachment and return its details. """
    # for the two biggies we hit the question first,
    # otherwise check the question template first
    if name == "image.gif" or name == "qtemplate.html":
        fname = DB.get_q_att_fname(qtid, name, variation, version)
        if fname:
            return DB.get_q_att_mimetype(qtid, name, variation, version), fname
        fname = DB.get_qt_att_fname(qtid, name, version)
        if fname:
            return DB.get_qt_att_mimetype(qtid, name, version), fname
    else:
        fname = DB.get_qt_att_fname(qtid, name, version)
        if fname:
            return DB.get_qt_att_mimetype(qtid, name, version), fname
        fname = DB.get_q_att_fname(qtid, name, variation, version)
        if fname:
            return DB.get_q_att_mimetype(qtid, name, variation, version), fname
    return None, None
Пример #39
0
def qtlog_as_html(topic, qtid):
    """Show the most recent log errors for the given qtid.
    """
    versionre = re.compile(r'version=(\d+),')
    variationre = re.compile(r'variation=(\d+),')
    priorityre = re.compile(r'priority=([^,]+),')
    facilityre = re.compile(r'facility=([^,]+),')
    messagere = re.compile(r'message=(.+)$', re.MULTILINE)

    out = ""
    name = DB.get_qt_name(qtid)
    out += "<h2>Log Entries for %s, topic %s</h2>" % (name, topic)
    out += "<p><i>These can be created from within __marker.py or __results.py by calling "
    out += "log(priority, mesg), for example:</i> "
    out += "<pre>log('error','User entered a value we can't parse.')</pre></p>"
    out += "<p><i>Typical priorities might be 'error', 'info', 'noise'</i></p>"
    out += "<table style='border: solid 1px black;' border='1'><tr><th>Time</th><th>Ver</th>"
    out += "<th>Variation</th><th>Pri</th><th>Fac</th><th>Message</th></tr>"
    entries = Audit.get_records_by_object(qtid, limit=100, offset=0)
    for entry in entries:
        try:
            version = versionre.findall(entry['message'])[0]
        except (IndexError, TypeError):
            version = '.'
        try:
            variation = variationre.findall(entry['message'])[0]
        except (IndexError, TypeError):
            variation = '.'
        try:
            priority = priorityre.findall(entry['message'])[0]
        except (IndexError, TypeError):
            priority = '.'
        try:
            facility = facilityre.findall(entry['message'])[0]
        except (IndexError, TypeError):
            facility = '.'
        try:
            message = messagere.findall(entry['message'])[0]
        except (IndexError, TypeError):
            message = '.'
        out += "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (entry['time'].strftime("%Y/%b/%d %H:%M:%S"), version, variation, priority, facility, message)
    out += "</table>"
    return out
Пример #40
0
def index():
    """ Main landing page. Welcome them and give them some login instructions.
    """
    if 'user_id' in session:
        return redirect(url_for("main_top"))

    if OaConfig.default == "landing":
        mesg_login = DB.get_message("loginmotd")
        alt_landing = os.path.join(OaConfig.theme_path, "landing_page.html")
        if os.path.isfile(alt_landing):
            tmpf = open(alt_landing)
            tmpl = tmpf.read()
            tmpf.close()
            return render_template_string(tmpl, mesg_login=mesg_login)
        return render_template("landing_page.html", mesg_login=mesg_login)
    if OaConfig.default == "locallogin":
        return redirect(url_for("login_local"))
    if OaConfig.default == "webauth":
        return redirect(url_for("login_webauth_submit"))
    return render_template("landing_page.html")
Пример #41
0
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
Пример #42
0
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
Пример #43
0
def exam_edit_submit(request, user_id, cid, exam_id):
    """ Accept the submitted exam edit/create form.
        If exam_id is not provided, create a new one.
    """

    # TODO: More validation. Currently we trust the client validation,
    # although the user is authenticated staff so probably not too high a
    # risk of shenanigans.

    title = str(request.form['assess_title'])
    atype = int(request.form['assess_type'])
    startdate = request.form['startdate']
    starthour = int(request.form['examstart_hour'])
    startmin = int(request.form['examstart_minute'])
    enddate = request.form['enddate']
    endhour = int(request.form['examend_hour'])
    endmin = int(request.form['examend_minute'])
    duration = int(request.form['duration'])
    code = request.form['assess_code']
    instant = int(request.form['assess_instant'])
    if "instructions" in request.form:
        instructions = request.form['instructions']
    else:
        instructions = ""

    astart = datetime.datetime.strptime(startdate, "%a %d %b %Y")
    astart = astart.replace(hour=starthour, minute=startmin)
    aend = datetime.datetime.strptime(enddate, "%a %d %b %Y")
    aend = aend.replace(hour=endhour, minute=endmin)

    qns = {}
    for k in request.form.keys():

        v = request.form.getlist(k)
        if k.startswith("question_"):
            _, q, p = k.split("_")
            if q not in qns:
                qns[q] = []
            if not v[0] == '---':
                qns[q].append(int(v[0]))

    if not exam_id:
        exam_id = Exams.create(cid,
                               user_id,
                               title,
                               atype,
                               duration,
                               astart,
                               aend,
                               instructions,
                               code=code,
                               instant=instant)
    else:  # update
        Exams.set_title(exam_id, title)
        Exams.set_duration(exam_id, duration)
        Exams.set_type(exam_id, atype)
        Exams.set_description(exam_id, instructions)
        Exams.set_start_time(exam_id, astart)
        Exams.set_end_time(exam_id, aend)
        Exams.set_code(exam_id, code)
        Exams.set_instant(exam_id, instant)

    for pos, qts in qns.iteritems():
        if pos:
            DB.update_exam_qt_in_pos(exam_id, int(pos), qts)

    return exam_id
Пример #44
0
def get_q_att(qid, name):
    """ Return (mimetype, data) with the relevant attachment.
        If it's not found in question, look in questiontemplate.
    """
    qtid = DB.get_q_parent(qid)
    variation = DB.get_q_variation(qid)
    version = DB.get_q_version(qid)
    # for the two biggies we hit the question first,
    # otherwise check the question template first
    if name == "image.gif" or name == "qtemplate.html":
        data = DB.get_q_att(qtid, name, variation, version)
        if data:
            return DB.get_q_att_mimetype(qtid, name, variation, version), data
        data = DB.get_qt_att(qtid, name, version)
        if data:
            return DB.get_qt_att_mimetype(qtid, name, version), data
    else:
        data = DB.get_qt_att(qtid, name, version)
        if data:
            return DB.get_qt_att_mimetype(qtid, name, version), data
        data = DB.get_q_att(qtid, name, variation, version)
        if data:
            return DB.get_q_att_mimetype(qtid, name, variation, version), data
    return None, None
Пример #45
0
def render_q_html(q_id, readonly=False):
    """ Fetch the question html and get it ready for display - replacing
        links with appropriate targets and filling in form details."""
    try:
        q_id = int(q_id)
        assert q_id > 0
    except (ValueError, TypeError, AssertionError):
        L.warn("renderQuestionHTML(%s,%s) called with bad qid?" %
               (q_id, readonly))
    qt_id = DB.get_q_parent(q_id)
    try:
        qt_id = int(qt_id)
        assert qt_id > 0
    except (ValueError, TypeError, AssertionError):
        L.warn("renderQuestionHTML(%s,%s), getparent failed? " %
               (q_id, readonly))
    variation = DB.get_q_variation(q_id)
    version = DB.get_q_version(q_id)
    data = DB.get_q_att(qt_id, "qtemplate.html", variation, version)
    if not data:
        L.warn("Unable to retrieve qtemplate for q_id: %s" % q_id)
        return "QuestionError"
    try:
        out = unicode(data, "utf-8")
    except UnicodeDecodeError:
        try:
            out = unicode(
                DB.get_q_att(qt_id, "qtemplate.html", variation, version),
                "latin-1")
        except UnicodeDecodeError as err:
            L.error("unicode error decoding qtemplate for q_id %s: %s" %
                    (q_id, err))
            raise
    out = out.replace(
        "This question is not verified yet, please report any error!", "")

    out = out.replace("ANS_", "Q_%d_ANS_" % (q_id, ))
    out = out.replace(
        "$IMAGES$", "%s/att/qatt/%s/%s/%s/" %
        (OaConfig.parentURL, qt_id, version, variation))
    out = out.replace(
        "$APPLET$", "%s/att/qatt/%s/%s/%s/" %
        (OaConfig.parentURL, qt_id, version, variation))
    out = out.replace(
        "$STATIC$", "%s/att/qtatt/%s/%s/%s/" %
        (OaConfig.parentURL, qt_id, version, variation))
    if readonly:
        out = out.replace("<INPUT ", "<INPUT READONLY ")
        out = out.replace("<SELECT ",
                          "<SELECT DISABLED=DISABLED STYLE='color: black;'")
    guesses = DB.get_q_guesses(q_id)
    for guess in guesses.keys():
        # noinspection PyComparisonWithNone
        if guesses[guess] == None:  # If it's 0 we want to leave it alone
            guesses[guess] = ""
        if guesses[guess] == "None":
            guesses[guess] = ""
            # for each question
    if guesses:
        for ques in range(25, 0, -1):
            if ("G%d" % ques) in guesses:
                out = out.replace("VAL_%d" % ques,
                                  htmlesc(guesses["G%d" % ques]))
                for part in range(50, 0, -1):
                    if guesses["G%d" % ques] == "%s.0" % part or guesses[
                            "G%d" % ques] == "%s" % part:
                        out = out.replace("Oa_SEL_%d_%d" % (ques, part),
                                          "SELECTED")
                        out = out.replace("Oa_CHK_%d_%d" % (ques, part),
                                          "CHECKED")
                    else:
                        out = out.replace("Oa_SEL_%d_%d" % (ques, part), "")
                        out = out.replace("Oa_CHK_%d_%d" % (ques, part), "")
            else:
                out = out.replace("VAL_%d" % (ques, ), "")
    for ques in range(25, 0, -1):
        out = out.replace("VAL_%d" % (ques, ), "")
    return out
Пример #46
0
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,
    )
Пример #47
0
def qts_to_zip(qt_ids, extra_info=None):
    """ Take a list of QTemplate IDs and return a binary string containing
        them as an .oaq file.
        (a zip file in special format)
    """

    qdir = tempfile.mkdtemp(prefix="oa")
    info = {
        'oasis': {
            'oa_version': "3.9.4",
            'qt_version': '0.9',
            'url': OaConfig.parentURL
        },
        'qtemplates': {},
        'extra': extra_info
    }

    arc = zipfile.ZipFile(os.path.join(qdir, "oasisqe.zip"),
                          'w',
                          zipfile.ZIP_DEFLATED)
    for qt_id in qt_ids:
        qtemplate = DB.get_qtemplate(qt_id)
        qtdir = os.path.join(qdir, str(qt_id))
        attachments = DB.get_qt_atts(qt_id)
        if 'qtemplate.html' not in attachments:
            attachments.append('qtemplate.html')
        if 'datfile.txt' not in attachments:
            attachments.append('datfile.txt')
        if 'image.gif' not in attachments:
            attachments.append('image.gif')
        os.mkdir(qtdir)
        os.mkdir(os.path.join(qtdir, "attach"))
        info["qtemplates"][qt_id] = {'qtemplate': qtemplate}
        info["qtemplates"][qt_id]["attachments"] = []

        for name in attachments:
            if not name:
                name = 'UNKNOWN'
            mtype = DB.get_qt_att_mimetype(qt_id, name)
            if not mtype:
                mtype = ""
            data = DB.get_qt_att(qt_id, name)
            if not data:
                data = ""
            info["qtemplates"][qt_id]["attachments"].append([name, mtype, len(data)])
            subdir = os.path.join(qtdir, "attach", name)
            outf = open(subdir, "wb")
            outf.write(data)
            outf.close()
            arc.write(subdir,
                      os.path.join("%s" % qt_id, "attach", name),
                      zipfile.ZIP_DEFLATED)

    infof = open(os.path.join(qdir, "info.json"), "wb")
    infof.write(json.dumps(info))
    infof.close()
    arc.write(os.path.join(qdir, "info.json"),
              os.path.join("info.json"),
              zipfile.ZIP_DEFLATED)
    arc.close()

    readback = open(os.path.join(qdir, "oasisqe.zip"), "rb")
    data = readback.read()
    readback.close()
    shutil.rmtree(qdir)
    return data
Пример #48
0
def get_q_att_fname(qid, name):
    """ Return (mimetype, filename) with the relevant filename.
        If it's not found in question, look in questiontemplate.
    """
    qtid = DB.get_q_parent(qid)
    variation = DB.get_q_variation(qid)
    version = DB.get_q_version(qid)
    # for the two biggies we hit the question first,
    # otherwise check the question template first
    if name == "image.gif" or name == "qtemplate.html":

        fname = DB.get_q_att_fname(qtid, name, variation, version)
        if fname:
            return DB.get_q_att_mimetype(qtid, name, variation, version), fname
        fname = DB.get_qt_att_fname(qtid, name, version)
        if fname:
            return DB.get_qt_att_mimetype(qtid, name, version), fname
    else:
        fname = DB.get_qt_att_fname(qtid, name, version)
        if fname:
            return DB.get_qt_att_mimetype(qtid, name, version), fname
        fname = DB.get_q_att_fname(qtid, name, variation, version)
        if fname:
            return DB.get_q_att_mimetype(qtid, name, variation, version), fname
    return None, None
Пример #49
0
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,
    )
Пример #50
0
def gen_q_from_var(qt_id, student, exam, position, version, variation):
    """ Generate a question given a specific variation. """
    qvars = None
    q_id = DB.create_q(qt_id, DB.get_qt_name(qt_id), student, 1, variation,
                       version, exam)
    try:
        q_id = int(q_id)
        assert (q_id > 0)
    except (ValueError, TypeError, AssertionError):
        L.error("OaDB.createQuestion(%s,...) FAILED" % qt_id)
    imageexists = DB.get_q_att_mimetype(qt_id, "image.gif", variation, version)
    if not imageexists:
        if not qvars:
            qvars = DB.get_qt_variation(qt_id, variation, version)
        qvars['Oasis_qid'] = q_id
        image = DB.get_qt_att(qt_id, "image.gif", version)
        if image:
            newimage = gen_q_image(qvars, image)
            DB.create_q_att(qt_id, variation, "image.gif", "image/gif",
                            newimage, version)
    htmlexists = DB.get_q_att_mimetype(qt_id, "qtemplate.html", variation,
                                       version)
    if not htmlexists:
        if not qvars:
            qvars = DB.get_qt_variation(qt_id, variation, version)
        html = DB.get_qt_att(qt_id, "qtemplate.html", version)
        if html:
            qvars['Oasis_qid'] = q_id
            newhtml = gen_q_html(qvars, html)
            L.info("generating new qattach qtemplate.html for %s" % q_id)
            DB.create_q_att(qt_id, variation, "qtemplate.html",
                            "application/oasis-html", newhtml, version)
    try:
        q_id = int(q_id)
        assert (q_id > 0)
    except (ValueError, TypeError, AssertionError):
        L.error("generateQuestionFromVar(%s,%s), can't find qid %s? " %
                (qt_id, student, q_id))
    if exam >= 1:
        DB.add_exam_q(student, exam, q_id, position)
    return q_id
Пример #51
0
def render_mark_results_script(qtid, qid, marks, script):
    """Run the provided script to show the marking for the
       question.
    """
    version = DB.get_q_version(qid)
    variation = DB.get_q_variation(qid)
    qvars = DB.get_qt_variation(qtid, variation, version)
    questionhtml = render_q_html(qid, readonly=True)
    reshtml = ""
    qvars["__builtins__"] = {
        'MyFuncs': OqeSmartmarkFuncs,
        'withinTolerance': script_funcs.within_tolerance,
        'math': math,
        'round': round,
        'float': float,
        'log': script_funcs.result_log_fn(qid),
        'dir': dir,
        'abs': abs,
        'None': None,
        'True': True,
        'False': False,
        'questionHTML': questionhtml,
        'int': int,
        'resultsHTML': reshtml
    }
    qvars['markeroutput'] = marks
    guesses = [
        int(var[1:]) for var in marks.keys()
        if re.search(r"^G([0-9]+)$", var) > 0
    ]
    answers = [
        int(var[1:]) for var in marks.keys()
        if re.search(r"^A([0-9]+)$", var) > 0
    ]
    tolerances = [
        int(var[1:]) for var in marks.keys()
        if re.search(r"^T([0-9]+)$", var) > 0
    ]
    scores = [
        int(var[1:]) for var in marks.keys()
        if re.search(r"^M([0-9]+)$", var) > 0
    ]
    comments = [
        int(var[1:]) for var in marks.keys()
        if re.search(r"^C([0-9]+)$", var) > 0
    ]
    qvars['guesses'] = {}
    qvars['answers'] = {}
    qvars['tolerances'] = {}
    qvars['scores'] = {}
    qvars['comments'] = {}
    for guess in guesses:
        qvars['guesses'][guess] = marks['G%d' % guess]
    for answer in answers:
        qvars['answers'][answer] = marks['A%d' % answer]
    for tolerance in tolerances:
        qvars['tolerances'][tolerance] = marks['T%d' % tolerance]
    for score in scores:
        qvars['scores'][score] = marks['M%d' % score]
    for comment in comments:
        qvars['comments'][comment] = marks['C%d' % comment]
    qvars['numparts'] = len(answers)
    qvars['parts'] = range(1, len(answers) + 1)
    try:
        exec(script, qvars)
    except BaseException:
        (etype, value, tb) = sys.exc_info()
        script_funcs.q_log(
            qid, "error", "__results.py",
            "Reverting to standard display: __results.py: %s" %
            (traceback.format_exception(etype, value, tb)[-2:]))
    if 'resultsHTML' in qvars:
        if len(qvars['resultsHTML']) > 2:
            reshtml = qvars['resultsHTML']
            for v in qvars.keys():
                reshtml = reshtml.replace("<IMG SRC %s>" % v,
                                          '<IMG SRC="$OaQID$%s" />' % qvars[v])
            reshtml = reshtml.replace("$OaQID$", "%d/" % qid)
            return reshtml
    script_funcs.q_log(qid, "error", "__results.py",
                       "'resultsHTML' not set, using standard renderer.")
    return render_mark_results_standard(qid, marks)