Added multiple choice category
This commit is contained in:
parent
0e981fce87
commit
2d18b1b2bf
@ -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
|
||||
|
74
database.py
74
database.py
@ -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
40
main.py
@ -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
24
scripts/file_parsing.py
Normal 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
|
@ -26,3 +26,7 @@
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
}
|
||||
|
||||
.thin-border {
|
||||
border-width: 0.15em;
|
||||
}
|
||||
|
@ -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">
|
||||
|
87
templates/multiple_choice.html
Normal file
87
templates/multiple_choice.html
Normal 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 %}
|
Loading…
Reference in New Issue
Block a user