2023-10-15 19:11:08 +02:00
|
|
|
import Component from '@glimmer/component';
|
2020-01-18 10:13:50 +01:00
|
|
|
import { inject as service } from '@ember/service';
|
2023-10-29 19:16:33 +01:00
|
|
|
import type IntlService from 'ember-intl/services/intl';
|
|
|
|
import type Option from 'croodle/models/option';
|
|
|
|
import type User from 'croodle/models/user';
|
|
|
|
import type { Answer } from 'croodle/utils/answers-for-answer-type';
|
|
|
|
import type Poll from 'croodle/models/poll';
|
|
|
|
|
|
|
|
export interface PollEvaluationSummarySignature {
|
|
|
|
Args: {
|
|
|
|
poll: Poll;
|
2023-11-04 14:54:30 +01:00
|
|
|
timeZone: string | undefined;
|
2023-10-29 19:16:33 +01:00
|
|
|
};
|
|
|
|
}
|
2019-04-20 23:29:59 +02:00
|
|
|
|
2023-11-04 14:54:30 +01:00
|
|
|
export interface BestOption {
|
|
|
|
answers: Record<'yes' | 'no' | 'maybe', number>;
|
|
|
|
option: Option;
|
|
|
|
score: number;
|
|
|
|
}
|
|
|
|
|
2023-10-29 19:16:33 +01:00
|
|
|
export default class PollEvaluationSummary extends Component<PollEvaluationSummarySignature> {
|
|
|
|
@service declare intl: IntlService;
|
2015-11-03 12:57:41 +01:00
|
|
|
|
2023-11-04 14:54:30 +01:00
|
|
|
get bestOptions(): BestOption[] | null {
|
2023-10-15 19:11:08 +02:00
|
|
|
const { poll } = this.args;
|
2023-10-15 17:32:11 +02:00
|
|
|
const { isFreeText, options, users } = poll;
|
|
|
|
|
2015-11-03 12:57:41 +01:00
|
|
|
// can not evaluate answer type free text
|
2023-10-15 17:32:11 +02:00
|
|
|
if (isFreeText) {
|
2023-11-04 14:54:30 +01:00
|
|
|
return null;
|
2015-11-03 12:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// can not evaluate a poll without users
|
2023-10-15 17:32:11 +02:00
|
|
|
if (users.length < 1) {
|
2023-11-04 14:54:30 +01:00
|
|
|
return null;
|
2015-11-03 12:57:41 +01:00
|
|
|
}
|
|
|
|
|
2023-10-29 19:16:33 +01:00
|
|
|
const answers = poll.answers.reduce(
|
2023-11-04 14:54:30 +01:00
|
|
|
(answers, answer: Answer) => {
|
2023-10-29 19:16:33 +01:00
|
|
|
answers[answer.type] = 0;
|
|
|
|
return answers;
|
|
|
|
},
|
2023-11-04 14:54:30 +01:00
|
|
|
{} as Record<'yes' | 'no' | 'maybe', number>,
|
2023-10-29 19:16:33 +01:00
|
|
|
);
|
2023-11-04 14:54:30 +01:00
|
|
|
const evaluation: BestOption[] = options.map((option: Option) => {
|
2016-06-20 20:48:48 +02:00
|
|
|
return {
|
2023-10-16 14:26:00 +02:00
|
|
|
answers: { ...answers },
|
2016-06-20 20:48:48 +02:00
|
|
|
option,
|
2023-10-15 20:37:03 +02:00
|
|
|
score: 0,
|
2016-06-20 20:48:48 +02:00
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2023-10-29 19:16:33 +01:00
|
|
|
users.forEach((user: User) => {
|
2019-04-20 23:29:59 +02:00
|
|
|
user.selections.forEach(({ type }, i) => {
|
2023-11-04 14:54:30 +01:00
|
|
|
if (!type) {
|
|
|
|
// type may be undefined if poll does not force an answer to all options
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const evaluationForOption = evaluation[i];
|
|
|
|
if (evaluationForOption === undefined) {
|
|
|
|
throw new Error(
|
|
|
|
'Mismatch between number of options in poll and selections for user',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (type !== 'yes' && type !== 'no' && type !== 'maybe') {
|
|
|
|
throw new Error(
|
|
|
|
`Encountered not supported type of user selection: ${type}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
evaluationForOption.answers[type]++;
|
2015-11-03 12:57:41 +01:00
|
|
|
|
2019-04-20 23:29:59 +02:00
|
|
|
switch (type) {
|
2015-11-03 12:57:41 +01:00
|
|
|
case 'yes':
|
2023-10-29 19:16:33 +01:00
|
|
|
evaluation[i]!.score += 2;
|
2015-11-03 12:57:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'maybe':
|
2023-10-29 19:16:33 +01:00
|
|
|
evaluation[i]!.score += 1;
|
2015-11-03 12:57:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'no':
|
2023-10-29 19:16:33 +01:00
|
|
|
evaluation[i]!.score -= 2;
|
2015-11-03 12:57:41 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-04-20 23:29:59 +02:00
|
|
|
evaluation.sort((a, b) => b.score - a.score);
|
2015-11-03 12:57:41 +01:00
|
|
|
|
2023-11-04 14:54:30 +01:00
|
|
|
const bestOptions = [];
|
2023-10-29 19:16:33 +01:00
|
|
|
const bestScore = evaluation[0]!.score;
|
2023-11-04 14:54:30 +01:00
|
|
|
for (const evaluationForOption of evaluation) {
|
|
|
|
if (evaluationForOption.score === bestScore) {
|
|
|
|
bestOptions.push(evaluationForOption);
|
2016-01-20 03:19:10 +01:00
|
|
|
} else {
|
2015-11-03 12:57:41 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bestOptions;
|
2020-01-18 10:13:50 +01:00
|
|
|
}
|
|
|
|
|
2023-09-21 12:30:14 +02:00
|
|
|
get lastParticipationAt() {
|
2023-10-15 19:11:08 +02:00
|
|
|
const { users } = this.args.poll;
|
2023-10-15 17:32:11 +02:00
|
|
|
|
2023-09-21 12:30:14 +02:00
|
|
|
let lastParticipationAt = null;
|
2019-04-20 23:29:59 +02:00
|
|
|
|
2023-10-28 19:15:06 +02:00
|
|
|
for (const { creationDate } of users) {
|
|
|
|
if (lastParticipationAt === null || creationDate >= lastParticipationAt) {
|
2023-09-21 12:30:14 +02:00
|
|
|
lastParticipationAt = creationDate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lastParticipationAt;
|
|
|
|
}
|
2020-01-18 10:13:50 +01:00
|
|
|
}
|
2023-11-04 14:54:30 +01:00
|
|
|
|
|
|
|
declare module '@glint/environment-ember-loose/registry' {
|
|
|
|
export default interface Registry {
|
|
|
|
PollEvaluationSummary: typeof PollEvaluationSummary;
|
|
|
|
}
|
|
|
|
}
|