164 lines
4.7 KiB
Python
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)
|