コード例 #1
0
def delete_quiz_questions(qq_id):
    '''
    Handles DELETE requests on a specific QuizQuestion
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to delete quiz questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    qq = models.QuizQuestion.query.get_or_404(qq_id)

    # validation - All of the quizzes using this qq_id must be HIDDEN to be able to delete
    for quiz in models.Quiz.query.all():
        for qq in quiz.quiz_questions:
            if qq.id == qq_id and quiz.status != "HIDDEN":
                response = ({
                    "message": "Quiz not accessible at this time"
                }, 403, {
                    "Content-Type": "application/json"
                })
                return make_response(response)

    models.DB.session.delete(qq)
    models.DB.session.commit()
    response = ({
        "message": "Quiz Question Deleted from database"
    }, 200, {
        "Content-Type": "application/json"
    })
    #NOTE see previous note about using 204 vs 200
    #NOTE the above is a bloody tuple, not 3 separate parameters to make_response
    return make_response(response)
コード例 #2
0
def delete_quizzes(qid):
    '''
    Handles DELETE requests on a specific quiz
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to delete quizzes"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)
    quiz = models.Quiz.query.get_or_404(qid)

    # validation - quiz must be HIDDEN to be able to delete w/o affecting students who are taking it
    if quiz.status != "HIDDEN":
        response = ({
            "message": "Quiz not accessible at this time"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    models.DB.session.delete(quiz)
    models.DB.session.commit()
    response = ({
        "message": "Quiz deleted from database"
    }, 200, {
        "Content-Type": "application/json"
    })
    #NOTE see previous note about using 204 vs 200
    return make_response(response)
コード例 #3
0
def delete_question(question_id):
    '''
    Delete given question.
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to delete questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    q = models.Question.query.get_or_404(question_id)

    # validation - All of the quizzes containing question_id must be HIDDEN to be able to update
    for quiz in models.Quiz.query.all():
        for qq in quiz.quiz_questions:
            if qq.question_id == question_id and quiz.status != "HIDDEN":
                response = ({
                    "message": "Quiz not accessible at this time"
                }, 403, {
                    "Content-Type": "application/json"
                })
                return make_response(response)

    models.DB.session.delete(q)
    models.DB.session.commit()
    response = ({
        "message": "Question Deleted from database"
    }, 200, {
        "Content-Type": "application/json"
    })
    return make_response(response)
コード例 #4
0
def put_question(question_id):
    '''
    Update a given question.
    This method may be called by post_new_question if we detect a POST request
    coming from an HTML form that really wanted to send us a PUT.
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to modify questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    # validation - All of the quizzes containing question_id must be HIDDEN to be able to update
    for quiz in models.Quiz.query.all():
        for qq in quiz.quiz_questions:
            if qq.question_id == question_id and quiz.status != "HIDDEN":
                response = ({
                    "message": "Quiz not accessible at this time"
                }, 403, {
                    "Content-Type": "application/json"
                })
                return make_response(response)

    #NOTE HTML5 forms can not submit a PUT (only POST), so we reject any non-json request
    #NOTE update to the above; we're going to sort out in the POST whether it's a PUT or not
    if not request.json:
        #abort(406, "JSON format required for request") # not acceptable
        title = request.form['title']
        stem = request.form['stem']
        answer = request.form['answer']

    else:
        title = request.json['title']
        stem = request.json['stem']
        answer = request.json['answer']

    # validate that all required information was sent
    if answer is None or stem is None or title is None:
        abort(400,
              "Unable to modify question due to missing data")  # bad request

    q = models.Question.query.get_or_404(question_id)
    q.title = sanitize(title)
    q.stem = sanitize(stem)
    q.answer = sanitize(answer)

    models.DB.session.commit()
    if request.json:
        response = ({
            "message": "Question updated in database"
        }, 200, {
            "Content-Type": "application/json"
        })
        #NOTE should it be 204? probably but I prefer to return a message so that CURL displays something indicating that the operation succeeded
        return make_response(response)
    else:
        flash("Question successfully updated in database.", "shiny")
        return redirect(request.referrer)
コード例 #5
0
def delete_distractor(distractor_id):
    '''
    Delete given distractor.
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to delete distractors"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    d = models.Distractor.query.get_or_404(distractor_id)

    # validation - All of the quizzes containing a question that distractor_id is related to must be HIDDEN to be able to update
    for quiz in models.Quiz.query.all():
        for qq in quiz.quiz_questions:
            question = models.Question.query.get(qq.question_id)
            for d in question.distractors:
                if d.id == distractor_id and quiz.status != "HIDDEN":
                    response = ({
                        "message": "Quiz not accessible at this time"
                    }, 403, {
                        "Content-Type": "application/json"
                    })
                    return make_response(response)

    models.DB.session.delete(d)
    models.DB.session.commit()
    response = ({
        "message": "Distractor deleted from database"
    }, 204, {
        "Content-Type": "application/json"
    })
    return make_response(response)
コード例 #6
0
def post_quizzes_status(qid):
    '''
    Modifies the status of given quiz
    '''
    if not current_user.is_instructor():
        if request.json:
            response = ({
                "message": "You are not allowed to get quiz status"
            }, 403, {
                "Content-Type": "application/json"
            })
            return make_response(response)
        else:
            flash("You are not allowed to get quiz status", "postError")

    quiz = models.Quiz.query.get_or_404(qid)

    if not request.json:
        abort(406, "JSON format required for request")  # not acceptable
    new_status = sanitize(request.json['status'])
    #FIXME how about check that the status is actually valid, eh? :)
    if (quiz.set_status(new_status)):
        response = ({
            "message": "OK"
        }, 200, {
            "Content-Type": "application/json"
        })
        models.DB.session.commit()
    else:
        response = ({
            "message": "Unable to switch to new status"
        }, 400, {
            "Content-Type": "application/json"
        })
    return make_response(response)
コード例 #7
0
def post_new_quiz_question():
    '''
    Add a new QuizQuestion.
    '''
    if not current_user.is_instructor():
        if request.json:
            response = ({
                "message":
                "You are not allowed to create quiz questions"
            }, 403, {
                "Content-Type": "application/json"
            })
            return make_response(response)
        else:
            flash("You are not allowed to create quiz questions", "postError")
            return redirect(request.referrer)

    if request.json:
        question_id = request.json['qid']
        if question_id is None or request.json['distractors_ids'] is None:
            abort(400, "Unable to create new quiz question due to missing data"
                  )  # bad request
        distractors_ids = [did for did in request.json['distractors_ids']]
    else:
        abort(406, "JSON format required for request")  # not acceptable
        #TODO apply same modifications tha in post_new_question but also check
        # whether we currently have a form sending that data first
        # NOTE of particular interest is how the form will send those arrays.
        # that was easy in json but I vaguely remember reading some issues
        # with the variable number of fields and regular forms format

    q = models.Question.query.get_or_404(question_id)
    qq = models.QuizQuestion(question=q)
    distractors = [
        models.Distractor.query.get_or_404(id) for id in distractors_ids
    ]
    #TODO verify that distractors belong to that question; that led to a funny
    # glitch in the deployement 2021 scripts whereby all distractors were
    # assigned to question #2 by the script but, since they were listed also
    # when creating the QuizQuestion, everything looked like it was working
    # just fine. Beware when your programs look like they are working...
    #TODO verify that distractors are also all different

    for d in distractors:
        qq.distractors.append(d)

    models.DB.session.add(qq)
    models.DB.session.commit()

    response = ({
        "message": "Quiz Question added to database"
    }, 201, {
        "Content-Type": "application/json"
    })
    return make_response(response)
コード例 #8
0
def post_new_distractor_for_question(question_id):
    '''
    Add a distractor to the specified question.
    '''
    if not current_user.is_instructor():
        if request.json:
            response = ({
                "message": "You are not allowed to create distrators"
            }, 403, {
                "Content-Type": "application/json"
            })
            return make_response(response)
        else:
            flash("You are not allowed to create distrators", "postError")
            return redirect(request.referrer)

    #TODO validation - All of the quizzes containing question_id must be HIDDEN to be able to add distractor

    if request.json:
        answer = request.json['answer']
    else:
        #FIXME do we want to continue handling both formats?
        answer = request.form['answer']

    #TODO detect if did was passed too; if so, then it's an update
    if 'did' in request.form:
        return put_distractor(request.form['did'])

    # validate that all required information was sent
    if answer is None:
        abort(400, "Unable to create new distractor due to missing data"
              )  # bad request

    q = models.Question.query.get_or_404(question_id)

    #NOTE same potential bug here than above, still a feature
    # escaped_answer = json.dumps(answer) # escapes "" used in code
    escaped_answer = Markup.escape(answer)  # escapes HTML characters
    escaped_answer = sanitize(escaped_answer)

    q.distractors.append(
        models.Distractor(answer=escaped_answer, question_id=q.id))
    models.DB.session.commit()

    if request.json:
        response = ({
            "message": "Distractor added to Question in database"
        }, 201, {
            "Content-Type": "application/json"
        })
        return make_response(response)
    else:
        flash("Distractor successfully added to database.", "shiny")
        return redirect(request.referrer)
コード例 #9
0
def get_distractor(distractor_id):
    if not current_user.is_instructor():
        response = ({
            "message":
            "You are not allowed to view individual distractors"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    d = models.Distractor.query.get_or_404(distractor_id)
    return jsonify({"answer": d.answer})
コード例 #10
0
def put_distractor(distractor_id):
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to modify distractors"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    # validation - All of the quizzes containing a question that distractor_id is related to must be HIDDEN to be able to update
    for quiz in models.Quiz.query.all():
        for qq in quiz.quiz_questions:
            question = models.Question.query.get(qq.question_id)
            for d in question.distractors:
                if d.id == distractor_id and quiz.status != "HIDDEN":
                    response = ({
                        "message":
                        "Quiz not accessible at this time, unable to update distractors."
                    }, 403, {
                        "Content-Type": "application/json"
                    })
                    return make_response(response)

    if not request.json:
        #TODO NOW accept form data if it's been sent from the POST handler with an ID
        answer = request.form['answer']
        #abort(406, "JSON format required for request") # not acceptable
    else:
        answer = request.json['answer']

    # validate that all required information was sent
    if answer is None:
        abort(400,
              "Unable to modify distractor due to missing data")  # bad request

    d = models.Distractor.query.get_or_404(distractor_id)
    d.answer = sanitize(answer)

    models.DB.session.commit()

    if request.json:
        response = ({
            "message": "Distractor updated in database"
        }, 200, {
            "Content-Type": "application/json"
        })
        #NOTE see previous note about using 204 vs 200
        return make_response(response)
    else:
        flash("Distractor successfully modified in database.", "shiny")
        return redirect(request.referrer)
コード例 #11
0
def get_quizzes_responses(qid):
    '''
    Returns all the data on all the attempts made so far on that quiz by students.
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to view quizzes responses"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    attempts = models.QuizAttempt.query.filter_by(quiz_id=qid).all()
    return jsonify([a.dump_as_dict() for a in attempts])
コード例 #12
0
def get_all_quizzes():
    '''
    Get us all quizzes, for debugging purposes
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to view all quizzes"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    quizzes = models.Quiz.query.all()
    return jsonify([q.dump_as_dict() for q in quizzes])
コード例 #13
0
def put_quiz_questions(qq_id):
    '''
    Handles PUT requests on a specific QuizQuestion
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to modify quiz questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    qq = models.QuizQuestion.query.get_or_404(qq_id)

    # validation - All of the quizzes using this qq_id must be HIDDEN to be able to update
    for quiz in models.Quiz.query.all():
        for qq in quiz.quiz_questions:
            if qq.id == qq_id and quiz.status != "HIDDEN":
                response = ({
                    "message": "Quiz not accessible at this time"
                }, 403, {
                    "Content-Type": "application/json"
                })
                return make_response(response)

    if not request.json:
        abort(406, "JSON format required for request")  # not acceptable

    # validate that all required information was sent
    if request.json['qid'] is None or request.json['distractors_ids'] is None:
        abort(400, "Unable to modify quiz question due to missing data"
              )  # bad request

    question_id = request.json['qid']
    distractors_ids = [did for did in request.json['distractors_ids']]

    qq.question = models.Question.query.get_or_404(question_id)
    distractors = [
        models.Distractor.query.get_or_404(id) for id in distractors_ids
    ]

    qq.distractors = distractors
    models.DB.session.commit()

    response = ({
        "message": "Quiz Question updated in database"
    }, 201, {
        "Content-Type": "application/json"
    })
    return make_response(response)
コード例 #14
0
def get_quizzes(qid):
    '''
    Handles GET requests on a specific quiz
    '''
    if not current_user.is_instructor():
        response = ({
            "message":
            "You are not allowed to download individual quizzes"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    quiz = models.Quiz.query.get_or_404(qid)
    return jsonify(quiz.dump_as_dict())
コード例 #15
0
def get_quiz_questions(qq_id):
    '''
    Handles GET requests on a specific QuizQuestion
    '''
    if not current_user.is_instructor():
        response = ({
            "message":
            "You are not allowed to access individual quiz questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    qq = models.QuizQuestion.query.get_or_404(qq_id)
    return jsonify(qq.dump_as_dict())
コード例 #16
0
def get_all_quiz_questions():
    '''
    Get, in JSON format, all the QuizQuestions from the database.
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to view all quiz questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    all = models.QuizQuestion.query.all()
    result = [q.dump_as_dict() for q in all]
    return jsonify(result)
コード例 #17
0
def get_all_questions():
    '''
    Get, in JSON format, all the questions from the database,
    including, for each, all its distractors.
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to view all questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    all_questions = [q.dump_as_dict() for q in models.Question.query.all()]
    return jsonify(all_questions)
コード例 #18
0
def put_quizzes(qid):
    '''
    Handles PUT requests on a specific quiz
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to modify quizzes"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    quiz = models.Quiz.query.get_or_404(qid)

    # validation - quiz must be HIDDEN to be able to modify w/o affecting students who are taking it
    if quiz.status != "HIDDEN":
        response = ({
            "message": "Quiz not accessible at this time"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    if not request.json:
        abort(406, "JSON format required for request")  # not acceptable

    # validate that all required information was sent
    if quiz.title is None or quiz.description is None or request.json[
            'questions_ids'] is None:
        abort(400, "Unable to modify quiz due to missing data")  # bad request

    quiz.title = sanitize(request.json['title'])
    quiz.description = sanitize(request.json['description'])

    quiz.quiz_questions = []
    for qid in request.json['questions_ids']:
        question = models.QuizQuestion.query.get_or_404(qid)
        quiz.quiz_questions.append(question)

    models.DB.session.commit()

    response = ({
        "message": "Quiz updated in database"
    }, 200, {
        "Content-Type": "application/json"
    })
    #NOTE see previous note about using 204 vs 200
    return make_response(response)
コード例 #19
0
def get_distractors_for_question(question_id):
    '''
    Get all distractors for the specified question.
    '''
    if not current_user.is_instructor():
        response = ({
            "message":
            "You are not allowed to view individual distractors"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    q = models.Question.query.get_or_404(question_id)
    result = [d.dump_as_dict() for d in q.distractors]
    return jsonify(result)
コード例 #20
0
def post_new_quiz():
    '''
    Create a new quiz
    '''
    if not current_user.is_instructor():
        if request.json:
            response = ({
                "message": "You are not allowed to create quizzes"
            }, 403, {
                "Content-Type": "application/json"
            })
            return make_response(response)
        else:
            flash("You are not allowed to create quizzes", "postError")
            return redirect(request.referrer)

    title = request.json['title']
    description = request.json['description']

    # validate that all required information was sent
    if title is None or description is None:
        abort(400,
              "Unable to create new quiz due to missing data")  # bad request

    if request.json['questions_ids'] is None:
        abort(400,
              "Unable to create new quiz due to missing data")  # bad request

    bleached_title = sanitize(title)
    bleached_description = sanitize(description)

    q = models.Quiz(title=bleached_title, description=bleached_description)

    # Adding the questions, based on the questions_id that were submitted
    for qid in request.json['questions_ids']:
        question = models.QuizQuestion.query.get_or_404(qid)
        q.quiz_questions.append(question)

    models.DB.session.add(q)
    models.DB.session.commit()

    response = ({
        "message": "Quiz added to database"
    }, 201, {
        "Content-Type": "application/json"
    })
    return make_response(response)
コード例 #21
0
def get_quizzes_status(qid):
    '''
    Returns the status of a given quiz
    '''
    if not current_user.is_instructor():
        response = ({
            "message": "You are not allowed to get quiz status"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    quiz = models.Quiz.query.get_or_404(qid)

    if not request.json:
        abort(406, "JSON format required for request")  # not acceptable

    response = (f'{{ "status" : {quiz.status} }}', 403, {
        "Content-Type": "application/json"
    })
    return make_response(response)
コード例 #22
0
def get_question(question_id):
    '''
    Get, in JSON format, a specified question from the database.
    We extend here the concept of "Question" by also including ALL its distractors.
    The fact that *all* of the distractors are included is the main difference
    between Question and QuizQuestion.
    While the answer and distractors are shuffled together as alternatives from which
    the student will have to pick, this is not intended to be presented to any
    student since it has ALL the distractors. It is more intended as a debugging
    feature.
    '''
    if not current_user.is_instructor():
        response = ({
            "message":
            "You are not allowed to access individual questions"
        }, 403, {
            "Content-Type": "application/json"
        })
        return make_response(response)

    q = models.Question.query.get_or_404(question_id)
    return jsonify(q.dump_as_dict())
コード例 #23
0
def post_new_question():
    '''
    Add a question and its answer to the database.
    '''
    if not current_user.is_instructor():
        if request.json:
            response = ({
                "message": "You are not allowed to create questions"
            }, 403, {
                "Content-Type": "application/json"
            })
            return make_response(response)
        else:
            flash("You are not allowed to create questions", "postError")
            return redirect(request.referrer)

    if request.json:
        title = request.json['title']
        stem = request.json['stem']
        answer = request.json['answer']
    else:
        title = request.form['title']
        stem = request.form['stem']
        answer = request.form['answer']
        #NOTE if the POST from the HTML form refers to an existing question, we forward it to the PUT route.
        # We are able to tell this is the case because the form embeds an hidden field named "qid".
        if 'qid' in request.form:
            return put_question(request.form['qid'])

    # validate that all required information was sent
    if answer is None or stem is None or title is None:
        abort(
            400,
            "Unable to create new question due to missing data")  # bad request

    #NOTE the code below is highly suspicious... why did we keep the jason.dumps lines since both use answer
    # instead of the 2nd line using escaped_answer?
    # Taking a shot at fixing this
    # might be why Paul reported seeing double quotes in the JSON still
    # UPDATE - Not a bug actually...
    # escaped_answer = json.dumps(answer) # escapes "" used in code
    escaped_answer = Markup.escape(answer)  # escapes HTML characters
    escaped_answer = sanitize(escaped_answer)

    # escaped_stem = json.dumps(stem)
    escaped_stem = Markup.escape(stem)
    escaped_stem = sanitize(escaped_stem)

    # escaped_title = json.dumps(title)
    escaped_title = Markup.escape(title)
    escaped_title = sanitize(escaped_title)

    q = models.Question(title=escaped_title,
                        stem=escaped_stem,
                        answer=escaped_answer)
    models.DB.session.add(q)
    models.DB.session.commit()

    if request.json:
        response = ({
            "message": "Question & answer added to database"
        }, 201, {
            "Content-Type": "application/json"
        })
        return make_response(response)
    else:
        flash("Question successfully added to database.", "shiny")
        return redirect(request.referrer)