TODO-app/app.py
2025-12-16 20:02:55 +01:00

164 lines
4.6 KiB
Python

import os
from datetime import datetime
from flask import Flask, render_template, redirect, url_for, request, flash
from 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(__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 = request.form.get("status") or "not-started"
if not title or not description:
flash("Title och description krävs.", "danger")
return redirect(url_for("dashboard"))
if status not in STATUS_VALUES:
status = "not-started"
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()
app.run(host="127.0.0.1", port=5001, debug=True)