Added multiple choice category

This commit is contained in:
Matthew Welch 2021-03-18 20:08:19 -07:00
parent 0e981fce87
commit 2d18b1b2bf
7 changed files with 213 additions and 25 deletions

View File

@ -14,7 +14,7 @@ class Config:
DB_NAME = environ.get("DB_NAME")
class ProductionConfig(Config):
class Production(Config):
FLASK_ENV = "production"
DEBUG = False
DB_SOCKET_DIR = environ.get("DB_SOCKET_DIR", "/cloudsql")
@ -22,7 +22,7 @@ class ProductionConfig(Config):
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}"
class DevelopmentConfig(Config):
class Development(Config):
FLASK_ENV = "development"
DEBUG = True
TESTING = True

View File

@ -1,17 +1,20 @@
from flask import current_app, g
import sqlalchemy
from typing import Union, Optional, Literal, Type, List, Tuple
import random
import os
from sqlalchemy import Column, JSON, String, Integer, create_engine, ForeignKey, func, ARRAY
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.orm import sessionmaker, relationship, scoped_session
from werkzeug.utils import import_string
environment_configuration = os.environ['CONFIGURATION_SETUP']
config = import_string(environment_configuration)
engine = create_engine(config.DB_URL)
Session = sessionmaker(bind=engine)
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
Base = declarative_base()
@ -40,7 +43,7 @@ class HiddenAnswer(Base):
difficulty = Column(Integer)
hint = Column(JSON)
all_question_relationship = relationship("AllQuestions", primaryjoin="HiddenAnswer.question_id==AllQuestions.question_id", lazy="joined", uselist=False)
all_question_relationship = relationship("AllQuestions", foreign_keys=[question_id], lazy="joined", uselist=False)
question = association_proxy("all_question_relationship", "question")
answer = association_proxy("all_question_relationship", "answer")
addresses = association_proxy("all_question_relationship", "addresses")
@ -63,7 +66,7 @@ class MultipleChoice(Base):
hint = Column(JSON)
wrong_answers = Column(ARRAY(String))
all_question_relationship = relationship("AllQuestions", primaryjoin="MultipleChoice.question_id==AllQuestions.question_id", lazy="joined", uselist=False)
all_question_relationship = relationship("AllQuestions", foreign_keys=[question_id], lazy="joined", uselist=False)
question = association_proxy("all_question_relationship", "question")
answer = association_proxy("all_question_relationship", "answer")
addresses = association_proxy("all_question_relationship", "addresses")
@ -74,36 +77,75 @@ class MultipleChoice(Base):
self.hint = hint
self.wrong_answers = wrong_answers
self.all_question_relationship = base_question
self.answer_list = None
def randomize_answer_list(self):
answer_list: List[str] = [*self.wrong_answers, self.answer]
random.shuffle(answer_list)
self.answer_list = answer_list
def __repr__(self):
return f"<Question Multiple Choice: {self.question_id}>"
def get_all_questions():
session = Session()
def add_multiple_choice_question(question, answer, addresses, difficulty, hint, wrong_answers):
session: sqlalchemy.orm.session.Session = Session()
question_id = session.query(AllQuestions).count()
base_question = AllQuestions(question_id, question, answer, addresses)
multiple_choice_question = MultipleChoice(question_id, difficulty, hint, wrong_answers, base_question)
session.add(base_question)
session.add(multiple_choice_question)
session.commit()
def get_all_questions() -> List[AllQuestions]:
session: sqlalchemy.orm.session.Session = Session()
return session.query(AllQuestions).all()
def get_all_hidden_answer():
session = Session()
def get_all_hidden_answer() -> List[HiddenAnswer]:
session: sqlalchemy.orm.session.Session = Session()
return session.query(HiddenAnswer).all()
def get_all_multiple_choice():
session = Session()
def get_all_multiple_choice() -> List[MultipleChoice]:
session: sqlalchemy.orm.session.Session = Session()
return session.query(MultipleChoice).all()
def get_category_count(category):
session = Session()
def get_category_count(category: Union[Type[MultipleChoice], Type[HiddenAnswer], Type[AllQuestions]]) -> int:
session: sqlalchemy.orm.session.Session = Session()
return session.query(category).count()
def get_question(category, question_id):
session = Session()
def get_question(category: Union[Type[MultipleChoice], Type[HiddenAnswer], Type[AllQuestions]], question_id: int) -> Optional[Union[Type[MultipleChoice], Type[HiddenAnswer], Type[AllQuestions]]]:
session: sqlalchemy.orm.session.Session = Session()
return session.query(category).filter(category.question_id == question_id).one_or_none()
def get_random_question_of_difficulty(category, difficulty):
session = Session()
def get_random_question_of_difficulty(category: Union[Type[MultipleChoice], Type[HiddenAnswer]], difficulty: Literal[1, 2, 3]):
session: sqlalchemy.orm.session.Session = Session()
return session.query(category).filter(category.difficulty == difficulty).order_by(func.random()).first()
def get_random_hidden_answer(difficulty: Optional[Literal[1, 2, 3]] = None) -> HiddenAnswer:
session: sqlalchemy.orm.session.Session = Session()
if difficulty is not None:
return session.query(HiddenAnswer).filter(HiddenAnswer.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: sqlalchemy.orm.session.Session = Session()
print(type(session))
if difficulty is not None:
return session.query(MultipleChoice).filter(MultipleChoice.difficulty == difficulty).order_by(func.random()).first()
return session.query(MultipleChoice).order_by(func.random()).first()
def check_answer(question_id: int, guess: str) -> Tuple[bool, str]:
session: sqlalchemy.orm.session.Session = Session()
question: AllQuestions = session.query(AllQuestions).filter(AllQuestions.question_id == question_id).one_or_none()
if question:
answer = question.answer
return answer == guess, answer

40
main.py
View File

@ -1,5 +1,5 @@
import os
from flask import Flask, render_template
from flask import Flask, render_template, request, jsonify
import database
app = Flask(__name__)
@ -9,17 +9,47 @@ app.config.from_object(environment_configuration)
@app.route("/")
def index():
easy = database.get_random_question_of_difficulty(database.HiddenAnswer, 1)
medium = database.get_random_question_of_difficulty(database.HiddenAnswer, 2)
hard = database.get_random_question_of_difficulty(database.HiddenAnswer, 3)
return multiple_choice_category()
@app.route("/category/hidden_answer")
def hidden_answer_category():
easy = database.get_random_hidden_answer(1)
medium = database.get_random_hidden_answer(2)
hard = database.get_random_hidden_answer(3)
return render_template(
"hidden_answer.html",
title="Home",
title="Hidden Answer",
easy=easy,
medium=medium,
hard=hard,
)
@app.route("/category/multiple_choice")
def multiple_choice_category():
easy = database.get_random_multiple_choice(3)
easy.randomize_answer_list()
medium = database.get_random_multiple_choice(2)
medium.randomize_answer_list()
hard = database.get_random_multiple_choice(3)
hard.randomize_answer_list()
return render_template(
"multiple_choice.html",
title="Multiple Choice",
easy=easy,
# medium=medium,
# hard=hard,
)
@app.route("/category/multiple_choice/check_answer", methods=["GET"])
def check_answer():
question_id = request.args.get("question_id", type=int)
answer = request.args.get("answer", type=str)
if question_id is not None and answer is not None:
return jsonify(database.check_answer(question_id, answer))
if __name__ == "__main__":
app.run()

24
scripts/file_parsing.py Normal file
View File

@ -0,0 +1,24 @@
import pathlib
import database
def parse_multiple_choice_questions(question_file: pathlib.Path):
file_data = question_file.read_text("utf-8").split("\n")
questions = []
for line in file_data:
question_data = line.split("~")
if len(question_data) == 4:
correct_answer_index = int(question_data[0])-1
question = question_data[1]
answer_list = question_data[2].split("//")
bible_verse = question_data[3] if question_data[3].lower() != "none" else None
correct_answer = answer_list.pop(correct_answer_index)
questions.append({
"question": question,
"answer": correct_answer,
"addresses": bible_verse,
"difficulty": None,
"hint": None,
"wrong_answers": answer_list,
})
return questions

View File

@ -26,3 +26,7 @@
writing-mode: vertical-rl;
text-orientation: mixed;
}
.thin-border {
border-width: 0.15em;
}

View File

@ -4,13 +4,14 @@
<meta charset="UTF-8">
<title>Quiz The Word - {{ title }}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/cyborg/bootstrap.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/darkly/bootstrap.min.css">
<link rel="stylesheet" href="/static/style.css">
{% block head %}{% endblock %}
</head>
<body class="d-flex flex-column h-100">
<nav class="nav vertical-nav">
<a class="nav-link" href="#">Hidden Answer</a>
<a class="nav-link" href="{{ url_for('multiple_choice_category') }}">Multiple Choice</a>
<a class="nav-link" href="{{ url_for('hidden_answer_category') }}">Hidden Answer</a>
</nav>
{% block body %}{% endblock %}
<footer class="mt-auto py-3">

View File

@ -0,0 +1,87 @@
{% extends "base.html" %}
{% block body %}
<main class="flex-shrink-0">
<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>#}
</div>
</div>
<div class="text-center difficulty">
<div class="btn-group btn-group-toggle" data-toggle="buttons" onclick="change_difficulty()">
<label class="btn btn-secondary active">
<input type="radio" name="difficulty" id="easy" autocomplete="off" value="easy" checked>Easy
</label>
<label class="btn btn-secondary">
<input type="radio" name="difficulty" id="medium" autocomplete="off" value="medium">Medium
</label>
<label class="btn btn-secondary">
<input type="radio" name="difficulty" id="hard" autocomplete="off" value="hard">Hard
</label>
</div>
</div>
</main>
{% endblock %}
{% block scripts %}
<script>
function change_difficulty() {
let difficulty = $("input[name='difficulty']:checked").val();
console.log(difficulty);
$(".question").css("display", "none");
if (difficulty === "easy") {
$("#easy-question").css("display", "block");
} else if (difficulty === "medium") {
$("#medium-question").css("display", "block");
} else if (difficulty === "hard") {
$("#hard-question").css("display", "block");
}
}
function checkAnswer(question_id, button_index, answer) {
let loading_spinner = $("<div>")
.attr("id", "check-answer")
.addClass("text-center")
.text("Checking answer")
.append($("<div>")
.addClass("spinner-border thin-border ml-1")
.attr("role", "status"));
$(".question-text:visible").append(loading_spinner);
$(`button[id*='${question_id}']`).attr("disabled", true);
$.ajax(`{{ url_for("check_answer") }}?question_id=${question_id}&answer=${answer}`,{
method: "GET",
datatype: "json",
success: (data) => {
$("#check-answer").remove();
if (data[0] === true) {
console.log("answer correct");
$("#"+question_id+"-"+button_index).addClass("btn-success");
} else {
console.log("answer wrong");
$("#"+question_id+"-"+button_index).addClass("btn-danger");
$(`button[id*='${question_id}']`).each((i, element) => {
element = $(element);
if (element.text() === data[1]) {
element.addClass("btn-success");
}
})
}
}
});
}
/*function showAnswer() {
$(".question:visible").find(".answer").css("display", "block");
$(".show-answer:visible").css("display", "none");
}*/
</script>
{% endblock %}