diff --git a/app.py b/app.py index e359730..c0ff822 100644 --- a/app.py +++ b/app.py @@ -10,7 +10,7 @@ from typing import Optional import markdown as md import requests from fastapi import FastAPI, File, Form, HTTPException, UploadFile -from fastapi.responses import HTMLResponse, PlainTextResponse, Response +from fastapi.responses import HTMLResponse, PlainTextResponse, Response, JSONResponse API_BASE = os.getenv("API_BASE", "http://gx10.aquantico.lan:8093").rstrip("/") OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://gx10.aquantico.lan:11434").rstrip("/") @@ -224,6 +224,11 @@ def get_prompts(): return c.execute("SELECT id,name,prompt FROM prompts ORDER BY name").fetchall() +def _job_get(job_id: int): + with db() as c: + return c.execute("SELECT * FROM jobs WHERE id=?", (job_id,)).fetchone() + + def _job_set(job_id: int, **fields): if not fields: return @@ -234,13 +239,17 @@ def _job_set(job_id: int, **fields): def _process_upload_job(job_id: int): - with db() as c: - j = c.execute("SELECT * FROM jobs WHERE id=?", (job_id,)).fetchone() + j = _job_get(job_id) if not j: return + if j["status"] == "cancelled": + return _job_set(job_id, status="running", started_at=now_iso()) try: + j = _job_get(job_id) + if not j or j["status"] == "cancelled": + return p = Path(j["file_path"]) data = p.read_bytes() filename = p.name @@ -249,6 +258,10 @@ def _process_upload_job(job_id: int): r.raise_for_status() payload = r.json() + j = _job_get(job_id) + if not j or j["status"] == "cancelled": + return + content_md = payload.get("formatted_text", "") doc_title = (j["title"] or "").strip() or filename with db() as c: @@ -264,13 +277,17 @@ def _process_upload_job(job_id: int): def _process_analysis_job(job_id: int): - with db() as c: - j = c.execute("SELECT * FROM jobs WHERE id=?", (job_id,)).fetchone() + j = _job_get(job_id) if not j: return + if j["status"] == "cancelled": + return _job_set(job_id, status="running", started_at=now_iso()) try: + j = _job_get(job_id) + if not j or j["status"] == "cancelled": + return with db() as c: doc = c.execute("SELECT * FROM documents WHERE id=?", (j["document_id"],)).fetchone() prm = c.execute("SELECT * FROM prompts WHERE id=?", (j["prompt_id"],)).fetchone() @@ -291,6 +308,10 @@ def _process_analysis_job(job_id: int): r.raise_for_status() answer = r.json().get("response", "") + j = _job_get(job_id) + if not j or j["status"] == "cancelled": + return + with db() as c: cur = c.execute( """ @@ -707,8 +728,7 @@ def prompt_delete(prompt_id: int): return HTMLResponse("") -@app.get("/jobs", response_class=HTMLResponse) -def jobs_page(queued: Optional[int] = None): +def _jobs_payload(limit: int = 200): with db() as c: jobs = c.execute( """ @@ -717,27 +737,81 @@ def jobs_page(queued: Optional[int] = None): LEFT JOIN projects p ON p.id=j.project_id LEFT JOIN documents d ON d.id=j.document_id LEFT JOIN prompts pr ON pr.id=j.prompt_id - ORDER BY j.id DESC LIMIT 200 - """ + ORDER BY j.id DESC LIMIT ? + """, + (limit,), ).fetchall() + return [dict(j) for j in jobs] - rows = "".join([ - f"
Job #{j['id']} [{j['kind']}] · {j['status']}
" - f"{j['created_at']}
" - f"{('Projekt: '+str(j['project_name'])+'
') if j['project_name'] else ''}" - f"{('Dokument: '+str(j['document_title'])+'
') if j['document_title'] else ''}" - f"{('Prompt: '+str(j['prompt_name'])+'
') if j['prompt_name'] else ''}" - f"{('Fehler:
'+str(j['error']).replace('<','<')+'
') if j['error'] else ''}" - f"{(f'Ergebnis öffnen') if j['result_document_id'] else ''}" - f"
" for j in jobs - ]) +@app.get("/jobs/data") +def jobs_data(limit: int = 200): + return {"items": _jobs_payload(limit)} + + +@app.post("/jobs/{job_id}/cancel") +def jobs_cancel(job_id: int): + j = _job_get(job_id) + if not j: + raise HTTPException(404, "job not found") + if j["status"] in ("done", "error", "cancelled"): + return {"ok": True, "status": j["status"]} + _job_set(job_id, status="cancelled", finished_at=now_iso(), error="Cancelled by user") + return {"ok": True, "status": "cancelled"} + + +@app.post("/jobs/{job_id}/delete") +def jobs_delete(job_id: int): + with db() as c: + c.execute("DELETE FROM jobs WHERE id=?", (job_id,)) + return {"ok": True} + + +@app.get("/jobs", response_class=HTMLResponse) +def jobs_page(queued: Optional[int] = None): notice = f"

Job #{queued} wurde eingereiht.

" if queued else "" body = f"""

Hintergrundverarbeitung

-

Maximal 2 Jobs laufen gleichzeitig.

+

Maximal 2 Jobs gleichzeitig. Seite aktualisiert automatisch.

{notice} -{rows or '

Keine Jobs.

'} +
+ """ return layout("Jobs", body)