feat: change answer submission architecture
This commit is contained in:
@@ -89,7 +89,7 @@ export class AdminQuestionsController {
|
|||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
public async getQuestionById(@Param('id', ParseIntPipe) id: number) {
|
public async getQuestionById(@Param('id', ParseIntPipe) id: number) {
|
||||||
return await this.questionsService.findOneForAdmin(id);
|
return await this.questionsService.findOneByIdForAdmin(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
|
|||||||
@@ -38,17 +38,13 @@ export class CreateQuestionDTO {
|
|||||||
})
|
})
|
||||||
content!: string;
|
content!: string;
|
||||||
|
|
||||||
@IsPositive({
|
// @IsPositive({
|
||||||
message: t('validation.wrongField', {
|
// message: t('validation.wrongField', {
|
||||||
field: '$t(questions.fields.order)',
|
// field: '$t(questions.fields.order)',
|
||||||
}),
|
// }),
|
||||||
})
|
// })
|
||||||
@IsNotEmpty({
|
// @IsOptional()
|
||||||
message: t('validation.requiredField', {
|
// order?: number;
|
||||||
field: '$t(questions.fields.order)',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
order!: number;
|
|
||||||
|
|
||||||
@IsPositive({
|
@IsPositive({
|
||||||
message: t('validation.wrongField', {
|
message: t('validation.wrongField', {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ConflictException, HttpStatus, Injectable } from '@nestjs/common';
|
import { ConflictException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Question } from '../entities/question.entity';
|
import { Question } from '../entities/question.entity';
|
||||||
import { QueryFailedError, Repository } from 'typeorm';
|
import { DataSource, QueryFailedError, Repository } from 'typeorm';
|
||||||
import { TestsService } from '@/modules/tests/providers/tests.service';
|
import { TestsService } from '@/modules/tests/providers/tests.service';
|
||||||
import { I18nService } from 'nestjs-i18n';
|
import { I18nService } from 'nestjs-i18n';
|
||||||
import { CreateQuestionDTO } from '../dtos/create-question.dto';
|
import { CreateQuestionDTO } from '../dtos/create-question.dto';
|
||||||
@@ -11,10 +11,16 @@ import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
|||||||
import { UpdateQuestionDTO } from '../dtos/update-question.dto';
|
import { UpdateQuestionDTO } from '../dtos/update-question.dto';
|
||||||
import { validateMetadata } from '../helpers/validate-metadata.helper';
|
import { validateMetadata } from '../helpers/validate-metadata.helper';
|
||||||
import { AppException } from '@/common/exceptions/app.exception';
|
import { AppException } from '@/common/exceptions/app.exception';
|
||||||
|
import { Test } from '@/modules/tests/entities/test.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class QuestionsCommandProvider {
|
export class QuestionsCommandProvider {
|
||||||
constructor(
|
constructor(
|
||||||
|
/**
|
||||||
|
* Inject DataSource
|
||||||
|
*/
|
||||||
|
private readonly dataSource: DataSource,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject Questions Repository
|
* Inject Questions Repository
|
||||||
*/
|
*/
|
||||||
@@ -35,13 +41,31 @@ export class QuestionsCommandProvider {
|
|||||||
public async createOne(createQuestionDto: CreateQuestionDTO) {
|
public async createOne(createQuestionDto: CreateQuestionDTO) {
|
||||||
const test = await this.testsService.adminGetOne(createQuestionDto.test);
|
const test = await this.testsService.adminGetOne(createQuestionDto.test);
|
||||||
|
|
||||||
|
let maxOrder: { max: string } | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
maxOrder = await this.questionsRepository
|
||||||
|
.createQueryBuilder('q')
|
||||||
|
.select('MAX(q.order)', 'max')
|
||||||
|
.where('q.testId = :testId', { testId: test.id })
|
||||||
|
.getRawOne();
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
|
||||||
const newQuestion = this.questionsRepository.create({
|
const newQuestion = this.questionsRepository.create({
|
||||||
...createQuestionDto,
|
...createQuestionDto,
|
||||||
test,
|
test,
|
||||||
|
order: (Number(maxOrder?.max) || 0) + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.questionsRepository.save(newQuestion);
|
await this.dataSource.transaction(async (manager) => {
|
||||||
|
await manager.save(newQuestion);
|
||||||
|
|
||||||
|
await manager.increment(Test, { id: test.id }, 'questionsCount', 1);
|
||||||
|
});
|
||||||
|
|
||||||
return new AppResponse(
|
return new AppResponse(
|
||||||
newQuestion,
|
newQuestion,
|
||||||
this.i18nService.translate('common.messages.createSuccess', {
|
this.i18nService.translate('common.messages.createSuccess', {
|
||||||
@@ -117,7 +141,10 @@ export class QuestionsCommandProvider {
|
|||||||
let question: Question | null = null;
|
let question: Question | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
question = await this.questionsRepository.findOneBy({ id });
|
question = await this.questionsRepository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: { test: true },
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new AppTimeoutException(err);
|
throw new AppTimeoutException(err);
|
||||||
}
|
}
|
||||||
@@ -130,7 +157,16 @@ export class QuestionsCommandProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.questionsRepository.delete(question.id);
|
await this.dataSource.transaction(async (manager) => {
|
||||||
|
await manager.delete(Question, question.id);
|
||||||
|
|
||||||
|
await manager.decrement(
|
||||||
|
Test,
|
||||||
|
{ id: question.test.id },
|
||||||
|
'questionsCount',
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
throw new AppTimeoutException(err);
|
throw new AppTimeoutException(err);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
|||||||
import { PaginatedResponse } from '@/common/responses';
|
import { PaginatedResponse } from '@/common/responses';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Question } from '../entities/question.entity';
|
import { Question } from '../entities/question.entity';
|
||||||
import { Repository } from 'typeorm';
|
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
|
||||||
import { TestsService } from '@/modules/tests/providers/tests.service';
|
import { TestsService } from '@/modules/tests/providers/tests.service';
|
||||||
import { AppException } from '@/common/exceptions/app.exception';
|
import { AppException } from '@/common/exceptions/app.exception';
|
||||||
|
|
||||||
@@ -39,8 +39,6 @@ export class QuestionsQueryProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(selectedTest);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [questions, total] = await this.questionsRepository.findAndCount({
|
const [questions, total] = await this.questionsRepository.findAndCount({
|
||||||
where: {
|
where: {
|
||||||
@@ -69,7 +67,19 @@ export class QuestionsQueryProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findOneForAdmin(id: number) {
|
public async findByOptionsForAdmin(options: FindManyOptions<Question>) {
|
||||||
|
let questions: Array<Question> = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
questions = await this.questionsRepository.find(options);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findOneByIdForAdmin(id: number) {
|
||||||
let question: Question | null = null;
|
let question: Question | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -91,4 +101,16 @@ export class QuestionsQueryProvider {
|
|||||||
|
|
||||||
return question;
|
return question;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async findOneForAdmin(options: FindOneOptions<Question>) {
|
||||||
|
let question: Question | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
question = await this.questionsRepository.findOne(options);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return question;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { FindQuestionsDTO } from '../dtos/find-questions.dto';
|
|||||||
import { UpdateQuestionDTO } from '../dtos/update-question.dto';
|
import { UpdateQuestionDTO } from '../dtos/update-question.dto';
|
||||||
import { QuestionsCommandProvider } from './questions-command.provider';
|
import { QuestionsCommandProvider } from './questions-command.provider';
|
||||||
import { QuestionsQueryProvider } from './questions-query.provider';
|
import { QuestionsQueryProvider } from './questions-query.provider';
|
||||||
|
import { FindManyOptions, FindOneOptions } from 'typeorm';
|
||||||
|
import { Question } from '../entities/question.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class QuestionsService {
|
export class QuestionsService {
|
||||||
@@ -27,8 +29,16 @@ export class QuestionsService {
|
|||||||
return await this.questionsQueryProvider.findAllForAdmin(query);
|
return await this.questionsQueryProvider.findAllForAdmin(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findOneForAdmin(id: number) {
|
public async findByOptionsForAdmin(options: FindManyOptions<Question>) {
|
||||||
return await this.questionsQueryProvider.findOneForAdmin(id);
|
return await this.questionsQueryProvider.findByOptionsForAdmin(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findOneByIdForAdmin(id: number) {
|
||||||
|
return await this.questionsQueryProvider.findOneByIdForAdmin(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findOneForAdmin(options: FindOneOptions<Question>) {
|
||||||
|
return await this.questionsQueryProvider.findOneForAdmin(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateOne(id: number, updateQuestionDto: UpdateQuestionDTO) {
|
public async updateOne(id: number, updateQuestionDto: UpdateQuestionDTO) {
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ export class Submission {
|
|||||||
})
|
})
|
||||||
status!: SubmissionStatus;
|
status!: SubmissionStatus;
|
||||||
|
|
||||||
|
@Column({ type: 'int', nullable: false, default: 1 })
|
||||||
|
currentQuestionOrder!: number;
|
||||||
|
|
||||||
@OneToMany(() => SubmissionAnswer, (answer) => answer.submission)
|
@OneToMany(() => SubmissionAnswer, (answer) => answer.submission)
|
||||||
answers!: Array<SubmissionAnswer>;
|
answers!: Array<SubmissionAnswer>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { BadRequestException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
BadRequestException,
|
|
||||||
ForbiddenException,
|
|
||||||
HttpStatus,
|
|
||||||
Injectable,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { Submission } from '../entities/submission.entity';
|
import { Submission } from '../entities/submission.entity';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
@@ -21,6 +16,8 @@ import { NumericMetadata } from '@/modules/questions/interfaces/numeric-metadata
|
|||||||
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
||||||
import { ChoicesService } from '@/modules/questions/providers/choices.service';
|
import { ChoicesService } from '@/modules/questions/providers/choices.service';
|
||||||
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';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnswerSubmissionProvider {
|
export class AnswerSubmissionProvider {
|
||||||
@@ -42,6 +39,17 @@ export class AnswerSubmissionProvider {
|
|||||||
@InjectRepository(SubmissionAnswer)
|
@InjectRepository(SubmissionAnswer)
|
||||||
private readonly submissionAnswerRepository: Repository<SubmissionAnswer>,
|
private readonly submissionAnswerRepository: Repository<SubmissionAnswer>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject SubmissionResult Repository
|
||||||
|
*/
|
||||||
|
@InjectRepository(SubmissionResult)
|
||||||
|
private readonly submissionResultRepository: Repository<SubmissionResult>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject Questions Service
|
||||||
|
*/
|
||||||
|
private readonly questionsService: QuestionsService,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject Choices Service
|
* Inject Choices Service
|
||||||
*/
|
*/
|
||||||
@@ -84,35 +92,9 @@ export class AnswerSubmissionProvider {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
submission = await this.submissionRepository.findOne({
|
submission = await this.submissionRepository.findOne({
|
||||||
where: { id: submissionId },
|
where: { id: submissionId, user: { id: user.id } },
|
||||||
relations: {
|
relations: {
|
||||||
user: true,
|
test: true,
|
||||||
test: {
|
|
||||||
questions: {
|
|
||||||
choices: true,
|
|
||||||
rangeRules: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
answers: {
|
|
||||||
question: true,
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
resultRange: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
order: {
|
|
||||||
answers: {
|
|
||||||
question: {
|
|
||||||
order: 'ASC',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
test: {
|
|
||||||
questions: {
|
|
||||||
choices: {
|
|
||||||
order: 'ASC',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -127,44 +109,69 @@ export class AnswerSubmissionProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submission.user.id !== user.id) {
|
// if (submission.user.id !== user.id) {
|
||||||
throw new ForbiddenException('auth.errors.accessDenied');
|
// throw new ForbiddenException('auth.errors.accessDenied');
|
||||||
}
|
// }
|
||||||
|
|
||||||
const allQuestions = [...submission.test.questions].sort(
|
// const allQuestions = [...submission.test.questions].sort(
|
||||||
(a, b) => a.order - b.order,
|
// (a, b) => a.order - b.order,
|
||||||
);
|
// );
|
||||||
const answeredQuestions = submission.answers.map((el) => el.question);
|
// const answeredQuestions = submission.answers.map((el) => el.question);
|
||||||
|
|
||||||
const questionStats = this.getQuestionStats(
|
// const questionStats = this.getQuestionStats(
|
||||||
allQuestions,
|
// allQuestions,
|
||||||
answeredQuestions,
|
// answeredQuestions,
|
||||||
);
|
// );
|
||||||
|
|
||||||
|
const currentQuestion: Question | null =
|
||||||
|
await this.questionsService.findOneForAdmin({
|
||||||
|
where: {
|
||||||
|
test: { id: submission.test.id },
|
||||||
|
order: submission.currentQuestionOrder,
|
||||||
|
},
|
||||||
|
relations: {
|
||||||
|
choices: true,
|
||||||
|
rangeRules: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
if (!questionStats.current) {
|
|
||||||
return new AppResponse({
|
return new AppResponse({
|
||||||
isCompleted: true,
|
isCompleted: true,
|
||||||
currentQuestion: null,
|
currentQuestion: null,
|
||||||
result: submission.result
|
result: result
|
||||||
? {
|
? {
|
||||||
score: submission.result.score,
|
score: result.score,
|
||||||
probability: submission.result.probability,
|
probability: result.probability,
|
||||||
title: submission.result.resultRange?.title,
|
title: result.resultRange?.title,
|
||||||
description: submission.result.resultRange?.description,
|
description: result.resultRange?.description,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkAnswerCorrectness(questionStats.current, answerSubmissionDto);
|
this.checkAnswerCorrectness(currentQuestion, answerSubmissionDto);
|
||||||
|
|
||||||
const submissionAnswer = this.submissionAnswerRepository.create({
|
const submissionAnswer = this.submissionAnswerRepository.create({
|
||||||
submission,
|
submission,
|
||||||
question: questionStats.current,
|
question: currentQuestion,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
questionStats.current.type === QuestionType.MULTI_CHOICE &&
|
currentQuestion.type === QuestionType.MULTI_CHOICE &&
|
||||||
answerSubmissionDto.choiceId
|
answerSubmissionDto.choiceId
|
||||||
) {
|
) {
|
||||||
const choice = await this.choicesService.findOneForAdmin(
|
const choice = await this.choicesService.findOneForAdmin(
|
||||||
@@ -181,7 +188,7 @@ export class AnswerSubmissionProvider {
|
|||||||
|
|
||||||
submissionAnswer.choice = choice;
|
submissionAnswer.choice = choice;
|
||||||
} else if (
|
} else if (
|
||||||
questionStats.current.type === QuestionType.NUMERIC &&
|
currentQuestion.type === QuestionType.NUMERIC &&
|
||||||
answerSubmissionDto.numericValue
|
answerSubmissionDto.numericValue
|
||||||
) {
|
) {
|
||||||
submissionAnswer.numericValue = answerSubmissionDto.numericValue;
|
submissionAnswer.numericValue = answerSubmissionDto.numericValue;
|
||||||
@@ -193,7 +200,19 @@ export class AnswerSubmissionProvider {
|
|||||||
throw new AppTimeoutException(err);
|
throw new AppTimeoutException(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!questionStats.next) {
|
const nextQuestion: Question | null =
|
||||||
|
await this.questionsService.findOneForAdmin({
|
||||||
|
where: {
|
||||||
|
test: { id: submission.test.id },
|
||||||
|
order: submission.currentQuestionOrder + 1,
|
||||||
|
},
|
||||||
|
relations: {
|
||||||
|
choices: true,
|
||||||
|
rangeRules: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!nextQuestion) {
|
||||||
const result = await this.calculateResultProvider.calculate(submission);
|
const result = await this.calculateResultProvider.calculate(submission);
|
||||||
return new AppResponse({
|
return new AppResponse({
|
||||||
isCompleted: true,
|
isCompleted: true,
|
||||||
@@ -202,14 +221,9 @@ export class AnswerSubmissionProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextQuestion = questionStats.next;
|
|
||||||
|
|
||||||
return new AppResponse({
|
return new AppResponse({
|
||||||
isCompleted: false,
|
isCompleted: false,
|
||||||
isFinal:
|
// isFinal: ,
|
||||||
nextQuestion.id === allQuestions[allQuestions.length - 1].id
|
|
||||||
? true
|
|
||||||
: undefined,
|
|
||||||
currentQuestion: {
|
currentQuestion: {
|
||||||
type: nextQuestion.type,
|
type: nextQuestion.type,
|
||||||
content: nextQuestion.content,
|
content: nextQuestion.content,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { QuestionType } from '@/modules/questions/enums/question-type.enum';
|
|||||||
import { QuestionsService } from '@/modules/questions/providers/questions.service';
|
import { QuestionsService } from '@/modules/questions/providers/questions.service';
|
||||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||||
import { ResultRangeService } from '@/modules/scoring/providers/result-range.service';
|
import { ResultRangeService } from '@/modules/scoring/providers/result-range.service';
|
||||||
|
import { SubmissionAnswer } from '../entities/submission-answer.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CalculateResultProvider {
|
export class CalculateResultProvider {
|
||||||
@@ -26,13 +27,33 @@ export class CalculateResultProvider {
|
|||||||
* Inject ResultRange Service
|
* Inject ResultRange Service
|
||||||
*/
|
*/
|
||||||
private readonly resultRangeService: ResultRangeService,
|
private readonly resultRangeService: ResultRangeService,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject SubmissionAnswer Repository
|
||||||
|
*/
|
||||||
|
@InjectRepository(SubmissionAnswer)
|
||||||
|
private readonly submissionAnswerRepository: Repository<SubmissionAnswer>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async calculate(submission: Submission) {
|
public async calculate(submission: Submission) {
|
||||||
let score = 0;
|
let score = 0;
|
||||||
|
|
||||||
|
let submissionAnswers: Array<SubmissionAnswer> = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
submissionAnswers = await this.submissionAnswerRepository.find({
|
||||||
|
where: { submission: { id: submission.id } },
|
||||||
|
relations: {
|
||||||
|
question: true,
|
||||||
|
choice: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
submission.answers.map(async (answer) => {
|
submissionAnswers.map(async (answer) => {
|
||||||
if (
|
if (
|
||||||
answer.question.type === QuestionType.MULTI_CHOICE &&
|
answer.question.type === QuestionType.MULTI_CHOICE &&
|
||||||
answer.choice
|
answer.choice
|
||||||
@@ -42,7 +63,7 @@ export class CalculateResultProvider {
|
|||||||
answer.question.type === QuestionType.NUMERIC &&
|
answer.question.type === QuestionType.NUMERIC &&
|
||||||
answer.numericValue !== undefined
|
answer.numericValue !== undefined
|
||||||
) {
|
) {
|
||||||
const question = await this.questionsService.findOneForAdmin(
|
const question = await this.questionsService.findOneByIdForAdmin(
|
||||||
answer.question.id,
|
answer.question.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +84,15 @@ export class CalculateResultProvider {
|
|||||||
|
|
||||||
let totalScores = 0;
|
let totalScores = 0;
|
||||||
|
|
||||||
submission.test.questions.forEach((question) => {
|
const testQuestions = await this.questionsService.findByOptionsForAdmin({
|
||||||
|
where: { test: { id: submission.test.id } },
|
||||||
|
relations: {
|
||||||
|
choices: true,
|
||||||
|
rangeRules: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
testQuestions.forEach((question) => {
|
||||||
const answerScores: Array<number> = [];
|
const answerScores: Array<number> = [];
|
||||||
if (question.type === QuestionType.MULTI_CHOICE) {
|
if (question.type === QuestionType.MULTI_CHOICE) {
|
||||||
question.choices.forEach((choice) => {
|
question.choices.forEach((choice) => {
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ export class Test {
|
|||||||
@OneToMany(() => Question, (question) => question.test)
|
@OneToMany(() => Question, (question) => question.test)
|
||||||
questions!: Array<Question>;
|
questions!: Array<Question>;
|
||||||
|
|
||||||
|
@Column({ type: 'int', nullable: false, default: 0 })
|
||||||
|
questionsCount!: number;
|
||||||
|
|
||||||
@OneToMany(() => ResultRange, (range) => range.test)
|
@OneToMany(() => ResultRange, (range) => range.test)
|
||||||
resultRanges!: Array<ResultRange>;
|
resultRanges!: Array<ResultRange>;
|
||||||
|
|
||||||
|
|||||||
@@ -105,4 +105,28 @@ export class TestsCommandProvider {
|
|||||||
throw new AppTimeoutException(err);
|
throw new AppTimeoutException(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async incrementQuestionsCount(testId: number, count?: number) {
|
||||||
|
try {
|
||||||
|
await this.testRepository.increment(
|
||||||
|
{ id: testId },
|
||||||
|
'questionsCount',
|
||||||
|
count || 1,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async decrementQuestionsCount(testId: number, count?: number) {
|
||||||
|
try {
|
||||||
|
await this.testRepository.decrement(
|
||||||
|
{ id: testId },
|
||||||
|
'questionsCount',
|
||||||
|
count || 1,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppTimeoutException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,4 +48,18 @@ export class TestsService {
|
|||||||
public async deleteOne(id: number) {
|
public async deleteOne(id: number) {
|
||||||
return await this.testsCommandProvider.deleteOne(id);
|
return await this.testsCommandProvider.deleteOne(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async incrementQuestionsCount(testId: number, count?: number) {
|
||||||
|
return await this.testsCommandProvider.incrementQuestionsCount(
|
||||||
|
testId,
|
||||||
|
count,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async decrementQuestionsCount(testId: number, count?: number) {
|
||||||
|
return await this.testsCommandProvider.decrementQuestionsCount(
|
||||||
|
testId,
|
||||||
|
count,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user