diff --git a/.flaskenv b/.flaskenv index 85b6b69..d0218b1 100644 --- a/.flaskenv +++ b/.flaskenv @@ -1,2 +1,2 @@ FLASK_APP=trivia.create_app -FLASK_ENV=development \ No newline at end of file +FLASK_ENV=production \ No newline at end of file diff --git a/README.md b/README.md index cae2767..59623a8 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,11 @@ on at `localhost:5000` unless git clone https://github.com/Xevion/trivia.git cd trivia pip install -r requirements.txt -python wsgi.py +flask run ``` +Edit .flaskenv to change the configuration preset. + ## Application Design The webapp has two sides: Client and Server. diff --git a/cli.py b/cli.py index fb29939..bcf298a 100644 --- a/cli.py +++ b/cli.py @@ -5,7 +5,6 @@ A simple CLI implementation using the application's API. """ import curses -import time from datetime import datetime from typing import List @@ -18,7 +17,7 @@ lastAttempt: float = -1 lastUpdate: float = -1 -def refreshScores() -> bool: +def refreshScores() -> None: """ Refreshes scoreboard data safely, handling a unresponsive or downed scoreboard. Uses If-Modified-Since headers properly. @@ -30,21 +29,24 @@ def refreshScores() -> bool: global lastUpdate, lastAttempt, scores # Send with If-Modified-Since header if this is not the first time - headers = {'If-Modified-Since': datetime.fromtimestamp(lastAttempt).strftime('%a, %d %b %Y %I:%M:%S')} if lastAttempt > 0 else {} + useTime = max(lastAttempt, lastUpdate) + headers = { + 'If-Modified-Since': datetime.fromtimestamp(useTime, pytz.utc).strftime( + '%a, %d %b %Y %I:%M:%S %Z')} if useTime > 0 else {} # Send request with headers try: resp = requests.get('http://127.0.0.1:5000/api/scores/', headers=headers) except requests.exceptions.ConnectionError: resp = None finally: - lastAttempt = time.time() + lastAttempt = datetime.utcnow().timestamp() if resp is not None and resp.ok: if resp.status_code == 304 and len(scores) != 0: pass else: # Changes found, update! - lastUpdate = time.time() + lastUpdate = datetime.utcnow().timestamp() scores = resp.json() # Calculate totals, preliminary sort by total @@ -54,25 +56,25 @@ def refreshScores() -> bool: # Calculate ranks with tie handling logic for i, team in enumerate(scores): + # Check that previous score is the same, if so add a 'T' for tie if i > 0 and scores[i - 1]['total'] == team['total']: team['rank'] = scores[i - 1]['rank'] + # Check if we have a T if not team['rank'].startswith('T'): team['rank'] = 'T' + team['rank'].strip() + # Check if previous score has a T if not scores[i - 1]['rank'].startswith('T'): scores[i - 1]['rank'] = 'T' + scores[i - 1]['rank'].strip() - else: + # Otherwise just add a space in front instead team['rank'] = " " + str(i + 1) - return True - return False - def main(screen) -> None: """ - Mainloop function + Mainloop function. :param screen: Curses screen """ @@ -80,7 +82,7 @@ def main(screen) -> None: screen.redrawwin() while True: # Refresh scores every 10 seconds - if time.time() - lastAttempt > 0.5: + if datetime.utcnow().timestamp() - lastAttempt > 0.5: refreshScores() # Get current terminal size and clear @@ -90,6 +92,7 @@ def main(screen) -> None: # Build table data global scores table = [[team['rank'], team['id'], team['name'], team['total'], *team['scores']] for team in scores[:y - 4]] + # Round number headers scoreSet = map(str, range(1, max(8, len(scores[0]['scores'])) + 1)) if scores else [] table.insert(0, ['Rank', 'ID', 'Team Name', 'T', *scoreSet]) table = SingleTable(table, title='EfTA Trivia Night') @@ -104,7 +107,8 @@ def main(screen) -> None: # Terminal Size strpos = str((x, y)) screen.addstr(y - 1, 1, strpos) - screen.addstr(y - 1, 1 + len(strpos) + 1, f'({str(round(0.5 - (time.time() - lastAttempt), 3)).zfill(3)})') + screen.addstr(y - 1, 1 + len(strpos) + 1, + f'({str(round(0.5 - (datetime.utcnow().timestamp() - lastAttempt), 3)).zfill(3)})') # Update curses screen screen.refresh() diff --git a/trivia/api.py b/trivia/api.py index 0afd479..a981b08 100644 --- a/trivia/api.py +++ b/trivia/api.py @@ -27,7 +27,14 @@ def scores(): try: if request.headers['If-Modified-Since']: # Acquire epoch time from header - epoch = time.mktime(time.strptime(request.headers['If-Modified-Since'], "%a, %d %b %Y %I:%M:%S")) + if current_app.config['DEBUG']: + print(request.headers['If-Modified-Since']) + epoch = datetime.strptime(request.headers['If-Modified-Since'], "%a, %d %b %Y %I:%M:%S %Z") + if current_app.config['DEBUG']: + print(epoch) + epoch = epoch.timestamp() + if current_app.config['DEBUG']: + print(epoch, lastChange, lastChange - epoch) if epoch >= lastChange: status_code = 304 except KeyError: diff --git a/trivia/config.py b/trivia/config.py index 8f7c35c..210e123 100644 --- a/trivia/config.py +++ b/trivia/config.py @@ -6,6 +6,7 @@ config.py configs = { 'production': 'trivia.config.Config', + 'productiondep': 'trivia.config.ConfigDeprecated', 'development': 'trivia.config.Config', 'demo': 'trivia.config.DemoConfig' } @@ -15,6 +16,8 @@ class Config(object): # Main Configuration SCORE_FILE = 'scores.json' POLLING_INTERVAL = 5 + DEBUG = False + CONVERT_OLD = True # Demo Configuration DEMO = False @@ -23,9 +26,14 @@ class Config(object): DEMO_MAX_SCORES = 0 +def ConfigDeprecated(Config): + CONVERT_OLD = True + + class DemoConfig(Config): # Main Configuration SCORE_FILE = 'demo.json' + DEBUG = True # Demo Configuration DEMO = True diff --git a/trivia/create_app.py b/trivia/create_app.py index b465566..d066dde 100644 --- a/trivia/create_app.py +++ b/trivia/create_app.py @@ -1,10 +1,11 @@ -from flask_apscheduler import APScheduler from flask import Flask +from flask_apscheduler import APScheduler from trivia.config import configs scheduler: APScheduler = None + def create_app(env=None): app = Flask(__name__) @@ -23,14 +24,16 @@ def create_app(env=None): scheduler.start() # Add score file polling - scheduler.add_job(id='polling', func=utils.refreshScores, trigger="interval", seconds=app.config['POLLING_INTERVAL']) + scheduler.add_job(id='polling', func=utils.refreshScores, trigger="interval", + seconds=app.config['POLLING_INTERVAL']) if app.config['DEMO']: app.logger.info('Generating Demo Data...') # Generate initial Demo data utils.generateDemo() # Begin altering demo data regularly - scheduler.add_job(id='altering', func=utils.alterDemo, trigger="interval", seconds=app.config['DEMO_ALTERATION_INTERVAL']) + scheduler.add_job(id='altering', func=utils.alterDemo, trigger="interval", + seconds=app.config['DEMO_ALTERATION_INTERVAL']) utils.refreshScores() diff --git a/trivia/templates/index.html b/trivia/templates/index.html index dd30735..39c0d03 100644 --- a/trivia/templates/index.html +++ b/trivia/templates/index.html @@ -86,7 +86,11 @@ {% for team in teams %}