Move from AWS to GCP
This commit is contained in:
parent
dd763deaea
commit
bc31348313
@ -1,8 +0,0 @@
|
||||
# Elastic Beanstalk Files
|
||||
.elasticbeanstalk/*
|
||||
!.elasticbeanstalk/*.cfg.yml
|
||||
!.elasticbeanstalk/*.global.yml
|
||||
|
||||
.idea/
|
||||
__pycache__/
|
||||
venv/
|
7
.gcloudignore
Normal file
7
.gcloudignore
Normal file
@ -0,0 +1,7 @@
|
||||
.idea/
|
||||
__pycache__/
|
||||
venv/
|
||||
.gcloudignore
|
||||
.git
|
||||
.gitignore
|
||||
*.tsv
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,9 +1,3 @@
|
||||
|
||||
# Elastic Beanstalk Files
|
||||
.elasticbeanstalk/*
|
||||
!.elasticbeanstalk/*.cfg.yml
|
||||
!.elasticbeanstalk/*.global.yml
|
||||
|
||||
.env
|
||||
.idea/
|
||||
__pycache__/
|
||||
|
@ -1,52 +0,0 @@
|
||||
from flask import Flask, render_template, redirect, url_for
|
||||
from botocore.exceptions import ClientError
|
||||
import database
|
||||
|
||||
application = Flask(__name__)
|
||||
application.config.from_pyfile("config.py")
|
||||
|
||||
|
||||
@application.route("/")
|
||||
def index():
|
||||
try:
|
||||
easy = database.get_random_question_difficulty(1)
|
||||
medium = database.get_random_question_difficulty(2)
|
||||
hard = database.get_random_question_difficulty(3)
|
||||
return render_template(
|
||||
"index.html",
|
||||
title="Quiz The Word",
|
||||
easy=easy,
|
||||
medium=medium,
|
||||
hard=hard,
|
||||
)
|
||||
except ClientError as e:
|
||||
return e.response["Error"]["Message"]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
@application.route("/question/<int:question_index>")
|
||||
def question(question_index):
|
||||
try:
|
||||
item_count = database.get_question_count()
|
||||
if question_index < item_count:
|
||||
item = database.get_question(question_index)
|
||||
return render_template(
|
||||
"index.html",
|
||||
title="Bible Trivia",
|
||||
question=item["Question"],
|
||||
answer=item["Answer"],
|
||||
addresses=item["Address"],
|
||||
category=item["Category"],
|
||||
difficulty=item["Difficulty"],
|
||||
)
|
||||
else:
|
||||
return "<h1>Question not found.</h1>"
|
||||
except ClientError as e:
|
||||
return e.response["Error"]["Message"]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
application.run()
|
12
config.py
12
config.py
@ -5,6 +5,12 @@ from dotenv import load_dotenv
|
||||
load_dotenv(".env")
|
||||
|
||||
SECRET_KEY = environ.get("SECRET_KEY")
|
||||
AWS_ACCESS_KEY_ID = environ.get("AWS_ACCESS_KEY_ID")
|
||||
AWS_SECRET_ACCESS_KEY = environ.get("AWS_SECRET_ACCESS_KEY")
|
||||
REGION_NAME = environ.get("REGION_NAME")
|
||||
DB_USER = environ.get("DB_USER")
|
||||
DB_PASSWORD = environ.get("DB_PASSWORD")
|
||||
DB_HOST = environ.get("DB_HOST")
|
||||
DB_PORT = environ.get("DB_PORT")
|
||||
DB_NAME = environ.get("DB_NAME")
|
||||
DB_SOCKET_DIR = environ.get("DB_SOCKET_DIR", "/cloudsql")
|
||||
CLOUD_SQL_CONNECTION_NAME = environ.get("CLOUD_SQL_CONNECTION_NAME")
|
||||
# DB_URL = f"postgres+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?host={DB_SOCKET_DIR}/{CLOUD_SQL_CONNECTION_NAME}"
|
||||
DB_URL = f"postgres+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
|
||||
|
100
database.py
100
database.py
@ -1,36 +1,86 @@
|
||||
from flask import current_app, g
|
||||
import boto3
|
||||
from boto3.dynamodb.conditions import Key, Attr
|
||||
import random
|
||||
from dynamodb_json import json_util
|
||||
import sqlalchemy
|
||||
from sqlalchemy import Column, JSON, String, Integer, create_engine, ForeignKey, func
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm import sessionmaker, relationship
|
||||
from config import DB_URL
|
||||
|
||||
engine = create_engine(DB_URL)
|
||||
Session = sessionmaker(bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
def get_table(table_name):
|
||||
table = getattr(g, table_name, None)
|
||||
if table is None:
|
||||
dynamo = boto3.resource("dynamodb", aws_access_key_id=current_app.config["AWS_ACCESS_KEY_ID"],
|
||||
aws_secret_access_key=current_app.config["AWS_SECRET_ACCESS_KEY"], region_name=current_app.config["REGION_NAME"])
|
||||
setattr(g, table_name, dynamo.Table(table_name))
|
||||
return dynamo.Table(table_name)
|
||||
return table
|
||||
class AllQuestions(Base):
|
||||
__tablename__ = "all_questions"
|
||||
|
||||
question_id = Column(Integer, primary_key=True, nullable=False)
|
||||
question = Column(String)
|
||||
answer = Column(String)
|
||||
addresses = Column(String)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Question: {self.question_id}>"
|
||||
|
||||
|
||||
def get_question_count():
|
||||
table = get_table("BibleQuestions")
|
||||
return table.item_count
|
||||
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)
|
||||
|
||||
all_question_relationship = relationship("AllQuestions", primaryjoin="HiddenAnswer.question_id==AllQuestions.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")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Question Hidden Answer: {self.question_id}>"
|
||||
|
||||
|
||||
def get_question(id):
|
||||
table = get_table("BibleQuestions")
|
||||
response = table.get_item(Key={"id": id})
|
||||
return json_util.loads(response["Item"])
|
||||
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)
|
||||
wrong_answers = Column(String)
|
||||
|
||||
all_question_relationship = relationship("AllQuestions", primaryjoin="MultipleChoice.question_id==AllQuestions.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")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Question Multiple Choice: {self.question_id}>"
|
||||
|
||||
|
||||
def get_questions_of_difficulty(difficulty):
|
||||
table = get_table("BibleQuestions")
|
||||
return table.scan(FilterExpression=Attr("Difficulty").eq(difficulty))
|
||||
def get_all_questions():
|
||||
session = Session()
|
||||
return session.query(AllQuestions).all()
|
||||
|
||||
|
||||
def get_random_question_difficulty(difficulty):
|
||||
questions = get_questions_of_difficulty(difficulty)
|
||||
return questions["Items"][random.randint(0, questions["Count"]-1)]
|
||||
def get_all_hidden_answer():
|
||||
session = Session()
|
||||
return session.query(HiddenAnswer).all()
|
||||
|
||||
|
||||
def get_all_multiple_choice():
|
||||
session = Session()
|
||||
return session.query(MultipleChoice).all()
|
||||
|
||||
|
||||
def get_category_count(category):
|
||||
session = Session()
|
||||
return session.query(category).count()
|
||||
|
||||
|
||||
def get_question(category, question_id):
|
||||
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()
|
||||
return session.query(category).filter(category.difficulty == difficulty).order_by(func.random()).first()
|
||||
|
23
main.py
Normal file
23
main.py
Normal file
@ -0,0 +1,23 @@
|
||||
from flask import Flask, render_template
|
||||
import database
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile("config.py")
|
||||
|
||||
|
||||
@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 render_template(
|
||||
"hidden_answer.html",
|
||||
title="Home",
|
||||
easy=easy,
|
||||
medium=medium,
|
||||
hard=hard,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
@ -1,16 +1,11 @@
|
||||
boto3==1.16.25
|
||||
botocore==1.19.25
|
||||
click==7.1.2
|
||||
dynamodb-json==1.3
|
||||
Flask==1.1.2
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
jmespath==0.10.0
|
||||
Jinja2==2.11.3
|
||||
MarkupSafe==1.1.1
|
||||
python-dateutil==2.8.1
|
||||
pg8000==1.17.0
|
||||
psycopg2==2.8.6
|
||||
python-dotenv==0.15.0
|
||||
s3transfer==0.3.3
|
||||
simplejson==3.17.2
|
||||
six==1.15.0
|
||||
urllib3==1.26.2
|
||||
scramp==1.2.0
|
||||
SQLAlchemy==1.3.23
|
||||
Werkzeug==1.0.1
|
||||
|
@ -19,3 +19,10 @@
|
||||
.difficulty {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.vertical-nav {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
}
|
||||
|
25
templates/base.html
Normal file
25
templates/base.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="h-100" lang="en">
|
||||
<head>
|
||||
<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="/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>
|
||||
</nav>
|
||||
{% block body %}{% endblock %}
|
||||
<footer class="mt-auto py-3">
|
||||
<div class="container text-center">
|
||||
<span class="">Created By ItIsGood.com</span>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</html>
|
66
templates/hidden_answer.html
Normal file
66
templates/hidden_answer.html
Normal file
@ -0,0 +1,66 @@
|
||||
{% 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 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 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>
|
||||
<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>
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
</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 showAnswer() {
|
||||
$(".question:visible").find(".answer").css("display", "block");
|
||||
$(".show-answer:visible").css("display", "none");
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,129 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="h-100" lang="en">
|
||||
<head>
|
||||
<title>{{ 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">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
|
||||
<body class="d-flex flex-column h-100">
|
||||
<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 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"></div>
|
||||
<p>{{ easy["Difficulty"] }}</p>
|
||||
</div>
|
||||
</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>
|
||||
<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"></div>
|
||||
<p>{{ medium["Difficulty"] }}</p>
|
||||
</div>
|
||||
</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>
|
||||
<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"></div>
|
||||
<p>{{ hard["Difficulty"] }}</p>
|
||||
</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" value="easy" checked>Easy
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="difficulty" id="medium" value="medium">Medium
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="difficulty" id="hard" value="hard">Hard
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="mt-auto py-3">
|
||||
<div class="container text-center">
|
||||
<span class="">Created By ItIsGood.com</span>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<script>
|
||||
let easy_addresses = {{ easy["Address"]|tojson }};
|
||||
let medium_addresses = {{ medium["Address"]|tojson }};
|
||||
let hard_addresses = {{ hard["Address"]|tojson }};
|
||||
|
||||
function parse_bible_verses(bible_verses) {
|
||||
let addresses = [];
|
||||
for (let address of bible_verses) {
|
||||
let verses = "";
|
||||
if (address["Verses"] != null) {
|
||||
for (let verse of address["Verses"]) {
|
||||
if (verses != "") {
|
||||
verses += ", ";
|
||||
} else {
|
||||
verses = ":";
|
||||
}
|
||||
if (typeof verse != "number") {
|
||||
verses += `${verse[0]}-${verse[1]}`;
|
||||
} else {
|
||||
verses += verse.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (address["Chapter"] != null) {
|
||||
addresses.push(`${address["Book"]} ${address["Chapter"]}${verses}`);
|
||||
} else if (address["Book"] != null) {
|
||||
addresses.push(`${address["Book"]}`);
|
||||
}
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
function show_bible_verses(element_id, bible_verses) {
|
||||
let bibleVerses = $(element_id);
|
||||
if (bible_verses.length > 0) {
|
||||
for (let address of bible_verses) {
|
||||
if (address != bible_verses[0]) {
|
||||
bibleVerses.append(`; ${address}`);
|
||||
} else {
|
||||
bibleVerses.append(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show_bible_verses("#easy-bible-verses", parse_bible_verses(easy_addresses));
|
||||
show_bible_verses("#medium-bible-verses", parse_bible_verses(medium_addresses));
|
||||
show_bible_verses("#hard-bible-verses", parse_bible_verses(hard_addresses));
|
||||
|
||||
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 showAnswer() {
|
||||
$(".question:visible").find(".answer").css("display", "block");
|
||||
$(".show-answer:visible").css("display", "none");
|
||||
}
|
||||
</script>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user