perfectionist
This commit is contained in:
+128
-56
@@ -9,6 +9,7 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
|
||||
@@ -109,7 +110,6 @@ async def upload_files(
|
||||
gender: str = Form(...),
|
||||
fat_percentage: float = Form(...),
|
||||
focus: str = Form(default="Endurance"),
|
||||
session_id: str = Form(default="default"),
|
||||
next_testing_date: str = Form(...),
|
||||
report_type: str = Form(default="full"),
|
||||
spirometry_pdf: UploadFile = File(...),
|
||||
@@ -179,6 +179,10 @@ async def upload_files(
|
||||
# Prepare patient information
|
||||
patient_name = f"{first_name} {last_name}"
|
||||
print(f"DEBUG: Received next_testing_date: '{next_testing_date}'")
|
||||
|
||||
# Generate session_id internally using timestamp for unique identification
|
||||
session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
patient_info = {
|
||||
"patient_name": patient_name,
|
||||
"first_name": first_name,
|
||||
@@ -290,8 +294,18 @@ async def upload_files(
|
||||
@app.get("/preview", response_class=HTMLResponse)
|
||||
async def preview(request: Request):
|
||||
"""Preview generated report"""
|
||||
# Check for required session data
|
||||
if not request.session.get("report_path"):
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
|
||||
# Ensure metrics exist in session, initialize if missing
|
||||
if "metrics" not in request.session:
|
||||
request.session["metrics"] = {"pnoe": {}, "spirometry": {}}
|
||||
|
||||
# Ensure patient_info exists
|
||||
if "patient_info" not in request.session:
|
||||
request.session["patient_info"] = {}
|
||||
|
||||
return render_template(
|
||||
"preview.html", {"request": request, "session": request.session}
|
||||
)
|
||||
@@ -309,8 +323,16 @@ async def serve_graph(filename: str):
|
||||
@app.get("/edit", response_class=HTMLResponse)
|
||||
async def edit_form(request: Request):
|
||||
"""Display edit metrics form"""
|
||||
if not request.session.get("metrics"):
|
||||
# Check for required session data
|
||||
if not request.session.get("report_path") or not request.session.get(
|
||||
"patient_info"
|
||||
):
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
|
||||
# Ensure metrics exist in session, initialize if missing
|
||||
if "metrics" not in request.session:
|
||||
request.session["metrics"] = {"pnoe": {}, "spirometry": {}}
|
||||
|
||||
return render_template(
|
||||
"edit.html", {"request": request, "session": request.session}
|
||||
)
|
||||
@@ -325,69 +347,117 @@ async def edit_metrics(request: Request):
|
||||
# Get form data
|
||||
form_data = await request.form()
|
||||
|
||||
# Helper function to safely convert form values to float
|
||||
def safe_float(value):
|
||||
"""Convert form value to float, return None if empty or invalid"""
|
||||
if not value or value.strip() == "":
|
||||
return None
|
||||
try:
|
||||
return float(value)
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
# Build metric overrides
|
||||
metric_overrides = {"pnoe": {}, "spirometry": {}}
|
||||
|
||||
# Pnoe overrides
|
||||
if form_data.get("vo2_max"):
|
||||
metric_overrides["pnoe"]["vo2_max"] = float(form_data["vo2_max"])
|
||||
if form_data.get("vo2_max_per_kg"):
|
||||
metric_overrides["pnoe"]["vo2_max_per_kg"] = float(form_data["vo2_max_per_kg"])
|
||||
if form_data.get("peak_vt"):
|
||||
metric_overrides["pnoe"]["peak_vt"] = float(form_data["peak_vt"])
|
||||
if form_data.get("peak_vt_hr"):
|
||||
metric_overrides["pnoe"]["peak_vt_hr"] = float(form_data["peak_vt_hr"])
|
||||
if form_data.get("fat_max_value"):
|
||||
metric_overrides["pnoe"]["fat_max_value"] = float(form_data["fat_max_value"])
|
||||
if form_data.get("fat_max_hr"):
|
||||
metric_overrides["pnoe"]["fat_max_hr"] = float(form_data["fat_max_hr"])
|
||||
# Pnoe overrides - only add if value is provided and valid
|
||||
vo2_max_val = safe_float(form_data.get("vo2_max"))
|
||||
if vo2_max_val is not None:
|
||||
metric_overrides["pnoe"]["vo2_max"] = vo2_max_val
|
||||
|
||||
# VT1 and VT2 overrides
|
||||
if (
|
||||
form_data.get("vt1_hr")
|
||||
or form_data.get("vt1_speed")
|
||||
or form_data.get("vt1_time")
|
||||
):
|
||||
metric_overrides["pnoe"]["vt1"] = {
|
||||
"HeartRate": float(form_data.get("vt1_hr", 0)),
|
||||
"Speed": float(form_data.get("vt1_speed", 0)),
|
||||
"Time": float(form_data.get("vt1_time", 0)),
|
||||
vo2_max_per_kg_val = safe_float(form_data.get("vo2_max_per_kg"))
|
||||
if vo2_max_per_kg_val is not None:
|
||||
metric_overrides["pnoe"]["vo2_max_per_kg"] = vo2_max_per_kg_val
|
||||
|
||||
peak_vt_val = safe_float(form_data.get("peak_vt"))
|
||||
if peak_vt_val is not None:
|
||||
metric_overrides["pnoe"]["peak_vt"] = peak_vt_val
|
||||
|
||||
peak_vt_hr_val = safe_float(form_data.get("peak_vt_hr"))
|
||||
if peak_vt_hr_val is not None:
|
||||
metric_overrides["pnoe"]["peak_vt_hr"] = peak_vt_hr_val
|
||||
|
||||
fat_max_value_val = safe_float(form_data.get("fat_max_value"))
|
||||
if fat_max_value_val is not None:
|
||||
metric_overrides["pnoe"]["fat_max_value"] = fat_max_value_val
|
||||
|
||||
fat_max_hr_val = safe_float(form_data.get("fat_max_hr"))
|
||||
if fat_max_hr_val is not None:
|
||||
metric_overrides["pnoe"]["fat_max_hr"] = fat_max_hr_val
|
||||
|
||||
# VT1 and VT2 overrides - use existing values if not provided
|
||||
existing_metrics = request.session.get("metrics", {})
|
||||
existing_pnoe = existing_metrics.get("pnoe", {})
|
||||
existing_vt1 = existing_pnoe.get("vt1", {})
|
||||
existing_vt2 = existing_pnoe.get("vt2", {})
|
||||
|
||||
vt1_hr_val = safe_float(form_data.get("vt1_hr"))
|
||||
vt1_speed_val = safe_float(form_data.get("vt1_speed"))
|
||||
vt1_time_val = safe_float(form_data.get("vt1_time"))
|
||||
|
||||
if vt1_hr_val is not None or vt1_speed_val is not None or vt1_time_val is not None:
|
||||
vt1_dict = {
|
||||
"HeartRate": vt1_hr_val
|
||||
if vt1_hr_val is not None
|
||||
else existing_vt1.get("HeartRate", 0),
|
||||
"Speed": vt1_speed_val
|
||||
if vt1_speed_val is not None
|
||||
else existing_vt1.get("Speed", 0),
|
||||
"Time": vt1_time_val
|
||||
if vt1_time_val is not None
|
||||
else existing_vt1.get("Time", 0),
|
||||
}
|
||||
metric_overrides["pnoe"]["vt1"] = vt1_dict
|
||||
|
||||
if (
|
||||
form_data.get("vt2_hr")
|
||||
or form_data.get("vt2_speed")
|
||||
or form_data.get("vt2_time")
|
||||
):
|
||||
metric_overrides["pnoe"]["vt2"] = {
|
||||
"HeartRate": float(form_data.get("vt2_hr", 0)),
|
||||
"Speed": float(form_data.get("vt2_speed", 0)),
|
||||
"Time": float(form_data.get("vt2_time", 0)),
|
||||
vt2_hr_val = safe_float(form_data.get("vt2_hr"))
|
||||
vt2_speed_val = safe_float(form_data.get("vt2_speed"))
|
||||
vt2_time_val = safe_float(form_data.get("vt2_time"))
|
||||
|
||||
if vt2_hr_val is not None or vt2_speed_val is not None or vt2_time_val is not None:
|
||||
vt2_dict = {
|
||||
"HeartRate": vt2_hr_val
|
||||
if vt2_hr_val is not None
|
||||
else existing_vt2.get("HeartRate", 0),
|
||||
"Speed": vt2_speed_val
|
||||
if vt2_speed_val is not None
|
||||
else existing_vt2.get("Speed", 0),
|
||||
"Time": vt2_time_val
|
||||
if vt2_time_val is not None
|
||||
else existing_vt2.get("Time", 0),
|
||||
}
|
||||
metric_overrides["pnoe"]["vt2"] = vt2_dict
|
||||
|
||||
# Heart rate zones
|
||||
# Heart rate zones - only add if value is provided
|
||||
for i in range(1, 6):
|
||||
zone_key = f"zone{i}_bpm"
|
||||
if form_data.get(zone_key):
|
||||
metric_overrides["pnoe"][zone_key] = form_data[zone_key]
|
||||
zone_val = form_data.get(zone_key)
|
||||
if zone_val and zone_val.strip():
|
||||
metric_overrides["pnoe"][zone_key] = zone_val.strip()
|
||||
|
||||
# Spirometry overrides
|
||||
if form_data.get("fvc_best"):
|
||||
metric_overrides["spirometry"]["fvc_best"] = float(form_data["fvc_best"])
|
||||
if form_data.get("fvc_pred"):
|
||||
metric_overrides["spirometry"]["fvc_pred"] = float(form_data["fvc_pred"])
|
||||
if form_data.get("fev1_best"):
|
||||
metric_overrides["spirometry"]["fev1_best"] = float(form_data["fev1_best"])
|
||||
if form_data.get("fev1_pred"):
|
||||
metric_overrides["spirometry"]["fev1_pred"] = float(form_data["fev1_pred"])
|
||||
if form_data.get("fev1_fvc_pct_best"):
|
||||
metric_overrides["spirometry"]["fev1_fvc_pct_best"] = float(
|
||||
form_data["fev1_fvc_pct_best"]
|
||||
)
|
||||
if form_data.get("fev1_fvc_pct_pred"):
|
||||
metric_overrides["spirometry"]["fev1_fvc_pct_pred"] = float(
|
||||
form_data["fev1_fvc_pct_pred"]
|
||||
)
|
||||
# Spirometry overrides - only add if value is provided and valid
|
||||
fvc_best_val = safe_float(form_data.get("fvc_best"))
|
||||
if fvc_best_val is not None:
|
||||
metric_overrides["spirometry"]["fvc_best"] = fvc_best_val
|
||||
|
||||
fvc_pred_val = safe_float(form_data.get("fvc_pred"))
|
||||
if fvc_pred_val is not None:
|
||||
metric_overrides["spirometry"]["fvc_pred"] = fvc_pred_val
|
||||
|
||||
fev1_best_val = safe_float(form_data.get("fev1_best"))
|
||||
if fev1_best_val is not None:
|
||||
metric_overrides["spirometry"]["fev1_best"] = fev1_best_val
|
||||
|
||||
fev1_pred_val = safe_float(form_data.get("fev1_pred"))
|
||||
if fev1_pred_val is not None:
|
||||
metric_overrides["spirometry"]["fev1_pred"] = fev1_pred_val
|
||||
|
||||
fev1_fvc_pct_best_val = safe_float(form_data.get("fev1_fvc_pct_best"))
|
||||
if fev1_fvc_pct_best_val is not None:
|
||||
metric_overrides["spirometry"]["fev1_fvc_pct_best"] = fev1_fvc_pct_best_val
|
||||
|
||||
fev1_fvc_pct_pred_val = safe_float(form_data.get("fev1_fvc_pct_pred"))
|
||||
if fev1_fvc_pct_pred_val is not None:
|
||||
metric_overrides["spirometry"]["fev1_fvc_pct_pred"] = fev1_fvc_pct_pred_val
|
||||
|
||||
try:
|
||||
# Get file paths from session
|
||||
@@ -468,6 +538,7 @@ async def edit_metrics(request: Request):
|
||||
"fat_percentage": patient_info.get("fat_percentage", 0),
|
||||
"gender": patient_info.get("gender", "female"),
|
||||
}
|
||||
# Calculate fat_mass and lean_mass (extract_patient_info does this when no SECA file)
|
||||
context_gen.extract_patient_info(patient_info.get("last_name", ""))
|
||||
|
||||
spirometry_overrides = metric_overrides.get("spirometry", {})
|
||||
@@ -514,7 +585,6 @@ async def generate_report(
|
||||
height: str = Form(..., description="Patient height (e.g., 5'4\")"),
|
||||
weight: str = Form(..., description="Patient weight (e.g., 123lbs)"),
|
||||
focus: str = Form(default="Endurance", description="Training focus"),
|
||||
session_id: str = Form(default="default", description="Session ID"),
|
||||
spirometry_pdf: UploadFile = File(..., description="Spirometry PDF file"),
|
||||
pnoe_csv: UploadFile = File(..., description="Pnoe CSV file"),
|
||||
seca_excel: UploadFile = File(..., description="SECA Excel file"),
|
||||
@@ -534,7 +604,6 @@ async def generate_report(
|
||||
height: Patient height
|
||||
weight: Patient weight
|
||||
focus: Training focus (default: Endurance)
|
||||
session_id: Session identifier (default: default)
|
||||
|
||||
Returns:
|
||||
ReportResponse with report path, graphs generated, and analysis data
|
||||
@@ -571,6 +640,9 @@ async def generate_report(
|
||||
with open(seca_path, "wb") as f:
|
||||
shutil.copyfileobj(seca_excel.file, f)
|
||||
|
||||
# Generate session_id internally using timestamp for unique identification
|
||||
session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
# Prepare patient information
|
||||
patient_info = {
|
||||
"patient_name": patient_name,
|
||||
|
||||
Reference in New Issue
Block a user