From fe03222917d1503ef69258cd991ca43530a57d1c Mon Sep 17 00:00:00 2001 From: Mark Saroufim Date: Sat, 20 Jun 2026 09:24:16 -0700 Subject: [PATCH] Show starter code on leaderboard page --- frontend/src/api/api.ts | 1 + .../pages/leaderboard/Leaderboard.test.tsx | 46 +++++++++++-------- .../src/pages/leaderboard/Leaderboard.tsx | 14 ++++-- kernelboard/api/leaderboard.py | 12 +++++ kernelboard/leaderboard.py | 15 +++++- 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index 832d403b..7b5cd468 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -23,6 +23,7 @@ export interface LeaderboardDetail { description: string; name: string; reference: string; + starter: string; benchmarks: Record[]; gpu_types: string[]; rankings: Record< diff --git a/frontend/src/pages/leaderboard/Leaderboard.test.tsx b/frontend/src/pages/leaderboard/Leaderboard.test.tsx index ff6e1f51..7ec63dfe 100644 --- a/frontend/src/pages/leaderboard/Leaderboard.test.tsx +++ b/frontend/src/pages/leaderboard/Leaderboard.test.tsx @@ -30,6 +30,7 @@ const mockDeadline = "2025-06-29T17:00:00-07:00"; const mockExpiredDeadline = "2024-01-29T12:00:00-02:00"; const mockDescription = "Implement a 2D the given specifications"; const mockReference = "import torch"; +const mockStarter = "def custom_kernel(data):\n return data"; const mockName = "test-game"; describe("Leaderboard", () => { @@ -44,12 +45,13 @@ describe("Leaderboard", () => { // -------------------- Basic rendering -------------------- - it("renders name, description, gpu types; rankings visible on Rankings tab; reference visible after switching to Reference tab", () => { + it("renders name, description, gpu types; rankings visible on Rankings tab; starter code visible after switching to Starter Code tab", () => { const mockData = { deadline: mockDeadline, description: mockDescription, name: mockName, reference: mockReference, + starter: mockStarter, gpu_types: ["T1", "T2"], rankings: { T1: [ @@ -90,7 +92,7 @@ describe("Leaderboard", () => { // Tabs exist const rankingsTab = screen.getByRole("tab", { name: /Rankings/i }); - const referenceTab = screen.getByRole("tab", { name: /Reference/i }); + const referenceTab = screen.getByRole("tab", { name: /Starter Code/i }); const submissionTab = screen.getByRole("tab", { name: /Submission/i }); expect(rankingsTab).toBeInTheDocument(); expect(referenceTab).toBeInTheDocument(); @@ -100,11 +102,10 @@ describe("Leaderboard", () => { expect(screen.getByText(/user1/)).toBeInTheDocument(); expect(screen.getByText(/user2/)).toBeInTheDocument(); - // Switch to Reference tab before asserting its content + // Switch to Starter Code tab before asserting its content fireEvent.click(referenceTab); - expect(screen.getByText(/Reference Implementation/i)).toBeInTheDocument(); - expect(screen.getByText(/import/)).toBeInTheDocument(); - expect(screen.getByText(/torch/)).toBeInTheDocument(); + expect(screen.getByText(/Starter Submission/i)).toBeInTheDocument(); + expect(screen.getByText(/custom_kernel/)).toBeInTheDocument(); }); it("shows loading state", () => { @@ -276,15 +277,16 @@ describe("Leaderboard", () => { expect(screen.queryAllByTestId("ranking-0-row")).toHaveLength(3); }); - // -------------------- Reference codeblock -------------------- + // -------------------- Starter codeblock -------------------- - it("show reference codeblock (after switching to Reference tab)", () => { + it("shows starter codeblock (after switching to Starter Code tab)", () => { const mockData = { name: "test-code", description: "", deadline: "", gpu_types: ["T1"], reference: mockReference, + starter: mockStarter, rankings: { T1: [] }, }; @@ -298,23 +300,23 @@ describe("Leaderboard", () => { renderWithRouter(); - // Must switch to Reference tab first - fireEvent.click(screen.getByRole("tab", { name: /Reference/i })); + // Must switch to Starter Code tab first + fireEvent.click(screen.getByRole("tab", { name: /Starter Code/i })); - // Reference codeblock should be visible - expect(screen.getByText(/Reference Implementation/i)).toBeInTheDocument(); - expect(screen.getByText(/import/)).toBeInTheDocument(); - expect(screen.getByText(/torch/)).toBeInTheDocument(); + // Starter codeblock should be visible + expect(screen.getByText(/Starter Submission/i)).toBeInTheDocument(); + expect(screen.getByText(/custom_kernel/)).toBeInTheDocument(); }); // -------------------- Tabs behavior (switching) -------------------- - it("starts on Rankings tab by default and can switch to Reference and back", () => { + it("starts on Rankings tab by default and can switch to Starter Code and back", () => { const mockData = { deadline: mockDeadline, description: mockDescription, name: mockName, reference: mockReference, + starter: mockStarter, gpu_types: ["T1"], rankings: { T1: [ @@ -342,11 +344,10 @@ describe("Leaderboard", () => { // Default selected tab should be Rankings (content visible) expect(screen.getByText(/user1/)).toBeInTheDocument(); - // Switch to Reference tab - fireEvent.click(screen.getByRole("tab", { name: /Reference/i })); - expect(screen.getByText(/Reference Implementation/i)).toBeInTheDocument(); - expect(screen.getByText(/import/)).toBeInTheDocument(); - expect(screen.getByText(/torch/)).toBeInTheDocument(); + // Switch to Starter Code tab + fireEvent.click(screen.getByRole("tab", { name: /Starter Code/i })); + expect(screen.getByText(/Starter Submission/i)).toBeInTheDocument(); + expect(screen.getByText(/custom_kernel/)).toBeInTheDocument(); // Switch back to Rankings tab fireEvent.click(screen.getByRole("tab", { name: /Rankings/i })); @@ -359,6 +360,7 @@ describe("Leaderboard", () => { description: mockDescription, name: mockName, reference: mockReference, + starter: mockStarter, gpu_types: ["T1"], rankings: { T1: [] }, }; @@ -386,6 +388,7 @@ describe("Leaderboard", () => { deadline: mockDeadline, gpu_types: ["T1"], reference: mockReference, + starter: mockStarter, rankings: { T1: [] }, }; @@ -416,6 +419,7 @@ describe("Leaderboard", () => { deadline: mockDeadline, gpu_types: ["T1"], reference: mockReference, + starter: mockStarter, rankings: { T1: [] }, }; @@ -451,6 +455,7 @@ describe("Leaderboard", () => { deadline: mockDeadline, gpu_types: ["T1"], reference: mockReference, + starter: mockStarter, rankings: { T1: [] }, }; @@ -488,6 +493,7 @@ describe("Leaderboard", () => { deadline: mockExpiredDeadline, gpu_types: ["T1"], reference: mockReference, + starter: mockStarter, rankings: { T1: [] }, }; diff --git a/frontend/src/pages/leaderboard/Leaderboard.tsx b/frontend/src/pages/leaderboard/Leaderboard.tsx index 6da52148..2b6f61d4 100644 --- a/frontend/src/pages/leaderboard/Leaderboard.tsx +++ b/frontend/src/pages/leaderboard/Leaderboard.tsx @@ -284,7 +284,7 @@ const LeaderboardContent = memo(function LeaderboardContent() { allowScrollButtonsMobile > - + @@ -330,13 +330,19 @@ const LeaderboardContent = memo(function LeaderboardContent() { - {/* Reference Implementation Tab */} + {/* Starter Code Tab */} - Reference Implementation + Starter Submission - + {data.starter ? ( + + ) : ( + + No starter submission is available for this problem. + + )} diff --git a/kernelboard/api/leaderboard.py b/kernelboard/api/leaderboard.py index f22acf25..36c391a2 100644 --- a/kernelboard/api/leaderboard.py +++ b/kernelboard/api/leaderboard.py @@ -80,6 +80,9 @@ def to_api_leaderboard_item(data: dict[str, Any]): reference = leaderboard_data["reference"] or "" reference = reference.replace("\\n", "\n") + starter = leaderboard_data["starter"] or "" + starter = starter.replace("\\n", "\n") + benchmarks = leaderboard_data.get("benchmarks") or [] gpu_types = leaderboard_data["gpu_types"] @@ -113,6 +116,7 @@ def to_api_leaderboard_item(data: dict[str, Any]): "gpu_types": gpu_types, "description": description, "reference": reference, + "starter": starter, "benchmarks": benchmarks, "rankings": rankings, } @@ -130,6 +134,13 @@ def _get_query(): task->>'lang' AS lang, description AS description, task->'files'->>'reference.py' AS reference, + ( + SELECT code + FROM leaderboard.templates + WHERE leaderboard_id = %(leaderboard_id)s + ORDER BY CASE WHEN lang = 'Python' THEN 0 ELSE 1 END + LIMIT 1 + ) AS starter, task->'benchmarks' AS benchmarks FROM leaderboard.leaderboard WHERE id = %(leaderboard_id)s @@ -213,6 +224,7 @@ def _get_query(): 'lang', lang, 'description', description, 'reference', reference, + 'starter', starter, 'benchmarks', benchmarks, 'gpu_types', (SELECT jsonb_agg(gpu_type) FROM gpu_types) ) FROM leaderboard_info) diff --git a/kernelboard/leaderboard.py b/kernelboard/leaderboard.py index fdf5428b..2f8f6edd 100644 --- a/kernelboard/leaderboard.py +++ b/kernelboard/leaderboard.py @@ -18,7 +18,14 @@ def leaderboard(leaderboard_id: int): deadline, task->>'lang' AS lang, description AS description, - task->'files'->>'reference.py' AS reference + task->'files'->>'reference.py' AS reference, + ( + SELECT code + FROM leaderboard.templates + WHERE leaderboard_id = %(leaderboard_id)s + ORDER BY CASE WHEN lang = 'Python' THEN 0 ELSE 1 END + LIMIT 1 + ) AS starter FROM leaderboard.leaderboard WHERE id = %(leaderboard_id)s ), @@ -86,6 +93,7 @@ def leaderboard(leaderboard_id: int): 'lang', lang, 'description', description, 'reference', reference, + 'starter', starter, 'gpu_types', (SELECT jsonb_agg(gpu_type) FROM gpu_types) ) FROM leaderboard_info) ) AS result FROM (SELECT gpu_type FROM gpu_types) g; @@ -118,6 +126,9 @@ def leaderboard(leaderboard_id: int): reference = leaderboard_data["reference"] or "" reference = reference.replace("\\n", "\n") + starter = leaderboard_data["starter"] or "" + starter = starter.replace("\\n", "\n") + gpu_types = leaderboard_data["gpu_types"] gpu_types.sort() @@ -150,6 +161,6 @@ def leaderboard(leaderboard_id: int): lang=lang, gpu_types=gpu_types, description=description, - reference=reference, + reference=starter, rankings=rankings, )