Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions front_end/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@
"average": "Average",
"score": "Score",
"coverage": "Coverage",
"totalCoverage": "Total Coverage",
"totalScore": "Total Score",
"totalTake": "Total Take",
"weightedAverageScore": "Weighted Average Score",
Expand Down Expand Up @@ -524,6 +525,8 @@
"noMedals": "No medals yet",
"leaderboard": "Leaderboard",
"openLeaderboard": "Open Leaderboard",
"liveLeaderboardDisclaimer": "This is a live leaderboard; rankings may change on a daily basis.",
"legacyPeerDisclaimer": "The Peer Accuracy leaderboards used slightly different math before 2024. See details <link>here</link>.",
"namesPrediction": "<name>{username}'s</name> Prediction",
"includeMyForecast": "include your latest forecast in this comment",
"privateComment": "private comment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ const LeaderboardHeader: FC<Props> = ({ filters }) => {
};

return (
<section className="text-metac-blue-800 dark:text-metac-blue-800-dark flex w-full flex-col items-center gap-3.5 max-sm:pt-3 sm:m-8 sm:mb-6 sm:gap-6">
<section className="flex w-full flex-col items-center gap-3.5 text-blue-800 dark:text-blue-800-dark max-sm:pt-3 sm:m-8 sm:mb-6 sm:gap-6">
<div className="flex flex-col gap-3.5">
<div className="flex flex-col items-center justify-center gap-3">
{category !== "all" && (
<Link
href={`?year=${year}&duration=${duration}`}
className="text-metac-blue-700 dark:text-metac-blue-700-dark text-base leading-5"
className="text-base leading-5 text-blue-700 dark:text-blue-700-dark"
>
{t("leaderboards")}
</Link>
)}
<div className="text-metac-blue-900 dark:text-metac-blue-900-dark text-2xl font-bold sm:text-4xl">
<div className="text-2xl font-bold text-blue-900 dark:text-blue-900-dark sm:text-4xl">
{category !== "all" ? (
<div className="flex items-center gap-4">
<span>{t(RANKING_CATEGORIES[category].translationKey)}</span>
Expand All @@ -64,7 +64,7 @@ const LeaderboardHeader: FC<Props> = ({ filters }) => {
)}
</div>
</div>
<div className="text-metac-gray-700 dark:text-metac-gray-700-dark max-w-3xl px-5 py-2 text-center text-sm font-normal sm:py-0 sm:text-base">
<div className="max-w-3xl px-5 py-2 text-center text-sm font-normal text-gray-700 dark:text-gray-700-dark sm:py-0 sm:text-base">
{RANKING_CATEGORIES[category].explanation}
</div>
</div>
Expand Down Expand Up @@ -95,13 +95,13 @@ const LeaderboardHeader: FC<Props> = ({ filters }) => {
</div>
)}
</div>
<div className="dark:text-metac-blue-600-dark flex justify-center gap-5 font-medium leading-6 sm:hidden">
<div className="flex justify-center gap-5 font-medium leading-6 dark:text-blue-600-dark sm:hidden">
{/* comments and questionWriting leaderboards only exist for 1-year durations, so no selector is shown */}
{durations &&
duration &&
["all", "peer", "baseline"].includes(category) && (
<div className="flex items-center gap-2.5">
<span className="text-metac-blue-800 dark:text-metac-blue-800-dark text-base font-medium">
<span className="text-base font-medium text-blue-800 dark:text-blue-800-dark">
{t("duration:")}
</span>
<Listbox
Expand All @@ -115,7 +115,7 @@ const LeaderboardHeader: FC<Props> = ({ filters }) => {
)}
{periods && year && (
<div className="flex items-center gap-2.5">
<span className="text-metac-blue-800 dark:text-metac-blue-800-dark text-base font-medium">
<span className="text-base font-medium text-blue-800 dark:text-blue-800-dark">
{t("timePeriod")}
</span>
<Listbox
Expand All @@ -128,6 +128,20 @@ const LeaderboardHeader: FC<Props> = ({ filters }) => {
</div>
)}
</div>
{Number(year) + Number(duration) > 2024 && (
<div className="max-w-3xl px-5 py-2 text-center text-xs font-normal text-gray-700 dark:text-gray-700-dark sm:py-0">
{t("liveLeaderboardDisclaimer")}
</div>
)}
{category === "peer" && Number(year) + Number(duration) < 2024 && (
<div className="max-w-3xl px-5 py-2 text-center text-xs font-normal text-gray-700 dark:text-gray-700-dark sm:py-0">
{t.rich("legacyPeerDisclaimer", {
link: (chunks) => (
<Link href="/help/medals-faq/#peer-medals">{chunks}</Link>
),
})}
</div>
)}
</section>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const LeaderboardTable: FC<Props> = ({
year={year}
duration={duration}
category={category}
scoreType={leaderboardDetails.score_type}
/>
)}

Expand All @@ -77,13 +78,19 @@ const LeaderboardTable: FC<Props> = ({
<th className="hidden w-24 px-4 py-2.5 text-right @md:!table-cell">
{category === "comments" ? t("comments") : t("questions")}
</th>
{leaderboardDetails.score_type === "peer_global" && (
<th className="w-24 px-4 py-2.5 text-right">
{t("totalCoverage")}
</th>
)}
<th className="w-20 px-4 py-2.5 text-right">{t("score")}</th>
</tr>
{!!entriesToDisplay.length ? (
entriesToDisplay.map((entry) => (
<LeaderboardRow
key={`ranking-row-${category}-${entry.user ? entry.user.id : entry.aggregation_method!}`}
rowEntry={entry}
scoreType={leaderboardDetails.score_type}
href={
entry.user
? `/accounts/profile/${entry.user.id}?mode=medals`
Expand Down Expand Up @@ -125,6 +132,7 @@ const LeaderboardTable: FC<Props> = ({
year={year}
duration={duration}
category={category}
scoreType={leaderboardDetails.score_type}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import Link from "next/link";
import { FC } from "react";

import { Href } from "@/types/navigation";
import { CategoryKey, LeaderboardEntry } from "@/types/scoring";
import {
CategoryKey,
LeaderboardEntry,
LeaderboardType,
} from "@/types/scoring";
import { abbreviatedNumber } from "@/utils/number_formatters";

import MedalIcon from "../../../components/medal_icon";
Expand All @@ -16,13 +20,26 @@ import {

type Props = {
rowEntry: LeaderboardEntry;
scoreType: LeaderboardType;
href: Href;
isUserRow?: boolean;
};

const LeaderboardRow: FC<Props> = ({ rowEntry, href, isUserRow = false }) => {
const { user, aggregation_method, rank, contribution_count, score, medal } =
rowEntry;
const LeaderboardRow: FC<Props> = ({
rowEntry,
scoreType,
href,
isUserRow = false,
}) => {
const {
user,
aggregation_method,
rank,
coverage,
contribution_count,
score,
medal,
} = rowEntry;

return (
<tr
Expand Down Expand Up @@ -72,6 +89,16 @@ const LeaderboardRow: FC<Props> = ({ rowEntry, href, isUserRow = false }) => {
{abbreviatedNumber(contribution_count, 3, 0)}
</Link>
</td>
{scoreType == "peer_global" && (
<td className="hidden w-24 p-0 font-mono text-base leading-4 @md:!table-cell">
<Link
href={href}
className="flex items-center justify-end px-4 py-2.5 text-sm no-underline"
>
{abbreviatedNumber(coverage, 3, 0)}
</Link>
</td>
)}
<td
className={classNames(
"w-20 p-0 font-mono text-base leading-4",
Expand All @@ -94,12 +121,14 @@ type UserLeaderboardRowProps = {
category: CategoryKey;
year: string;
duration: string;
scoreType: LeaderboardType;
};
export const UserLeaderboardRow: FC<UserLeaderboardRowProps> = ({
userEntry,
year,
duration,
category,
scoreType,
}) => {
// only show this row for users who are logged in and have any ranking data
// in this category
Expand All @@ -109,7 +138,14 @@ export const UserLeaderboardRow: FC<UserLeaderboardRowProps> = ({
? "/medals"
: `/contributions/?${SCORING_CATEGORY_FILTER}=${category}&${CONTRIBUTIONS_USER_FILTER}=${userEntry.user!.id}&${SCORING_YEAR_FILTER}=${year}&${SCORING_DURATION_FILTER}=${duration}`;

return <LeaderboardRow rowEntry={userEntry} href={userHref} isUserRow />;
return (
<LeaderboardRow
rowEntry={userEntry}
scoreType={scoreType}
href={userHref}
isUserRow
/>
);
};

export default LeaderboardRow;
14 changes: 7 additions & 7 deletions front_end/src/app/(main)/(leaderboards)/ranking_categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ export const RANKING_CATEGORIES: Record<
explanation: (
<span>
<strong>Peer Accuracy</strong> measures how accurate a user was compared
to others. Users are ranked by the average of their{" "}
<a href="/help/scores-faq/#peer-score">Peer scores</a>. If a user
forecast
{" N<40"} questions, then their average score includes (40-N) questions
with a 0 score. This lowers the chance of a user getting lucky on a few
questions and winning a medal. Learn more{" "}
<a href="/help/medals-faq/#peer-medals">here</a>.
to others. Users are ranked by the sum of their{" "}
<a href="/help/scores-faq/#peer-score">Peer scores</a>, divided by the
sum of their <a href="/help/scores-faq/#coverage">Coverages</a>. This
creates a weighted average, where each prediction is counted
proportionally to how long it was standing. To reduce the impact of
luck, all forecasters start with a prior of 30 questions with a score of
0. Learn more <a href="/help/medals-faq/#peer-medals">here</a>.
</span>
),
},
Expand Down