from __future__ import annotations import json from pathlib import Path from flask import Flask from config import Config from .cli import register_cli, seed_badges from .extensions import csrf, db, login_manager from .forms import QuickTaskForm from .routes import auth, main, scoreboard, settings, tasks from .routes.main import load_icon_svg from .services.app_settings import get_quick_task_config from .services.badges import sync_existing_badges from .services.bootstrap import ensure_schema_and_admins from .services.dates import MONTH_NAMES, local_now from .services.monthly import archive_months_missing_up_to_previous def create_app(config_class: type[Config] = Config) -> Flask: app = Flask(__name__, static_folder="static", template_folder="templates") app.config.from_object(config_class) manifest_path = Path(app.root_path).parent / "CloudronManifest.json" try: app.config["APP_VERSION"] = json.loads(manifest_path.read_text(encoding="utf-8")).get("version", "0.0.0") except FileNotFoundError: app.config["APP_VERSION"] = "0.0.0" app.config["DATA_DIR"].mkdir(parents=True, exist_ok=True) app.config["UPLOAD_FOLDER"].mkdir(parents=True, exist_ok=True) db.init_app(app) login_manager.init_app(app) csrf.init_app(app) with app.app_context(): db.create_all() ensure_schema_and_admins() seed_badges() sync_existing_badges() register_cli(app) app.register_blueprint(main.bp) app.register_blueprint(auth.bp) app.register_blueprint(tasks.bp) app.register_blueprint(scoreboard.bp) app.register_blueprint(settings.bp) app.jinja_env.globals["icon_svg"] = lambda name: load_icon_svg(name, app.static_folder) @app.before_request def ensure_archives(): archive_months_missing_up_to_previous() @app.context_processor def inject_globals(): quick_task_form = QuickTaskForm(prefix="quick") quick_task_config = get_quick_task_config() quick_task_form.effort.choices = [ (key, values["label"]) for key, values in quick_task_config.items() ] def asset_version(filename: str) -> int: path = Path(app.static_folder) / filename try: return int(path.stat().st_mtime) except FileNotFoundError: return 1 return { "app_name": app.config["APP_NAME"], "app_version": app.config["APP_VERSION"], "nav_items": [ ("tasks.my_tasks", "Meine Aufgaben", "house"), ("tasks.all_tasks", "Alle", "list"), ("tasks.create", "Neu", "plus"), ("tasks.calendar_view", "Kalender", "calendar"), ("scoreboard.index", "Highscore", "trophy"), ("settings.index", "Optionen", "gear"), ], "mobile_nav_items": [ ("tasks.my_tasks", "Meine Aufgaben", "house"), ("tasks.all_tasks", "Alle Aufgaben", "list"), ("tasks.calendar_view", "Kalender", "calendar"), ("scoreboard.index", "Highscore", "trophy"), ("settings.index", "Optionen", "gear"), ], "icon_svg": lambda name: load_icon_svg(name, app.static_folder), "asset_version": asset_version, "now_local": local_now(), "quick_task_form": quick_task_form, "quick_task_config": quick_task_config, } @app.template_filter("date_de") def date_de(value): return value.strftime("%d.%m.%Y") if value else "—" @app.template_filter("datetime_de") def datetime_de(value): return value.strftime("%d.%m.%Y, %H:%M") if value else "—" @app.template_filter("month_name") def month_name(value): return MONTH_NAMES[value] return app