File

apps/recallassess/recallassess-api/src/api/admin/knowledge-review/services/knowledge-review-question.service.ts

Index

Methods

Constructor

constructor(prisma: BNestPrismaService)
Parameters :
Name Type Optional
prisma BNestPrismaService No

Methods

Async addQuestion
addQuestion(addQuestionDto: KnowledgeReviewQuestionAddDto, knowledgeReviewId: number)
Parameters :
Name Type Optional
addQuestionDto KnowledgeReviewQuestionAddDto No
knowledgeReviewId number No
Private Async checkCourseEnrollments
checkCourseEnrollments(courseId: number)

Check if the associated course has enrollments

Parameters :
Name Type Optional
courseId number No
Returns : Promise<void>
Async deleteQuestion
deleteQuestion(knowledge_review_question_id: number)
Parameters :
Name Type Optional
knowledge_review_question_id number No
Returns : Promise<void>
Async getQuestionsByKnowledgeReview
getQuestionsByKnowledgeReview(knowledgeReviewId: number)
Parameters :
Name Type Optional
knowledgeReviewId number No
Async reorderQuestions
reorderQuestions(questions: literal type[])
Parameters :
Name Type Optional
questions literal type[] No
Returns : Promise<void>
Async saveQuestion
saveQuestion(knowledgeReviewQuestionId: number, updateQuestionDto: KnowledgeReviewQuestionSaveDto)
Parameters :
Name Type Optional
knowledgeReviewQuestionId number No
updateQuestionDto KnowledgeReviewQuestionSaveDto No
import { BNestPrismaService } from "@bish-nest/core/services/database/prisma/prisma.service";
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { plainToInstance } from "class-transformer";
import { KnowledgeReviewQuestionDto } from "../dto/quiz/knowledge-review-question.dto";
import { KnowledgeReviewQuestionAddDto } from "../dto/quiz/knowledge-review-question-add.dto";
import { KnowledgeReviewQuestionListDto } from "../dto/quiz/knowledge-review-question-list.dto";
import { KnowledgeReviewQuestionSaveDto } from "../dto/quiz/knowledge-review-question-save.dto";

@Injectable()
export class KnowledgeReviewQuestionService {
  constructor(private readonly prisma: BNestPrismaService) {}

  /**
   * Check if the associated course has enrollments
   */
  private async checkCourseEnrollments(courseId: number): Promise<void> {
    const enrollmentCount = await this.prisma.client.learningGroupParticipant.count({
      where: {
        course_id: courseId,
      },
    });

    if (enrollmentCount > 0) {
      throw new BadRequestException(
        `Cannot modify knowledge review question. The associated course has ${enrollmentCount} enrolled participant(s). Knowledge review questions for courses with enrollments cannot be modified or deleted.`,
      );
    }
  }

  async getQuestionsByKnowledgeReview(knowledgeReviewId: number): Promise<KnowledgeReviewQuestionListDto[]> {
    const questions = await this.prisma.client.knowledgeReviewQuestion.findMany({
      where: { knowledge_review_id: knowledgeReviewId },
      include: {
        knowledgeReviewAnswers: {
          orderBy: { sort_order: "asc" },
        },
      },
      orderBy: { sort_order: "asc" },
    });

    // Map knowledgeReviewAnswers to answers for the DTO
    const questionsWithAnswers = questions.map((question) => ({
      ...question,
      answers: question.knowledgeReviewAnswers,
    }));

    return plainToInstance(KnowledgeReviewQuestionListDto, questionsWithAnswers, { excludeExtraneousValues: true });
  }

  async addQuestion(
    addQuestionDto: KnowledgeReviewQuestionAddDto,
    knowledgeReviewId: number,
  ): Promise<KnowledgeReviewQuestionDto> {
    // Check if course has enrollments before adding question
    if (addQuestionDto.course_id) {
      await this.checkCourseEnrollments(addQuestionDto.course_id);
    }

    // Compute next sort_order = (max sort_order for this knowledge review) + 1
    let nextSortOrder = 0;
    if (knowledgeReviewId !== undefined) {
      const agg = await this.prisma.client.knowledgeReviewQuestion.aggregate({
        where: { knowledge_review_id: knowledgeReviewId },
        _max: { sort_order: true },
      });
      nextSortOrder = ((agg._max?.sort_order as number | null) ?? -1) + 1;
    }

    addQuestionDto.sort_order = addQuestionDto.sort_order ?? nextSortOrder;

    const question = await this.prisma.client.knowledgeReviewQuestion.create({
      data: {
        course_id: addQuestionDto.course_id,
        knowledge_review_id: knowledgeReviewId,
        question_text: addQuestionDto.question_text,
        question_type: (addQuestionDto.question_type as any) || "MULTIPLE_CHOICE",
        sort_order: addQuestionDto.sort_order,
      },
    });

    return plainToInstance(KnowledgeReviewQuestionDto, question, { excludeExtraneousValues: true });
  }

  async saveQuestion(
    knowledgeReviewQuestionId: number,
    updateQuestionDto: KnowledgeReviewQuestionSaveDto,
  ): Promise<KnowledgeReviewQuestionDto> {
    const existingQuestion = await this.prisma.client.knowledgeReviewQuestion.findUnique({
      where: { id: knowledgeReviewQuestionId },
    });

    if (!existingQuestion) {
      throw new NotFoundException(`Knowledge review question with ID ${knowledgeReviewQuestionId} not found`);
    }

    // Check if course has enrollments before saving
    await this.checkCourseEnrollments(existingQuestion.course_id);

    const question = await this.prisma.client.knowledgeReviewQuestion.update({
      where: { id: knowledgeReviewQuestionId },
      data: {
        question_text: updateQuestionDto.question_text,
        question_type: updateQuestionDto.question_type as any,
        sort_order: updateQuestionDto.sort_order,
      },
      include: {
        knowledgeReviewAnswers: true,
      },
    });

    return plainToInstance(KnowledgeReviewQuestionDto, question, { excludeExtraneousValues: true });
  }

  async deleteQuestion(knowledge_review_question_id: number): Promise<void> {
    const existingQuestion = await this.prisma.client.knowledgeReviewQuestion.findUnique({
      where: { id: knowledge_review_question_id },
    });

    if (!existingQuestion) {
      throw new NotFoundException(`Knowledge review question with ID ${knowledge_review_question_id} not found`);
    }

    // Check if course has enrollments before deleting
    await this.checkCourseEnrollments(existingQuestion.course_id);

    // Delete associated knowledge review results first
    await this.prisma.client.knowledgeReviewResult.deleteMany({
      where: { knowledge_review_question_id: knowledge_review_question_id },
    });

    // Delete associated answers
    await this.prisma.client.knowledgeReviewAnswer.deleteMany({
      where: { knowledge_review_question_id: knowledge_review_question_id },
    });

    // Delete the question
    await this.prisma.client.knowledgeReviewQuestion.delete({
      where: { id: knowledge_review_question_id },
    });
  }

  async reorderQuestions(questions: { id: number; sort_order: number }[]): Promise<void> {
    // Check enrollments for all questions being reordered
    if (questions.length > 0) {
      const firstQuestion = await this.prisma.client.knowledgeReviewQuestion.findUnique({
        where: { id: questions[0].id },
      });

      if (firstQuestion) {
        await this.checkCourseEnrollments(firstQuestion.course_id);
      }
    }

    const updatePromises = questions.map((question) =>
      this.prisma.client.knowledgeReviewQuestion.update({
        where: { id: question.id },
        data: { sort_order: question.sort_order },
      }),
    );

    await Promise.all(updatePromises);
  }
}

results matching ""

    No results matching ""