From 3478894fa77103e1c09dd8192981a43e825872d6 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 4 Sep 2024 18:17:17 +0300 Subject: [PATCH 1/2] feat(graphs): add user forecast for multiple choice question graphs --- .../multiple_choice_chart_card.tsx | 34 +++++++++++++++++-- .../post_card/question_chart_tile/index.tsx | 4 +++ front_end/src/utils/questions.ts | 28 +++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/front_end/src/app/(main)/questions/[id]/components/detailed_question_card/multiple_choice_chart_card.tsx b/front_end/src/app/(main)/questions/[id]/components/detailed_question_card/multiple_choice_chart_card.tsx index 5ed279a2f8..a24d5fb359 100644 --- a/front_end/src/app/(main)/questions/[id]/components/detailed_question_card/multiple_choice_chart_card.tsx +++ b/front_end/src/app/(main)/questions/[id]/components/detailed_question_card/multiple_choice_chart_card.tsx @@ -16,6 +16,7 @@ import { generateChoiceItemsFromMultipleChoiceForecast } from "@/utils/charts"; import { getForecastPctDisplayValue } from "@/utils/forecasts"; import ChoicesTooltip from "../choices_tooltip"; +import { generateUserForecastsForMultipleQuestion } from "@/utils/questions"; const MAX_VISIBLE_CHECKBOXES = 6; @@ -47,7 +48,7 @@ const MultipleChoiceChartCard: FC = ({ const [choiceItems, setChoiceItems] = useState( generateList(question) ); - + const userForecasts = generateUserForecastsForMultipleQuestion(question); const timestampsCount = timestamps.length; const prevTimestampsCount = usePrevious(timestampsCount); // sync BE driven data with local state @@ -77,6 +78,30 @@ const MultipleChoiceChartCard: FC = ({ [choiceItems, cursorIndex] ); + const tooltipUserChoices = useMemo(() => { + if (!userForecasts) { + return []; + } + + return userForecasts.map( + ({ choice, values, color, timestamps: optionTimestamps }) => { + const timestampIndex = optionTimestamps?.findLastIndex( + (timestamp) => timestamp <= cursorTimestamp + ); + + return { + choiceLabel: choice, + color, + valueLabel: getForecastPctDisplayValue( + timestampIndex === -1 || timestampIndex === undefined + ? null + : values?.[timestampIndex] + ), + }; + } + ); + }, [userForecasts, cursorTimestamp]); + const { isActive: isTooltipActive, getReferenceProps, @@ -150,6 +175,7 @@ const MultipleChoiceChartCard: FC = ({ ? new Date(question.actual_close_time).getTime() < Date.now() : false } + userForecasts={userForecasts} /> @@ -170,7 +196,11 @@ const MultipleChoiceChartCard: FC = ({ style={floatingStyles} {...getFloatingProps()} > - + )} diff --git a/front_end/src/components/post_card/question_chart_tile/index.tsx b/front_end/src/components/post_card/question_chart_tile/index.tsx index 0bb4273174..f0a8fc52b1 100644 --- a/front_end/src/components/post_card/question_chart_tile/index.tsx +++ b/front_end/src/components/post_card/question_chart_tile/index.tsx @@ -11,6 +11,7 @@ import { generateChoiceItemsFromMultipleChoiceForecast } from "@/utils/charts"; import QuestionNumericTile from "./question_numeric_tile"; import { useTranslations } from "next-intl"; +import { generateUserForecastsForMultipleQuestion } from "@/utils/questions"; type Props = { question: QuestionWithForecasts; @@ -62,6 +63,8 @@ const QuestionChartTile: FC = ({ const choices = generateChoiceItemsFromMultipleChoiceForecast(question, { activeCount: visibleChoicesCount, }); + const userForecasts = generateUserForecastsForMultipleQuestion(question); + return ( = ({ visibleChoicesCount={visibleChoicesCount} defaultChartZoom={defaultChartZoom} question={question} + userForecasts={userForecasts} /> ); } diff --git a/front_end/src/utils/questions.ts b/front_end/src/utils/questions.ts index 11e7cc9a2e..a58145426a 100644 --- a/front_end/src/utils/questions.ts +++ b/front_end/src/utils/questions.ts @@ -15,6 +15,7 @@ import { MultipleChoiceForecast, Question, QuestionType, + QuestionWithMultipleChoiceForecasts, QuestionWithNumericForecasts, } from "@/types/question"; import { abbreviatedNumber } from "@/utils/number_formatters"; @@ -209,6 +210,33 @@ export function getPredictionQuestion( ); } +export const generateUserForecastsForMultipleQuestion = ( + question: QuestionWithMultipleChoiceForecasts +): UserChoiceItem[] | undefined => { + const latest = question.aggregations.recency_weighted.latest; + const options = question.options!; + + const choiceOrdering: number[] = options.map((_, i) => i); + choiceOrdering.sort((a, b) => { + const aCenter = latest?.forecast_values[a] ?? 0; + const bCenter = latest?.forecast_values[b] ?? 0; + return bCenter - aCenter; + }); + + return options.map((choice, index) => { + const userForecasts = question.my_forecasts?.history; + return { + choice, + values: + userForecasts?.map((forecast) => forecast.forecast_values[index]) ?? [], + timestamps: userForecasts?.map((forecast) => forecast.start_time) ?? [], + color: + MULTIPLE_CHOICE_COLOR_SCALE[choiceOrdering[index]] ?? + METAC_COLORS.gray["400"], + }; + }); +}; + export const generateUserForecasts = ( questions: QuestionWithNumericForecasts[] ): UserChoiceItem[] => { From 9b693d9edd04fb52d15ab4603ff5702a59dca876 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 4 Sep 2024 18:17:21 +0300 Subject: [PATCH 2/2] fix(graphs): make color of my forecast for group questions the same as line on feed page --- .../src/components/post_card/group_of_questions_tile/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front_end/src/components/post_card/group_of_questions_tile/index.tsx b/front_end/src/components/post_card/group_of_questions_tile/index.tsx index 598d75bc19..300c17886c 100644 --- a/front_end/src/components/post_card/group_of_questions_tile/index.tsx +++ b/front_end/src/components/post_card/group_of_questions_tile/index.tsx @@ -56,7 +56,7 @@ const GroupOfQuestionsTile: FC = ({ questions, curationStatus }) => { userForecasts={ user ? generateUserForecasts( - questions as QuestionWithNumericForecasts[] + sortedQuestions as QuestionWithNumericForecasts[] ) : undefined }