Add buttons to go to the next/previous question

This commit is contained in:
Matthew Welch 2021-08-29 22:04:15 -07:00
parent 965cd99aeb
commit 5519b04a46
5 changed files with 188 additions and 55 deletions

View File

@ -1,5 +1,5 @@
import os import os
from flask import request, render_template, jsonify, Flask, url_for, redirect, flash from flask import request, render_template, jsonify, Flask, url_for, redirect, flash, session
from flask_security import Security, SQLAlchemySessionUserDatastore, login_user, logout_user, current_user from flask_security import Security, SQLAlchemySessionUserDatastore, login_user, logout_user, current_user
from QuizTheWord import database from QuizTheWord import database
from QuizTheWord.admin import admin from QuizTheWord.admin import admin
@ -72,6 +72,36 @@ def check_answer():
return jsonify(database.check_answer(question_id, answer)) return jsonify(database.check_answer(question_id, answer))
@app.route("/category/hidden_answer/next_question")
def next_hidden_answer():
if "hidden_answer_ids" not in session:
session["hidden_answer_ids"] = []
difficulty = request.args.get("difficulty", 1)
next_question = database.next_hidden_answer(session["hidden_answer_ids"], difficulty)
if next_question:
session["hidden_answer_ids"].append(next_question.question_id)
session.modified = True
return jsonify(next_question.get_dict())
else:
session.pop("hidden_answer_ids", None)
return jsonify(None)
@app.route("/category/multiple_choice/next_question")
def next_multiple_choice():
if "multiple_choice_ids" not in session:
session["multiple_choice_ids"] = []
difficulty = request.args.get("difficulty", 1)
next_question = database.next_multiple_choice(session["multiple_choice_ids"], difficulty)
if next_question:
session["multiple_choice_ids"].append(next_question.question_id)
session.modified = True
return jsonify(next_question.get_dict_shuffled_choices())
else:
session.pop("multiple_choice_ids", None)
return jsonify(None)
@app.route("/login", methods=["GET", "POST"]) @app.route("/login", methods=["GET", "POST"])
def login(): def login():
next_page = request.args.get("next", default=url_for("index")) next_page = request.args.get("next", default=url_for("index"))

View File

@ -5,7 +5,7 @@ import sqlalchemy
from typing import Union, Optional, Literal, Type, List, Tuple from typing import Union, Optional, Literal, Type, List, Tuple
import random import random
from sqlalchemy import Column, JSON, String, Integer, create_engine, ForeignKey, func, Boolean, UnicodeText, DateTime from sqlalchemy import Column, JSON, String, Integer, create_engine, ForeignKey, func, Boolean, UnicodeText, DateTime
from sqlalchemy.sql.expression import or_, and_ from sqlalchemy.sql.expression import and_
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import sessionmaker, relationship, scoped_session from sqlalchemy.orm import sessionmaker, relationship, scoped_session
@ -410,11 +410,27 @@ def query_users(offset, limit, query: dict = None, sort=None, order=None) -> Tup
if sort and order: if sort and order:
order_by = getattr(getattr(User, sort), order)() order_by = getattr(getattr(User, sort), order)()
q = session.query(User).filter(*query_params).order_by(order_by) q = session.query(User).filter(*query_params).order_by(order_by)
questions = q.offset(offset).limit(limit).all() users = q.offset(offset).limit(limit).all()
count = q.count() count = q.count()
return questions, count return users, count
def get_all_roles() -> List[Role]: def get_all_roles() -> List[Role]:
session = get_session() session = get_session()
return session.query(Role).all() return session.query(Role).all()
def next_hidden_answer(previous_questions: List[int], difficulty) -> HiddenAnswer:
session = get_session()
return session.query(HiddenAnswer)\
.filter(HiddenAnswer.hidden_answer_difficulty == difficulty,
HiddenAnswer.question_id.notin_(previous_questions))\
.order_by(func.random()).first()
def next_multiple_choice(previous_questions: List[int], difficulty) -> MultipleChoice:
session = get_session()
return session.query(MultipleChoice)\
.filter(MultipleChoice.multiple_choice_difficulty == difficulty,
MultipleChoice.question_id.notin_(previous_questions))\
.order_by(func.random()).first()

View File

@ -0,0 +1,67 @@
let difficulty_selection = $(".difficulty-selection");
let btn_previous_question = $("#btn-previous-question");
let btn_next_question = $("#btn-next-question");
let control_btns = $(".control-btn");
let current_difficulty = 1;
difficulty_selection.on("click", change_difficulty);
btn_next_question.on("click", next_question);
btn_previous_question.on("click", previous_question);
update_question();
function get_new_question(difficulty) {
control_btns.attr("disabled", true);
$.ajax(next_question_url, {
method: "GET",
cache: false,
data: {difficulty: difficulty},
dataType: "json",
success: (data, textStatus, jqXHR) => {
control_btns.attr("disabled", false);
question_received(data);
},
})
}
function change_difficulty(event) {
difficulty_selection.removeClass("active");
let target = $(event.target);
target.addClass("active");
if (target.prop("innerText") === "Easy") {
current_difficulty = 1;
} else if (target.prop("innerText") === "Medium") {
current_difficulty = 2;
} else {
current_difficulty = 3;
}
update_question();
}
function next_question() {
let difficulty = questions[current_difficulty-1]
let question_index = difficulty["current_question"]+1;
if (difficulty["questions"].length === question_index) {
get_new_question(current_difficulty);
} else {
difficulty["current_question"] += 1;
update_question();
}
}
function previous_question() {
questions[current_difficulty-1]["current_question"] -= 1;
update_question();
}
function question_received(data) {
let difficulty = questions[current_difficulty-1];
if (data) {
difficulty["questions"].push(data);
difficulty["current_question"] += 1;
update_question();
} else {
difficulty["last_index"] = difficulty["current_question"];
update_question();
}
}

View File

@ -11,20 +11,22 @@
<p id="question-answer" class="d-none answer"></p> <p id="question-answer" class="d-none answer"></p>
<div id="bible-verses"></div> <div id="bible-verses"></div>
</div> </div>
<button id="btn-previous-question" class="btn btn-primary mt-2 float-start control-btn">Previous</button>
<button id="btn-next-question" class="btn btn-primary mt-2 float-end">Next</button>
</div> </div>
</div> </div>
<ul class="nav nav-pills justify-content-center mt-2"> <ul class="nav nav-pills justify-content-center mt-2">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active difficulty-selection" id="btn-show-easy" type="button" aria-controls="easy" aria-selected="true">Easy <button class="nav-link active difficulty-selection control-btn" id="btn-show-easy" type="button" aria-controls="easy" aria-selected="true">Easy
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link difficulty-selection" id="btn-show-medium" type="button" aria-controls="medium" aria-selected="false">Medium <button class="nav-link difficulty-selection control-btn" id="btn-show-medium" type="button" aria-controls="medium" aria-selected="false">Medium
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link difficulty-selection" id="btn-show-hard" type="button" aria-controls="hard" aria-selected="false">Hard <button class="nav-link difficulty-selection control-btn" id="btn-show-hard" type="button" aria-controls="hard" aria-selected="false">Hard
</button> </button>
</li> </li>
</ul> </ul>
@ -34,36 +36,41 @@
{% block scripts %} {% block scripts %}
<script> <script>
next_question_url = "{{ url_for("next_hidden_answer") }}";
let questions = [ let questions = [
{{ easy.get_dict()|tojson }}, {
{{ medium.get_dict()|tojson }}, "current_question": 0,
{{ hard.get_dict()|tojson }}, "questions": [{{ easy.get_dict()|tojson }}],
},
{
"current_question": 0,
"questions": [{{ medium.get_dict()|tojson }}],
},
{
"current_question": 0,
"questions": [{{ hard.get_dict()|tojson }}],
},
]; ];
let difficulty_selection = $(".difficulty-selection");
let btn_show_answer = $("#btn-show-answer"); let btn_show_answer = $("#btn-show-answer");
let question_answer = $("#question-answer"); let question_answer = $("#question-answer");
let current_difficulty = 1;
change_difficulty(current_difficulty);
difficulty_selection.on("click", (event) => {
difficulty_selection.removeClass("active");
let target = $(event.target);
target.addClass("active");
if (target.prop("innerText") === "Easy") {
current_difficulty = 1;
} else if (target.prop("innerText") === "Medium") {
current_difficulty = 2;
} else {
current_difficulty = 3;
}
change_difficulty(current_difficulty);
})
btn_show_answer.on("click", showAnswer); btn_show_answer.on("click", showAnswer);
function change_difficulty(difficulty) { function update_question() {
let question = questions[difficulty-1]; let difficulty = questions[current_difficulty-1];
let question_index = difficulty["current_question"];
let question = difficulty["questions"][question_index];
if (question_index === 0) {
btn_previous_question.attr("disabled", true);
} else {
btn_previous_question.attr("disabled", false);
}
if (question_index === difficulty["last_index"]) {
btn_next_question.attr("disabled", true);
} else {
btn_next_question.attr("disabled", false);
}
if (question["answer_visible"]) { if (question["answer_visible"]) {
btn_show_answer.css("display", "none"); btn_show_answer.css("display", "none");
question_answer.removeClass("d-none"); question_answer.removeClass("d-none");
@ -77,9 +84,12 @@
} }
function showAnswer() { function showAnswer() {
questions[current_difficulty-1]["answer_visible"] = true; let question_index = questions[current_difficulty-1]["current_question"];
let question = questions[current_difficulty-1]["questions"][question_index];
question["answer_visible"] = true;
question_answer.removeClass("d-none"); question_answer.removeClass("d-none");
btn_show_answer.css("display", "none"); btn_show_answer.css("display", "none");
} }
</script> </script>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
{% endblock %} {% endblock %}

View File

@ -9,20 +9,22 @@
<h5 id="question-text"></h5> <h5 id="question-text"></h5>
<div id="question-choices" class="list-group"></div> <div id="question-choices" class="list-group"></div>
</div> </div>
<button id="btn-previous-question" class="btn btn-primary mt-2 float-start control-btn">Previous</button>
<button id="btn-next-question" class="btn btn-primary mt-2 float-end control-btn">Next</button>
</div> </div>
</div> </div>
<ul class="nav nav-pills justify-content-center mt-2"> <ul class="nav nav-pills justify-content-center mt-2">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active difficulty-selection" id="btn-show-easy" type="button" aria-controls="easy" aria-selected="true">Easy <button class="nav-link active difficulty-selection control-btn" id="btn-show-easy" type="button" aria-controls="easy" aria-selected="true">Easy
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link difficulty-selection" id="btn-show-medium" type="button" aria-controls="medium" aria-selected="false">Medium <button class="nav-link difficulty-selection control-btn" id="btn-show-medium" type="button" aria-controls="medium" aria-selected="false">Medium
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link difficulty-selection" id="btn-show-hard" type="button" aria-controls="hard" aria-selected="false">Hard <button class="nav-link difficulty-selection control-btn" id="btn-show-hard" type="button" aria-controls="hard" aria-selected="false">Hard
</button> </button>
</li> </li>
</ul> </ul>
@ -32,32 +34,37 @@
{% block scripts %} {% block scripts %}
<script> <script>
next_question_url = "{{ url_for("next_multiple_choice") }}";
let questions = [ let questions = [
{{ easy.get_dict_shuffled_choices()|tojson }}, {
{{ medium.get_dict_shuffled_choices()|tojson }}, "current_question": 0,
{{ hard.get_dict_shuffled_choices()|tojson }}, "questions": [{{ easy.get_dict_shuffled_choices()|tojson }}],
},
{
"current_question": 0,
"questions": [{{ medium.get_dict_shuffled_choices()|tojson }}],
},
{
"current_question": 0,
"questions": [{{ hard.get_dict_shuffled_choices()|tojson }}],
},
]; ];
let difficulty_selection = $(".difficulty-selection");
let current_difficulty = 1;
change_difficulty(current_difficulty); function update_question() {
let difficulty = questions[current_difficulty-1];
difficulty_selection.on("click", (event) => { let question_index = difficulty["current_question"];
difficulty_selection.removeClass("active"); let question = difficulty["questions"][question_index];
let target = $(event.target); if (question_index === 0) {
target.addClass("active"); btn_previous_question.attr("disabled", true);
if (target.prop("innerText") === "Easy") {
current_difficulty = 1;
} else if (target.prop("innerText") === "Medium") {
current_difficulty = 2;
} else { } else {
current_difficulty = 3; btn_previous_question.attr("disabled", false);
}
if (question_index === difficulty["last_index"]) {
btn_next_question.attr("disabled", true);
} else {
btn_next_question.attr("disabled", false);
} }
change_difficulty(current_difficulty);
})
function change_difficulty(difficulty) {
let question = questions[difficulty-1];
$("#question-text").text(question["question_text"]); $("#question-text").text(question["question_text"]);
$("#bible-verses").text(question["addresses"]); $("#bible-verses").text(question["addresses"]);
$("#question-choices").empty(); $("#question-choices").empty();
@ -83,7 +90,9 @@
} }
function checkAnswer(question_id, button_index, answer) { function checkAnswer(question_id, button_index, answer) {
let question = questions[current_difficulty-1]; let difficulty = questions[current_difficulty-1];
let question_index = difficulty["current_question"];
let question = difficulty["questions"][question_index];
question["chosen_answer"] = button_index; question["chosen_answer"] = button_index;
let loading_spinner = $("<div>") let loading_spinner = $("<div>")
.attr("id", "check-answer") .attr("id", "check-answer")
@ -116,4 +125,5 @@
}); });
} }
</script> </script>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
{% endblock %} {% endblock %}