Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 0 additions & 31 deletions docker-compose-github.yml

This file was deleted.

32 changes: 15 additions & 17 deletions flask/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
# base image
FROM python:3.7.2-alpine
FROM python:3.11-alpine

# install dependencies
RUN apk update && \
apk add --virtual build-deps gcc python-dev musl-dev && \
apk add postgresql-dev && \
apk add netcat-openbsd
apk add --no-cache \
gcc \
python3-dev \
musl-dev \
postgresql-dev \
libpq \
netcat-openbsd

# set working directory
WORKDIR /usr/src/app

# add and install requirements
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install -r requirements.txt

# add entrypoint.sh
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh
RUN ["chmod", "+x", "/usr/src/app/entrypoint.sh"]
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh

# add app
COPY . /usr/src/app
COPY . .

# run server
CMD ["/usr/src/app/entrypoint.sh"]
CMD ["./entrypoint.sh"]
5 changes: 5 additions & 0 deletions flask/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from flask_restful import Resource, Api
from flask_sqlalchemy import SQLAlchemy
from flask_caching import Cache
from flask_cors import CORS


# instantiate the db
Expand All @@ -18,6 +19,8 @@ def create_app(script_info=None):
# instantiate the app
app = Flask(__name__)

CORS(app, resources={r"/*": {"origins": "*"}})

# set config
app_settings = os.getenv('APP_SETTINGS')
app.config.from_object(app_settings)
Expand All @@ -39,6 +42,8 @@ def create_app(script_info=None):
app.register_blueprint(clues_blueprint)
from app.api.endpoints.categories import categories_blueprint
app.register_blueprint(categories_blueprint)
from app.api.endpoints.board import board_blueprint
app.register_blueprint(board_blueprint)

# shell context for flask cli
@app.shell_context_processor
Expand Down
56 changes: 56 additions & 0 deletions flask/app/api/endpoints/board.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from flask import Blueprint, request

from flask_restful import Resource, Api
from sqlalchemy import asc, desc, func, select, text
from sqlalchemy.orm import aliased
from app import db, cache
from app.api.models import Clues


board_blueprint = Blueprint('board', __name__)
api = Api(board_blueprint)


def get_board(cat_limit):

valid_categories = (
db.session.query(Clues.category)
.group_by(Clues.category)
.having(func.count(Clues.id) == 5)
.order_by(func.random())
.limit(cat_limit)
.all()
)

category_names = [cat.category for cat in valid_categories]

# Query all clues from these categories at once
board = (
Clues.query
.filter(Clues.category.in_(category_names))
.order_by(asc(Clues.value)) # Sort by value first
.all()
)

return [clue.to_json() for clue in board]

class BoardRandom(Resource):
def get(self):
# handle the int queries
try:
cat_limit = min(int(request.args.get('cat_limit', 6)), 8)
except ValueError:
return {
'status' : 'failure',
'error': repr('cat_limit and/or clue_limit query parameter is not a valid number')
}, 400


result = get_board(cat_limit)

return {
'status' : 'success',
'data': result
}, 200

api.add_resource(BoardRandom, "/random_board")
60 changes: 29 additions & 31 deletions flask/app/api/endpoints/clues.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import random

from flask import Blueprint, request

from flask_restful import Resource, Api
from sqlalchemy import func, or_
from sqlalchemy import func

from app import db, cache
from app.api.models import Clues
from app.api.exceptions import LimitNotANumberError, LimitOverMaxError, \
OffsetNotANumberError, OrderByInvalidError, \
SortInvalidError, IdNotFoundError
from app.api.exceptions import LimitNotANumberError, LimitOverMaxError, IdNotFoundError

clues_blueprint = Blueprint('clues', __name__)
api = Api(clues_blueprint)
Expand All @@ -27,30 +26,13 @@ def get_clues(limit, offset, order_by, sort):
'response' : Clues.response
}

####
# Checking for exceptions
####

digits = '0123456789'
for c in str(limit):
if digits.find(c) == -1:
raise LimitNotANumberError('"Limit" query parameter is not a valid number')
if int(limit) > 1000:
raise LimitOverMaxError('Requested too many resources. Maximum "limit" is 1000')
for c in str(offset):
if digits.find(c) == -1:
raise OffsetNotANumberError('"offset" query parameter is not a valid number')
if string_to_col.get(order_by, 'none') == 'none':
raise OrderByInvalidError('"order_by" query parameter is invalid')
if sort != 'asc' and sort != 'desc':
raise SortInvalidError('"sort" query parameter must be "asc" or "desc"')

if sort == 'asc':
return [clue.to_json() for clue in Clues.query
.order_by(string_to_col[order_by])
.limit(limit)
.offset(offset)
.all()]

if sort == 'desc':
return [clue.to_json() for clue in Clues.query
.order_by(string_to_col[order_by].desc())
Expand All @@ -62,18 +44,28 @@ def get_clues(limit, offset, order_by, sort):
# TODO: search category and/or value
class CluesList(Resource):
def get(self):
limit = request.args.get('limit', 50)
offset = request.args.get('offset', 0)
order_by = request.args.get('order_by', 'id')
sort = request.args.get('sort', 'asc')
# handle the int queries
try:
result = get_clues(limit, offset, order_by, sort)
except Exception as e:
limit = min(int(request.args.get('limit', 50)), 1000)
offset = request.args.get('offset', 0)
except ValueError:
return {
'status' : 'failure',
'error': repr(e)
'error': repr('Limit and/or offset query parameter is not a valid number')
}, 400

order_by = request.args.get('order_by', 'id')
sort = request.args.get('sort', 'asc')

#handle the string queries
valid_columns = ['id', 'game_id', 'value', 'daily_double', 'round', 'category', 'clue', 'response']
if order_by not in valid_columns:
return {'status': 'failure', 'error': f'Invalid order_by. Must be one of: {valid_columns}'}, 400
if sort != 'asc' and sort != 'desc':
return {'status': 'failure', 'error': f'The "sort" query must either be "asc" or "desc"'}, 400

result = get_clues(limit, offset, order_by, sort)

return {
'status' : 'success',
'data': result
Expand Down Expand Up @@ -116,9 +108,15 @@ def get_clue_random(limit, category, difficulty):

if category is None and difficulty is None:
results = []
max = Clues.query.with_entities(func.max(Clues.id)).first()[0]
usedIds = [0]
helper = Clues.query.with_entities(func.max(Clues.id)).first()
max = None
if not helper:
raise IdNotFoundError('The max doesnt exist or something')
else:
max = helper[0]

usedIds = [0]

randId = 0
randResult = []

Expand Down
10 changes: 5 additions & 5 deletions flask/app/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ class Games(db.Model):
score3 = db.Column(db.Integer)

def __repr__(self):
return f'Game [id = {self.id}, episode_num = {episode_num}, ' + \
f'season_id = {season_id}, air_date = {air_date}, notes = {notes}, ' + \
f'contestant1 = {contestant1}, contestant2 = {contestant2}, ' + \
f'contestant3 = {contestant3}, winner = {winner}, score1 = {score1}, ' + \
f'score2 = {score2}, score3 = {score3}]'
return f'Game [id = {self.id}, episode_num = {self.episode_num}, ' + \
f'season_id = {self.season_id}, air_date = {self.air_date}, notes = {self.notes}, ' + \
f'contestant1 = {self.contestant1}, contestant2 = {self.contestant2}, ' + \
f'contestant3 = {self.contestant3}, winner = {self.winner}, score1 = {self.score1}, ' + \
f'score2 = {self.score2}, score3 = {self.score3}]'

def to_json(self):
return {
Expand Down
15 changes: 8 additions & 7 deletions flask/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Flask==1.0.2
Flask-RESTful==0.3.7
Flask-SQLAlchemy==2.3.2
psycopg2-binary==2.7.7
Flask-Caching==1.7.2
gunicorn==19.9.0
redis==3.1.0
Flask==3.0.0
Flask-RESTful==0.3.10
Flask-SQLAlchemy==3.1.1
psycopg[binary]==3.3.2
Flask-Caching==2.1.0
gunicorn==21.2.0
redis==5.0.1
flask-cors==4.0.0
1 change: 0 additions & 1 deletion postgres/init/jeopardy201908021145.sql
Original file line number Diff line number Diff line change
Expand Up @@ -366827,7 +366827,6 @@ COPY public.clues (id, game_id, value, daily_double, round, category, clue, resp
367364 6314 2000 f DJ! THE "PITS" Both cyanide & laetrile are an extract of them apricot pits
\.


--
-- Data for Name: contestants; Type: TABLE DATA; Schema: public; Owner: postgres
--
Expand Down