Import gender tees + front/back split ratings; front/back/9 hole selection
Course import now fetches both male and female tees, each with their own per-tee stroke indices extracted from the holes array. Front and back 9 slope/rating are stored per tee for more accurate WHS handicap calculation. New game creation replaces the 9/18 radio with a course-aware selector: 18-hole courses offer 18 holes, Front 9, or Back 9; 9-hole courses auto- select. start_hole is stored on the game and used throughout scoring, handicap calculation, and scorecard rendering. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+40
-16
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
from datetime import datetime, timezone
|
||||
@@ -43,7 +44,7 @@ async def _get_course_detail(cid: str, db: aiosqlite.Connection):
|
||||
) as c:
|
||||
holes = [dict(r) for r in await c.fetchall()]
|
||||
async with db.execute(
|
||||
"SELECT id, name, slope, rating FROM course_tees WHERE course_id = ? ORDER BY name",
|
||||
"SELECT id, name, slope, rating, gender, front_slope, front_rating, back_slope, back_rating FROM course_tees WHERE course_id = ? ORDER BY gender, name",
|
||||
(cid,),
|
||||
) as c:
|
||||
tees = [dict(r) for r in await c.fetchall()]
|
||||
@@ -197,10 +198,10 @@ async def import_course(api_id: int, request: Request,
|
||||
holes_count = n
|
||||
break
|
||||
|
||||
# Hole par + stroke index from first tee that has hole data
|
||||
# Hole par + course-level stroke index from first male tee with hole data (fallback for display)
|
||||
hole_pars: dict[int, int] = {}
|
||||
hole_sis: dict[int, int] = {}
|
||||
for t in all_tees:
|
||||
for t in tees_male:
|
||||
hole_data = t.get("holes") or []
|
||||
if hole_data:
|
||||
for i, h in enumerate(hole_data[:holes_count], 1):
|
||||
@@ -220,15 +221,30 @@ async def import_course(api_id: int, request: Request,
|
||||
"INSERT INTO course_holes (id, course_id, hole_number, par, stroke_index) VALUES (?,?,?,?,?)",
|
||||
(secrets.token_urlsafe(10), cid, i, hole_pars.get(i, 4), hole_sis.get(i)),
|
||||
)
|
||||
for t in tees_male:
|
||||
tname = (t.get("tee_name") or "").strip()
|
||||
slope = t.get("slope_rating")
|
||||
rating = t.get("course_rating")
|
||||
if tname and slope and rating:
|
||||
await db.execute(
|
||||
"INSERT INTO course_tees (id, course_id, name, slope, rating) VALUES (?,?,?,?,?)",
|
||||
(secrets.token_urlsafe(10), cid, tname, slope, rating),
|
||||
)
|
||||
|
||||
def _tee_stroke_indices(t: dict) -> str | None:
|
||||
"""Extract per-hole stroke indices from a tee's holes array as JSON."""
|
||||
si_map = {}
|
||||
for i, h in enumerate((t.get("holes") or [])[:holes_count], 1):
|
||||
if h.get("handicap"):
|
||||
si_map[i] = h["handicap"]
|
||||
return json.dumps(si_map) if si_map else None
|
||||
|
||||
for gender, tee_list in (("male", tees_male), ("female", tees_female)):
|
||||
for t in tee_list:
|
||||
tname = (t.get("tee_name") or "").strip()
|
||||
slope = t.get("slope_rating")
|
||||
rating = t.get("course_rating")
|
||||
if tname and slope and rating:
|
||||
await db.execute(
|
||||
"""INSERT INTO course_tees
|
||||
(id, course_id, name, slope, rating, gender, stroke_indices,
|
||||
front_slope, front_rating, back_slope, back_rating)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(secrets.token_urlsafe(10), cid, tname, slope, rating, gender, _tee_stroke_indices(t),
|
||||
t.get("front_slope_rating"), t.get("front_course_rating"),
|
||||
t.get("back_slope_rating"), t.get("back_course_rating")),
|
||||
)
|
||||
await db.commit()
|
||||
return RedirectResponse(
|
||||
f"/admin/courses/{cid}/edit?success=Course+imported+successfully.", status_code=302
|
||||
@@ -339,11 +355,15 @@ async def delete_course(cid: str, user: dict = Depends(require_admin),
|
||||
@router.post("/admin/courses/{cid}/tees/new")
|
||||
async def add_tee(cid: str, name: str = Form(...),
|
||||
slope: int = Form(...), rating: float = Form(...),
|
||||
gender: str = Form("male"),
|
||||
front_slope: int | None = Form(None), front_rating: float | None = Form(None),
|
||||
back_slope: int | None = Form(None), back_rating: float | None = Form(None),
|
||||
user: dict = Depends(require_admin),
|
||||
db: aiosqlite.Connection = Depends(get_db)):
|
||||
gender = gender if gender in ("male", "female") else "male"
|
||||
await db.execute(
|
||||
"INSERT INTO course_tees (id, course_id, name, slope, rating) VALUES (?,?,?,?,?)",
|
||||
(secrets.token_urlsafe(10), cid, name.strip(), slope, rating),
|
||||
"INSERT INTO course_tees (id, course_id, name, slope, rating, gender, front_slope, front_rating, back_slope, back_rating) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||
(secrets.token_urlsafe(10), cid, name.strip(), slope, rating, gender, front_slope, front_rating, back_slope, back_rating),
|
||||
)
|
||||
await db.commit()
|
||||
return RedirectResponse(f"/admin/courses/{cid}/edit?success=Tee+added.", status_code=302)
|
||||
@@ -352,11 +372,15 @@ async def add_tee(cid: str, name: str = Form(...),
|
||||
@router.post("/admin/courses/{cid}/tees/{tid}/edit")
|
||||
async def edit_tee(cid: str, tid: str, name: str = Form(...),
|
||||
slope: int = Form(...), rating: float = Form(...),
|
||||
gender: str = Form("male"),
|
||||
front_slope: int | None = Form(None), front_rating: float | None = Form(None),
|
||||
back_slope: int | None = Form(None), back_rating: float | None = Form(None),
|
||||
user: dict = Depends(require_admin),
|
||||
db: aiosqlite.Connection = Depends(get_db)):
|
||||
gender = gender if gender in ("male", "female") else "male"
|
||||
await db.execute(
|
||||
"UPDATE course_tees SET name = ?, slope = ?, rating = ? WHERE id = ? AND course_id = ?",
|
||||
(name.strip(), slope, rating, tid, cid),
|
||||
"UPDATE course_tees SET name = ?, slope = ?, rating = ?, gender = ?, front_slope = ?, front_rating = ?, back_slope = ?, back_rating = ? WHERE id = ? AND course_id = ?",
|
||||
(name.strip(), slope, rating, gender, front_slope, front_rating, back_slope, back_rating, tid, cid),
|
||||
)
|
||||
await db.commit()
|
||||
return RedirectResponse(f"/admin/courses/{cid}/edit?success=Tee+updated.", status_code=302)
|
||||
|
||||
Reference in New Issue
Block a user