TODO-app/app/__init__.py

164 lines
4.7 KiB
Python

import os
from datetime import datetime
from flask import Flask, render_template, redirect, url_for, request, flash
from app.validation import normalize_status, validate_todo_fields
from app.db import get_db, init_db
# =====================================================
# Constants
# =====================================================
STATUS_VALUES = ("not-started", "in-progress", "done")
# =====================================================
# Flask App Factory
# =====================================================
def create_app(test_config: dict | None = None) -> Flask:
"""
Skapar och konfigurerar Flask-applikationen.
Används både för runtime och tester.
"""
app = Flask(__name__)
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "dev-secret")
# -------------------------------------------------
# App Configuration
# -------------------------------------------------
default_db = os.path.join(os.path.dirname(os.path.dirname(__file__)), "todo.db")
app.config.update(
DB_PATH=default_db,
TESTING=False,
)
if test_config:
app.config.update(test_config)
# -------------------------------------------------
# Initiera databasen
# -------------------------------------------------
init_db(app)
# =================================================
# Routes
# =================================================
@app.route("/")
def index():
"""
Root → redirect till dashboard
"""
return redirect(url_for("dashboard"))
@app.route("/dashboard", methods=["GET"])
def dashboard():
"""
Visar alla todos
"""
with get_db(app) as conn:
rows = conn.execute(
"""
SELECT * FROM todo
ORDER BY
CASE status
WHEN 'not-started' THEN 1
WHEN 'in-progress' THEN 2
ELSE 3
END,
id DESC
"""
).fetchall()
return render_template(
"index.html",
todos=rows,
status_values=STATUS_VALUES,
)
@app.route("/todo/create", methods=["POST"])
def create_todo():
"""
Skapar en ny todo
"""
title = (request.form.get("title") or "").strip()
description = (request.form.get("description") or "").strip()
status = normalize_status(request.form.get("status", ""))
ok, errors = validate_todo_fields(title, description)
if not ok:
flash("Title och description krävs.", "danger")
return redirect(url_for("dashboard"))
now = datetime.now().isoformat(timespec="seconds")
with get_db(app) as conn:
conn.execute(
"""
INSERT INTO todo (title, description, status, created)
VALUES (?, ?, ?, ?)
""",
(title, description, status, now),
)
flash("Todo skapad!", "success")
return redirect(url_for("dashboard"))
@app.route("/todo/<int:todo_id>/status", methods=["POST"])
def update_status(todo_id: int):
"""
Uppdaterar status på en todo
"""
status = request.form.get("status")
if status not in STATUS_VALUES:
flash("Ogiltig status.", "danger")
return redirect(url_for("dashboard"))
now = datetime.now().isoformat(timespec="seconds")
with get_db(app) as conn:
cur = conn.execute(
"""
UPDATE todo
SET status = ?, edited = ?
WHERE id = ?
""",
(status, now, todo_id),
)
flash(
"Status uppdaterad." if cur.rowcount else "Todo hittades inte.",
"success" if cur.rowcount else "danger",
)
return redirect(url_for("dashboard"))
@app.route("/todo/<int:todo_id>/delete", methods=["POST"])
def delete_todo(todo_id: int):
"""
Tar bort en todo
"""
with get_db(app) as conn:
cur = conn.execute(
"DELETE FROM todo WHERE id = ?",
(todo_id,),
)
flash(
"Todo borttagen." if cur.rowcount else "Todo hittades inte.",
"success" if cur.rowcount else "danger",
)
return redirect(url_for("dashboard"))
return app
# =====================================================
# Main
# =====================================================
if __name__ == "__main__":
app = create_app()
debug = os.environ.get("FLASK_DEBUG", "0") == "1"
app.run(host="0.0.0.0", port=5001, debug=debug)