feat: add guest answering logic
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
export interface GuestSubmissionAnswer {
|
||||||
|
questionId: number;
|
||||||
|
choiceId?: number;
|
||||||
|
numericValue?: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface GuestSubmissionResult {
|
||||||
|
score: number;
|
||||||
|
probability: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { GuestSubmissionAnswer } from './guest-submission-answer.interface';
|
||||||
|
import { GuestSubmissionResult } from './guest-submission-result.interface';
|
||||||
|
|
||||||
|
export interface GuestSubmissionSession {
|
||||||
|
testId: number;
|
||||||
|
currentQuestionId: number | null;
|
||||||
|
answers: Array<GuestSubmissionAnswer>;
|
||||||
|
result?: GuestSubmissionResult;
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
export interface GuestSubmissionSession {
|
|
||||||
testId: number;
|
|
||||||
currentQuestionId: number;
|
|
||||||
answers: Array<{
|
|
||||||
questionId: number;
|
|
||||||
choiceId: number;
|
|
||||||
numericValue: number;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
@@ -8,19 +8,19 @@ import { User } from '@/modules/users/entities/user.entity';
|
|||||||
import { AppException } from '@/common/exceptions/app.exception';
|
import { AppException } from '@/common/exceptions/app.exception';
|
||||||
import { RedisService } from '@/common/modules/redis/providers/redis.service';
|
import { RedisService } from '@/common/modules/redis/providers/redis.service';
|
||||||
import { isUUID } from 'class-validator';
|
import { isUUID } from 'class-validator';
|
||||||
import { GuestSubmissionSession } from '../interfaces/guest-submission-session.interfaces';
|
import { GuestSubmissionSession } from '../interfaces/guest-submission-session.interface';
|
||||||
import { Question } from '@/modules/questions/entities/question.entity';
|
import { Question } from '@/modules/questions/entities/question.entity';
|
||||||
import { AppResponse } from '@/common/responses';
|
import { AppResponse } from '@/common/responses';
|
||||||
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
||||||
import { NumericMetadata } from '@/modules/questions/interfaces/numeric-metadata.interface';
|
import { NumericMetadata } from '@/modules/questions/interfaces/numeric-metadata.interface';
|
||||||
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
||||||
import { CalculateResultProvider } from './calculate-result.provider';
|
import { CalculateResultProvider } from './calculate-result.provider';
|
||||||
import { QuestionsService } from '@/modules/questions/providers/questions.service';
|
|
||||||
import { SubmissionResult } from '../entities/submission-result.entity';
|
import { SubmissionResult } from '../entities/submission-result.entity';
|
||||||
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
||||||
import { SingleChoiceCondition } from '@/modules/questions/interfaces/single-choice-condition.interface';
|
import { SingleChoiceCondition } from '@/modules/questions/interfaces/single-choice-condition.interface';
|
||||||
import { NumericCondition } from '@/modules/questions/interfaces/numeric-condition.interface';
|
import { NumericCondition } from '@/modules/questions/interfaces/numeric-condition.interface';
|
||||||
import { SubmissionStatus } from '../enums/submission-status.enum';
|
import { SubmissionStatus } from '../enums/submission-status.enum';
|
||||||
|
import { GuestSubmissionAnswer } from '../interfaces/guest-submission-answer.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnswerSubmissionProvider {
|
export class AnswerSubmissionProvider {
|
||||||
@@ -49,9 +49,10 @@ export class AnswerSubmissionProvider {
|
|||||||
private readonly submissionResultRepository: Repository<SubmissionResult>,
|
private readonly submissionResultRepository: Repository<SubmissionResult>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject Questions Service
|
* Inject Questions Repository
|
||||||
*/
|
*/
|
||||||
private readonly questionsService: QuestionsService,
|
@InjectRepository(Question)
|
||||||
|
private readonly questionsRepository: Repository<Question>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject CalculateResult Provider
|
* Inject CalculateResult Provider
|
||||||
@@ -200,7 +201,8 @@ export class AnswerSubmissionProvider {
|
|||||||
{ currentQuestion: null, status: SubmissionStatus.COMPLETED },
|
{ currentQuestion: null, status: SubmissionStatus.COMPLETED },
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await this.calculateResultProvider.calculate(
|
const result =
|
||||||
|
await this.calculateResultProvider.calculateAuthenticated(
|
||||||
submission,
|
submission,
|
||||||
manager,
|
manager,
|
||||||
);
|
);
|
||||||
@@ -263,6 +265,95 @@ export class AnswerSubmissionProvider {
|
|||||||
`guest_submission:${id}`,
|
`guest_submission:${id}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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: 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.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 = this.getNextTransition(
|
||||||
|
currentQuestion,
|
||||||
|
answerSubmissionDto,
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextQuestion = nextTransition.toQuestion ?? null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (nextTransition.type === QuestionTransitionType.END || !nextQuestion) {
|
||||||
|
await this.redisService.set<GuestSubmissionSession>(
|
||||||
|
`guest_submission:${id}`,
|
||||||
|
{
|
||||||
|
...submission,
|
||||||
|
currentQuestionId: null,
|
||||||
|
answers: [...submission.answers, newAnswer],
|
||||||
|
},
|
||||||
|
3600,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
|
||||||
return submission;
|
return submission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { EntityManager, LessThanOrEqual, MoreThanOrEqual } from 'typeorm';
|
import {
|
||||||
|
EntityManager,
|
||||||
|
In,
|
||||||
|
LessThanOrEqual,
|
||||||
|
MoreThanOrEqual,
|
||||||
|
Repository,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { SubmissionResult } from '../entities/submission-result.entity';
|
import { SubmissionResult } from '../entities/submission-result.entity';
|
||||||
import { Submission } from '../entities/submission.entity';
|
import { Submission } from '../entities/submission.entity';
|
||||||
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
||||||
@@ -7,13 +15,29 @@ import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
|||||||
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
||||||
import { Question } from '@/modules/questions/entities/question.entity';
|
import { Question } from '@/modules/questions/entities/question.entity';
|
||||||
import { ResultRange } from '@/modules/scoring/entities/result-range.entity';
|
import { ResultRange } from '@/modules/scoring/entities/result-range.entity';
|
||||||
|
import { GuestSubmissionSession } from '../interfaces/guest-submission-session.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CalculateResultProvider {
|
export class CalculateResultProvider {
|
||||||
public async calculate(submission: Submission, manager: EntityManager) {
|
constructor(
|
||||||
let score = 0;
|
/**
|
||||||
|
* Inject Question Repository
|
||||||
|
*/
|
||||||
|
@InjectRepository(Question)
|
||||||
|
private readonly questionRepository: Repository<Question>,
|
||||||
|
|
||||||
let submissionAnswers: Array<SubmissionAnswer> = [];
|
/**
|
||||||
|
* Inject Result Range Repository
|
||||||
|
*/
|
||||||
|
@InjectRepository(ResultRange)
|
||||||
|
private readonly resultRangeRepository: Repository<ResultRange>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async calculateAuthenticated(
|
||||||
|
submission: Submission,
|
||||||
|
manager: EntityManager,
|
||||||
|
) {
|
||||||
|
let submissionAnswers: SubmissionAnswer[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
submissionAnswers = await manager.find(SubmissionAnswer, {
|
submissionAnswers = await manager.find(SubmissionAnswer, {
|
||||||
@@ -29,42 +53,145 @@ export class CalculateResultProvider {
|
|||||||
throw new AppTimeoutException(err);
|
throw new AppTimeoutException(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const answer of submissionAnswers) {
|
const result = await this.calculateFromAnswers(
|
||||||
if (answer.question.type === QuestionType.MULTI_CHOICE && answer.choice) {
|
submissionAnswers.map((answer) => ({
|
||||||
score += answer.question.weight * answer.choice.score;
|
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 (
|
} else if (
|
||||||
answer.question.type === QuestionType.NUMERIC &&
|
question.type === QuestionType.NUMERIC &&
|
||||||
answer.numericValue !== undefined
|
answer.numericValue !== undefined
|
||||||
) {
|
) {
|
||||||
|
question.rangeRules?.forEach((rule) => {
|
||||||
if (
|
if (
|
||||||
answer.question.rangeRules &&
|
answer.numericValue! >= rule.min &&
|
||||||
answer.question.rangeRules.length > 0
|
answer.numericValue! <= rule.max
|
||||||
) {
|
) {
|
||||||
answer.question.rangeRules.forEach((rule) => {
|
score += question.weight * rule.score;
|
||||||
if (
|
|
||||||
answer.numericValue !== undefined &&
|
|
||||||
answer.numericValue >= rule.min &&
|
|
||||||
answer.numericValue <= rule.max
|
|
||||||
) {
|
|
||||||
score += answer.question.weight * rule.score;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let totalScores = 0;
|
let totalScores = 0;
|
||||||
|
|
||||||
const testQuestions = await manager.find(Question, {
|
questions.forEach((question) => {
|
||||||
where: { test: { id: submission.test.id } },
|
const answerScores: number[] = [];
|
||||||
relations: {
|
|
||||||
choices: true,
|
|
||||||
rangeRules: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
testQuestions.forEach((question) => {
|
|
||||||
const answerScores: Array<number> = [];
|
|
||||||
if (question.type === QuestionType.MULTI_CHOICE) {
|
if (question.type === QuestionType.MULTI_CHOICE) {
|
||||||
question.choices.forEach((choice) => {
|
question.choices.forEach((choice) => {
|
||||||
answerScores.push(choice.score);
|
answerScores.push(choice.score);
|
||||||
@@ -84,32 +211,131 @@ export class CalculateResultProvider {
|
|||||||
totalScores += question.weight * maxScore;
|
totalScores += question.weight * maxScore;
|
||||||
});
|
});
|
||||||
|
|
||||||
const resultRanges = await manager.find(ResultRange, {
|
const resultRanges = await loadResultRanges(score);
|
||||||
where: {
|
|
||||||
test: { id: submission.test.id },
|
|
||||||
minScore: LessThanOrEqual(score),
|
|
||||||
maxScore: MoreThanOrEqual(score),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const submissionResult = manager.create(SubmissionResult, {
|
|
||||||
submission,
|
|
||||||
score,
|
|
||||||
probability: Math.ceil((score / totalScores) * 100),
|
|
||||||
resultRange: resultRanges.length > 0 ? resultRanges[0] : undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await manager.save(submissionResult);
|
|
||||||
} catch (err) {
|
|
||||||
throw new AppTimeoutException(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
score,
|
score,
|
||||||
probability: submissionResult.probability,
|
probability: totalScores > 0 ? Math.ceil((score / totalScores) * 100) : 0,
|
||||||
title: submissionResult.resultRange?.title,
|
resultRange: resultRanges.length > 0 ? resultRanges[0] : undefined,
|
||||||
description: submissionResult.resultRange?.description,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// import { Injectable } from '@nestjs/common';
|
||||||
|
// import { EntityManager, LessThanOrEqual, MoreThanOrEqual } from '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';
|
||||||
|
|
||||||
|
// @Injectable()
|
||||||
|
// export class CalculateResultProvider {
|
||||||
|
// public async calculateAutenticated(
|
||||||
|
// submission: Submission,
|
||||||
|
// manager: EntityManager,
|
||||||
|
// ) {
|
||||||
|
// let score = 0;
|
||||||
|
|
||||||
|
// let submissionAnswers: Array<SubmissionAnswer> = [];
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// submissionAnswers = await manager.find(SubmissionAnswer, {
|
||||||
|
// where: { submission: { id: submission.id } },
|
||||||
|
// relations: {
|
||||||
|
// question: {
|
||||||
|
// rangeRules: true,
|
||||||
|
// },
|
||||||
|
// choice: true,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// } catch (err) {
|
||||||
|
// throw new AppTimeoutException(err);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (const answer of submissionAnswers) {
|
||||||
|
// if (answer.question.type === QuestionType.MULTI_CHOICE && answer.choice) {
|
||||||
|
// score += answer.question.weight * answer.choice.score;
|
||||||
|
// } else if (
|
||||||
|
// answer.question.type === QuestionType.NUMERIC &&
|
||||||
|
// answer.numericValue !== undefined
|
||||||
|
// ) {
|
||||||
|
// if (
|
||||||
|
// answer.question.rangeRules &&
|
||||||
|
// answer.question.rangeRules.length > 0
|
||||||
|
// ) {
|
||||||
|
// answer.question.rangeRules.forEach((rule) => {
|
||||||
|
// if (
|
||||||
|
// answer.numericValue !== undefined &&
|
||||||
|
// answer.numericValue >= rule.min &&
|
||||||
|
// answer.numericValue <= rule.max
|
||||||
|
// ) {
|
||||||
|
// score += answer.question.weight * rule.score;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let totalScores = 0;
|
||||||
|
|
||||||
|
// const testQuestions = await manager.find(Question, {
|
||||||
|
// where: { test: { id: submission.test.id } },
|
||||||
|
// relations: {
|
||||||
|
// choices: true,
|
||||||
|
// rangeRules: true,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// testQuestions.forEach((question) => {
|
||||||
|
// const answerScores: Array<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 manager.find(ResultRange, {
|
||||||
|
// where: {
|
||||||
|
// test: { id: submission.test.id },
|
||||||
|
// minScore: LessThanOrEqual(score),
|
||||||
|
// maxScore: MoreThanOrEqual(score),
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const submissionResult = manager.create(SubmissionResult, {
|
||||||
|
// submission,
|
||||||
|
// score,
|
||||||
|
// probability: Math.ceil((score / totalScores) * 100),
|
||||||
|
// resultRange: resultRanges.length > 0 ? resultRanges[0] : undefined,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// await manager.save(submissionResult);
|
||||||
|
// } catch (err) {
|
||||||
|
// throw new AppTimeoutException(err);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// score,
|
||||||
|
// probability: submissionResult.probability,
|
||||||
|
// title: submissionResult.resultRange?.title,
|
||||||
|
// description: submissionResult.resultRange?.description,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { SubmissionStatus } from '../enums/submission-status.enum';
|
|||||||
import { Test } from '@/modules/tests/entities/test.entity';
|
import { Test } from '@/modules/tests/entities/test.entity';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { RedisService } from '@/common/modules/redis/providers/redis.service';
|
import { RedisService } from '@/common/modules/redis/providers/redis.service';
|
||||||
import { GuestSubmissionSession } from '../interfaces/guest-submission-session.interfaces';
|
import { GuestSubmissionSession } from '../interfaces/guest-submission-session.interface';
|
||||||
import { TestStatus } from '@/modules/tests/enums/test-status.enum';
|
import { TestStatus } from '@/modules/tests/enums/test-status.enum';
|
||||||
import { QuestionTransition } from '@/modules/questions/entities/question-transition.entity';
|
import { QuestionTransition } from '@/modules/questions/entities/question-transition.entity';
|
||||||
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
||||||
@@ -162,6 +162,7 @@ export class StartSubmissionProvider {
|
|||||||
|
|
||||||
const guestSubmissionId = randomUUID();
|
const guestSubmissionId = randomUUID();
|
||||||
|
|
||||||
|
try {
|
||||||
await this.redisService.set<GuestSubmissionSession>(
|
await this.redisService.set<GuestSubmissionSession>(
|
||||||
`guest_submission:${guestSubmissionId}`,
|
`guest_submission:${guestSubmissionId}`,
|
||||||
{
|
{
|
||||||
@@ -171,6 +172,9 @@ export class StartSubmissionProvider {
|
|||||||
},
|
},
|
||||||
3600,
|
3600,
|
||||||
);
|
);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
|
||||||
return new AppResponse({
|
return new AppResponse({
|
||||||
guestSubmissionId,
|
guestSubmissionId,
|
||||||
|
|||||||
Reference in New Issue
Block a user