feat(scoring): rescale scores to 0.0-1.0 with 0.05 resolution
Per-question score is now a float in [0.0, 1.0] snapped to a 21-level 0.05 grid, replacing the previous 0-1000 integer scale. Easier to read on a leaderboard, ties become acceptable rather than vanishingly rare, and small clock-skew differences no longer split rankings. DB schema: score is REAL now (SQLite type affinity is loose enough that existing rows still read fine, but new inserts go in as floats). Frontend: added fmtScore() helpers in admin.js and quiz.js to render two decimal places consistently (0.85, 1.20, 5.00) so float-arithmetic sums never display as 0.8500000000000001. Tests: linear_decay/flat/exponential_decay assertions updated; added a snap-to-grid invariant test.
This commit is contained in:
@@ -621,7 +621,7 @@ class RoomManager:
|
||||
rows = await cursor.fetchall()
|
||||
board = []
|
||||
for rank, row in enumerate(rows, start=1):
|
||||
item = {"rank": rank, "name": row["name"], "score": int(row["score"])}
|
||||
item = {"rank": rank, "name": row["name"], "score": float(row["score"])}
|
||||
if include_ids:
|
||||
item["student_id"] = row["student_id"]
|
||||
if you_student_id is not None and row["student_id"] == you_student_id:
|
||||
@@ -636,14 +636,17 @@ class RoomManager:
|
||||
return item["rank"]
|
||||
return None
|
||||
|
||||
async def total_for(self, sid: str, student_id: str) -> int:
|
||||
async def total_for(self, sid: str, student_id: str) -> float:
|
||||
async with connect(self.settings.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT COALESCE(SUM(score), 0) AS total FROM submissions WHERE sid = ? AND student_id = ?",
|
||||
(sid, student_id),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
return int(row["total"])
|
||||
# Snap to two decimals so the sum stays display-friendly even after
|
||||
# many small float additions; the per-question scores are already
|
||||
# on a 0.05 grid, so this is mostly defensive.
|
||||
return round(float(row["total"]), 2)
|
||||
|
||||
async def submission_for(self, sid: str, student_id: str, question_idx: int) -> dict[str, Any] | None:
|
||||
async with connect(self.settings.db_path) as db:
|
||||
|
||||
Reference in New Issue
Block a user