diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index e9175d79..d355a412 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -5,19 +5,25 @@ import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import AppLayout from "./components/app-layout/AppLayout";
import { CssBaseline, ThemeProvider } from "@mui/material";
import { createAppTheme } from "./components/common/styles/theme";
-import Leaderboard from "./pages/leaderboard/Leaderboard";
-import LeaderboardEditor from "./pages/leaderboard/LeaderboardEditor";
import Home from "./pages/home/Home";
-import News from "./pages/news/News";
-import WorkingGroups from "./pages/working-groups/WorkingGroups";
-import Lectures from "./pages/lectures/Lectures";
-import Live from "./pages/live/Live";
import ErrorPage from "./pages/Error";
-import Login from "./pages/login/login";
-import Privacy from "./pages/privacy/Privacy";
+import Loading from "./components/common/loading";
import { useAuthStore } from "./lib/store/authStore";
import { useThemeStore } from "./lib/store/themeStore";
-import { useEffect, useMemo } from "react";
+import { lazy, Suspense, useEffect, useMemo } from "react";
+
+const Leaderboard = lazy(() => import("./pages/leaderboard/Leaderboard"));
+const LeaderboardEditor = lazy(
+ () => import("./pages/leaderboard/LeaderboardEditor"),
+);
+const News = lazy(() => import("./pages/news/News"));
+const WorkingGroups = lazy(
+ () => import("./pages/working-groups/WorkingGroups"),
+);
+const Lectures = lazy(() => import("./pages/lectures/Lectures"));
+const Live = lazy(() => import("./pages/live/Live"));
+const Login = lazy(() => import("./pages/login/login"));
+const Privacy = lazy(() => import("./pages/privacy/Privacy"));
const errorRoutes = [
{
@@ -67,34 +73,39 @@ function App() {
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- {/* error handling page */}
- {errorRoutes.map(({ path, code, title, description }) => (
+ }>
+
+ } />
+ } />
+ } />
- }
+ path="/leaderboard/:id/editor"
+ element={}
/>
- ))}
-
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ {/* error handling page */}
+ {errorRoutes.map(({ path, code, title, description }) => (
+
+ }
+ />
+ ))}
+
+
diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts
index fcb88a7d..c7ef9b6f 100644
--- a/frontend/src/api/api.ts
+++ b/frontend/src/api/api.ts
@@ -29,6 +29,7 @@ export interface LeaderboardDetail {
rankings: Record<
string,
Array<{
+ user_id: string;
file_name: string;
prev_score: number;
rank: number;
diff --git a/frontend/src/components/app-layout/NavBar.test.tsx b/frontend/src/components/app-layout/NavBar.test.tsx
index a390d4bc..e95fdbee 100644
--- a/frontend/src/components/app-layout/NavBar.test.tsx
+++ b/frontend/src/components/app-layout/NavBar.test.tsx
@@ -1,37 +1,37 @@
-import { render, screen } from "@testing-library/react";
+import { fireEvent, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
+import { useLocation } from "react-router-dom";
+import { renderWithRouter } from "../../tests/test-utils";
import NavBar from "./NavBar";
-describe("NavBar", () => {
- it("renders Working Groups link with correct URL", () => {
- render();
-
- const workingGroupsLink = screen.getByRole("link", {
- name: /working groups/i,
- });
+function CurrentPath() {
+ return ;
+}
- expect(workingGroupsLink).toBeInTheDocument();
- expect(workingGroupsLink).toHaveAttribute("href", "/working-groups");
- // Internal link should not have target="_blank" or rel="noopener"
- expect(workingGroupsLink).not.toHaveAttribute("target", "_blank");
- expect(workingGroupsLink).not.toHaveAttribute("rel", "noopener");
+describe("NavBar", () => {
+ it("renders the site navigation links", () => {
+ renderWithRouter();
+ for (const [name, href] of [
+ ["News", "/news"],
+ ["Events", "/events"],
+ ["Projects", "/working-groups"],
+ ]) {
+ const link = screen.getByRole("link", { name });
+ expect(link).toHaveAttribute("href", href);
+ expect(link).not.toHaveAttribute("target", "_blank");
+ }
});
- it("renders all expected navigation links in correct order", () => {
- render();
-
- const links = screen.getAllByRole("link");
- const navigationLinks = links.filter((link) =>
- ["News", "Working Groups", "Events", "Resources", "Docs"].includes(
- link.textContent || "",
- ),
+ it("navigates inside the app without reloading the document", () => {
+ renderWithRouter(
+ <>
+
+
+ >,
);
-
- expect(navigationLinks).toHaveLength(5);
- expect(navigationLinks[0]).toHaveTextContent("News");
- expect(navigationLinks[1]).toHaveTextContent("Working Groups");
- expect(navigationLinks[2]).toHaveTextContent("Events");
- expect(navigationLinks[3]).toHaveTextContent("Resources");
- expect(navigationLinks[4]).toHaveTextContent("Docs");
+ fireEvent.click(screen.getByRole("link", { name: "News" }));
+ expect(screen.getByTestId("current-path")).toHaveTextContent("/news");
+ fireEvent.click(screen.getByRole("link", { name: "GPU MODE" }));
+ expect(screen.getByTestId("current-path")).toHaveTextContent("/");
});
});
diff --git a/frontend/src/components/app-layout/NavBar.tsx b/frontend/src/components/app-layout/NavBar.tsx
index 8f10edbf..b1f12c32 100644
--- a/frontend/src/components/app-layout/NavBar.tsx
+++ b/frontend/src/components/app-layout/NavBar.tsx
@@ -14,6 +14,7 @@ import { appBarStyle, brandStyle } from "./styles";
import { ConstrainedContainer } from "./ConstrainedContainer";
import NavUserProfile from "./NavUserProfile";
import { useThemeStore } from "../../lib/store/themeStore";
+import { Link as RouterLink } from "react-router-dom";
export interface NavLink {
label: string;
@@ -61,7 +62,7 @@ export default function NavBar() {
const Brand = () => (
-
+
(
import("../../components/markdown-renderer/MarkdownRenderer"));
+
interface TopUser {
rank: number;
score: number;
@@ -204,7 +205,11 @@ export default function Home() {
>
Submit Your First Kernel via cli tool!
-
+ {isQuickStartOpen && (
+ }>
+
+
+ )}
diff --git a/frontend/src/pages/leaderboard/Leaderboard.tsx b/frontend/src/pages/leaderboard/Leaderboard.tsx
index 699910e6..6ff8db1a 100644
--- a/frontend/src/pages/leaderboard/Leaderboard.tsx
+++ b/frontend/src/pages/leaderboard/Leaderboard.tsx
@@ -10,8 +10,8 @@ import {
Button,
} from "@mui/material";
import Grid from "@mui/material/Grid";
-import { memo, useCallback, useEffect, useState } from "react";
-import { fetchLeaderBoard, searchUsers } from "../../api/api";
+import { lazy, memo, Suspense, useCallback, useEffect, useMemo, useState } from "react";
+import { fetchLeaderBoard } from "../../api/api";
import { fetcherApiCallback } from "../../lib/hooks/useApi";
import { isExpired, toDateLocal } from "../../lib/date/utils";
import RankingsList from "./components/RankingLists";
@@ -23,7 +23,6 @@ import Loading from "../../components/common/loading";
import { ConstrainedContainer } from "../../components/app-layout/ConstrainedContainer";
import { useAuthStore } from "../../lib/store/authStore";
import SubmissionHistorySection from "./components/submission-history/SubmissionHistorySection";
-import UserTrendChart from "./components/UserTrendChart";
import {
SubmissionSidebarProvider,
useSubmissionSidebarState,
@@ -41,6 +40,7 @@ import {
} from "@mui/material";
import quickStartMarkdown from "../home/quick-start.md?raw";
+const UserTrendChart = lazy(() => import("./components/UserTrendChart"));
const DEFAULT_SIDEBAR_WIDTH = 600;
export const CardTitle = styled(Typography)(() => ({
@@ -89,12 +89,6 @@ const LeaderboardContent = memo(function LeaderboardContent() {
const isAuthed = !!(me && me.authenticated);
const userId = me?.user?.identity ?? null;
- // State for top users (strongest submissions) and default GPU type
- const [defaultUsers, setDefaultUsers] = useState<
- Array<{ userId: string; username: string }>
- >([]);
- const [defaultGpuType, setDefaultGpuType] = useState(null);
-
// Sync tab state with query parameter
const [searchParams, setSearchParams] = useSearchParams();
@@ -124,59 +118,22 @@ const LeaderboardContent = memo(function LeaderboardContent() {
if (id) call(id);
}, [id, call]);
- // Fetch top users (strongest submissions) when rankings are available
- // Select from the GPU with the most unique users
- useEffect(() => {
- const findTopUsers = async () => {
- if (!id || !data?.rankings) return;
-
- const gpuTypes = Object.keys(data.rankings);
- if (gpuTypes.length === 0) return;
-
- // Find the GPU type with the most unique users
- const mostActiveGpu = gpuTypes.reduce((currentMax, gpuType) => {
- const rankings = data.rankings[gpuType];
- const userCount = rankings ? rankings.length : 0;
- const maxCount = data.rankings[currentMax]?.length ?? 0;
- return userCount > maxCount ? gpuType : currentMax;
- }, gpuTypes[0]);
-
- // Set the default GPU type to the one with most users
- setDefaultGpuType(mostActiveGpu);
-
- const mostActiveGpuRankings = data.rankings[mostActiveGpu];
- if (!mostActiveGpuRankings || mostActiveGpuRankings.length === 0) return;
-
- // Get top 5 users (sorted by score ascending)
- const topUserNames = mostActiveGpuRankings
+ // The ranking response already identifies users. Avoid five username-search
+ // requests (which can also pick the wrong account when names collide).
+ const { defaultUsers, defaultGpuType } = useMemo(() => {
+ const rankings = data?.rankings ?? {};
+ const gpuTypes = Object.keys(rankings);
+ const gpuType = gpuTypes.reduce((best, gpu) =>
+ best === null || rankings[gpu].length > rankings[best].length ? gpu : best,
+ null);
+ return {
+ defaultGpuType: gpuType,
+ defaultUsers: (gpuType ? rankings[gpuType] : [])
.slice(0, 5)
- .map((r) => r.user_name)
- .filter(Boolean);
-
- if (topUserNames.length === 0) return;
-
- try {
- // Search for each user by username to get their user_id
- const userPromises = topUserNames.map((userName: string) =>
- searchUsers(id, userName, 1)
- );
- const results = await Promise.all(userPromises);
-
- const foundUsers = results
- .filter((result) => result.users && result.users.length > 0)
- .map((result) => ({
- userId: result.users[0].user_id,
- username: result.users[0].username,
- }));
-
- setDefaultUsers(foundUsers);
- } catch (err) {
- console.error("Failed to fetch top users:", err);
- }
+ .filter((entry) => entry.user_id && entry.user_name)
+ .map((entry) => ({ userId: entry.user_id, username: entry.user_name })),
};
-
- findTopUsers();
- }, [id, data?.rankings]);
+ }, [data?.rankings]);
if (error) return ;
if (loading || !data) return ;
@@ -303,7 +260,9 @@ const LeaderboardContent = memo(function LeaderboardContent() {
Performance Trend
-
+ }>
+
+
>
diff --git a/frontend/src/pages/leaderboard/components/UserTrendChart.tsx b/frontend/src/pages/leaderboard/components/UserTrendChart.tsx
index 69328108..9c5522f6 100644
--- a/frontend/src/pages/leaderboard/components/UserTrendChart.tsx
+++ b/frontend/src/pages/leaderboard/components/UserTrendChart.tsx
@@ -1,5 +1,12 @@
import React, { useEffect, useState, useCallback, useRef, useMemo } from "react";
-import ReactECharts from "echarts-for-react";
+import ReactECharts from "echarts-for-react/lib/core";
+import * as echarts from "echarts/core";
+import { LineChart } from "echarts/charts";
+import {
+ DataZoomComponent, GridComponent, LegendComponent, TitleComponent,
+ ToolboxComponent, TooltipComponent,
+} from "echarts/components";
+import { CanvasRenderer } from "echarts/renderers";
import {
Box,
Typography,
@@ -37,6 +44,12 @@ import type {
} from "./submissionTypes";
import { useSubmissionSidebarActions } from "./SubmissionSidebarContext";
+// Register only the chart features used here instead of the full ECharts bundle.
+echarts.use([
+ LineChart, DataZoomComponent, GridComponent, LegendComponent,
+ TitleComponent, ToolboxComponent, TooltipComponent, CanvasRenderer,
+]);
+
// Display name prefix for custom (KernelAgent) entries
const CUSTOM_ENTRY_PREFIX = "KernelAgent";
@@ -1165,6 +1178,7 @@ export default function UserTrendChart({ leaderboardId, defaultUsers, defaultGpu
{renderSearchInput()}
'hacked'
+ AND NOT EXISTS (
+ SELECT 1 FROM leaderboard.submission_job_status sjs
+ WHERE sjs.submission_id = s.id AND sjs.status = 'hacked'
+ )
),
- scoped_runs AS MATERIALIZED (
- SELECT r.*
+
+ -- Total submission count per user per GPU type for this leaderboard.
+ submission_counts AS MATERIALIZED (
+ SELECT s.user_id, r.runner, COUNT(*) AS submission_count
FROM scoped_submissions s
JOIN LATERAL (
- SELECT id, submission_id, runner, score, secret, passed
- FROM leaderboard.runs
+ SELECT DISTINCT runner FROM leaderboard.runs
WHERE submission_id = s.id
OFFSET 0
) r ON TRUE
- ),
-
- -- Total submission count per user per GPU type for this leaderboard.
- submission_counts AS MATERIALIZED (
- SELECT s.user_id, r.runner, COUNT(DISTINCT s.id) AS submission_count
- FROM scoped_submissions s
- JOIN scoped_runs r ON r.submission_id = s.id
- WHERE s.leaderboard_id = %(leaderboard_id)s
- AND s.status <> 'hacked'
- AND NOT EXISTS (
- SELECT 1
- FROM leaderboard.submission_job_status sjs
- WHERE sjs.submission_id = s.id
- AND sjs.status = 'hacked'
- )
GROUP BY s.user_id, r.runner
),
@@ -278,6 +270,7 @@ def _get_query():
-- user's runs on that GPU type are ranked by score.
ranked_runs AS (
SELECT r.runner AS runner,
+ s.user_id AS user_id,
u.user_name AS user_name,
r.score AS score,
s.submission_time AS submission_time,
@@ -288,22 +281,21 @@ def _get_query():
PARTITION BY r.runner, s.user_id
ORDER BY r.score ASC, s.submission_time ASC, s.id ASC, r.id ASC
) AS rank
- FROM scoped_runs r
- JOIN scoped_submissions s ON r.submission_id = s.id
+ FROM scoped_submissions s
+ -- Read the covering public-score index, without fetching every
+ -- secret/failed run from the heap. Keep one candidate per GPU
+ -- and submission before ranking users.
+ JOIN LATERAL (
+ SELECT DISTINCT ON (runner) id, submission_id, runner, score
+ FROM leaderboard.runs
+ WHERE submission_id = s.id
+ AND NOT secret AND score IS NOT NULL AND passed
+ ORDER BY runner, score, id
+ OFFSET 0
+ ) r ON TRUE
LEFT JOIN leaderboard.user_info u ON s.user_id = u.id
LEFT JOIN submission_counts sc ON s.user_id = sc.user_id AND r.runner = sc.runner
- WHERE NOT r.secret
- AND r.score IS NOT NULL
- AND r.passed
- AND s.leaderboard_id = %(leaderboard_id)s
- AND s.status <> 'hacked'
- AND NOT EXISTS (
- SELECT 1
- FROM leaderboard.submission_job_status sjs
- WHERE sjs.submission_id = s.id
- AND sjs.status = 'hacked'
- )
- AND EXISTS (
+ WHERE EXISTS (
SELECT 1
FROM leaderboard.runs sr
WHERE sr.submission_id = s.id
@@ -311,6 +303,7 @@ def _get_query():
AND sr.runner = r.runner
AND sr.mode = 'leaderboard'
AND sr.passed
+ OFFSET 0
)
AND NOT EXISTS (
SELECT 1
@@ -319,6 +312,7 @@ def _get_query():
AND sr.secret
AND sr.runner = r.runner
AND sr.passed = FALSE
+ OFFSET 0
)
),
@@ -360,6 +354,7 @@ def _get_query():
'rankings', (SELECT jsonb_object_agg(g.gpu_type, (
SELECT jsonb_agg(
jsonb_build_object(
+ 'user_id', r.user_id,
'user_name', r.user_name,
'score', r.score,
'file_name', r.file_name,
@@ -376,7 +371,7 @@ def _get_query():
)
ORDER BY r.score ASC
)
- FROM top_runs r WHERE r.runner = g.gpu_type))),
+ FROM top_runs r WHERE r.runner = g.gpu_type)) FROM gpu_types g),
'leaderboard', (SELECT jsonb_build_object(
'name', name,
@@ -388,7 +383,7 @@ def _get_query():
'benchmarks', benchmarks,
'gpu_types', (SELECT jsonb_agg(gpu_type) FROM gpu_types)
) FROM leaderboard_info)
- ) AS result FROM (SELECT gpu_type FROM gpu_types) g;
+ ) AS result WHERE EXISTS (SELECT 1 FROM gpu_types);
"""
return query
diff --git a/requirements.txt b/requirements.txt
index d6bfce0a..7ae236ec 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@ click>=8.1.8,<8.2.0
coverage>=7.7.1,<7.8.0
dotenv>=0.9.9,<1.0.0
Flask>=3.1.0,<3.2.0
+Flask-Compress>=1.26,<2.0
Flask-Login>=0.6.3,<1.0.0
Flask-Session>=0.8.0,<1.0.0
flask-talisman>=1.1.0,<2.0.0
diff --git a/tests/api/test_leaderboard_api.py b/tests/api/test_leaderboard_api.py
index c3edcaf3..e504a9f6 100644
--- a/tests/api/test_leaderboard_api.py
+++ b/tests/api/test_leaderboard_api.py
@@ -7,6 +7,20 @@ def test_leaderboard(client):
assert b"conv2d" in response.data
+def test_rankings_include_account_ids_for_trend_selection(client, app):
+ response = client.get("/api/leaderboard/339")
+ rankings = response.get_json()["data"]["rankings"]
+ with app.app_context():
+ conn = get_db_connection()
+ with conn.cursor() as cur:
+ cur.execute("SELECT id, user_id FROM leaderboard.submission WHERE leaderboard_id = 339")
+ account_ids = dict(cur.fetchall())
+ assert rankings
+ for entries in rankings.values():
+ for entry in entries:
+ assert entry["user_id"] == account_ids[entry["submission_id"]]
+
+
def test_concluded_leaderboard_includes_line_counts(client):
response = client.get("/api/leaderboard/339")
assert response.status_code == 200
diff --git a/tests/test_compression.py b/tests/test_compression.py
new file mode 100644
index 00000000..5f4bd565
--- /dev/null
+++ b/tests/test_compression.py
@@ -0,0 +1,37 @@
+import gzip
+
+from flask import jsonify
+
+
+def test_json_responses_are_compressed_without_changing_content(app, client):
+ payload = {"text": "kernelboard " * 500}
+ app.add_url_rule("/compression-test", view_func=lambda: jsonify(payload))
+
+ original = client.get("/compression-test")
+ compressed = client.get("/compression-test", headers={"Accept-Encoding": "gzip"})
+
+ assert compressed.status_code == 200
+ assert compressed.headers["Content-Encoding"] == "gzip"
+ assert "Accept-Encoding" in compressed.headers["Vary"]
+ assert gzip.decompress(compressed.data) == original.data
+ assert len(compressed.data) < len(original.data) / 5
+
+
+def test_static_scripts_are_compressed_and_ranges_are_preserved(app, client, tmp_path):
+ assets = tmp_path / "app" / "assets"
+ assets.mkdir(parents=True)
+ script = b"console.log('kernelboard');\n" * 500
+ (assets / "test.js").write_bytes(script)
+ app.static_folder = str(tmp_path)
+
+ compressed = client.get("/assets/test.js", headers={"Accept-Encoding": "gzip"})
+ assert compressed.headers["Content-Encoding"] == "gzip"
+ assert gzip.decompress(compressed.data) == script
+
+ partial = client.get(
+ "/assets/test.js",
+ headers={"Accept-Encoding": "gzip", "Range": "bytes=0-9"},
+ )
+ assert partial.status_code == 206
+ assert "Content-Encoding" not in partial.headers
+ assert partial.data == script[:10]
diff --git a/tests/test_hacked_submission_filters.py b/tests/test_hacked_submission_filters.py
index a03d73ff..5a1784dd 100644
--- a/tests/test_hacked_submission_filters.py
+++ b/tests/test_hacked_submission_filters.py
@@ -7,7 +7,7 @@
def test_leaderboard_queries_filter_hacked_submissions():
expected_counts = {
- "kernelboard/api/leaderboard.py": 7,
+ "kernelboard/api/leaderboard.py": 6,
"kernelboard/api/leaderboard_summaries.py": 2,
"kernelboard/index.py": 1,
"kernelboard/leaderboard.py": 1,