Add support for adding/removing multiple choice wrong answers

Make multiple choice view more flexible with the number of choices
This commit is contained in:
Matthew Welch 2021-03-27 13:55:04 -07:00
parent 6ad7f2817a
commit 85909e7087
10 changed files with 110 additions and 80 deletions

View File

@ -24,12 +24,21 @@ def questions():
@roles_required("admin")
def edit_question(question_id):
if request.method == "POST":
print(f"edit question: {request.form.get('question_text')}")
database.update_question(question_id,
request.form.get("question_text"),
request.form.get("answer"),
request.form.get("addresses")
)
data = {
"question_text": request.form.get("question_text"),
"answer": request.form.get("answer"),
"addresses": request.form.get("addresses"),
}
multiple_choice_difficulty = request.form.get("multiple_choice_difficulty", None, int)
wrong_answers = request.form.getlist("wrong_answers")
hidden_answer_difficulty = request.form.get("hidden_answer_difficulty", None, int)
if multiple_choice_difficulty is not None:
data["multiple_choice_difficulty"] = multiple_choice_difficulty
if len(wrong_answers) > 0:
data["wrong_answers"] = wrong_answers
if hidden_answer_difficulty is not None:
data["hidden_answer_difficulty"] = hidden_answer_difficulty
database.update_question(question_id, data)
return redirect(url_for("admin.edit_question", question_id=question_id))
question: database.AllQuestions = get_question(database.AllQuestions, question_id)
if "application/json" in request.accept_mimetypes.values():
@ -54,7 +63,7 @@ def query_questions():
response_dict["rows"].append({
"id": question.question_id,
"question_id": question.question_id,
"question": question.question,
"question": question.question_text,
"answer": question.answer,
"multiple_choice": getattr(question, "multiple_choice", None) is not None,
"hidden_answer": getattr(question, "hidden_answer", None) is not None,

View File

@ -4,7 +4,7 @@
<form class="container mt-5" method="post">
<div class="form-group">
<label for="question_text">Question</label>
<input id="question_text" name="question_text" type="text" class="form-control" value="{{ question.question }}">
<input id="question_text" name="question_text" type="text" class="form-control" value="{{ question.question_text }}">
</div>
<div class="form-group">
<label for="answer">Answer</label>
@ -16,41 +16,45 @@
</div>
{% if question.multiple_choice is not none %}
<label class="mt-3">Multiple Choice</label>
<div class="form-group">
<label for="multiple_choice_difficulty">Difficulty</label>
<div class="container border mb-1">
<div class="form-group">
<select id="multiple_choice_difficulty" name="multiple_choice_difficulty" class="custom-select">
<option {% if question.multiple_choice.difficulty is none %}selected=""{% endif %}>None</option>
<option {% if question.multiple_choice.difficulty == 1 %}selected=""{% endif %} value="1">Easy</option>
<option {% if question.multiple_choice.difficulty == 2 %}selected=""{% endif %} value="2">Medium</option>
<option {% if question.multiple_choice.difficulty == 3 %}selected=""{% endif %} value="3">Hard</option>
</select>
</div>
<div class="form-group">
<label>Wrong Answers</label>
<ul class="list-group">
{% for answer in question.multiple_choice.wrong_answers %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<input id="answer_{{ loop.index0 }}" name="wrong_answers" class="form-control" value="{{ answer }}">
<button class="btn" type="button" onclick="$(this).parent().remove()"><i class="fas fa-times"></i></button>
</li>
{% endfor %}
</ul>
<button id="add_wrong_answer" class="btn btn-secondary" type="button">Add Answer</button>
<label for="multiple_choice_difficulty">Difficulty</label>
<div class="form-group">
<select id="multiple_choice_difficulty" name="multiple_choice_difficulty" class="custom-select">
<option {% if question.multiple_choice.multiple_choice_difficulty is none %}selected=""{% endif %}>None</option>
<option {% if question.multiple_choice.multiple_choice_difficulty == 1 %}selected=""{% endif %} value="1">Easy</option>
<option {% if question.multiple_choice.multiple_choice_difficulty == 2 %}selected=""{% endif %} value="2">Medium</option>
<option {% if question.multiple_choice.multiple_choice_difficulty == 3 %}selected=""{% endif %} value="3">Hard</option>
</select>
</div>
<div class="form-group">
<label>Wrong Answers</label>
<ul id="wrong_answer_list" class="list-group">
{% for answer in question.multiple_choice.wrong_answers %}
<li class="list-group-item d-flex justify-content-between align-items-center wrong_answer">
<input id="answer_{{ loop.index0 }}" name="wrong_answers" class="form-control" value="{{ answer }}">
<button class="btn" type="button" onclick="remove_item($(this).parent())"><i class="fas fa-times"></i></button>
</li>
{% endfor %}
</ul>
<button id="add_wrong_answer" class="btn btn-secondary" type="button">Add Answer</button>
</div>
</div>
</div>
{% endif %}
{% if question.hidden_answer is not none %}
<label class="mt-3">Hidden Answer</label>
<div class="form-group">
<label for="hidden_answer_difficulty">Difficulty</label>
<div class="container border mb-1">
<div class="form-group">
<select id="hidden_answer_difficulty" name="hidden_answer_difficulty" class="custom-select">
<option {% if question.hidden_answer.difficulty is none %}selected=""{% endif %}>None</option>
<option {% if question.hidden_answer.difficulty == 1 %}selected=""{% endif %} value="1">Easy</option>
<option {% if question.hidden_answer.difficulty == 2 %}selected=""{% endif %} value="2">Medium</option>
<option {% if question.hidden_answer.difficulty == 3 %}selected=""{% endif %} value="3">Hard</option>
</select>
<label for="hidden_answer_difficulty">Difficulty</label>
<div class="form-group">
<select id="hidden_answer_difficulty" name="hidden_answer_difficulty" class="custom-select">
<option {% if question.hidden_answer.hidden_answer_difficulty is none %}selected=""{% endif %}>None</option>
<option {% if question.hidden_answer.hidden_answer_difficulty == 1 %}selected=""{% endif %} value="1">Easy</option>
<option {% if question.hidden_answer.hidden_answer_difficulty == 2 %}selected=""{% endif %} value="2">Medium</option>
<option {% if question.hidden_answer.hidden_answer_difficulty == 3 %}selected=""{% endif %} value="3">Hard</option>
</select>
</div>
</div>
</div>
{% endif %}
@ -61,7 +65,28 @@
{% block scripts %}
<script>
$("#add_wrong_answer").on("click", () => {
let item = $("<li>")
.addClass("list-group-item d-flex justify-content-between align-items-center wrong_answer")
.append($("<input>")
.attr("name", "wrong_answers")
.addClass("form-control"))
.append();
let btn = $("<button>")
.attr("type", "button")
.addClass("btn")
.on("click", () => {
remove_item(item);
})
.append($("<i>")
.addClass("fas fa-times"));
item.append(btn);
$("#wrong_answer_list").append(item);
})
function remove_item(element) {
if ($("#wrong_answer_list").children().length > 1) {
element.remove();
}
}
</script>
{% endblock %}

View File

@ -5,7 +5,7 @@ from QuizTheWord import database
from QuizTheWord.admin import admin
app = Flask(__name__)
environment_configuration = os.environ['CONFIGURATION_SETUP']
environment_configuration = os.environ.get('CONFIGURATION_SETUP', "QuizTheWord.config.Development")
with app.app_context():
app.config.from_object(environment_configuration)
user_datastore = SQLAlchemySessionUserDatastore(database.get_session(), database.User, database.Role)

View File

@ -21,11 +21,11 @@ class Production(Config):
DEBUG = False
DB_SOCKET_DIR = environ.get("DB_SOCKET_DIR", "/cloudsql")
CLOUD_SQL_CONNECTION_NAME = environ.get("CLOUD_SQL_CONNECTION_NAME")
DB_URL = f"postgres+psycopg2://{Config.DB_USER}:{Config.DB_PASSWORD}@{Config.DB_HOST}:{Config.DB_PORT}/{Config.DB_NAME}?host={DB_SOCKET_DIR}/{CLOUD_SQL_CONNECTION_NAME}"
DB_URL = f"postgresql+psycopg2://{Config.DB_USER}:{Config.DB_PASSWORD}@{Config.DB_HOST}:{Config.DB_PORT}/{Config.DB_NAME}?host={DB_SOCKET_DIR}/{CLOUD_SQL_CONNECTION_NAME}"
class Development(Config):
FLASK_ENV = "development"
DEBUG = True
TESTING = True
DB_URL = f"postgres+psycopg2://{Config.DB_USER}:{Config.DB_PASSWORD}@{Config.DB_HOST}:{Config.DB_PORT}/{Config.DB_NAME}"
DB_URL = f"postgresql+psycopg2://{Config.DB_USER}:{Config.DB_PASSWORD}@{Config.DB_HOST}:{Config.DB_PORT}/{Config.DB_NAME}"

View File

@ -69,7 +69,7 @@ class AllQuestions(Base):
__tablename__ = "all_questions"
question_id = Column(Integer, primary_key=True, nullable=False)
question = Column(String)
question_text = Column(String)
answer = Column(String)
addresses = Column(String)
@ -78,7 +78,7 @@ class AllQuestions(Base):
def __init__(self, question_id, question, answer, addresses):
self.question_id = question_id
self.question = question
self.question_text = question
self.answer = answer
self.addresses = addresses
@ -88,7 +88,7 @@ class AllQuestions(Base):
def get_dict(self):
result = {
"question_id": self.question_id,
"question": self.question,
"question": self.question_text,
"answer": self.answer,
"addresses": self.addresses,
}
@ -103,18 +103,18 @@ class HiddenAnswer(Base):
__tablename__ = "category_hidden_answer"
question_id = Column(Integer, ForeignKey("all_questions.question_id"), primary_key=True)
difficulty = Column(Integer)
hint = Column(JSON)
hidden_answer_difficulty = Column(Integer)
hidden_answer_hint = Column(JSON)
all_question_relationship = relationship("AllQuestions", lazy="joined", back_populates="hidden_answer")
question = association_proxy("all_question_relationship", "question")
question_text = association_proxy("all_question_relationship", "question_text")
answer = association_proxy("all_question_relationship", "answer")
addresses = association_proxy("all_question_relationship", "addresses")
def __init__(self, question_id, difficulty, hint, base_question):
self.question_id = question_id
self.difficulty = difficulty
self.hint = hint
self.hidden_answer_difficulty = difficulty
self.hidden_answer_hint = hint
self.all_question_relationship = base_question
def __repr__(self):
@ -123,8 +123,8 @@ class HiddenAnswer(Base):
def get_dict(self):
return {
"question_id": self.question_id,
"difficulty": self.difficulty,
"hint": self.hint,
"difficulty": self.hidden_answer_difficulty,
"hint": self.hidden_answer_hint,
}
@ -132,19 +132,19 @@ class MultipleChoice(Base):
__tablename__ = "category_multiple_choice"
question_id = Column(Integer, ForeignKey("all_questions.question_id"), primary_key=True)
difficulty = Column(Integer)
hint = Column(JSON)
multiple_choice_difficulty = Column(Integer)
multiple_choice_hint = Column(JSON)
wrong_answers = Column(ARRAY(String))
all_question_relationship = relationship("AllQuestions", lazy="joined", back_populates="multiple_choice")
question = association_proxy("all_question_relationship", "question")
question_text = association_proxy("all_question_relationship", "question_text")
answer = association_proxy("all_question_relationship", "answer")
addresses = association_proxy("all_question_relationship", "addresses")
def __init__(self, question_id, difficulty, hint, wrong_answers, base_question):
self.question_id = question_id
self.difficulty = difficulty
self.hint = hint
self.multiple_choice_difficulty = difficulty
self.multiple_choice_hint = hint
self.wrong_answers = wrong_answers
self.all_question_relationship = base_question
self.answer_list = None
@ -160,8 +160,8 @@ class MultipleChoice(Base):
def get_dict(self):
return {
"question_id": self.question_id,
"difficulty": self.difficulty,
"hint": self.hint,
"difficulty": self.multiple_choice_difficulty,
"hint": self.multiple_choice_hint,
"wrong_answers": self.wrong_answers,
}
@ -205,20 +205,20 @@ Optional[Union[MultipleChoice, HiddenAnswer, AllQuestions]]:
def get_random_question_of_difficulty(category: Union[Type[MultipleChoice], Type[HiddenAnswer]],
difficulty: Literal[1, 2, 3]):
session = get_session()
return session.query(category).filter(category.difficulty == difficulty).order_by(func.random()).first()
return session.query(category).filter(category.hidden_answer_difficulty == difficulty).order_by(func.random()).first()
def get_random_hidden_answer(difficulty: Optional[Literal[1, 2, 3]] = None) -> HiddenAnswer:
session = get_session()
if difficulty is not None:
return session.query(HiddenAnswer).filter(HiddenAnswer.difficulty == difficulty).order_by(func.random()).first()
return session.query(HiddenAnswer).filter(HiddenAnswer.hidden_answer_difficulty == difficulty).order_by(func.random()).first()
return session.query(HiddenAnswer).order_by(func.random()).first()
def get_random_multiple_choice(difficulty: Optional[Literal[1, 2, 3]] = None) -> MultipleChoice:
session = get_session()
if difficulty is not None:
return session.query(MultipleChoice).filter(MultipleChoice.difficulty == difficulty).order_by(
return session.query(MultipleChoice).filter(MultipleChoice.multiple_choice_difficulty == difficulty).order_by(
func.random()).first()
return session.query(MultipleChoice).order_by(func.random()).first()
@ -252,10 +252,14 @@ def query_all_questions(offset, limit, query: dict = None, sort=None, order=None
return questions, count
def update_question(question_id, question_text, answer, addresses):
def update_question(question_id, data):
session = get_session()
question: AllQuestions = session.query(AllQuestions).get(question_id)
question.question = question_text
question.answer = answer
question.addresses = addresses
for column in data.keys():
if column in AllQuestions.__table__.columns:
setattr(question, column, data[column])
if column in MultipleChoice.__table__.columns:
setattr(question.multiple_choice, column, data[column])
if column in HiddenAnswer.__table__.columns:
setattr(question.hidden_answer, column, data[column])
session.commit()

View File

@ -5,7 +5,7 @@
<h1 class="title">Quiz The Word</h1>
<div id="easy-question" class="container-md align-content-center question">
<div class="card question-text">
<p id="easy-question">{{ easy.question }}</p>
<p id="easy-question">{{ easy.question_text }}</p>
<button class="btn btn-secondary show-answer" onclick="showAnswer()">Show Answer</button>
<p id="easy-answer" class="answer">{{ easy.answer }}</p>
<div id="easy-bible-verses">{{ easy.addresses }}</div>
@ -13,7 +13,7 @@
</div>
<div id="medium-question" class="container-md align-content-center question" style="display: none">
<div class="card question-text">
<p id="medium-question">{{ medium.question }}</p>
<p id="medium-question">{{ medium.question_text }}</p>
<button class="btn btn-secondary show-answer" onclick="showAnswer()">Show Answer</button>
<p id="medium-answer" class="answer">{{ medium.answer }}</p>
<div id="medium-bible-verses">{{ medium.addresses }}</div>
@ -21,7 +21,7 @@
</div>
<div id="hard-question" class="container-md align-content-center question" style="display: none">
<div class="card question-text">
<p id="hard-question">{{ hard.question }}</p>
<p id="hard-question">{{ hard.question_text }}</p>
<button class="btn btn-secondary show-answer" onclick="showAnswer()">Show Answer</button>
<p id="hard-answer" class="answer">{{ hard.answer }}</p>
<div id="hard-bible-verses">{{ hard.addresses }}</div>

View File

@ -5,14 +5,10 @@
<h1 class="title">Quiz The Word</h1>
<div id="easy-question" class="container-md align-content-center question">
<div class="card question-text">
<p id="easy-question">{{ easy.question }}</p>
<button id="{{ easy.question_id }}-0" class="btn btn-secondary m-1" onclick="checkAnswer({{ easy.question_id }}, 0, '{{ easy.answer_list[0] }}')">{{ easy.answer_list[0] }}</button>
<button id="{{ easy.question_id }}-1" class="btn btn-secondary m-1" onclick="checkAnswer({{ easy.question_id }}, 1, '{{ easy.answer_list[1] }}')">{{ easy.answer_list[1] }}</button>
<button id="{{ easy.question_id }}-2" class="btn btn-secondary m-1" onclick="checkAnswer({{ easy.question_id }}, 2, '{{ easy.answer_list[2] }}')">{{ easy.answer_list[2] }}</button>
<button id="{{ easy.question_id }}-3" class="btn btn-secondary m-1" onclick="checkAnswer({{ easy.question_id }}, 3, '{{ easy.answer_list[3] }}')">{{ easy.answer_list[3] }}</button>
{# <button class="btn btn-secondary show-answer" onclick="showAnswer()">Show Answer</button>#}
{# <p id="easy-answer" class="answer">{{ easy.answer }}</p>#}
{# <div id="easy-bible-verses">{{ easy.addresses }}</div>#}
<p id="easy-question">{{ easy.question_text }}</p>
{% for answer in easy.answer_list %}
<button id="{{ easy.question_id }}-{{ loop.index0 }}" class="btn btn-secondary m-1" onclick="checkAnswer({{ easy.question_id }}, {{ loop.index0 }}, '{{ answer }}')">{{ answer }}</button>
{% endfor %}
</div>
</div>
@ -78,10 +74,5 @@
}
});
}
/*function showAnswer() {
$(".question:visible").find(".answer").css("display", "block");
$(".show-answer:visible").css("display", "none");
}*/
</script>
{% endblock %}

View File

@ -9,3 +9,4 @@ python-dotenv==0.15.0
scramp==1.2.0
SQLAlchemy==1.3.23
Werkzeug==1.0.1
Flask-Security-Too~=4.0.0