Skip to content

Commit 6bda42e

Browse files
committed
Address ngc92 feedback: unify queries and return all runs per submission
- Unified SQL queries in get_user_submissions() by building WHERE clause conditionally instead of duplicating the entire query - Changed response structure to return all runs per submission as a list with (gpu_type, score) tuples instead of using DISTINCT ON for one run - Simplified runs serialization in get_user_submission endpoint using dict comprehension with explicit field selection - Added test for multiple runs per submission
1 parent 778da42 commit 6bda42e

3 files changed

Lines changed: 86 additions & 54 deletions

File tree

src/kernelbot/api/main.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,8 @@ async def get_user_submission(
777777
if str(submission["user_id"]) != str(user_info["user_id"]):
778778
raise HTTPException(status_code=403, detail="Not authorized to view this submission")
779779

780+
# RunItem is a TypedDict (already a dict), select fields to expose
781+
run_fields = ("start_time", "end_time", "mode", "secret", "runner", "score", "passed")
780782
return {
781783
"id": submission["submission_id"],
782784
"leaderboard_id": submission["leaderboard_id"],
@@ -786,18 +788,7 @@ async def get_user_submission(
786788
"submission_time": submission["submission_time"],
787789
"done": submission["done"],
788790
"code": submission["code"],
789-
"runs": [
790-
{
791-
"start_time": r["start_time"],
792-
"end_time": r["end_time"],
793-
"mode": r["mode"],
794-
"secret": r["secret"],
795-
"runner": r["runner"],
796-
"score": r["score"],
797-
"passed": r["passed"],
798-
}
799-
for r in submission["runs"]
800-
],
791+
"runs": [{k: r[k] for k in run_fields} for r in submission["runs"]],
801792
}
802793
except HTTPException:
803794
raise

src/libkernelbot/leaderboard_db.py

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -865,62 +865,66 @@ def get_user_submissions(
865865
offset: Offset for pagination
866866
867867
Returns:
868-
List of submission dictionaries with summary info
868+
List of submission dictionaries with summary info and runs
869869
"""
870870
# Validate and clamp inputs
871871
limit = max(1, min(limit, 100))
872872
offset = max(0, offset)
873873

874874
try:
875+
# Build query with conditional WHERE clause
876+
where_clause = "WHERE s.user_id = %s"
877+
params: list = [user_id]
878+
875879
if leaderboard_name:
876-
query = """
877-
SELECT DISTINCT ON (s.id)
878-
s.id,
879-
lb.name as leaderboard_name,
880-
s.file_name,
881-
s.submission_time,
882-
s.done,
883-
r.runner as gpu_type,
884-
r.score
885-
FROM leaderboard.submission s
886-
JOIN leaderboard.leaderboard lb ON s.leaderboard_id = lb.id
887-
LEFT JOIN leaderboard.runs r ON r.submission_id = s.id
888-
AND NOT r.secret AND r.passed
889-
WHERE s.user_id = %s AND lb.name = %s
890-
ORDER BY s.id, s.submission_time DESC
891-
LIMIT %s OFFSET %s
892-
"""
893-
self.cursor.execute(query, (user_id, leaderboard_name, limit, offset))
894-
else:
895-
query = """
896-
SELECT DISTINCT ON (s.id)
897-
s.id,
898-
lb.name as leaderboard_name,
899-
s.file_name,
900-
s.submission_time,
901-
s.done,
902-
r.runner as gpu_type,
903-
r.score
904-
FROM leaderboard.submission s
905-
JOIN leaderboard.leaderboard lb ON s.leaderboard_id = lb.id
906-
LEFT JOIN leaderboard.runs r ON r.submission_id = s.id
907-
AND NOT r.secret AND r.passed
908-
WHERE s.user_id = %s
909-
ORDER BY s.id, s.submission_time DESC
910-
LIMIT %s OFFSET %s
911-
"""
912-
self.cursor.execute(query, (user_id, limit, offset))
880+
where_clause += " AND lb.name = %s"
881+
params.append(leaderboard_name)
882+
883+
# First, get distinct submissions
884+
submission_query = f"""
885+
SELECT s.id, lb.name as leaderboard_name, s.file_name,
886+
s.submission_time, s.done
887+
FROM leaderboard.submission s
888+
JOIN leaderboard.leaderboard lb ON s.leaderboard_id = lb.id
889+
{where_clause}
890+
ORDER BY s.submission_time DESC
891+
LIMIT %s OFFSET %s
892+
"""
893+
self.cursor.execute(submission_query, params + [limit, offset])
894+
submissions = self.cursor.fetchall()
913895

896+
if not submissions:
897+
return []
898+
899+
# Get all runs for these submissions
900+
submission_ids = [row[0] for row in submissions]
901+
runs_query = """
902+
SELECT submission_id, runner as gpu_type, score
903+
FROM leaderboard.runs
904+
WHERE submission_id = ANY(%s) AND NOT secret AND passed
905+
"""
906+
self.cursor.execute(runs_query, (submission_ids,))
907+
runs_by_submission: dict = {}
908+
for run_row in self.cursor.fetchall():
909+
sub_id = run_row[0]
910+
if sub_id not in runs_by_submission:
911+
runs_by_submission[sub_id] = []
912+
runs_by_submission[sub_id].append({
913+
"gpu_type": run_row[1],
914+
"score": run_row[2],
915+
})
916+
917+
# Build result with runs grouped by submission
914918
results = []
915-
for row in self.cursor.fetchall():
919+
for row in submissions:
920+
sub_id = row[0]
916921
results.append({
917-
"id": row[0],
922+
"id": sub_id,
918923
"leaderboard_name": row[1],
919924
"file_name": row[2],
920925
"submission_time": row[3],
921926
"done": row[4],
922-
"gpu_type": row[5],
923-
"score": row[6],
927+
"runs": runs_by_submission.get(sub_id, []),
924928
})
925929
return results
926930
except psycopg2.Error as e:

tests/test_leaderboard_db.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,3 +738,40 @@ def test_get_user_submissions_pagination(database, submit_leaderboard):
738738
assert len(result_offset) == 2
739739
assert result_offset[0]["id"] == result_all[2]["id"]
740740

741+
742+
def test_get_user_submissions_with_multiple_runs(database, submit_leaderboard):
743+
"""Test get_user_submissions returns all runs per submission"""
744+
with database as db:
745+
# Create a submission
746+
sub1 = db.create_submission(
747+
"submit-leaderboard",
748+
"multi_run.py",
749+
5,
750+
"code",
751+
datetime.datetime.now(tz=datetime.timezone.utc),
752+
user_name="user5",
753+
)
754+
755+
# Add multiple runs on different GPUs
756+
_create_submission_run(db, sub1, runner="A100", score=1.5, secret=False)
757+
_create_submission_run(db, sub1, runner="H100", score=2.0, secret=False)
758+
db.mark_submission_done(sub1)
759+
760+
# Get submissions
761+
result = db.get_user_submissions(user_id="5")
762+
assert len(result) == 1
763+
764+
# Verify runs list contains both runs
765+
submission = result[0]
766+
assert "runs" in submission
767+
assert len(submission["runs"]) == 2
768+
769+
# Verify run data
770+
gpu_types = {r["gpu_type"] for r in submission["runs"]}
771+
assert "A100" in gpu_types
772+
assert "H100" in gpu_types
773+
774+
scores = {r["score"] for r in submission["runs"]}
775+
assert 1.5 in scores
776+
assert 2.0 in scores
777+

0 commit comments

Comments
 (0)