feat: initial commit

This commit is contained in:
nikola
2026-05-19 14:53:39 +02:00
commit 6d1b20c4b0
90 changed files with 243385 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
import csv
import uuid
from datetime import datetime, timezone
from pathlib import Path
from flask import Flask, redirect, render_template, request, send_file, url_for
from app.diagram import render_diagram
from app.explorer import render_explorer
from app.parser import parse_gam_raw_csv, sharing_matrix, summarize, top_stats
BASE_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = BASE_DIR / "data"
DATA_DIR.mkdir(parents=True, exist_ok=True)
app = Flask(
__name__,
template_folder=str(BASE_DIR / "templates"),
static_folder=str(BASE_DIR / "static"),
)
app.config["MAX_CONTENT_LENGTH"] = 50 * 1024 * 1024
STATE = {
"last_job": None,
"last_error": None,
}
def write_csv(path: Path, rows: list[dict]) -> None:
if not rows:
return
with path.open("w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
w.writeheader()
w.writerows(rows)
@app.get("/")
def index():
last_job = STATE["last_job"]
context = {
"has_data": False,
"stats": {},
"diagram_path": None,
"cleaned_path": None,
"summary_path": None,
"sharing_matrix_path": None,
"explorer_path": None,
"explorer_url": None,
"build_id": None,
"last_upload_time": None,
"process_ms": None,
"curated_count": None,
"error": STATE.get("last_error"),
}
if last_job:
context.update(last_job)
context["has_data"] = True
return render_template("index.html", **context)
@app.post("/upload")
def upload_csv():
upload = request.files.get("csv_file")
if not upload or not upload.filename:
return redirect(url_for("index"))
try:
t0 = datetime.now(timezone.utc)
job_id = uuid.uuid4().hex[:10]
job_dir = DATA_DIR / job_id
job_dir.mkdir(parents=True, exist_ok=True)
raw_path = job_dir / "gdrive_test_raw.csv"
upload.save(raw_path)
curated_rows = parse_gam_raw_csv(raw_path)
summary_rows = summarize(curated_rows)
sharing_rows = sharing_matrix(curated_rows)
stats = top_stats(curated_rows)
cleaned_csv = job_dir / "gdrive_curated.csv"
summary_csv = job_dir / "gdrive_summary.csv"
sharing_csv = job_dir / "gdrive_sharing_matrix.csv"
diagram_html = job_dir / "diagram.html"
explorer_html = job_dir / "explorer.html"
write_csv(cleaned_csv, curated_rows)
write_csv(summary_csv, summary_rows)
write_csv(sharing_csv, sharing_rows)
render_diagram(summary_rows, diagram_html)
build_id = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
render_explorer(curated_rows, explorer_html, build_id=build_id)
t1 = datetime.now(timezone.utc)
process_ms = int((t1 - t0).total_seconds() * 1000)
STATE["last_job"] = {
"stats": stats,
"diagram_path": f"/data/{job_id}/diagram.html",
"cleaned_path": f"/download/{job_id}/cleaned",
"summary_path": f"/download/{job_id}/summary",
"sharing_matrix_path": f"/download/{job_id}/sharing-matrix",
"explorer_path": f"/data/{job_id}/explorer.html",
"explorer_url": f"/data/{job_id}/explorer.html?v={build_id}",
"build_id": build_id,
"last_upload_time": t1.strftime("%Y-%m-%d %H:%M:%S UTC"),
"process_ms": process_ms,
"curated_count": len(curated_rows),
"job_id": job_id,
}
STATE["last_error"] = None
except Exception as exc:
STATE["last_error"] = (
"Upload failed. Check CSV format and try again. "
f"Error: {exc}"
)
return redirect(url_for("index"))
@app.get("/download/<job_id>/<kind>")
def download(job_id: str, kind: str):
job_dir = DATA_DIR / job_id
if kind == "cleaned":
return send_file(job_dir / "gdrive_curated.csv", as_attachment=True)
if kind == "summary":
return send_file(job_dir / "gdrive_summary.csv", as_attachment=True)
if kind == "sharing-matrix":
return send_file(job_dir / "gdrive_sharing_matrix.csv", as_attachment=True)
return redirect(url_for("index"))
@app.get("/data/<job_id>/diagram.html")
def serve_diagram(job_id: str):
return send_file(DATA_DIR / job_id / "diagram.html")
@app.get("/data/<job_id>/explorer.html")
def serve_explorer(job_id: str):
return send_file(DATA_DIR / job_id / "explorer.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080, debug=False)