diff --git a/config.py b/config.py index 1f452c0..70532a7 100644 --- a/config.py +++ b/config.py @@ -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 diff --git a/database.py b/database.py index e466bff..85e2ae4 100644 --- a/database.py +++ b/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"" -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 diff --git a/main.py b/main.py index a087455..e48fb4a 100644 --- a/main.py +++ b/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() diff --git a/scripts/file_parsing.py b/scripts/file_parsing.py new file mode 100644 index 0000000..6f1e428 --- /dev/null +++ b/scripts/file_parsing.py @@ -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 diff --git a/static/style.css b/static/style.css index a9f4b6f..26f0388 100644 --- a/static/style.css +++ b/static/style.css @@ -26,3 +26,7 @@ writing-mode: vertical-rl; text-orientation: mixed; } + +.thin-border { + border-width: 0.15em; +} diff --git a/templates/base.html b/templates/base.html index 3be764c..7cc991e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -4,13 +4,14 @@ Quiz The Word - {{ title }} - + {% block head %}{% endblock %} {% block body %}{% endblock %}