-
Notifications
You must be signed in to change notification settings - Fork 25
Feat/mc options change #4038
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
base: main
Are you sure you want to change the base?
Feat/mc options change #4038
Conversation
add options_history field to question model and migration add options_history to question serialization add options_history initialization to question creation add helper functions to question/services/multiple_choice_handlers.py and add 'automatic' to forecast.source selection fix build_question_forecasts import and remove options & options_history from admin panel edit tests for question creation, multiple_choice_rename_option, multiple_choice_options_reorder, multiple_choice_delete_options, multiple_choice_add_options mark some expected failures add options_history to openapi docs add csv reporting support for options_history update logic to play well with back/forward filling 0s add all_options_ever to serializer and api docs add current options to csv return add support for datetime isoformat in options_history
fix file restructure fix datetime iso format in history conflicts add support for None values in MC predictions fix tests and source logic fix test_comment_edit_include_forecast to explicitly set forecast times mark xfail tests
mc/3806/aggregations adjust aggregations to play nicely with placeholders improve test for comput_weighted_semi_standard_deviations add support for None s in prediction difference for sorting plus tests update prediction difference for display to handle placeholders reactivate skipped tests
mc/3801/scoring add OptionsHistoryType, multiple_choice_interpret_forecast, and test update test for change to function update string_location_to_scaled_location to accept all historical option values, and related test multiple choice forecasts require interpretation before scoring remove double written definition support the new MC format scoring tests for mc with placeholder values add support for None values fix some linting errors left from previous commit
mc/3802/backend/notifications add notification logic add mjml/html and update tasks to setup for notifications add withdrawal notifications and bulletin deactivation fix grace period end bug
mc/3804/backend/updating add admin form for changing options add comment author and text to admin panel action and mc change methods
mc/3805/frontend/graphing forecast only current option values aggregation explorer disclaimer to forecast during grace period add option reordering (should be in mc/3804)
mc/3805/ui Added tooltip and highlight for newly added MC options Updated highlight method and message copy grace period timer translation strings Co-authored-by: aseckin <atakanseckin@gmail.com>
| const pluralize = (count: number, singular: string) => | ||
| count === 1 ? singular : `${singular}s`; | ||
|
|
||
| if (days > 0) { | ||
| setTimeRemaining( | ||
| `${days} ${pluralize(days, "day")}, ${hours} ${pluralize(hours, "hour")}` | ||
| ); | ||
| } else if (hours > 0) { | ||
| setTimeRemaining( | ||
| `${hours} ${pluralize(hours, "hour")}, ${minutes} ${pluralize(minutes, "minute")}` | ||
| ); | ||
| } else if (minutes > 0) { | ||
| setTimeRemaining( | ||
| `${minutes} ${pluralize(minutes, "minute")}, ${seconds} ${pluralize(seconds, "second")}` | ||
| ); | ||
| } else { | ||
| setTimeRemaining(`${seconds} ${pluralize(seconds, "second")}`); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your pluralize adds "s" in English. That will be wrong for basically every other locale. You can check how to do it properly in tournaments_hero.tsx file for its translation keys.
| const getNewOptions = useCallback(() => { | ||
| if (!activeUserForecast) return []; | ||
|
|
||
| return choicesForecasts | ||
| .filter((choice, index) => { | ||
| const isCurrentOption = question.options.includes(choice.name); | ||
| const hasForecast = activeUserForecast.forecast_values[index] !== null; | ||
| return isCurrentOption && !hasForecast; | ||
| }) | ||
| .map((c) => ({ name: c.name, color: c.color })); | ||
| }, [activeUserForecast, choicesForecasts, question.options]); | ||
|
|
||
| const newOptions = getNewOptions(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newOptions computed via callback invoked at render. It defeats most of the value of useCallback and makes it easier to accidentally recompute more than needed. Make newOptions a useMemo
| if (diff <= 0) { | ||
| setTimeRemaining("expired"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's better to stop ticking immediately when expired and do not wait until unmount.
| const isFirstNewOption = | ||
| newOptions.length > 0 && choice.name === newOptions[0]?.name; | ||
| const isNewOption = newOptions.some( | ||
| (opt) => opt.name === choice.name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can compute inside a component
const newOptionNames = useMemo(() => new Set(newOptions.map(o => o.name)), [newOptions]);
const firstNewOptionName = newOptions[0]?.name;and then you'll have
const isNewOption = newOptionNames.has(choice.name);
const isFirstNewOption = choice.name === firstNewOptionName;I believe it's better from the performance standpoint.
| const isCurrentOption = question.options.includes(choice.name); | ||
| const hasForecast = activeUserForecast.forecast_values[index] !== null; | ||
| return isCurrentOption && !hasForecast; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that your
const hasForecast = activeUserForecast.forecast_values[index] !== null;assumes: the index of an item in choicesForecasts === the index of the same option in activeUserForecast.forecast_values, but choicesForecasts is built by generateChoiceOptions(), and that function reorders the array:
const resolutionIndex = allOptions.findIndex((_, index) => allOptions[index] === question.resolution);
...
choiceItems.unshift(resolutionItem);So even if choiceItems originally lined up by index with forecast_values, once you move the resolution option to the front, indices no longer match. I believe it can be fragile, correct me if I'm wrong.
I guess it's better to build a map from the canonical ordering that forecast_values uses and look up index by option name, not by render position.
|
|
||
| /** | ||
| * Hook to display remaining time until grace period ends | ||
| * Updates every second for a live countdown | ||
| */ | ||
| const useGracePeriodCountdown = (gracePeriodEnd: Date | null) => { | ||
| const [timeRemaining, setTimeRemaining] = useState<string>(""); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it would be better to separate hook from the component implementation for easier navigation
closes #3799
This is the final branch for the MC options changing project
This took all of the separate branches and molded them into one
Each of the individual branches are commits on this PR and have individually been QA'd
This branch addresses the final QA steps in preparation for merging into main
commits after "MC 3805 UI" are added after this branch existed and is bug fixing and tweaks so do not need to be looked at separately
TODO: