From 8844d39e6b905776e77de8831d96e89fbb9693d3 Mon Sep 17 00:00:00 2001 From: Ani-ket Date: Wed, 17 Jul 2024 21:14:40 +0530 Subject: [PATCH 1/5] createForm Api --- drizzle/schema.ts | 2 + src/controller/content/content.controller.ts | 4 +- src/controller/content/content.service.ts | 32 ++++++-- src/controller/content/dto/content.dto.ts | 77 +++++++++++++++++--- 4 files changed, 97 insertions(+), 18 deletions(-) diff --git a/drizzle/schema.ts b/drizzle/schema.ts index 632d884..c6c3ac4 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -3462,9 +3462,11 @@ export const quizTrackingRelation = relations( export const zuvyModuleForm = main.table('zuvy_module_form', { id: serial('id').primaryKey().notNull(), + chapterId: integer('chapter_id').references(() => zuvyModuleChapter.id).notNull(), question: text('question'), options: jsonb('options'), typeId: integer('type_id').references(() => zuvyQuestionTypes.id), + isRequired: boolean('is_required').notNull(), createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }), updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }), usage: integer('usage').default(0), diff --git a/src/controller/content/content.controller.ts b/src/controller/content/content.controller.ts index ec73660..561f603 100644 --- a/src/controller/content/content.controller.ts +++ b/src/controller/content/content.controller.ts @@ -624,10 +624,10 @@ export class ContentController { @ApiOperation({ summary: 'Create a form' }) @ApiBearerAuth() async createFormForModule ( - @Body() formQuestions: formBatchDto + @Body() formQuestion: formBatchDto ){ const res = await this.contentService.createFormForModule( - formQuestions + formQuestion ); return res; } diff --git a/src/controller/content/content.service.ts b/src/controller/content/content.service.ts index ed16260..abeefb8 100644 --- a/src/controller/content/content.service.ts +++ b/src/controller/content/content.service.ts @@ -1953,13 +1953,15 @@ export class ContentService { async createFormForModule(form: formBatchDto) { try { - const formQuestions = form.questions.map((f) => ({ + const formQuestion = form.questions.map((f) => ({ + chapterId: f.chapterId, question: f.question, options: f.options, typeId: f.typeId, + isRequired: f.isRequired, })); - const allFieldsFilled = formQuestions.every(question => question.question !== null && question.options !== null && question.typeId !== null); + const allFieldsFilled = formQuestion.every(question => question.question !== null && question.options !== null && question.typeId !== null && question.isRequired !== null); if (!allFieldsFilled) { return { status: "error", @@ -1970,13 +1972,31 @@ export class ContentService { const result = await db .insert(zuvyModuleForm) - .values(formQuestions) + .values(formQuestion) .returning(); - if (result.length > 0) { + + //console.log('Updated Chapter:', result); + + const formIds= result.length>0?result.map(obj => obj.id):[]; + + const updatedChapter = await db + .update(zuvyModuleChapter) + .set({ + formQuestions: formIds + }) + .where(eq(zuvyModuleChapter.id,formQuestion[0].chapterId)) + .returning(); + + //console.log('Updated Chapter:', updatedChapter); + + + if (result.length > 0 || updatedChapter.length>0) { return { + status: "success", code: 200, - result + result, + updatedChapter } } else { @@ -2048,7 +2068,7 @@ export class ContentService { question: sql`excluded.question`, options: sql`excluded.options`, typeId: sql`excluded.type_id`, - + isRequired: sql`excluded.is_required`, }, }); diff --git a/src/controller/content/dto/content.dto.ts b/src/controller/content/dto/content.dto.ts index 3dd6c49..4164962 100644 --- a/src/controller/content/dto/content.dto.ts +++ b/src/controller/content/dto/content.dto.ts @@ -849,6 +849,15 @@ export class CreateTypeDto{ export class formDto { + @ApiProperty({ + type: Number, + example: 34, + required: true + }) + @IsNumber() + @IsNotEmpty() + chapterId: number; + @ApiProperty({ type: String, example: 'What is your opinion about the course?', @@ -878,6 +887,14 @@ export class formDto { @IsOptional() typeId: number; + @ApiProperty({ + type: Boolean, + example: false + }) + @IsOptional() + @IsBoolean() + isRequired: boolean; + } export class formBatchDto { @@ -885,6 +902,7 @@ export class formBatchDto { type: [formDto], example: [ { + chapterId:34, question: 'What do you like about the course?', options: { 1: 'Option 1', @@ -893,9 +911,10 @@ export class formBatchDto { 4: 'Option 4', }, typeId: 1, - + isRequired:false, }, { + chapterId:34, question: 'What do you want to improve about the course?', options: { 1: 'Paris', @@ -904,22 +923,25 @@ export class formBatchDto { 4: 'Rome', }, typeId: 2, - + isRequired:false, }, { + chapterId:34, question: 'What is your opinion about the course?', typeId: 3, - + isRequired:false, }, { + chapterId:34, question: 'Choose date of opting the course', typeId: 4, - + isRequired:false, }, { + chapterId:34, question: 'Choose time of opting the course', typeId: 5, - + isRequired:false, }, ], required: true, @@ -941,6 +963,15 @@ export class editFormDto { @IsNumber() id: number; + @ApiProperty({ + type: Number, + example: 34, + required: true + }) + @IsNumber() + @IsNotEmpty() + chapterId: number; + @ApiProperty({ type: String, example: 'What is your opinion about the course?', @@ -970,6 +1001,13 @@ export class editFormDto { @IsOptional() typeId: number; + @ApiProperty({ + type: Boolean, + example: false + }) + @IsOptional() + @IsBoolean() + isRequired: boolean; } export class editFormBatchDto { @@ -978,6 +1016,7 @@ export class editFormBatchDto { example: [ { id: 1, + chapterId:34, question: 'What is the national animal of India?', options: { 1: 'Option 1', @@ -986,10 +1025,11 @@ export class editFormBatchDto { 4: 'Option 4', }, typeId: 1, - + isRequired:false, }, { id: 2, + chapterId:34, question: 'What is the capital of France?', options: { 1: 'Paris', @@ -998,25 +1038,28 @@ export class editFormBatchDto { 4: 'Rome', }, typeId: 2, - + isRequired:false, }, { id: 3, + chapterId:34, question: 'What is the national animal of India?', typeId: 3, - + isRequired:false, }, { id: 4, + chapterId:34, question: 'Choose date of opting the course', typeId: 4, - + isRequired:false, }, { id: 5, + chapterId:34, question: 'Choose time of opting the course', typeId: 5, - + isRequired:false, }, ], required: true, @@ -1027,4 +1070,18 @@ export class editFormBatchDto { @Type(() => editFormDto) questions: editFormDto[]; +} + +export class EditFormChapterBody { + @ApiProperty({ type: formDto }) + @IsOptional() + @ValidateNested() + @Type(() => formDto) + formDto: formDto; + + @ApiProperty({ type: editFormDto }) + @IsOptional() + @ValidateNested() + @Type(() => editFormDto) + editFormDto: editFormDto; } \ No newline at end of file From ed2ee9c67b9c5873daa827cb350abae11d2dbc4d Mon Sep 17 00:00:00 2001 From: Ani-ket Date: Thu, 18 Jul 2024 16:06:24 +0530 Subject: [PATCH 2/5] create and edit form api changes --- src/controller/content/content.controller.ts | 28 +- src/controller/content/content.service.ts | 353 ++++++++++++------- src/controller/content/dto/content.dto.ts | 67 ++-- 3 files changed, 283 insertions(+), 165 deletions(-) diff --git a/src/controller/content/content.controller.ts b/src/controller/content/content.controller.ts index 561f603..d13f36a 100644 --- a/src/controller/content/content.controller.ts +++ b/src/controller/content/content.controller.ts @@ -42,7 +42,8 @@ import { CreateChapterDto, formBatchDto, editFormBatchDto, - CreateTypeDto + CreateTypeDto, + CreateAndEditFormBody } from './dto/content.dto'; import { CreateProblemDto } from '../codingPlatform/dto/codingPlatform.dto'; import { difficulty, questionType } from 'drizzle/schema'; @@ -624,9 +625,11 @@ export class ContentController { @ApiOperation({ summary: 'Create a form' }) @ApiBearerAuth() async createFormForModule ( + @Query('chapterId') chapterId: number, @Body() formQuestion: formBatchDto ){ const res = await this.contentService.createFormForModule( + chapterId, formQuestion ); return res; @@ -661,8 +664,27 @@ export class ContentController { @Post('/editform') @ApiOperation({ summary: 'Create a form' }) @ApiBearerAuth() - async editFormForModule(@Body() formQuestions: editFormBatchDto) { - const res = await this.contentService.editFormQuestions(formQuestions); + async editFormForModule( + @Query('chapterId') chapterId: number, + @Body() formQuestions: editFormBatchDto) { + const res = await this.contentService.editFormQuestions( + chapterId, + formQuestions + ); + return res; + } + + + @Post('/createAndEditForm/:chapterId') + @ApiOperation({ summary: 'Create a form' }) + @ApiBearerAuth() + async createAndEditForm( + @Param('chapterId') chapterId: number, + @Body() formQuestions: CreateAndEditFormBody) { + const res = await this.contentService.createAndEditFormQuestions( + chapterId, + formQuestions + ); return res; } diff --git a/src/controller/content/content.service.ts b/src/controller/content/content.service.ts index abeefb8..b5369b1 100644 --- a/src/controller/content/content.service.ts +++ b/src/controller/content/content.service.ts @@ -57,10 +57,13 @@ import { projectDto, formBatchDto, editFormBatchDto, - CreateTypeDto + CreateTypeDto, + CreateAndEditFormBody, + formDto } from './dto/content.dto'; import { CreateProblemDto } from '../codingPlatform/dto/codingPlatform.dto'; import { PatchBootcampSettingDto } from '../bootcamp/dto/bootcamp.dto'; +import { isNullOrUndefined } from 'util'; // import Strapi from "strapi-sdk-js" const { ZUVY_CONTENT_URL, ZUVY_CONTENTS_API_URL } = process.env; // INPORTING env VALUSE ZUVY_CONTENT @@ -420,7 +423,7 @@ export class ContentService { } } - async createChapterForModule(moduleId: number, topicId: number,order:number, bootcampId: number, ) { + async createChapterForModule(moduleId: number, topicId: number, order: number, bootcampId: number,) { try { let newAssessment; let chapterData; @@ -517,7 +520,7 @@ export class ContentService { let modules = data.map((module: any) => { return { id: module.id, - name: module['projectData'].length > 0 ? module['projectData'][0]['title']: module.name, + name: module['projectData'].length > 0 ? module['projectData'][0]['title'] : module.name, description: module.description, typeId: module.typeId, order: module.order, @@ -554,7 +557,7 @@ export class ContentService { .select() .from(zuvyCourseModules) .where(eq(zuvyCourseModules.id, moduleId)); - + // const assessment = await db.query.zuvyOutsourseAssessments.findMany({ // where: (outsourseAssessments, { eq }) => // eq(outsourseAssessments.moduleId, module[0].id), @@ -601,34 +604,34 @@ export class ContentService { } async formatedChapterDetails(chapterDetails: any) { - try{ + try { chapterDetails.Quizzes = chapterDetails?.Quizzes.map((Quizzes) => { - let quizDetails = {...Quizzes.Quiz, } + let quizDetails = { ...Quizzes.Quiz, } delete Quizzes.Quiz - return {...Quizzes, ...quizDetails} + return { ...Quizzes, ...quizDetails } }) chapterDetails.OpenEndedQuestions = chapterDetails?.OpenEndedQuestions.map((OpenEndedQuestions) => { - let openEndedDetails = {...OpenEndedQuestions.OpenEndedQuestion, } + let openEndedDetails = { ...OpenEndedQuestions.OpenEndedQuestion, } delete OpenEndedQuestions.OpenEndedQuestion - return {...OpenEndedQuestions, ...openEndedDetails} + return { ...OpenEndedQuestions, ...openEndedDetails } }) chapterDetails.CodingQuestions = chapterDetails?.CodingQuestions.map((CodingQuestions) => { let codingOutsourseId = CodingQuestions.id - let codingDetails = {codingOutsourseId, ...CodingQuestions.CodingQuestion} + let codingDetails = { codingOutsourseId, ...CodingQuestions.CodingQuestion } delete CodingQuestions.CodingQuestion - return {...CodingQuestions, ...codingDetails} + return { ...CodingQuestions, ...codingDetails } }) - + chapterDetails.Forms = chapterDetails?.Forms.map((Forms) => { - let FormDetails = {...Forms.Form, } + let FormDetails = { ...Forms.Form, } delete Forms.Form - return {...Forms, ...FormDetails} + return { ...Forms, ...FormDetails } }) return chapterDetails - } catch(err) { + } catch (err) { throw err; } } @@ -651,33 +654,33 @@ export class ContentService { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, - where: (zuvyOutsourseQuizzes, {sql}) => sql`${zuvyOutsourseQuizzes.bootcampId} = ${bootcampId} AND ${zuvyOutsourseQuizzes.chapterId} = ${chapterId}`, + where: (zuvyOutsourseQuizzes, { sql }) => sql`${zuvyOutsourseQuizzes.bootcampId} = ${bootcampId} AND ${zuvyOutsourseQuizzes.chapterId} = ${chapterId}`, with: { - Quiz:true + Quiz: true } }, - OpenEndedQuestions:{ + OpenEndedQuestions: { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, - where: (zuvyOutsourseOpenEndedQuestions, {sql}) => sql`${zuvyOutsourseOpenEndedQuestions.bootcampId} = ${bootcampId} AND ${zuvyOutsourseOpenEndedQuestions.chapterId} = ${chapterId} AND ${zuvyOutsourseOpenEndedQuestions.moduleId} = ${moduleId}`, + where: (zuvyOutsourseOpenEndedQuestions, { sql }) => sql`${zuvyOutsourseOpenEndedQuestions.bootcampId} = ${bootcampId} AND ${zuvyOutsourseOpenEndedQuestions.chapterId} = ${chapterId} AND ${zuvyOutsourseOpenEndedQuestions.moduleId} = ${moduleId}`, with: { - OpenEndedQuestion:true + OpenEndedQuestion: true } }, - CodingQuestions:{ + CodingQuestions: { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, - where: (zuvyOutsourseCodingQuestions, {sql}) => sql`${zuvyOutsourseCodingQuestions.bootcampId} = ${bootcampId} AND ${zuvyOutsourseCodingQuestions.chapterId} = ${chapterId}`, + where: (zuvyOutsourseCodingQuestions, { sql }) => sql`${zuvyOutsourseCodingQuestions.bootcampId} = ${bootcampId} AND ${zuvyOutsourseCodingQuestions.chapterId} = ${chapterId}`, with: { - CodingQuestion:{ + CodingQuestion: { columns: { id: true, title: true, @@ -695,10 +698,10 @@ export class ContentService { return formatedData; } const chapterDetails = await db - .select() - .from(zuvyModuleChapter) - .where(eq(zuvyModuleChapter.id, chapterId)); - + .select() + .from(zuvyModuleChapter) + .where(eq(zuvyModuleChapter.id, chapterId)); + const modifiedChapterDetails: { id: number; title: string; @@ -744,7 +747,7 @@ export class ContentService { ) : []; modifiedChapterDetails.codingQuestionDetails = codingProblemDetails; - }else if (chapterDetails[0].topicId == 7) { + } else if (chapterDetails[0].topicId == 7) { const formDetails = chapterDetails[0].formQuestions !== null ? await db @@ -755,7 +758,7 @@ export class ContentService { ) : []; modifiedChapterDetails.formQuestionDetails = formDetails; - }else { + } else { let content = [ { title: chapterDetails[0].title, @@ -995,7 +998,7 @@ export class ContentService { .set({ usage: sql`${zuvyCodingQuestions.usage}::numeric + 1` }) .where(eq(zuvyCodingQuestions.id, editData.codingQuestions)); } - }else if (editData.formQuestions) { + } else if (editData.formQuestions) { if (editData.formQuestions.length == 0) { editData.formQuestions = null; } @@ -1050,7 +1053,7 @@ export class ContentService { .insert(zuvyModuleAssessment) .values(assessment) .returning(); - + return newAssessment; } catch (err) { throw err; @@ -1072,8 +1075,8 @@ export class ContentService { OutsourseCodingQuestions: true }, }); - - if (assessment == undefined ||assessment.length == 0) { + + if (assessment == undefined || assessment.length == 0) { throw ({ status: 'error', statusCode: 404, @@ -1082,11 +1085,11 @@ export class ContentService { } else { let { bootcampId, moduleId, chapterId, ModuleAssessment, OutsourseQuizzes, OutsourseOpenEndedQuestions, OutsourseCodingQuestions } = assessment[0]; let { mcqIds, openEndedQuestionIds, codingProblemIds, title, description, ...OutsourseAssessmentData__ } = assessmentBody; - + let assessment_id = ModuleAssessment.id; - - let assessmentData = { title, description}; - + + let assessmentData = { title, description }; + // filter out the ids that are not in the assessment let existingQuizIds = OutsourseQuizzes.map((q) => q.quiz_id).filter(id => id !== null); let existingOpenEndedQuestionIds = OutsourseOpenEndedQuestions.map((q) => q.openEndedQuestionId).filter(id => id !== null); @@ -1099,7 +1102,7 @@ export class ContentService { let quizIdsToAdd = mcqIds.filter((id) => !existingQuizIds.includes(id)); let openEndedQuestionIdsToAdd = openEndedQuestionIds.filter((id) => !existingOpenEndedQuestionIds.includes(id)); let codingQuestionIdsToAdd = codingProblemIds.filter((id) => !existingCodingQuestionIds.includes(id)); - + // Delete operations if (quizIdsToDelete.length > 0) { await db @@ -1116,21 +1119,21 @@ export class ContentService { .delete(zuvyOutsourseCodingQuestions) .where(sql`${zuvyOutsourseCodingQuestions.assessmentOutsourseId} = ${assessmentOutsourseId} AND ${inArray(zuvyOutsourseCodingQuestions.codingQuestionId, codingQuestionIdsToDelete)}`); } - + // Update assessment data let updatedOutsourseAssessment = await db.update(zuvyOutsourseAssessments).set(OutsourseAssessmentData__).where(eq(zuvyOutsourseAssessments.id, assessmentOutsourseId)).returning(); - + let updatedAssessment = await db .update(zuvyModuleAssessment) .set(assessmentData) .where(eq(zuvyModuleAssessment.id, assessment_id)) .returning(); - + // Insert new data let mcqArray = quizIdsToAdd.map(id => ({ quiz_id: id, bootcampId, chapterId, assessmentOutsourseId })); let openEndedQuestionsArray = openEndedQuestionIdsToAdd.map(id => ({ openEndedQuestionId: id, bootcampId, moduleId, chapterId, assessmentOutsourseId })); let codingProblemsArray = codingQuestionIdsToAdd.map(id => ({ codingQuestionId: id, bootcampId, moduleId, chapterId, assessmentOutsourseId })); - + if (mcqArray.length > 0) { let createZOMQ = await db.insert(zuvyOutsourseQuizzes).values(mcqArray).returning(); if (createZOMQ.length > 0) { @@ -1141,7 +1144,7 @@ export class ContentService { .where(sql`${inArray(zuvyModuleQuiz.id, toUpdateIds)}`); } } - + if (openEndedQuestionsArray.length > 0) { let createZOOQ = await db.insert(zuvyOutsourseOpenEndedQuestions).values(openEndedQuestionsArray).returning(); if (createZOOQ.length > 0) { @@ -1152,7 +1155,7 @@ export class ContentService { .where(sql`${inArray(zuvyOpenEndedQuestions.id, toUpdateIds)}`); } } - + if (codingProblemsArray.length > 0) { let createZOCQ = await db.insert(zuvyOutsourseCodingQuestions).values(codingProblemsArray).returning(); if (createZOCQ.length > 0) { @@ -1163,7 +1166,7 @@ export class ContentService { .where(sql`${inArray(zuvyCodingQuestions.id, toUpdateIds)}`); } } - } + } return { status: 'success', code: 200, @@ -1251,8 +1254,8 @@ export class ContentService { // openEndedQuesDetails, // codingQuesDetails, // }; - // } - return assessment; + // } + return assessment; } catch (err) { throw err; } @@ -1697,16 +1700,16 @@ export class ContentService { throw err; } } - async getStudentsOfAssessment(assessmentId:number, chapterId: number, moduleId: number, bootcampId: number, req) { + async getStudentsOfAssessment(assessmentId: number, chapterId: number, moduleId: number, bootcampId: number, req) { try { - let {id} = req.user[0]; + let { id } = req.user[0]; const assessment = await db.query.zuvyOutsourseAssessments.findMany({ where: (zuvyOutsourseAssessments, { eq }) => sql`${zuvyOutsourseAssessments.assessmentId} = ${assessmentId} AND ${zuvyOutsourseAssessments.bootcampId} = ${bootcampId} AND ${zuvyOutsourseAssessments.chapterId} = ${chapterId} AND ${zuvyOutsourseAssessments.moduleId} = ${moduleId}`, with: { submitedOutsourseAssessments: { where: (zuvyAssessmentSubmission, { eq }) => eq(zuvyAssessmentSubmission.userId, id), - columns:{ + columns: { id: true, marks: true, userId: true, @@ -1720,7 +1723,7 @@ export class ContentService { columns: { // id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, with: { Quiz: true, @@ -1730,7 +1733,7 @@ export class ContentService { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, with: { OpenEndedQuestion: true @@ -1740,7 +1743,7 @@ export class ContentService { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, with: { CodingQuestion: true @@ -1748,17 +1751,17 @@ export class ContentService { }, }, }) - if (assessment == undefined ||assessment.length == 0) { + if (assessment == undefined || assessment.length == 0) { throw ({ status: 'error', statusCode: 404, message: 'Assessment not found', }); - } - assessment[0]["totalQuizzes"] = assessment[0]?.Quizzes.length|| 0; + } + assessment[0]["totalQuizzes"] = assessment[0]?.Quizzes.length || 0; assessment[0]["totalOpenEndedQuestions"] = assessment[0]?.OpenEndedQuestions.length || 0; assessment[0]["totalCodingQuestions"] = assessment[0]?.CodingQuestions.length || 0; - + delete assessment[0].Quizzes; delete assessment[0].OpenEndedQuestions; delete assessment[0].CodingQuestions; @@ -1775,7 +1778,7 @@ export class ContentService { */ async startAssessmentForStudent(assessmentOutsourseId: number, req) { try { - let {id} = req.user[0]; + let { id } = req.user[0]; const assessment = await db.query.zuvyOutsourseAssessments.findMany({ where: (zuvyOutsourseAssessments, { eq }) => eq(zuvyOutsourseAssessments.id, assessmentOutsourseId), @@ -1785,17 +1788,17 @@ export class ContentService { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, with: { - CodingQuestion:true + CodingQuestion: true } }, Quizzes: { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, with: { Quiz: true, @@ -1805,7 +1808,7 @@ export class ContentService { columns: { id: true, assessmentOutsourseId: true, - bootcampId:true + bootcampId: true }, with: { OpenEndedQuestion: true @@ -1820,32 +1823,32 @@ export class ContentService { statusCode: 404, message: 'Assessment not found', }); - } + } let startedAt = new Date().toISOString(); let submission; - submission = await db.select().from(zuvyAssessmentSubmission).where( sql`${zuvyAssessmentSubmission.userId} = ${id} AND ${zuvyAssessmentSubmission.assessmentOutsourseId} = ${assessmentOutsourseId} AND ${zuvyAssessmentSubmission.submitedAt} IS NULL`); + submission = await db.select().from(zuvyAssessmentSubmission).where(sql`${zuvyAssessmentSubmission.userId} = ${id} AND ${zuvyAssessmentSubmission.assessmentOutsourseId} = ${assessmentOutsourseId} AND ${zuvyAssessmentSubmission.submitedAt} IS NULL`); if (submission.length == 0) { - submission = await db.insert(zuvyAssessmentSubmission).values({userId: id, assessmentOutsourseId, startedAt }).returning(); + submission = await db.insert(zuvyAssessmentSubmission).values({ userId: id, assessmentOutsourseId, startedAt }).returning(); } - + let formatedData = await this.formatedChapterDetails(assessment[0]); formatedData.Quizzes = formatedData.Quizzes.length formatedData.OpenEndedQuestions = formatedData.OpenEndedQuestions.length - return {...formatedData, submission: submission[0]}; + return { ...formatedData, submission: submission[0] }; } catch (err) { throw err; } } - async getAssessmentDetailsOfQuiz(assessment_outsourse_id:number, userId){ + async getAssessmentDetailsOfQuiz(assessment_outsourse_id: number, userId) { try { const assessment = await db.query.zuvyOutsourseQuizzes.findMany({ where: (zuvyOutsourseQuizzes, { eq }) => eq(zuvyOutsourseQuizzes.assessmentOutsourseId, assessment_outsourse_id), with: { - submissionsData:{ + submissionsData: { where: (zuvyQuizTracking, { eq }) => eq(zuvyQuizTracking.userId, userId), columns: { id: true, @@ -1857,7 +1860,7 @@ export class ContentService { }, Quiz: { columns: { - id:true, + id: true, question: true, options: true, difficulty: true, @@ -1869,20 +1872,20 @@ export class ContentService { }) if (assessment.length == 0) { return []; - } + } return assessment; } catch (err) { throw err; } } - async getAssessmentDetailsOfOpenEnded(assessment_outsourse_id:number, userId){ + async getAssessmentDetailsOfOpenEnded(assessment_outsourse_id: number, userId) { try { const assessment = await db.query.zuvyOutsourseOpenEndedQuestions.findMany({ where: (zuvyOutsourseOpenEndedQuestions, { eq }) => eq(zuvyOutsourseOpenEndedQuestions.assessmentOutsourseId, assessment_outsourse_id), with: { - submissionsData:{ + submissionsData: { where: (zuvyOpenEndedQuestionSubmission, { eq }) => eq(zuvyOpenEndedQuestionSubmission.userId, userId), columns: { id: true, @@ -1904,7 +1907,7 @@ export class ContentService { }) if (assessment.length == 0) { return []; - } + } return assessment; } catch (err) { throw err; @@ -1951,10 +1954,19 @@ export class ContentService { } - async createFormForModule(form: formBatchDto) { + async createFormForModule(chapterId: number, form: formBatchDto) { try { + + if (isNaN(chapterId)) { + return { + status: "error", + code: 400, + message: "Invalid chapterId. Please provide a valid number." + }; + } + const formQuestion = form.questions.map((f) => ({ - chapterId: f.chapterId, + chapterId, question: f.question, options: f.options, typeId: f.typeId, @@ -1962,35 +1974,33 @@ export class ContentService { })); const allFieldsFilled = formQuestion.every(question => question.question !== null && question.options !== null && question.typeId !== null && question.isRequired !== null); - if (!allFieldsFilled) - { return { - status: "error", - code: 400, - message: " One or more fields are empty. Please try again." - }; - } + if (!allFieldsFilled) { + return { + status: "error", + code: 400, + message: " One or more fields are empty. Please try again." + }; + } const result = await db .insert(zuvyModuleForm) .values(formQuestion) .returning(); - //console.log('Updated Chapter:', result); - const formIds= result.length>0?result.map(obj => obj.id):[]; - - const updatedChapter = await db + const formIds = result.length > 0 ? result.map(obj => obj.id) : []; + + const updatedChapter = await db .update(zuvyModuleChapter) .set({ formQuestions: formIds - }) - .where(eq(zuvyModuleChapter.id,formQuestion[0].chapterId)) + }) + .where(eq(zuvyModuleChapter.id, formQuestion[0].chapterId)) .returning(); - - //console.log('Updated Chapter:', updatedChapter); - if (result.length > 0 || updatedChapter.length>0) { + + if (result.length > 0 || updatedChapter.length > 0) { return { status: "success", @@ -2019,7 +2029,7 @@ export class ContentService { let queryString; if (!Number.isNaN(typeId) && questionType == undefined) { queryString = sql`${zuvyModuleForm.typeId} = ${typeId}`; - } + } const result = await db .select() .from(zuvyModuleForm) @@ -2035,52 +2045,95 @@ export class ContentService { } } - async editFormQuestions(editFormDetails: editFormBatchDto) { - try { - const isValid = editFormDetails.questions.every(question => { - if (typeof question.question !== 'string' || question.question.trim().length === 0) { - return false; - } - if (typeof question.options !== 'object' || Object.values(question.options).some(option => typeof option !== 'string' || option.trim().length === 0)) { - return false; - } - if (typeof question.typeId !== 'number' || question.typeId <= 0) { - return false; - } - return true; - }); + async editFormQuestions(chapterId: number, editFormDetails: editFormBatchDto) { + try { + const editFormQuestions = editFormDetails.questions.map((f) => ({ + chapterId, + id: f.id, + question: f.question, + options: f.options, + typeId: f.typeId, + isRequired: f.isRequired, + })); - if (!isValid) { + const allFieldsFilled = editFormQuestions.every( + (question) => + question.question !== null && + question.options !== null && + question.typeId !== null && + question.isRequired !== null + ); + + if (!allFieldsFilled) { return { - status: 'failure', + status: "error", code: 400, - message: 'All questions and options must have a length greater than 0', + message: "One or more fields are empty. Please try again.", }; } - - await db - .insert(zuvyModuleForm) - .values(editFormDetails.questions) - .onConflictDoUpdate({ - target: zuvyModuleForm.id, - set: { - question: sql`excluded.question`, - options: sql`excluded.options`, - typeId: sql`excluded.type_id`, - isRequired: sql`excluded.is_required`, - }, - }); - + + const results = []; + + for (const formQuestion of editFormQuestions) { + if (formQuestion.id) { + const existingRecord = await db + .select() + .from(zuvyModuleForm) + .where(eq(zuvyModuleForm.id, formQuestion.id)) + + if (existingRecord) { + const result = await db + .update(zuvyModuleForm) + .set({ + chapterId: formQuestion.chapterId, + question: formQuestion.question, + options: formQuestion.options, + typeId: formQuestion.typeId, + isRequired: formQuestion.isRequired, + }) + .where(eq(zuvyModuleForm.id, formQuestion.id)) + .returning(); + results.push(result); + } else { + return { + status: "Error", + code: 400, + message: "Form question id(s) are invalid", + }; + } + } else { + return { + status: "Error", + code: 400, + message: "Form question ids(s) are missing", + }; + } + } + + + const formIds = results.flat().map((obj) => obj.id); + + const updatedChapter = await db + .update(zuvyModuleChapter) + .set({ + formQuestions: formIds, + }) + .where(eq(zuvyModuleChapter.id, chapterId)) + .returning(); + return { - status: 'success', + status: "success", code: 200, - message: 'Form questions are updated successfully', + message: "Form questions are updated successfully", + results, + updatedChapter, }; } catch (error) { throw error; } } + async deleteForm(id: deleteQuestionDto) { try { @@ -2139,4 +2192,46 @@ export class ContentService { } } + + + + async createAndEditFormQuestions(chapterId: number, form: CreateAndEditFormBody) { + try { + + if (isNaN(chapterId)) { + return { + status: "error", + code: 400, + message: "Invalid chapterId. Please provide a valid number." + }; + } + + const res1 = await db + .select() + .from(zuvyModuleForm) + .where(eq(zuvyModuleForm.chapterId, chapterId)) + + const res2 = await db + .select({ + formQuestions: zuvyModuleChapter.formQuestions + }) + .from(zuvyModuleChapter) + .where(eq(zuvyModuleChapter.id, chapterId)) + + if (!(res1.length > 0 && res2.length > 0)) { + this.createFormForModule(chapterId,form.formQuestionDto) + + // }else if(form.formQuestionDto !==isNullOrUndefined){ + + // } + }else{ + this.editFormQuestions(chapterId,form.editFormquestionDto) + } + + + } catch (err) { + throw err; + } + } + } diff --git a/src/controller/content/dto/content.dto.ts b/src/controller/content/dto/content.dto.ts index 4164962..23f9510 100644 --- a/src/controller/content/dto/content.dto.ts +++ b/src/controller/content/dto/content.dto.ts @@ -849,14 +849,14 @@ export class CreateTypeDto{ export class formDto { - @ApiProperty({ - type: Number, - example: 34, - required: true - }) - @IsNumber() - @IsNotEmpty() - chapterId: number; + // @ApiProperty({ + // type: Number, + // example: 34, + // required: true + // }) + // @IsNumber() + // @IsNotEmpty() + // chapterId: number; @ApiProperty({ type: String, @@ -902,7 +902,7 @@ export class formBatchDto { type: [formDto], example: [ { - chapterId:34, + // chapterId:34, question: 'What do you like about the course?', options: { 1: 'Option 1', @@ -914,7 +914,7 @@ export class formBatchDto { isRequired:false, }, { - chapterId:34, + // chapterId:34, question: 'What do you want to improve about the course?', options: { 1: 'Paris', @@ -926,19 +926,19 @@ export class formBatchDto { isRequired:false, }, { - chapterId:34, + // chapterId:34, question: 'What is your opinion about the course?', typeId: 3, isRequired:false, }, { - chapterId:34, + // chapterId:34, question: 'Choose date of opting the course', typeId: 4, isRequired:false, }, { - chapterId:34, + // chapterId:34, question: 'Choose time of opting the course', typeId: 5, isRequired:false, @@ -963,14 +963,14 @@ export class editFormDto { @IsNumber() id: number; - @ApiProperty({ - type: Number, - example: 34, - required: true - }) - @IsNumber() - @IsNotEmpty() - chapterId: number; + // @ApiProperty({ + // type: Number, + // example: 34, + // required: true + // }) + // @IsNumber() + // @IsNotEmpty() + // chapterId: number; @ApiProperty({ type: String, @@ -1016,7 +1016,7 @@ export class editFormBatchDto { example: [ { id: 1, - chapterId:34, + // chapterId:34, question: 'What is the national animal of India?', options: { 1: 'Option 1', @@ -1029,7 +1029,7 @@ export class editFormBatchDto { }, { id: 2, - chapterId:34, + // chapterId:34, question: 'What is the capital of France?', options: { 1: 'Paris', @@ -1042,21 +1042,21 @@ export class editFormBatchDto { }, { id: 3, - chapterId:34, + // chapterId:34, question: 'What is the national animal of India?', typeId: 3, isRequired:false, }, { id: 4, - chapterId:34, + // chapterId:34, question: 'Choose date of opting the course', typeId: 4, isRequired:false, }, { id: 5, - chapterId:34, + // chapterId:34, question: 'Choose time of opting the course', typeId: 5, isRequired:false, @@ -1072,16 +1072,17 @@ export class editFormBatchDto { } -export class EditFormChapterBody { - @ApiProperty({ type: formDto }) +export class CreateAndEditFormBody { + @ApiProperty({ type: formBatchDto }) @IsOptional() @ValidateNested() - @Type(() => formDto) - formDto: formDto; + @Type(() => formBatchDto) + formQuestionDto: formBatchDto; - @ApiProperty({ type: editFormDto }) + @ApiProperty({ type: editFormBatchDto }) @IsOptional() @ValidateNested() - @Type(() => editFormDto) - editFormDto: editFormDto; + @Type(() => editFormBatchDto) + editFormquestionDto: editFormBatchDto; + //questions: any; } \ No newline at end of file From f9fb4635b0450044d84b35f6df7dba6acdb0d05c Mon Sep 17 00:00:00 2001 From: Ani-ket Date: Thu, 18 Jul 2024 21:29:13 +0530 Subject: [PATCH 3/5] refactoring of Admin Apis --- src/controller/content/content.controller.ts | 18 +- src/controller/content/content.service.ts | 199 +++++++++++-------- src/controller/content/dto/content.dto.ts | 2 +- 3 files changed, 131 insertions(+), 88 deletions(-) diff --git a/src/controller/content/content.controller.ts b/src/controller/content/content.controller.ts index d13f36a..b115da7 100644 --- a/src/controller/content/content.controller.ts +++ b/src/controller/content/content.controller.ts @@ -635,7 +635,7 @@ export class ContentController { return res; } - @Get('/allFormQuestions') + @Get('/allFormQuestions/:chapterId') @ApiOperation({ summary: 'Get all form Questions' }) @ApiQuery({ name: 'typeId', @@ -651,10 +651,12 @@ export class ContentController { }) @ApiBearerAuth() async getAllFormQuestions( + @Param('chapterId') chapterId: number, @Query('typeId') typeId: number, @Query('searchTerm') searchTerm: string, ): Promise { const res = await this.contentService.getAllFormQuestions( + chapterId, typeId, searchTerm, ); @@ -688,13 +690,13 @@ export class ContentController { return res; } - @Delete('/deleteFormQuestion') - @ApiOperation({ summary: 'Delete form question' }) - @ApiBearerAuth() - async deleteFormQuestion(@Body() questionIds: deleteQuestionDto) { - const res = await this.contentService.deleteForm(questionIds); - return res; - } + // @Delete('/deleteFormQuestion') + // @ApiOperation({ summary: 'Delete form question' }) + // @ApiBearerAuth() + // async deleteFormQuestion(@Body() questionIds: deleteQuestionDto) { + // const res = await this.contentService.deleteForm(questionIds); + // return res; + // } } diff --git a/src/controller/content/content.service.ts b/src/controller/content/content.service.ts index b5369b1..8bf9bb8 100644 --- a/src/controller/content/content.service.ts +++ b/src/controller/content/content.service.ts @@ -1990,10 +1990,22 @@ export class ContentService { const formIds = result.length > 0 ? result.map(obj => obj.id) : []; + const existingChapter = await db + .select() + .from(zuvyModuleChapter) + .where(eq(zuvyModuleChapter.id, chapterId)) + .limit(1); + + const chapter = existingChapter[0] as { formQuestions: number[] }; + + const existingFormQuestions = chapter.formQuestions || []; + + const updatedFormQuestions = [...existingFormQuestions, ...formIds]; + const updatedChapter = await db .update(zuvyModuleChapter) .set({ - formQuestions: formIds + formQuestions: updatedFormQuestions }) .where(eq(zuvyModuleChapter.id, formQuestion[0].chapterId)) .returning(); @@ -2022,6 +2034,7 @@ export class ContentService { } async getAllFormQuestions( + chapterId: number, typeId: number, searchTerm: string = '', ) { @@ -2036,6 +2049,7 @@ export class ContentService { .where( and( queryString, + sql`${zuvyModuleForm.chapterId} = ${chapterId}`, sql`((LOWER(${zuvyModuleForm.question}) LIKE '%' || ${searchTerm.toLowerCase()} || '%'))`, ), ); @@ -2056,7 +2070,7 @@ export class ContentService { typeId: f.typeId, isRequired: f.isRequired, })); - + const allFieldsFilled = editFormQuestions.every( (question) => question.question !== null && @@ -2064,7 +2078,7 @@ export class ContentService { question.typeId !== null && question.isRequired !== null ); - + if (!allFieldsFilled) { return { status: "error", @@ -2072,16 +2086,32 @@ export class ContentService { message: "One or more fields are empty. Please try again.", }; } - + const results = []; - + + const existingFormRecords = await db + .select() + .from(zuvyModuleForm) + .where(eq(zuvyModuleForm.chapterId, chapterId)); + + const existingFormIds = existingFormRecords.map((record) => record.id); + const providedFormIds = editFormQuestions.map((question) => question.id); + const idsToRemove = existingFormIds.filter(id => !providedFormIds.includes(id)); + + + if (idsToRemove.length > 0) { + await db + .delete(zuvyModuleForm) + .where(inArray(zuvyModuleForm.id, idsToRemove)); + } + for (const formQuestion of editFormQuestions) { if (formQuestion.id) { const existingRecord = await db .select() .from(zuvyModuleForm) .where(eq(zuvyModuleForm.id, formQuestion.id)) - + if (existingRecord) { const result = await db .update(zuvyModuleForm) @@ -2110,10 +2140,10 @@ export class ContentService { }; } } - - + + const formIds = results.flat().map((obj) => obj.id); - + const updatedChapter = await db .update(zuvyModuleChapter) .set({ @@ -2121,7 +2151,7 @@ export class ContentService { }) .where(eq(zuvyModuleChapter.id, chapterId)) .returning(); - + return { status: "success", code: 200, @@ -2133,64 +2163,64 @@ export class ContentService { throw error; } } - - async deleteForm(id: deleteQuestionDto) { - try { - const usedForm = await db - .select() - .from(zuvyModuleForm) - .where( - sql`${inArray(zuvyModuleForm.id, id.questionIds)} and ${zuvyModuleForm.usage} > 0`, - ); - let deletedQuestions; - if (usedForm.length > 0) { - const usedIds = usedForm.map((form) => form.id); - const remainingIds = id.questionIds.filter( - (questionId) => !usedIds.includes(questionId), - ); - deletedQuestions = - remainingIds.length > 0 - ? await db - .delete(zuvyModuleForm) - .where(sql`${inArray(zuvyModuleForm.id, remainingIds)}`) - .returning() - : []; - if (deletedQuestions.length > 0) { - return { - status: 'success', - code: 200, - message: `Form questions which is used in other places like chapters and assessment cannot be deleted`, - }; - } else { - return { - status: 'error', - code: 400, - message: `Questions cannot be deleted`, - }; - } - } - deletedQuestions = await db - .delete(zuvyModuleForm) - .where(sql`${inArray(zuvyModuleForm.id, id.questionIds)}`) - .returning(); - if (deletedQuestions.length > 0) { - return { - status: 'success', - code: 200, - message: 'The form questions has been deleted successfully', - }; - } else { - return { - status: 'error', - code: 400, - message: `Questions cannot be deleted`, - }; - } - } catch (err) { - throw err; - } - } + + // async deleteForm(id: deleteQuestionDto) { + // try { + // const usedForm = await db + // .select() + // .from(zuvyModuleForm) + // .where( + // sql`${inArray(zuvyModuleForm.id, id.questionIds)} and ${zuvyModuleForm.usage} > 0`, + // ); + // let deletedQuestions; + // if (usedForm.length > 0) { + // const usedIds = usedForm.map((form) => form.id); + // const remainingIds = id.questionIds.filter( + // (questionId) => !usedIds.includes(questionId), + // ); + // deletedQuestions = + // remainingIds.length > 0 + // ? await db + // .delete(zuvyModuleForm) + // .where(sql`${inArray(zuvyModuleForm.id, remainingIds)}`) + // .returning() + // : []; + // if (deletedQuestions.length > 0) { + // return { + // status: 'success', + // code: 200, + // message: `Form questions which is used in other places like chapters and assessment cannot be deleted`, + // }; + // } else { + // return { + // status: 'error', + // code: 400, + // message: `Questions cannot be deleted`, + // }; + // } + // } + // deletedQuestions = await db + // .delete(zuvyModuleForm) + // .where(sql`${inArray(zuvyModuleForm.id, id.questionIds)}`) + // .returning(); + // if (deletedQuestions.length > 0) { + // return { + // status: 'success', + // code: 200, + // message: 'The form questions has been deleted successfully', + // }; + // } else { + // return { + // status: 'error', + // code: 400, + // message: `Questions cannot be deleted`, + // }; + // } + // } catch (err) { + // throw err; + // } + // } @@ -2206,28 +2236,39 @@ export class ContentService { }; } + + + + if (form.formQuestionDto && !(form.editFormQuestionDto)) { + await this.createFormForModule(chapterId, form.formQuestionDto); + + + } else if (form.editFormQuestionDto && !(form.formQuestionDto)) { + await this.editFormQuestions(chapterId, form.editFormQuestionDto); + + } else if (form.editFormQuestionDto && form.formQuestionDto) { + + await this.editFormQuestions(chapterId, form.editFormQuestionDto); + await this.createFormForModule(chapterId, form.formQuestionDto); + } const res1 = await db .select() .from(zuvyModuleForm) .where(eq(zuvyModuleForm.chapterId, chapterId)) const res2 = await db - .select({ - formQuestions: zuvyModuleChapter.formQuestions - }) + .select() .from(zuvyModuleChapter) .where(eq(zuvyModuleChapter.id, chapterId)) - if (!(res1.length > 0 && res2.length > 0)) { - this.createFormForModule(chapterId,form.formQuestionDto) - - // }else if(form.formQuestionDto !==isNullOrUndefined){ - - // } - }else{ - this.editFormQuestions(chapterId,form.editFormquestionDto) - } + return { + status: "success", + code: 200, + message: "Form questions are updated successfully", + res1, + res2, + }; } catch (err) { throw err; diff --git a/src/controller/content/dto/content.dto.ts b/src/controller/content/dto/content.dto.ts index 23f9510..0c74ef6 100644 --- a/src/controller/content/dto/content.dto.ts +++ b/src/controller/content/dto/content.dto.ts @@ -1083,6 +1083,6 @@ export class CreateAndEditFormBody { @IsOptional() @ValidateNested() @Type(() => editFormBatchDto) - editFormquestionDto: editFormBatchDto; + editFormQuestionDto: editFormBatchDto; //questions: any; } \ No newline at end of file From 39ba9e2fce179842f168eab8cd3eb6d031cea337 Mon Sep 17 00:00:00 2001 From: Ani-ket Date: Fri, 19 Jul 2024 18:01:32 +0530 Subject: [PATCH 4/5] minor --- drizzle/schema.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drizzle/schema.ts b/drizzle/schema.ts index c6c3ac4..7515ce1 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -3484,35 +3484,35 @@ export const zuvyFormTracking = main.table("zuvy_form_tracking", { questionId: integer("question_id"), chapterId: integer("chapter_id"), status: varchar("status", { length: 255 }), - assessmentSubmissionId: integer("assessment_submission_id").references(() => zuvyAssessmentSubmission.id, { - onDelete: 'cascade', - onUpdate: 'cascade', - }), + // assessmentSubmissionId: integer("assessment_submission_id").references(() => zuvyAssessmentSubmission.id, { + // onDelete: 'cascade', + // onUpdate: 'cascade', + // }), chosenOptions: text("chosen_options").array(), answer: text("answer"), createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow(), }); -export const zuvyFormTrackingRelations = relations(zuvyFormTracking, ({ one }) => ({ +// export const zuvyFormTrackingRelations = relations(zuvyFormTracking, ({ one }) => ({ - formSubmissions: one(zuvyAssessmentSubmission, { - fields: [zuvyFormTracking.assessmentSubmissionId], - references: [zuvyAssessmentSubmission.id] - }) +// formSubmissions: one(zuvyAssessmentSubmission, { +// fields: [zuvyFormTracking.assessmentSubmissionId], +// references: [zuvyAssessmentSubmission.id] +// }) -})) +// })) -export const formChapterRelations = relations( - zuvyCourseModules, - ({many, one }) => ({ - moduleChapterData: many(zuvyModuleChapter), - chapterTrackingData: many(zuvyChapterTracking), - moduleTracking: many(zuvyModuleTracking), - formTrackingData: many(zuvyFormTracking), - moduleFormData: many (zuvyModuleForm) - }), -); +// export const formChapterRelations = relations( +// zuvyCourseModules, +// ({many, one }) => ({ +// moduleChapterData: many(zuvyModuleChapter), +// chapterTrackingData: many(zuvyChapterTracking), +// moduleTracking: many(zuvyModuleTracking), +// formTrackingData: many(zuvyFormTracking), +// moduleFormData: many (zuvyModuleForm) +// }), +// ); export const formModuleRelation= relations( zuvyModuleForm, From 30ba53a1d11beb98cab10d009925e9531c156093 Mon Sep 17 00:00:00 2001 From: Ani-ket Date: Fri, 19 Jul 2024 18:04:44 +0530 Subject: [PATCH 5/5] minor --- src/controller/content/content.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/controller/content/content.service.ts b/src/controller/content/content.service.ts index 8bf9bb8..921684e 100644 --- a/src/controller/content/content.service.ts +++ b/src/controller/content/content.service.ts @@ -2250,7 +2250,14 @@ export class ContentService { await this.editFormQuestions(chapterId, form.editFormQuestionDto); await this.createFormForModule(chapterId, form.formQuestionDto); + }else{ + return { + status: "error", + code: 400, + message: "Invalid input." + }; } + const res1 = await db .select() .from(zuvyModuleForm)