Skip to content

Leaderboard #3113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
df11532
Leaderboard create course config, leaderboard page routing, leaderboa…
tohyzhong Feb 14, 2025
255c926
added support for Top Leaderboard Display (select number of entries t…
tohyzhong Mar 8, 2025
cf3ede1
Background and default avatar for Leaderboard
tohyzhong Mar 8, 2025
fa7f186
Leaderboard Routing
tohyzhong Mar 8, 2025
b3f58c2
Leaderboard Dropdown for all leaderboard pages
tohyzhong Mar 8, 2025
15dffc2
Overall and Contest leaderboard (leaderboard + retrieval from backend)
tohyzhong Mar 8, 2025
8ce5de4
Button in Ground Control to manually trigger contest score calculatio…
tohyzhong Mar 8, 2025
4e24010
[*not working] Support redirecting to contest voting workspace for co…
tohyzhong Mar 8, 2025
6a123b6
prettier changes
tohyzhong Mar 8, 2025
9fd4d8a
Merge branch 'leaderboard' of https://github.com/source-academy/front…
tohyzhong Mar 8, 2025
5b6755d
Fix import order in AssessmentWorkspace component
tohyzhong Mar 9, 2025
62ea438
Refactor contest score calculation logic in Ground Control
tohyzhong Mar 9, 2025
1c0c5c2
Modularisation of Leaderboard subcomponents
Blerargh Mar 10, 2025
9f2b2a9
Formatting changes
Blerargh Mar 10, 2025
7f65d1c
Dropdown, csv Export, Podium fixes
tohyzhong Mar 12, 2025
b72b813
Set filename for Contests and Overall Leaderboard csv
tohyzhong Mar 12, 2025
1360753
Use no payload type
RichDom2185 Mar 16, 2025
4aed281
Remove unused parameter
RichDom2185 Mar 16, 2025
75ec8a1
Refactor LeaderboardExportButton to accept dynamic type and update fi…
tohyzhong Mar 17, 2025
ba4b33b
Add contests state and actions to leaderboard management.
tohyzhong Mar 17, 2025
b78304a
Add DispatchContestXp functionality and button to issue contest XP in…
tohyzhong Mar 18, 2025
0a41698
Refactor leaderboard components to use slice for top entries instead …
tohyzhong Mar 18, 2025
fddc6e9
Remove duplicate import of getAllContests and clean up imports in Lea…
tohyzhong Mar 18, 2025
593c67a
Updated contest details fetching for manual url entry
tohyzhong Mar 19, 2025
9138c53
Refactor ContestLeaderboard to filter top entries by rank and adjust …
tohyzhong Mar 20, 2025
e6f590f
Temporary removal of Achievements column in leaderboard and added def…
Blerargh Mar 20, 2025
2d0b960
Merge branch 'master' into leaderboard
tohyzhong Mar 27, 2025
b05cc8d
Merge branch 'master' into leaderboard
martin-henz Apr 1, 2025
95563ef
Merge branch 'master' into leaderboard
tohyzhong Apr 15, 2025
d26e047
Remove console.log statements from leaderboard saga and components
tohyzhong Apr 15, 2025
64a17b5
Fix: Tied contest popular vote/scores exceeding visible entries limit…
Blerargh Apr 15, 2025
17c28bb
Fix: Update ContestEntry type to include rank and adjust assessment w…
tohyzhong Apr 15, 2025
0d9d5dc
Fix: Use logical checking to circumvent setTimeout usage
Blerargh Apr 16, 2025
bdf2cd5
Refactor: Clean up unused type declarations and reorganize imports in…
tohyzhong Apr 16, 2025
5c29fbe
Merge branch 'master' into leaderboard
tohyzhong Apr 17, 2025
f9390e9
Refactor: Improve code formatting and consistency across leaderboard …
tohyzhong Apr 17, 2025
c8d3102
Refactor: Remove unnecessary console.log from ContestLeaderboard comp…
tohyzhong Apr 17, 2025
1e4f255
Remove files not supposed to be committed
RichDom2185 Apr 17, 2025
1dc925b
Revert incorrect DTS changes
RichDom2185 Apr 17, 2025
3243538
Avoid spaces in file names
RichDom2185 Apr 17, 2025
826f3ee
Standardize reducer import
RichDom2185 Apr 17, 2025
dda7e4b
Fix inconsistent HTML label for
RichDom2185 Apr 17, 2025
0185023
Organize imports
RichDom2185 Apr 17, 2025
5040583
Added support for retrieving only relevant rows in leaderboard page
Blerargh Apr 29, 2025
e94cc50
Refactor leaderboard data handling to support pagination and update r…
tohyzhong Apr 29, 2025
e08c26c
Updated leaderboard podium logic
tohyzhong Apr 30, 2025
2ea8cf0
Enhance leaderboard functionality to support visible entries in conte…
tohyzhong Apr 30, 2025
7c07b48
Update Contest Leaderboard export to support all users
tohyzhong Apr 30, 2025
4859269
Refactor leaderboard saga and actions for improved readability and ma…
tohyzhong Apr 30, 2025
f845af9
Fix API endpoint in getScoreLeaderboard and disable pagination page s…
tohyzhong May 1, 2025
dcab05c
Updated leaderboard display backend requests to use query parameters;…
Blerargh May 1, 2025
5666396
Refactor leaderboard export button to escape code fields and update c…
tohyzhong May 1, 2025
a44480e
Fix API endpoints in getScoreLeaderboard and getPopularVoteLeaderboar…
tohyzhong May 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added public/assets/Sample_Profile_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/Sample_Profile_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/Sample_Profile_3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/Sample_Profile_4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/Sample_Profile_5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/Sample_Profile_6.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/Sample_Profile_7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/default-avatar.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/leaderboard_background.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion src/commons/application/ApplicationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Chapter, Language, SourceError, Variant } from 'js-slang/dist/types';

import { AchievementState } from '../../features/achievement/AchievementTypes';
import { DashboardState } from '../../features/dashboard/DashboardTypes';
import { LeaderboardState } from '../../features/leaderboard/LeaderboardTypes';
import { PlaygroundState } from '../../features/playground/PlaygroundTypes';
import { PlaybackStatus, RecordingStatus } from '../../features/sourceRecorder/SourceRecorderTypes';
import { StoriesEnvState, StoriesState } from '../../features/stories/StoriesTypes';
Expand All @@ -21,11 +22,12 @@ import {
import { RouterState } from './types/CommonsTypes';
import { ExternalLibraryName } from './types/ExternalTypes';
import { SessionState } from './types/SessionTypes';
import { VscodeState as VscodeState } from './types/VscodeTypes';
import { VscodeState } from './types/VscodeTypes';

export type OverallState = {
readonly router: RouterState;
readonly achievement: AchievementState;
readonly leaderboard: LeaderboardState;
readonly playground: PlaygroundState;
readonly session: SessionState;
readonly stories: StoriesState;
Expand Down Expand Up @@ -347,6 +349,16 @@ export const defaultAchievement: AchievementState = {
assessmentOverviews: []
};

export const defaultLeaderboard: LeaderboardState = {
userXp: [],
paginatedUserXp: { rows: [], userCount: 0 },
contestScore: [],
contestPopularVote: [],
code: '',
contests: [],
initialRun: {}
};

const getDefaultLanguageConfig = (): SALanguage => {
const languageConfig = ALL_LANGUAGES.find(
sublang =>
Expand Down Expand Up @@ -606,6 +618,7 @@ export const defaultVscode: VscodeState = {
export const defaultState: OverallState = {
router: defaultRouter,
achievement: defaultAchievement,
leaderboard: defaultLeaderboard,
dashboard: defaultDashboard,
playground: defaultPlayground,
session: defaultSession,
Expand Down
4 changes: 3 additions & 1 deletion src/commons/application/reducers/RootReducer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { combineReducers, Reducer } from '@reduxjs/toolkit';
import { FeatureFlagsReducer as featureFlags } from 'src/commons/featureFlags';
import { SourceActionType } from 'src/commons/utils/ActionsHelper';

import { FeatureFlagsReducer as featureFlags } from '../../..//commons/featureFlags';
import { AchievementReducer as achievement } from '../../../features/achievement/AchievementReducer';
import { DashboardReducer as dashboard } from '../../../features/dashboard/DashboardReducer';
import { LeaderboardReducer as leaderboard } from '../../../features/leaderboard/LeaderboardReducer';
import { PlaygroundReducer as playground } from '../../../features/playground/PlaygroundReducer';
import { StoriesReducer as stories } from '../../../features/stories/StoriesReducer';
import { FileSystemReducer as fileSystem } from '../../fileSystem/FileSystemReducer';
Expand All @@ -17,6 +18,7 @@ import { VscodeReducer as vscode } from './VscodeReducer';
const rootReducer: Reducer<OverallState, SourceActionType> = combineReducers({
router,
achievement,
leaderboard,
dashboard,
playground,
session,
Expand Down
8 changes: 8 additions & 0 deletions src/commons/application/types/SessionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export type SessionState = {
readonly enableAchievements?: boolean;
readonly enableSourcecast?: boolean;
readonly enableStories?: boolean;
readonly enableOverallLeaderboard?: boolean;
readonly enableContestLeaderboard?: boolean;
readonly topLeaderboardDisplay?: number;
readonly topContestLeaderboardDisplay?: number;
readonly sourceChapter?: Chapter;
readonly sourceVariant?: Variant;
readonly moduleHelpText?: string;
Expand Down Expand Up @@ -105,6 +109,10 @@ export type CourseConfiguration = {
enableAchievements: boolean;
enableSourcecast: boolean;
enableStories: boolean;
enableOverallLeaderboard: boolean;
enableContestLeaderboard: boolean;
topLeaderboardDisplay: number;
topContestLeaderboardDisplay: number;
sourceChapter: Chapter;
sourceVariant: Variant;
moduleHelpText: string;
Expand Down
7 changes: 5 additions & 2 deletions src/commons/assessment/Assessment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import NotificationBadge from '../notificationBadge/NotificationBadge';
import { filterNotificationsByAssessment } from '../notificationBadge/NotificationBadgeHelper';
import Constants from '../utils/Constants';
import { beforeNow, getPrettyDate, getPrettyDateAfterHours } from '../utils/DateHelper';
import { useResponsive, useSession } from '../utils/Hooks';
import { useResponsive, useSession, useTypedSelector } from '../utils/Hooks';
import { assessmentTypeLink, convertParamToInt } from '../utils/ParamParseHelper';
import AssessmentNotFound from './AssessmentNotFound';
import {
Expand Down Expand Up @@ -262,6 +262,8 @@ const Assessment: React.FC = () => {
[assessmentConfigToLoad.type, assessmentOverviewsUnfiltered]
);

const fromLeaderboard: boolean = useTypedSelector(store => store.leaderboard.code) ? true : false;

// If assessmentId or questionId is defined but not numeric, redirect back to the Assessment overviews page
if (
(params.assessmentId && !params.assessmentId?.match(numberRegExp)) ||
Expand Down Expand Up @@ -290,7 +292,8 @@ const Assessment: React.FC = () => {
canSave:
role !== Role.Student ||
(overview.status !== AssessmentStatuses.submitted && !beforeNow(overview.closeAt)),
assessmentConfiguration: assessmentConfigToLoad
assessmentConfiguration: assessmentConfigToLoad,
fromContestLeaderboard: fromLeaderboard
};
return <AssessmentWorkspace {...assessmentWorkspaceProps} />;
}
Expand Down
1 change: 1 addition & 0 deletions src/commons/assessment/AssessmentTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export type ContestEntry = {
score?: number;
final_score?: number;
student_name?: string;
rank?: number;
};

export type ContestEntryCodeAnswer = {
Expand Down
20 changes: 18 additions & 2 deletions src/commons/assessmentWorkspace/AssessmentWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import { useNavigate } from 'react-router';
import { showSimpleConfirmDialog } from 'src/commons/utils/DialogHelper';
import { onClickProgress } from 'src/features/assessments/AssessmentUtils';
import LeaderboardActions from 'src/features/leaderboard/LeaderboardActions';
import Messages, { sendToWebview } from 'src/features/vscode/messages';
import { mobileOnlyTabIds } from 'src/pages/playground/PlaygroundTabs';

Expand Down Expand Up @@ -85,6 +86,7 @@
notAttempted: boolean;
canSave: boolean;
assessmentConfiguration: AssessmentConfiguration;
fromContestLeaderboard: boolean;
};

const workspaceLocation: WorkspaceLocation = 'assessment';
Expand Down Expand Up @@ -186,6 +188,17 @@
};
}, [dispatch]);

const code = useTypedSelector(store => store.leaderboard.code);
const initialRunCompleted = useTypedSelector(store => store.leaderboard.initialRun);
const votingId = props.assessmentId;

useEffect(() => {
if (initialRunCompleted[votingId] && props.fromContestLeaderboard && code != '') {
dispatch(WorkspaceActions.updateEditorValue(workspaceLocation, 0, code));
dispatch(LeaderboardActions.clearCode());
}
}, [dispatch]);

Check warning on line 200 in src/commons/assessmentWorkspace/AssessmentWorkspace.tsx

View workflow job for this annotation

GitHub Actions / lint (eslint)

React Hook useEffect has missing dependencies: 'code', 'initialRunCompleted', 'props.fromContestLeaderboard', and 'votingId'. Either include them or remove the dependency array

useEffect(() => {
if (assessmentOverview && assessmentOverview.maxTeamSize > 1) {
handleTeamOverviewFetch(props.assessmentId);
Expand Down Expand Up @@ -216,8 +229,7 @@
if (!assessment) {
return;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Check warning on line 232 in src/commons/assessmentWorkspace/AssessmentWorkspace.tsx

View workflow job for this annotation

GitHub Actions / lint (eslint)

React Hook useEffect has missing dependencies: 'assessment', 'handleAssessmentFetch', 'props.assessmentId', 'props.needsPassword', 'props.notAttempted', and 'props.questionId'. Either include them or remove the dependency array

/**
* Once there is an update (due to the assessment being fetched), check
Expand All @@ -225,7 +237,10 @@
*/
useEffect(() => {
checkWorkspaceReset();
});
if (assessment != undefined && question.type == 'voting') {
dispatch(LeaderboardActions.setWorkspaceInitialRun(votingId));
}
}, [dispatch, assessment]);

Check warning on line 243 in src/commons/assessmentWorkspace/AssessmentWorkspace.tsx

View workflow job for this annotation

GitHub Actions / lint (eslint)

React Hook useEffect has missing dependencies: 'checkWorkspaceReset', 'question.type', and 'votingId'. Either include them or remove the dependency array

/**
* Handles toggling enabling and disabling token counter depending on assessment properties
Expand Down Expand Up @@ -367,6 +382,7 @@
case QuestionTypes.voting:
const votingQuestionData: IContestVotingQuestion = question;
options.programPrependValue = votingQuestionData.prepend;
if (props.fromContestLeaderboard) options.editorValue = code;
options.programPostpendValue = votingQuestionData.postpend;
break;
case QuestionTypes.mcq:
Expand Down
Loading
Loading