fix: fix bugs and structure
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
import { ConflictException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { AnswerSubmissionDTO } from '../../dtos/answer-submission.dto';
|
||||
import { User } from '@/modules/users/entities/user.entity';
|
||||
import { Submission } from '../../entities/submission.entity';
|
||||
import { AppException } from '@/common/exceptions/app.exception';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
import { SubmissionResult } from '../../entities/submission-result.entity';
|
||||
import { AppResponse } from '@/common/responses';
|
||||
import { checkAnswerCorrectness } from '../../helpers/check-answer-correctness.helper';
|
||||
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
||||
import { getNextTransition } from '../../helpers/get-next-transition.helper';
|
||||
import { DataSource, QueryFailedError, Repository } from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { SubmissionAnswer } from '../../entities/submission-answer.entity';
|
||||
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
||||
import { SubmissionStatus } from '../../enums/submission-status.enum';
|
||||
import { CalculateResultProvider } from './calculate-result.provider';
|
||||
import { UNIQUE_SUBMISSION_QUESTION_IDX } from '../../constants';
|
||||
|
||||
@Injectable()
|
||||
export class AnswerAuthenticatedProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Submission Repository
|
||||
*/
|
||||
@InjectRepository(Submission)
|
||||
private readonly submissionRepository: Repository<Submission>,
|
||||
|
||||
/**
|
||||
* Inject SubmisionAnswer Repository
|
||||
*/
|
||||
@InjectRepository(SubmissionAnswer)
|
||||
private readonly submissionAnswerRepository: Repository<SubmissionAnswer>,
|
||||
|
||||
/**
|
||||
* Inject SubmissionResult Repository
|
||||
*/
|
||||
@InjectRepository(SubmissionResult)
|
||||
private readonly submissionResultRepository: Repository<SubmissionResult>,
|
||||
|
||||
/**
|
||||
* Inject DataSource
|
||||
*/
|
||||
private readonly dataSource: DataSource,
|
||||
|
||||
/**
|
||||
* Inject CalculateResult Provider
|
||||
*/
|
||||
private readonly calculateResultProvider: CalculateResultProvider,
|
||||
) {}
|
||||
|
||||
public async answer(
|
||||
id: string,
|
||||
answerSubmissionDto: AnswerSubmissionDTO,
|
||||
user: User,
|
||||
) {
|
||||
let submission: Submission | null = null;
|
||||
|
||||
const submissionId = Number(id);
|
||||
|
||||
if (Number.isNaN(submissionId)) {
|
||||
throw new AppException(
|
||||
'validation.wrongFieldFormat',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
{ field: '$t(submissions.fields.submissionId)' },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
submission = await this.submissionRepository.findOne({
|
||||
where: { id: submissionId, user: { id: user.id } },
|
||||
relations: {
|
||||
test: true,
|
||||
currentQuestion: {
|
||||
choices: true,
|
||||
outgoingTransitions: {
|
||||
toQuestion: {
|
||||
choices: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (!submission) {
|
||||
throw new AppException(
|
||||
'common.errors.entityNotFound',
|
||||
HttpStatus.NOT_FOUND,
|
||||
{ entity: '$t(submissions.fields.submission)' },
|
||||
);
|
||||
}
|
||||
|
||||
const currentQuestion = submission.currentQuestion;
|
||||
|
||||
if (!currentQuestion) {
|
||||
let result: SubmissionResult | null;
|
||||
|
||||
try {
|
||||
result = await this.submissionResultRepository.findOne({
|
||||
where: { submission: { id: submission.id } },
|
||||
relations: {
|
||||
resultRange: true,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
return new AppResponse({
|
||||
isCompleted: true,
|
||||
currentQuestion: null,
|
||||
result: result
|
||||
? {
|
||||
score: result.score,
|
||||
probability: result.probability,
|
||||
title: result.resultRange?.title,
|
||||
description: result.resultRange?.description,
|
||||
}
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
checkAnswerCorrectness(currentQuestion, answerSubmissionDto);
|
||||
|
||||
const submissionAnswer = this.submissionAnswerRepository.create({
|
||||
submission,
|
||||
question: currentQuestion,
|
||||
});
|
||||
|
||||
if (
|
||||
currentQuestion.type === QuestionType.MULTI_CHOICE &&
|
||||
answerSubmissionDto.choiceId !== undefined
|
||||
) {
|
||||
const choice = currentQuestion.choices.find(
|
||||
(el) => el.id === answerSubmissionDto.choiceId,
|
||||
);
|
||||
|
||||
if (!choice) {
|
||||
throw new AppException(
|
||||
'validation.wrongField',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
{ field: '$t(submissions.fields.choiceId)' },
|
||||
);
|
||||
}
|
||||
|
||||
submissionAnswer.choice = choice;
|
||||
} else if (
|
||||
currentQuestion.type === QuestionType.NUMERIC &&
|
||||
answerSubmissionDto.numericValue !== undefined
|
||||
) {
|
||||
submissionAnswer.numericValue = answerSubmissionDto.numericValue;
|
||||
}
|
||||
|
||||
const nextTransition = getNextTransition(
|
||||
currentQuestion,
|
||||
answerSubmissionDto,
|
||||
);
|
||||
|
||||
const nextQuestion = nextTransition.toQuestion ?? null;
|
||||
|
||||
let completedResponse: object | null = null;
|
||||
|
||||
try {
|
||||
completedResponse = await this.dataSource.transaction(async (manager) => {
|
||||
await manager.save(submissionAnswer);
|
||||
|
||||
if (
|
||||
nextTransition.type === QuestionTransitionType.END ||
|
||||
!nextQuestion
|
||||
) {
|
||||
await manager.update(
|
||||
Submission,
|
||||
{ id: submission.id },
|
||||
{ currentQuestion: null, status: SubmissionStatus.COMPLETED },
|
||||
);
|
||||
|
||||
const result =
|
||||
await this.calculateResultProvider.calculateAuthenticated(
|
||||
submission,
|
||||
manager,
|
||||
);
|
||||
|
||||
return {
|
||||
isCompleted: true,
|
||||
currentQuestion: null,
|
||||
result,
|
||||
};
|
||||
}
|
||||
|
||||
await manager.update(
|
||||
Submission,
|
||||
{ id: submission.id },
|
||||
{ currentQuestion: nextQuestion },
|
||||
);
|
||||
|
||||
return null;
|
||||
});
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof QueryFailedError &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
(err as any).constraint === UNIQUE_SUBMISSION_QUESTION_IDX
|
||||
) {
|
||||
throw new ConflictException(
|
||||
'submissions.errors.uniqueSubmissionQuestion',
|
||||
);
|
||||
}
|
||||
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (completedResponse) {
|
||||
return new AppResponse(completedResponse);
|
||||
}
|
||||
|
||||
return new AppResponse({
|
||||
isCompleted: false,
|
||||
// isFinal: ,
|
||||
currentQuestion: {
|
||||
type: nextQuestion?.type,
|
||||
content: nextQuestion?.content,
|
||||
required: nextQuestion?.required,
|
||||
metadata: nextQuestion?.metadata,
|
||||
choices: nextQuestion?.choices.map((el) => ({
|
||||
id: el.id,
|
||||
text: el.text,
|
||||
order: el.order,
|
||||
})),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import { HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { AnswerSubmissionDTO } from '../../dtos/answer-submission.dto';
|
||||
import { isUUID } from 'class-validator';
|
||||
import { AppException } from '@/common/exceptions/app.exception';
|
||||
import { GuestSubmissionSession } from '../../interfaces/guest-submission-session.interface';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
import { Question } from '@/modules/questions/entities/question.entity';
|
||||
import { AppResponse } from '@/common/responses';
|
||||
import { checkAnswerCorrectness } from '../../helpers/check-answer-correctness.helper';
|
||||
import { GuestSubmissionAnswer } from '../../interfaces/guest-submission-answer.interface';
|
||||
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
||||
import { getNextTransition } from '../../helpers/get-next-transition.helper';
|
||||
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { RedisService } from '@/common/modules/redis/providers/redis.service';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CalculateResultProvider } from './calculate-result.provider';
|
||||
|
||||
@Injectable()
|
||||
export class AnswerGuestProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Redis Service
|
||||
*/
|
||||
private readonly redisService: RedisService,
|
||||
|
||||
/**
|
||||
* Inject Questions Repository
|
||||
*/
|
||||
@InjectRepository(Question)
|
||||
private readonly questionsRepository: Repository<Question>,
|
||||
|
||||
/**
|
||||
* Inject CalculateResult Provider
|
||||
*/
|
||||
private readonly calculateResultProvider: CalculateResultProvider,
|
||||
) {}
|
||||
|
||||
public async answer(id: string, answerSubmissionDto: AnswerSubmissionDTO) {
|
||||
if (!isUUID(id)) {
|
||||
throw new AppException(
|
||||
'validation.wrongFieldFormat',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
{ field: '$t(submissions.fields.submissionId)' },
|
||||
);
|
||||
}
|
||||
|
||||
const lockKey = `lock:guest_submission:${id}`;
|
||||
const lockToken = await this.redisService.acquireLock(lockKey, 5);
|
||||
|
||||
if (!lockToken) {
|
||||
throw new AppException(
|
||||
'common.errors.processingInProgress',
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return this.answerGuest(id, answerSubmissionDto);
|
||||
} finally {
|
||||
await this.redisService.releaseLock(lockKey, lockToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async answerGuest(
|
||||
id: string,
|
||||
answerSubmissionDto: AnswerSubmissionDTO,
|
||||
) {
|
||||
let submission: GuestSubmissionSession | null = null;
|
||||
|
||||
try {
|
||||
submission = await this.redisService.get<GuestSubmissionSession>(
|
||||
`guest_submission:${id}`,
|
||||
);
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (!submission) {
|
||||
throw new AppException(
|
||||
'common.errors.entityNotFound',
|
||||
HttpStatus.NOT_FOUND,
|
||||
{ entity: '$t(submissions.fields.submission)' },
|
||||
);
|
||||
}
|
||||
|
||||
let currentQuestion: Question | null = null;
|
||||
|
||||
if (submission.currentQuestionId !== null) {
|
||||
try {
|
||||
currentQuestion = await this.questionsRepository.findOne({
|
||||
where: { id: submission.currentQuestionId },
|
||||
relations: {
|
||||
choices: true,
|
||||
outgoingTransitions: {
|
||||
toQuestion: {
|
||||
choices: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentQuestion) {
|
||||
return new AppResponse({
|
||||
isCompleted: true,
|
||||
currentQuestion: null,
|
||||
result: submission.result
|
||||
? {
|
||||
score: submission.result.score,
|
||||
probability: submission.result.probability,
|
||||
title: submission.result.title,
|
||||
description: submission.result.description,
|
||||
}
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
checkAnswerCorrectness(currentQuestion, answerSubmissionDto);
|
||||
|
||||
const newAnswer: GuestSubmissionAnswer = {
|
||||
questionId: currentQuestion.id,
|
||||
};
|
||||
|
||||
if (
|
||||
currentQuestion.type === QuestionType.MULTI_CHOICE &&
|
||||
answerSubmissionDto.choiceId !== undefined
|
||||
) {
|
||||
const choice = currentQuestion.choices.find(
|
||||
(el) => el.id === answerSubmissionDto.choiceId,
|
||||
);
|
||||
|
||||
if (!choice) {
|
||||
throw new AppException(
|
||||
'validation.wrongField',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
{ field: '$t(submissions.fields.choiceId)' },
|
||||
);
|
||||
}
|
||||
|
||||
newAnswer.choiceId = choice.id;
|
||||
} else if (
|
||||
currentQuestion.type === QuestionType.NUMERIC &&
|
||||
answerSubmissionDto.numericValue !== undefined
|
||||
) {
|
||||
newAnswer.numericValue = answerSubmissionDto.numericValue;
|
||||
}
|
||||
|
||||
const nextTransition = getNextTransition(
|
||||
currentQuestion,
|
||||
answerSubmissionDto,
|
||||
);
|
||||
|
||||
const nextQuestion = nextTransition.toQuestion ?? null;
|
||||
|
||||
submission.answers = [...submission.answers, newAnswer];
|
||||
|
||||
try {
|
||||
if (nextTransition.type === QuestionTransitionType.END || !nextQuestion) {
|
||||
submission.currentQuestionId = null;
|
||||
|
||||
const result =
|
||||
await this.calculateResultProvider.calculateGuest(submission);
|
||||
|
||||
await this.redisService.set<GuestSubmissionSession>(
|
||||
`guest_submission:${id}`,
|
||||
submission,
|
||||
3600,
|
||||
);
|
||||
|
||||
return new AppResponse({
|
||||
isCompleted: true,
|
||||
currentQuestion: null,
|
||||
result,
|
||||
});
|
||||
}
|
||||
|
||||
await this.redisService.set<GuestSubmissionSession>(
|
||||
`guest_submission:${id}`,
|
||||
{ ...submission, currentQuestionId: nextQuestion.id },
|
||||
3600,
|
||||
);
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
return new AppResponse({
|
||||
isCompleted: false,
|
||||
// isFinal: ,
|
||||
currentQuestion: {
|
||||
type: nextQuestion?.type,
|
||||
content: nextQuestion?.content,
|
||||
required: nextQuestion?.required,
|
||||
metadata: nextQuestion?.metadata,
|
||||
choices: nextQuestion?.choices.map((el) => ({
|
||||
id: el.id,
|
||||
text: el.text,
|
||||
order: el.order,
|
||||
})),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AnswerSubmissionDTO } from '../../dtos/answer-submission.dto';
|
||||
import { User } from '@/modules/users/entities/user.entity';
|
||||
import { AnswerAuthenticatedProvider } from './answer-authenticated.provider';
|
||||
import { AnswerGuestProvider } from './answer-guest.provider';
|
||||
|
||||
@Injectable()
|
||||
export class AnswerSubmissionProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Answer Autenticated Provider
|
||||
*/
|
||||
private readonly answerAuthenticatedProvider: AnswerAuthenticatedProvider,
|
||||
|
||||
/**
|
||||
* Inject Answer Guest Provider
|
||||
*/
|
||||
private readonly answerGuestProvider: AnswerGuestProvider,
|
||||
) {}
|
||||
|
||||
public async answer(
|
||||
id: string,
|
||||
answerSubmissionDto: AnswerSubmissionDTO,
|
||||
user?: User,
|
||||
) {
|
||||
if (user) {
|
||||
return await this.answerAuthenticatedProvider.answer(
|
||||
id,
|
||||
answerSubmissionDto,
|
||||
user,
|
||||
);
|
||||
} else {
|
||||
return await this.answerGuestProvider.answer(id, answerSubmissionDto);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
EntityManager,
|
||||
In,
|
||||
LessThanOrEqual,
|
||||
MoreThanOrEqual,
|
||||
Repository,
|
||||
} from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { SubmissionResult } from '../../entities/submission-result.entity';
|
||||
import { Submission } from '../../entities/submission.entity';
|
||||
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
import { SubmissionAnswer } from '../../entities/submission-answer.entity';
|
||||
import { Question } from '@/modules/questions/entities/question.entity';
|
||||
import { ResultRange } from '@/modules/scoring/entities/result-range.entity';
|
||||
import { GuestSubmissionSession } from '../../interfaces/guest-submission-session.interface';
|
||||
|
||||
@Injectable()
|
||||
export class CalculateResultProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Question Repository
|
||||
*/
|
||||
@InjectRepository(Question)
|
||||
private readonly questionRepository: Repository<Question>,
|
||||
|
||||
/**
|
||||
* Inject Result Range Repository
|
||||
*/
|
||||
@InjectRepository(ResultRange)
|
||||
private readonly resultRangeRepository: Repository<ResultRange>,
|
||||
) {}
|
||||
|
||||
public async calculateAuthenticated(
|
||||
submission: Submission,
|
||||
manager: EntityManager,
|
||||
) {
|
||||
let submissionAnswers: SubmissionAnswer[] = [];
|
||||
|
||||
try {
|
||||
submissionAnswers = await manager.find(SubmissionAnswer, {
|
||||
where: { submission: { id: submission.id } },
|
||||
relations: {
|
||||
question: {
|
||||
rangeRules: true,
|
||||
},
|
||||
choice: true,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
const result = await this.calculateFromAnswers(
|
||||
submissionAnswers.map((answer) => ({
|
||||
questionId: answer.question.id,
|
||||
question: answer.question,
|
||||
choiceId: answer.choice?.id,
|
||||
choice: answer.choice,
|
||||
numericValue: answer.numericValue,
|
||||
})),
|
||||
async (questionIds) =>
|
||||
manager.find(Question, {
|
||||
where: { id: In(questionIds) },
|
||||
relations: {
|
||||
choices: true,
|
||||
rangeRules: true,
|
||||
},
|
||||
}),
|
||||
async (score) =>
|
||||
manager.find(ResultRange, {
|
||||
where: {
|
||||
test: { id: submission.test.id },
|
||||
minScore: LessThanOrEqual(score),
|
||||
maxScore: MoreThanOrEqual(score),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const submissionResult = manager.create(SubmissionResult, {
|
||||
submission,
|
||||
score: result.score,
|
||||
probability: result.probability,
|
||||
resultRange: result.resultRange,
|
||||
});
|
||||
|
||||
try {
|
||||
await manager.save(submissionResult);
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
return {
|
||||
score: result.score,
|
||||
probability: result.probability,
|
||||
title: result.resultRange?.title,
|
||||
description: result.resultRange?.description,
|
||||
};
|
||||
}
|
||||
|
||||
public async calculateGuest(submission: GuestSubmissionSession) {
|
||||
const result = await this.calculateFromAnswers(
|
||||
submission.answers.map((answer) => ({
|
||||
questionId: answer.questionId,
|
||||
choiceId: answer.choiceId,
|
||||
numericValue: answer.numericValue,
|
||||
})),
|
||||
async (questionIds) =>
|
||||
this.questionRepository.find({
|
||||
where: { id: In(questionIds) },
|
||||
relations: {
|
||||
choices: true,
|
||||
rangeRules: true,
|
||||
},
|
||||
}),
|
||||
async (score) =>
|
||||
this.resultRangeRepository.find({
|
||||
where: {
|
||||
test: { id: submission.testId },
|
||||
minScore: LessThanOrEqual(score),
|
||||
maxScore: MoreThanOrEqual(score),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
submission.result = {
|
||||
score: result.score,
|
||||
probability: result.probability,
|
||||
title: result.resultRange?.title || '',
|
||||
description: result.resultRange?.description || '',
|
||||
};
|
||||
|
||||
return submission.result;
|
||||
}
|
||||
|
||||
private async calculateFromAnswers(
|
||||
answers: Array<{
|
||||
questionId: number;
|
||||
question?: Question;
|
||||
choiceId?: number;
|
||||
choice?: { id: number; score: number };
|
||||
numericValue?: number;
|
||||
}>,
|
||||
loadQuestions: (questionIds: number[]) => Promise<Question[]>,
|
||||
loadResultRanges: (score: number) => Promise<ResultRange[]>,
|
||||
) {
|
||||
let score = 0;
|
||||
|
||||
const questionIds = answers.map((a) => a.questionId);
|
||||
|
||||
const questions = await loadQuestions(questionIds);
|
||||
|
||||
const questionsMap = new Map<number, Question>();
|
||||
|
||||
questions.forEach((q) => questionsMap.set(q.id, q));
|
||||
|
||||
for (const answer of answers) {
|
||||
const question = answer.question ?? questionsMap.get(answer.questionId);
|
||||
|
||||
if (!question) continue;
|
||||
|
||||
if (
|
||||
question.type === QuestionType.MULTI_CHOICE &&
|
||||
answer.choiceId !== undefined
|
||||
) {
|
||||
const choice =
|
||||
answer.choice ??
|
||||
question.choices.find((c) => c.id === answer.choiceId);
|
||||
|
||||
if (choice) {
|
||||
score += question.weight * choice.score;
|
||||
}
|
||||
} else if (
|
||||
question.type === QuestionType.NUMERIC &&
|
||||
answer.numericValue !== undefined
|
||||
) {
|
||||
question.rangeRules?.forEach((rule) => {
|
||||
if (
|
||||
answer.numericValue! >= rule.min &&
|
||||
answer.numericValue! <= rule.max
|
||||
) {
|
||||
score += question.weight * rule.score;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let totalScores = 0;
|
||||
|
||||
questions.forEach((question) => {
|
||||
const answerScores: number[] = [];
|
||||
|
||||
if (question.type === QuestionType.MULTI_CHOICE) {
|
||||
question.choices.forEach((choice) => {
|
||||
answerScores.push(choice.score);
|
||||
});
|
||||
} else if (
|
||||
question.type === QuestionType.NUMERIC &&
|
||||
question.rangeRules &&
|
||||
question.rangeRules.length > 0
|
||||
) {
|
||||
question.rangeRules.forEach((rule) => {
|
||||
answerScores.push(rule.score);
|
||||
});
|
||||
}
|
||||
|
||||
const maxScore = answerScores.length > 0 ? Math.max(...answerScores) : 0;
|
||||
|
||||
totalScores += question.weight * maxScore;
|
||||
});
|
||||
|
||||
const resultRanges = await loadResultRanges(score);
|
||||
|
||||
return {
|
||||
score,
|
||||
probability: totalScores > 0 ? Math.ceil((score / totalScores) * 100) : 0,
|
||||
resultRange: resultRanges.length > 0 ? resultRanges[0] : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user