edit athlete data
This commit is contained in:
@@ -435,6 +435,87 @@ async def upload_image(activity_id: str, file: UploadFile = File(...)) -> JSONRe
|
||||
return JSONResponse({"ok": True, "filename": dest.name})
|
||||
|
||||
|
||||
@app.get("/api/athlete")
|
||||
async def get_athlete() -> JSONResponse:
|
||||
dd = _get_data_dir()
|
||||
athlete_path = dd / "athlete.json"
|
||||
if not athlete_path.exists():
|
||||
raise HTTPException(404, "athlete.json not found — run bincio extract first")
|
||||
|
||||
data = json.loads(athlete_path.read_text(encoding="utf-8"))
|
||||
|
||||
# Layer edits/athlete.yaml overrides on top
|
||||
overrides = _read_athlete_edits(dd)
|
||||
for key in ("max_hr", "ftp_w", "hr_zones", "power_zones", "seasons", "gear"):
|
||||
if key in overrides:
|
||||
data[key] = overrides[key]
|
||||
|
||||
return JSONResponse({
|
||||
"max_hr": data.get("max_hr"),
|
||||
"ftp_w": data.get("ftp_w"),
|
||||
"hr_zones": data.get("hr_zones"),
|
||||
"power_zones": data.get("power_zones"),
|
||||
"seasons": data.get("seasons", []),
|
||||
"gear": data.get("gear", {}),
|
||||
})
|
||||
|
||||
|
||||
@app.post("/api/athlete")
|
||||
async def save_athlete(payload: dict[str, Any]) -> JSONResponse:
|
||||
dd = _get_data_dir()
|
||||
athlete_path = dd / "athlete.json"
|
||||
if not athlete_path.exists():
|
||||
raise HTTPException(404, "athlete.json not found — run bincio extract first")
|
||||
|
||||
# Write edits/athlete.yaml with validated fields
|
||||
edits_dir = dd / "edits"
|
||||
edits_dir.mkdir(exist_ok=True)
|
||||
overrides: dict[str, Any] = {}
|
||||
if payload.get("max_hr") is not None:
|
||||
overrides["max_hr"] = int(payload["max_hr"])
|
||||
if payload.get("ftp_w") is not None:
|
||||
overrides["ftp_w"] = int(payload["ftp_w"])
|
||||
if payload.get("hr_zones") is not None:
|
||||
overrides["hr_zones"] = [[int(lo), int(hi)] for lo, hi in payload["hr_zones"]]
|
||||
if payload.get("power_zones") is not None:
|
||||
overrides["power_zones"] = [[int(lo), int(hi)] for lo, hi in payload["power_zones"]]
|
||||
if payload.get("seasons") is not None:
|
||||
overrides["seasons"] = [
|
||||
{"name": str(s["name"]), "start": str(s["start"]), "end": str(s["end"])}
|
||||
for s in payload["seasons"]
|
||||
]
|
||||
if payload.get("gear") is not None:
|
||||
overrides["gear"] = payload["gear"]
|
||||
|
||||
import yaml
|
||||
(edits_dir / "athlete.yaml").write_text(
|
||||
yaml.dump(overrides, allow_unicode=True, default_flow_style=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
# Patch athlete.json in-place (preserves power_curve, updated_at, etc.)
|
||||
data = json.loads(athlete_path.read_text(encoding="utf-8"))
|
||||
data.update(overrides)
|
||||
athlete_path.write_text(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
# Re-merge so _merged/athlete.json symlink stays valid
|
||||
from bincio.render.merge import merge_all
|
||||
merge_all(dd)
|
||||
|
||||
return JSONResponse({"ok": True})
|
||||
|
||||
|
||||
def _read_athlete_edits(data_dir: Path) -> dict:
|
||||
path = data_dir / "edits" / "athlete.yaml"
|
||||
if not path.exists():
|
||||
return {}
|
||||
try:
|
||||
import yaml
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
@app.delete("/api/activity/{activity_id}/images/{filename}")
|
||||
async def delete_image(activity_id: str, filename: str) -> JSONResponse:
|
||||
dd = _get_data_dir()
|
||||
|
||||
Reference in New Issue
Block a user