connections/app/app.py

127 lines
3.6 KiB
Python
Executable File

from flask import Flask, abort, render_template, request
import datetime
import json
import sqlite3
import subprocess
import config
app = Flask(__name__)
DEFAULT_CATEGORIES = [
{"name": "linux commands", "items": ["cd", "touch", "cat", "find"], "level": 1},
{"name": "types of HTML input", "items": ["radio", "password", "submit", "text"], "level": 2},
{"name": "sticky ___", "items": ["tape", "fingers", "situation", "wicket"], "level": 3},
{"name": "how i feel, spelled backwards", "items": ["live", "deliver", "desserts", "denier"], "level": 4},
]
def get_puzzle_numbers(cursor):
rows = cursor.execute("SELECT number FROM puzzle ORDER BY number").fetchall()
return [row[0] for row in rows]
def get_puzzle_categories(cursor, puzzle_number):
row = cursor.execute("SELECT data FROM puzzle WHERE number = ?", [puzzle_number]).fetchone()
if not row:
return None
try:
payload = json.loads(row[0])
categories = payload.get("categories")
if isinstance(categories, list) and len(categories) == 4:
return categories
except (json.JSONDecodeError, TypeError):
return None
return None
def render_puzzle(puzzle_number=None):
connection = sqlite3.connect(config.db_file)
cursor = connection.cursor()
puzzle_numbers = get_puzzle_numbers(cursor)
if puzzle_number is None and puzzle_numbers:
puzzle_number = puzzle_numbers[-1]
if puzzle_number is not None:
categories = get_puzzle_categories(cursor, puzzle_number)
if categories is None and puzzle_number in puzzle_numbers:
categories = DEFAULT_CATEGORIES
elif categories is None:
connection.close()
abort(404)
else:
categories = DEFAULT_CATEGORIES
connection.close()
return render_template(
'index.html',
categories=categories,
puzzle_numbers=puzzle_numbers,
current_puzzle=puzzle_number,
)
@app.route('/')
def index():
return render_puzzle()
@app.route('/<int:puzzle_number>')
def puzzle_by_number(puzzle_number):
return render_puzzle(puzzle_number)
@app.route('/new', methods=['GET', 'POST'])
def new_puzzle():
if request.method == 'GET':
return render_template('new.html', creation_date=datetime.date.today().isoformat())
author = request.form.get("author", "").strip()
creation_date = request.form.get("creation_date", "").strip() or datetime.date.today().isoformat()
categories = []
for i in range(1, 5):
name = request.form.get(f"category_{i}_name", "").strip()
words = []
for j in range(1, 5):
word = request.form.get(f"category_{i}_word_{j}", "").strip()
if word:
words.append(word)
categories.append({"name": name, "items": words, "level": i})
if not author:
return render_template(
'new.html',
error="Author is required.",
creation_date=creation_date,
form=request.form
), 400
if any(not cat["name"] or len(cat["items"]) != 4 for cat in categories):
return render_template(
'new.html',
error="Each category needs a name and exactly 4 words.",
creation_date=creation_date,
form=request.form
), 400
data = json.dumps({"categories": categories}, separators=(",", ":"))
connection = sqlite3.connect(config.db_file)
cursor = connection.cursor()
row = cursor.execute("SELECT COALESCE(MAX(number), 0) + 1 FROM puzzle").fetchone()
number = row[0]
cursor.execute(
"""
INSERT INTO puzzle (number, author, creation_date, data)
VALUES (?, ?, ?, ?)
""",
[number, author, creation_date, data]
)
connection.commit()
connection.close()
return render_template(
'new.html',
success=f"Saved puzzle #{number}.",
creation_date=creation_date
)
if __name__ == '__main__':
subprocess.run(["python3", "make_db.py"], check=True)
app.run(host='0.0.0.0', port=8000)