Skip to content
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

complicated formatting for continuous user prediction in comment #2229

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
124 changes: 109 additions & 15 deletions front_end/src/components/comment_feed/included_forecast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,117 @@ const ForecastValue: FC<ForecastValueProps> = ({ forecast }) => {
</ol>
);
}
if (forecast.question_type == "date") {
return (
<div
className="order-1 grow-0 text-xl font-bold text-gray-900 dark:text-gray-900-dark"
suppressHydrationWarning
>
{`${formatDate(locale, new Date(forecast.quartiles[1] * 1000))} (${formatDate(locale, new Date(forecast.quartiles[0] * 1000))} - ${formatDate(locale, new Date(forecast.quartiles[2] * 1000))})`}
</div>
);

// continuous questions get customized formatting
if (forecast.quartiles.length !== 3) return null;
const { range_min, range_max } = forecast.scaling;
if (!range_min || !range_max) return null;

const q1 =
forecast.quartiles[0] <= range_min
? "below"
: forecast.quartiles[0] >= range_max
? "above"
: "inRange";
const q2 =
forecast.quartiles[1] <= range_min
? "below"
: forecast.quartiles[1] >= range_max
? "above"
: "inRange";
const q3 =
forecast.quartiles[2] <= range_min
? "below"
: forecast.quartiles[2] >= range_max
? "above"
: "inRange";

const probBelow =
Math.round((forecast.continuous_cdf.at(0) || 0) * 1000) / 10;
const probAbove =
Math.round((1 - (forecast.continuous_cdf.at(-1) || 0)) * 1000) / 10;
const valueText: string[] =
forecast.question_type === "numeric"
? [
abbreviatedNumber(range_min),
abbreviatedNumber(forecast.quartiles[0]),
abbreviatedNumber(forecast.quartiles[1]),
abbreviatedNumber(forecast.quartiles[2]),
abbreviatedNumber(range_max),
]
: [
formatDate(locale, new Date(range_min * 1000)),
formatDate(locale, new Date(forecast.quartiles[0] * 1000)),
formatDate(locale, new Date(forecast.quartiles[1] * 1000)),
formatDate(locale, new Date(forecast.quartiles[2] * 1000)),
formatDate(locale, new Date(range_max * 1000)),
];
let text: string = "";
if (q1 === "below" && q2 === "below" && q3 === "below") {
text =
probBelow +
"% " +
(forecast.question_type === "numeric" ? "below " : "before ") +
valueText[0];
}
if (forecast.question_type == "numeric") {
return (
<div className="order-1 grow-0 text-xl font-bold text-gray-900 dark:text-gray-900-dark">
{`${abbreviatedNumber(forecast.quartiles[1])} (${abbreviatedNumber(forecast.quartiles[0])} - ${abbreviatedNumber(forecast.quartiles[2])})`}
</div>
);
if (q1 === "below" && q2 === "below" && q3 === "inRange") {
text =
probBelow +
"% " +
(forecast.question_type === "numeric" ? "below " : "before ") +
valueText[0] +
" (upper 75%=" +
valueText[3] +
")";
}
if (q1 === "below" && q2 === "inRange" && q3 === "inRange") {
text =
valueText[2] +
" (" +
probBelow +
"% " +
(forecast.question_type === "numeric" ? "below " : "before ") +
valueText[0] +
")";
}
if (q1 === "inRange" && q2 === "inRange" && q3 === "inRange") {
text = valueText[2] + " (" + valueText[1] + " - " + valueText[3] + ")";
}
if (q1 === "inRange" && q2 === "inRange" && q3 === "above") {
text =
valueText[2] +
" (" +
probAbove +
"% " +
(forecast.question_type === "numeric" ? "above " : "after ") +
valueText[4] +
")";
}
if (q1 === "inRange" && q2 === "above" && q3 === "above") {
text =
probAbove +
"% " +
(forecast.question_type === "numeric" ? "above " : "after ") +
valueText[4] +
" (lower 25%=" +
valueText[1] +
")";
}
if (q1 === "above" && q2 === "above" && q3 === "above") {
text =
probAbove +
"% " +
(forecast.question_type === "numeric" ? "above " : "after ") +
valueText[4];
}
if (q1 === "below" && q2 === "inRange" && q3 === "above") {
text = valueText[2] + " (" + valueText[1] + " - " + valueText[3] + ")";
}
return (
<div className="order-1 grow-0 text-xl font-bold text-gray-900 dark:text-gray-900-dark">
{`${text}`}
</div>
);
};

const IncludedForecast: FC<Props> = ({ author, forecast }) => {
Expand Down
3 changes: 2 additions & 1 deletion front_end/src/types/comment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ProjectPermissions } from "@/types/post";
import { QuestionType } from "@/types/question";
import { QuestionType, Scaling } from "@/types/question";
import { VoteDirection } from "@/types/votes";

export type AuthorType = {
Expand Down Expand Up @@ -48,6 +48,7 @@ export type ForecastType = {
options: string[];
continuous_cdf: number[];
quartiles: [number, number, number];
scaling: Scaling;
question_type: QuestionType;
};

Expand Down
16 changes: 10 additions & 6 deletions questions/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,7 @@ class Meta(GroupOfQuestionsWriteSerializer.Meta):

class ForecastSerializer(serializers.ModelSerializer):
quartiles = serializers.SerializerMethodField()
range_min = serializers.FloatField(source="question.range_min")
range_max = serializers.FloatField(source="question.range_max")
zero_point = serializers.FloatField(source="question.zero_point")
scaling = serializers.SerializerMethodField()
options = serializers.ListField(
child=serializers.CharField(), source="question.options"
)
Expand All @@ -246,9 +244,7 @@ class Meta:
"probability_yes_per_category",
"continuous_cdf",
"quartiles",
"range_min",
"range_max",
"zero_point",
"scaling",
"options",
"question_type",
)
Expand All @@ -258,6 +254,14 @@ def get_quartiles(self, forecast: Forecast):
if question.type in [Question.QuestionType.DATE, Question.QuestionType.NUMERIC]:
return get_scaled_quartiles_from_cdf(forecast.continuous_cdf, question)

def get_scaling(self, forecast: Forecast):
question = forecast.question
return {
"range_max": question.range_max,
"range_min": question.range_min,
"zero_point": question.zero_point,
}


class MyForecastSerializer(serializers.ModelSerializer):
start_time = serializers.SerializerMethodField()
Expand Down