Spaces:
Running
Running
Log analysis session database failures
Browse files- app.py +19 -1
- src/analysis.ts +8 -2
app.py
CHANGED
|
@@ -21,6 +21,8 @@ from zipfile import ZipFile
|
|
| 21 |
import httpx
|
| 22 |
import numpy as np
|
| 23 |
import cv2
|
|
|
|
|
|
|
| 24 |
from dotenv import load_dotenv
|
| 25 |
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
|
| 26 |
from fastapi.responses import FileResponse, JSONResponse
|
|
@@ -125,6 +127,8 @@ app = FastAPI(title="Sporalize AI", lifespan=lifespan)
|
|
| 125 |
@app.exception_handler(httpx.HTTPStatusError)
|
| 126 |
async def backend_error(request: Request, error: httpx.HTTPStatusError):
|
| 127 |
status = error.response.status_code
|
|
|
|
|
|
|
| 128 |
return JSONResponse({"detail": "Backend request failed"}, status_code=status if status < 500 else 502)
|
| 129 |
|
| 130 |
|
|
@@ -167,6 +171,19 @@ def drive_file_id(link):
|
|
| 167 |
return next(group for group in match.groups() if group)
|
| 168 |
|
| 169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
async def download_drive_file(link, destination):
|
| 171 |
file_id = drive_file_id(link)
|
| 172 |
url = f"https://drive.google.com/uc?export=download&confirm=t&id={file_id}"
|
|
@@ -275,10 +292,11 @@ def probe_drive(payload: dict):
|
|
| 275 |
file_id = drive_file_id(str(link))
|
| 276 |
with httpx.stream("GET", f"https://drive.google.com/uc?export=download&confirm=t&id={file_id}", follow_redirects=True, timeout=30) as response:
|
| 277 |
content_type = response.headers.get("content-type", "")
|
|
|
|
| 278 |
size = int(response.headers.get("content-length", "0") or 0)
|
| 279 |
if not response.is_success or "text/html" in content_type.lower():
|
| 280 |
raise ValueError("File is not publicly downloadable")
|
| 281 |
-
result.update(ok=True, fileId=file_id, fileName=
|
| 282 |
except Exception as error:
|
| 283 |
result.update(ok=False, code="unavailable", error=str(error))
|
| 284 |
results.append(result)
|
|
|
|
| 21 |
import httpx
|
| 22 |
import numpy as np
|
| 23 |
import cv2
|
| 24 |
+
from email.message import Message
|
| 25 |
+
from email.utils import collapse_rfc2231_value
|
| 26 |
from dotenv import load_dotenv
|
| 27 |
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
|
| 28 |
from fastapi.responses import FileResponse, JSONResponse
|
|
|
|
| 127 |
@app.exception_handler(httpx.HTTPStatusError)
|
| 128 |
async def backend_error(request: Request, error: httpx.HTTPStatusError):
|
| 129 |
status = error.response.status_code
|
| 130 |
+
logger.error("Analysis backend request failed: path=%s status=%s response=%s",
|
| 131 |
+
request.url.path, status, error.response.text[:500])
|
| 132 |
return JSONResponse({"detail": "Backend request failed"}, status_code=status if status < 500 else 502)
|
| 133 |
|
| 134 |
|
|
|
|
| 171 |
return next(group for group in match.groups() if group)
|
| 172 |
|
| 173 |
|
| 174 |
+
def drive_file_name(content_disposition, file_id):
|
| 175 |
+
message = Message()
|
| 176 |
+
if content_disposition:
|
| 177 |
+
message["content-disposition"] = content_disposition
|
| 178 |
+
name = message.get_filename()
|
| 179 |
+
if isinstance(name, tuple):
|
| 180 |
+
name = collapse_rfc2231_value(name)
|
| 181 |
+
name = (name or f"{file_id}.mp4").replace("\\", "/")
|
| 182 |
+
name = Path(name).name
|
| 183 |
+
name = "".join(character for character in name if character.isprintable()).strip()
|
| 184 |
+
return name[:255] or f"{file_id}.mp4"
|
| 185 |
+
|
| 186 |
+
|
| 187 |
async def download_drive_file(link, destination):
|
| 188 |
file_id = drive_file_id(link)
|
| 189 |
url = f"https://drive.google.com/uc?export=download&confirm=t&id={file_id}"
|
|
|
|
| 292 |
file_id = drive_file_id(str(link))
|
| 293 |
with httpx.stream("GET", f"https://drive.google.com/uc?export=download&confirm=t&id={file_id}", follow_redirects=True, timeout=30) as response:
|
| 294 |
content_type = response.headers.get("content-type", "")
|
| 295 |
+
content_disposition = response.headers.get("content-disposition", "")
|
| 296 |
size = int(response.headers.get("content-length", "0") or 0)
|
| 297 |
if not response.is_success or "text/html" in content_type.lower():
|
| 298 |
raise ValueError("File is not publicly downloadable")
|
| 299 |
+
result.update(ok=True, fileId=file_id, fileName=drive_file_name(content_disposition, file_id), sizeBytes=size or None, contentType=content_type)
|
| 300 |
except Exception as error:
|
| 301 |
result.update(ok=False, code="unavailable", error=str(error))
|
| 302 |
results.append(result)
|
src/analysis.ts
CHANGED
|
@@ -51,7 +51,10 @@ export function analysisRouter(db: SupabaseClient, secret: string) {
|
|
| 51 |
p_reference: playerId.success ? null : input.playerId,
|
| 52 |
p_name: input.name || `Session ${input.clientId.slice(0, 8)}`,
|
| 53 |
});
|
| 54 |
-
if (error)
|
|
|
|
|
|
|
|
|
|
| 55 |
return res.json(data);
|
| 56 |
} catch (error) { next(error); }
|
| 57 |
});
|
|
@@ -76,7 +79,10 @@ export function analysisRouter(db: SupabaseClient, secret: string) {
|
|
| 76 |
p_session_id: sessionId, p_user_id: res.locals.userId, p_status: input.status,
|
| 77 |
p_path: input.status === "completed" ? input.path : null, p_actions: actions,
|
| 78 |
});
|
| 79 |
-
if (error)
|
|
|
|
|
|
|
|
|
|
| 80 |
return res.json({ ok: true });
|
| 81 |
} catch (error) { next(error); }
|
| 82 |
});
|
|
|
|
| 51 |
p_reference: playerId.success ? null : input.playerId,
|
| 52 |
p_name: input.name || `Session ${input.clientId.slice(0, 8)}`,
|
| 53 |
});
|
| 54 |
+
if (error) {
|
| 55 |
+
console.error("Unable to prepare analysis session", { code: error.code, message: error.message });
|
| 56 |
+
return res.status(error.code === "P0001" ? 409 : 503).json({ error: "Unable to prepare analysis session" });
|
| 57 |
+
}
|
| 58 |
return res.json(data);
|
| 59 |
} catch (error) { next(error); }
|
| 60 |
});
|
|
|
|
| 79 |
p_session_id: sessionId, p_user_id: res.locals.userId, p_status: input.status,
|
| 80 |
p_path: input.status === "completed" ? input.path : null, p_actions: actions,
|
| 81 |
});
|
| 82 |
+
if (error) {
|
| 83 |
+
console.error("Unable to save analysis results", { code: error.code, message: error.message });
|
| 84 |
+
return res.status(error.code === "P0001" ? 409 : 503).json({ error: "Unable to save analysis" });
|
| 85 |
+
}
|
| 86 |
return res.json({ ok: true });
|
| 87 |
} catch (error) { next(error); }
|
| 88 |
});
|