diff --git a/front_end/messages/en.json b/front_end/messages/en.json index 8330cf2b25..2de8873d0c 100644 --- a/front_end/messages/en.json +++ b/front_end/messages/en.json @@ -620,6 +620,10 @@ "parentBackgroundInfo": "Parent Question Background Info", "childBackgroundInfo": "Child Question Background Info", "histogram": "Histogram", + "frequency": "Frequency", + "brierScoreForPlayer":"Brier scores for player predictions", + "scoreHistogram": "Score Histogram", + "scoreScatterPlot": "Score Scatter Plot", "expand": "Expand", "collapse": "Collapse", "resolutionCriteria": "Resolution Criteria", diff --git a/front_end/src/app/(main)/accounts/profile/components/track_record.tsx b/front_end/src/app/(main)/accounts/profile/components/track_record.tsx index 221eec1e64..f4e463bf70 100644 --- a/front_end/src/app/(main)/accounts/profile/components/track_record.tsx +++ b/front_end/src/app/(main)/accounts/profile/components/track_record.tsx @@ -1,10 +1,10 @@ "use client"; -import { format } from "date-fns"; import { useTranslations } from "next-intl"; import { FC } from "react"; import CalibrationChart from "@/app/(main)/charts/calibration_chart"; +import UserHistogram from "@/app/(main)/charts/user_histogram"; import { UserProfile } from "@/types/users"; const TrackRecord: FC<{ profile: UserProfile }> = ({ profile }) => { @@ -15,6 +15,16 @@ const TrackRecord: FC<{ profile: UserProfile }> = ({ profile }) => { return (
+

+ {t("scoreHistogram")} +

+ {profile.score_histogram && ( + + )} +

Calibration Curve

diff --git a/front_end/src/app/(main)/charts/calibration_chart.tsx b/front_end/src/app/(main)/charts/calibration_chart.tsx index 7d6096a638..fff596fb85 100644 --- a/front_end/src/app/(main)/charts/calibration_chart.tsx +++ b/front_end/src/app/(main)/charts/calibration_chart.tsx @@ -23,7 +23,6 @@ const CalibrationChart: React.FC<{ data: any; showIntervals?: boolean }> = ({ const { theme, getThemeColor } = useAppTheme(); const chartTheme = theme === "dark" ? darkTheme : lightTheme; const actualTheme = merge({}, chartTheme); - console.log(actualTheme.axis?.style?.axis?.stroke); return (
diff --git a/front_end/src/app/(main)/charts/user_histogram.tsx b/front_end/src/app/(main)/charts/user_histogram.tsx new file mode 100644 index 0000000000..efd94c4677 --- /dev/null +++ b/front_end/src/app/(main)/charts/user_histogram.tsx @@ -0,0 +1,155 @@ +"use client"; + +import { range } from "lodash"; +import { useTranslations } from "next-intl"; +import React from "react"; +import { + VictoryAxis, + VictoryChart, + VictoryContainer, + VictoryArea, +} from "victory"; + +import { darkTheme, lightTheme } from "@/constants/chart_theme"; +import useAppTheme from "@/hooks/use_app_theme"; + +type HistogramProps = { + rawHistogramData: { + bin_start: number; + bin_end: number; + pct_scores: number; + }[]; + color: string; +}; + +const UserHistogram: React.FC = ({ + rawHistogramData, + color, +}) => { + const histogramData = mapHistogramData(rawHistogramData); + const t = useTranslations(); + const { theme } = useAppTheme(); + const chartTheme = theme === "dark" ? darkTheme : lightTheme; + + const { ticks: ticksY, tickFormat: ticksYFormat } = generateTicksY(180); + return ( +
+ } + padding={{ top: 20, bottom: 65, left: 40, right: 20 }} + height={180} + > + + + + +
+ ); +}; + +const mapHistogramData = ( + userHistogram: { + bin_start: number; + bin_end: number; + pct_scores: number; + }[] +) => { + const mappedArray = [] as { x: number; y: number }[]; + userHistogram.forEach((data, index) => { + mappedArray.push( + ...[ + { x: data.bin_start, y: Math.max(data.pct_scores, 0) }, + { x: data.bin_end - 1, y: Math.max(data.pct_scores, 0) }, + ] + ); + }); + return mappedArray; +}; + +function generateTicksY(height: number) { + const desiredMajorTicks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]; + const minorTicksPerMajor = 9; + const desiredMajorTickDistance = 20; + let majorTicks = desiredMajorTicks; + const maxMajorTicks = Math.floor(height / desiredMajorTickDistance); + + if (maxMajorTicks < desiredMajorTicks.length) { + const step = 1 / (maxMajorTicks - 1); + majorTicks = Array.from({ length: maxMajorTicks }, (_, i) => i * step); + } + const ticks = []; + for (let i = 0; i < majorTicks.length - 1; i++) { + ticks.push(majorTicks[i]); + const step = (majorTicks[i + 1] - majorTicks[i]) / (minorTicksPerMajor + 1); + for (let j = 1; j <= minorTicksPerMajor; j++) { + ticks.push(majorTicks[i] + step * j); + } + } + ticks.push(majorTicks[majorTicks.length - 1]); + const tickFormat = (value: number): string => { + if (!majorTicks.includes(value)) { + return ""; + } + return value.toString(); + }; + return { ticks, tickFormat }; +} + +export default UserHistogram; diff --git a/front_end/src/types/users.ts b/front_end/src/types/users.ts index ab176bd781..a651332822 100644 --- a/front_end/src/types/users.ts +++ b/front_end/src/types/users.ts @@ -29,7 +29,12 @@ export type User = { export type UserProfile = User & { calibration_curve?: any; - score_histogram?: any; + score_histogram?: { + bin_start: number; + bin_end: number; + pct_scores: number; + }[]; + score_scatter_plot?: { score: number; score_timestamp: number }[]; nr_forecasts?: number; nr_comments?: number; avg_score?: number;