| """Minimal Flask app for Hugging Face Docker Space: landing, analysis, replay placeholder.""" |
|
|
| from __future__ import annotations |
|
|
| from pathlib import Path |
|
|
| import duckdb |
| from flask import Flask, render_template, request |
|
|
| APP_DIR = Path(__file__).resolve().parent |
| DEMO_CSV = APP_DIR / "demo_data" / "demo_trials.csv" |
|
|
| PRESETS: list[dict[str, str]] = [ |
| { |
| "id": "summary", |
| "label": "Summary (count, mean RT, age range)", |
| "sql": """ |
| SELECT |
| COUNT(*)::INT AS n_trials, |
| ROUND(AVG(rt_ms), 2) AS mean_rt_ms, |
| MIN(age)::INT AS min_age, |
| MAX(age)::INT AS max_age |
| FROM read_csv_auto(?) |
| """, |
| }, |
| { |
| "id": "by_age", |
| "label": "Mean RT by age", |
| "sql": """ |
| SELECT |
| age, |
| COUNT(*)::INT AS n_trials, |
| ROUND(AVG(rt_ms), 2) AS mean_rt_ms |
| FROM read_csv_auto(?) |
| GROUP BY age |
| ORDER BY age |
| """, |
| }, |
| ] |
|
|
| PRESET_CHOICES = [{"id": p["id"], "label": p["label"]} for p in PRESETS] |
|
|
|
|
| def create_app() -> Flask: |
| app = Flask(__name__, static_folder="static", template_folder="templates") |
|
|
| @app.get("/health") |
| def health() -> tuple[str, int]: |
| return "ok", 200 |
|
|
| @app.get("/") |
| def landing(): |
| return render_template("landing.html") |
|
|
| @app.get("/analysis") |
| def analysis(): |
| preset_id = (request.args.get("preset") or "summary").strip() |
| preset = next((p for p in PRESETS if p["id"] == preset_id), PRESETS[0]) |
| error: str | None = None |
| table_html: str | None = None |
| if not DEMO_CSV.is_file(): |
| error = f"Demo data missing at {DEMO_CSV}" |
| else: |
| try: |
| con = duckdb.connect(database=":memory:") |
| rel = con.execute(preset["sql"], [str(DEMO_CSV)]) |
| df = rel.fetchdf() |
| table_html = df.to_html(classes="data", index=False, border=0) |
| except Exception as exc: |
| error = str(exc) |
|
|
| return render_template( |
| "analysis.html", |
| presets=PRESET_CHOICES, |
| current_preset=preset["id"], |
| table_html=table_html, |
| error=error, |
| ) |
|
|
| @app.get("/replay") |
| def replay(): |
| return render_template("replay.html") |
|
|
| return app |
|
|
|
|
| app = create_app() |
|
|