From 7a7a9b12d8593235a1bc513b48523043e929df3d Mon Sep 17 00:00:00 2001 From: manasiTayade Date: Mon, 25 Nov 2024 12:18:40 +0530 Subject: [PATCH 1/2] completed content search api --- .../hasura/altLessonTracking.adapter.ts | 2 + .../hasura/altProgramAssociation.adapter.ts | 202 ++++++++++++++---- src/adapters/hasura/altStudent.adapter.ts | 4 +- .../altProgramAssociation.controller.ts | 9 +- 4 files changed, 170 insertions(+), 47 deletions(-) diff --git a/src/adapters/hasura/altLessonTracking.adapter.ts b/src/adapters/hasura/altLessonTracking.adapter.ts index 8e271042..3a6acc35 100644 --- a/src/adapters/hasura/altLessonTracking.adapter.ts +++ b/src/adapters/hasura/altLessonTracking.adapter.ts @@ -13,6 +13,7 @@ import { TermsProgramtoRulesDto } from "src/altProgramAssociation/dto/altTermsPr import { ALTModuleTrackingDto } from "src/altModuleTracking/dto/altModuleTracking.dto"; // import { HasuraUserService } from "./user.adapter"; import { ALTHasuraUserService } from "src/adapters/hasura/altUser.adapter"; +import { ALTProgramAssociationSearch } from "src/altProgramAssociation/dto/searchAltProgramAssociation.dto"; @Injectable() export class ALTLessonTrackingService { @@ -1189,4 +1190,5 @@ export class ALTLessonTrackingService { }); } } + } diff --git a/src/adapters/hasura/altProgramAssociation.adapter.ts b/src/adapters/hasura/altProgramAssociation.adapter.ts index 16804bb9..0b7f4600 100644 --- a/src/adapters/hasura/altProgramAssociation.adapter.ts +++ b/src/adapters/hasura/altProgramAssociation.adapter.ts @@ -62,7 +62,7 @@ export class ALTProgramAssociationService { method: "post", url: process.env.ALTHASURA, headers: { - "Authorization": request.headers.authorization, + Authorization: request.headers.authorization, "Content-Type": "application/json", }, data: subjectListData, @@ -115,7 +115,7 @@ export class ALTProgramAssociationService { method: "post", url: process.env.ALTHASURA, headers: { - "Authorization": request.headers.authorization, + Authorization: request.headers.authorization, "Content-Type": "application/json", }, data: TermsProgramtoRulesData, @@ -171,7 +171,7 @@ export class ALTProgramAssociationService { method: "post", url: process.env.ALTHASURA, headers: { - "Authorization": request.headers.authorization, + Authorization: request.headers.authorization, "Content-Type": "application/json", }, data: programData, @@ -302,7 +302,7 @@ export class ALTProgramAssociationService { method: "post", url: process.env.ALTHASURA, headers: { - "Authorization": request.headers.authorization, + Authorization: request.headers.authorization, "Content-Type": "application/json", }, data: searchData, @@ -327,23 +327,25 @@ export class ALTProgramAssociationService { }); } - public async getGlaUserContent( request: any, altTermsProgramDto: TermsProgramtoRulesDto, page: any, limit: any ) { - const decoded: any = jwt_decode(request.headers.authorization); - const altUserId = decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; + const altUserId = + decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; - console.log("altUserId", altUserId) - console.log("altTermsProgramDto", altTermsProgramDto) + console.log("altUserId", altUserId); + console.log("altTermsProgramDto", altTermsProgramDto); // get programtoRulesData - const programtoRulesData = await this.termsProgramtoRulesData(altTermsProgramDto, request) + const programtoRulesData = await this.termsProgramtoRulesData( + altTermsProgramDto, + request + ); // get altcoursetrackingdetails const promises = programtoRulesData.map((item) => @@ -351,45 +353,54 @@ export class ALTProgramAssociationService { ); const results = await Promise.allSettled(promises); - console.log("results", results) + console.log("results", results); - - function isFulfilled(result: PromiseSettledResult): result is PromiseFulfilledResult { - return result.status === 'fulfilled'; + function isFulfilled( + result: PromiseSettledResult + ): result is PromiseFulfilledResult { + return result.status === "fulfilled"; } // Filter for fulfilled results, then map to access the data const trackingDetails = results .filter(isFulfilled) // Use the type guard to filter only fulfilled results - .map(result => result.value.data.ContentBrowseTracking) // Now TypeScript knows `value` exists + .map((result) => result.value.data.ContentBrowseTracking) // Now TypeScript knows `value` exists .flat(); - console.log("programtoRulesData", programtoRulesData) - console.log("trackingDetails", trackingDetails) + console.log("programtoRulesData", programtoRulesData); + console.log("trackingDetails", trackingDetails); - const seenContentIds = new Set(trackingDetails.map(item => item.contentId)); + const seenContentIds = new Set( + trackingDetails.map((item) => item.contentId) + ); // Filter out seen courses from programtoRulesData - const unseenProgramRules = programtoRulesData.filter(item => !seenContentIds.has(item.contentId)); + const unseenProgramRules = programtoRulesData.filter( + (item) => !seenContentIds.has(item.contentId) + ); console.log("unseenProgramRules", unseenProgramRules); // pagination - + let paginatedData = this.paginateData(unseenProgramRules, page, limit); console.log(paginatedData); if (paginatedData.length < limit) { const additionalData = programtoRulesData.filter( - (item) => !seenContentIds.has(item.courseId) && !unseenProgramRules.includes(item) + (item) => + !seenContentIds.has(item.courseId) && + !unseenProgramRules.includes(item) + ); + const additionalPaginatedData = this.paginateData( + additionalData, + 1, + limit - paginatedData.length ); - const additionalPaginatedData = this.paginateData(additionalData, 1, limit - paginatedData.length); paginatedData = [...paginatedData, ...additionalPaginatedData]; } - - console.log("paginatedData with fallback", paginatedData); - + console.log("paginatedData with fallback", paginatedData); return new SuccessResponse({ statusCode: 200, @@ -421,7 +432,7 @@ export class ALTProgramAssociationService { method: "post", url: process.env.ALTHASURA, headers: { - "Authorization": request.headers.authorization, + Authorization: request.headers.authorization, "Content-Type": "application/json", }, data: TermsProgramtoRulesData, @@ -438,16 +449,14 @@ export class ALTProgramAssociationService { const result = response.data.data.ProgramTermAssoc; - console.log("result", JSON.parse(result[0].rules).prog) + console.log("result", JSON.parse(result[0].rules).prog); - return JSON.parse(result[0].rules).prog + return JSON.parse(result[0].rules).prog; } async altCourseTrackingDetails(contentId, altUserId, request) { - - console.log("contentId", contentId) - console.log("altUserId", altUserId) - + console.log("contentId", contentId); + console.log("altUserId", altUserId); const ProgressTrackingDetails = { query: `query GetProgressDetails($contentId: String, $userId: uuid!) { @@ -471,7 +480,7 @@ export class ALTProgramAssociationService { method: "post", url: process.env.ALTHASURA, headers: { - "Authorization": request.headers.authorization, + Authorization: request.headers.authorization, "Content-Type": "application/json", }, data: ProgressTrackingDetails, @@ -479,24 +488,129 @@ export class ALTProgramAssociationService { try { const response = await this.axios(configData); - const altcoursetrackingdetails = response.data - console.log("altcoursetrackingdetails", altcoursetrackingdetails) - return altcoursetrackingdetails - + const altcoursetrackingdetails = response.data; + console.log("altcoursetrackingdetails", altcoursetrackingdetails); + return altcoursetrackingdetails; } catch (error) { - throw new HttpException( - 'data not found', - error.response?.status || 500, - ); + throw new HttpException("data not found", error.response?.status || 500); } - - } paginateData(data, page = 1, limit = 5) { const startIndex = (page - 1) * limit; const endIndex = page * limit; - + return data.slice(startIndex, endIndex); } + public async contentSearch(request, body) { + //search the content + //always search for that class only where user is enrolled for format should be same as that of the content return API + const programId = body.programId; + console.log(programId); + + const requestBody = { + request: { + filters: { + primaryCategory: ["Learning Resource", "Practice Question Set"], + }, + query: body.searchQuery, + fields: [ + "name", + "mimeType", + "identifier", + "medium", + "board", + "subject", + "resourceType", + "primaryCategory", + "contentType", + "organisation", + ], + }, + }; + const sunbirdUrl = process.env.SUNBIRDURL; + let config = { + method: "post", + url: + sunbirdUrl + + `/api/content/v1/search?orgdetails=orgName,email&licenseDetails=name,description,url`, + data: requestBody, + }; + + const sunbirdSearch = await this.axios(config); + + //get the programData from programTermAssoc + const data = { + query: `query MyQuery { + ProgramTermAssoc(where: {programId: {_eq: "${programId}"}}) { + programId + rules + subject + medium + grade + board + } + } + `, + }; + + const config_data = { + method: "post", + url: process.env.ALTHASURA, + headers: { + Authorization: request.headers.authorization, + "Content-Type": "application/json", + }, + data: data, + }; + const response = await this.axios(config_data); + const rulesData = response.data.data.ProgramTermAssoc; + + //Parse the rules field in rulesData + rulesData.forEach((rule) => { + rule.rules = JSON.parse(rule.rules); + }); + //Extract identifiers from sunbirdSearch + const contentIdentifiers = sunbirdSearch.data.result.content.map( + (item) => item.identifier + ); + const questionSetIdentifiers = sunbirdSearch.data.result.QuestionSet.map( + (item) => ({ + id: item.identifier, + subject: item.subject[0], + }) + ); + // Process each rule and match contentId + const responseData = []; + rulesData.forEach((rule) => { + rule.rules.prog.forEach((item) => { + if (contentIdentifiers.includes(item.contentId)) { + const matchingQuestionSet = questionSetIdentifiers.find( + (qSet) => qSet.subject === rule.subject + ); + console.log(rule); + + responseData.push({ + contentId: item.contentId, + subject: rule.subject, + courseId: item.courseId || null, + contentType: item.contentType, + order: item.order, + allowedAttempts: item.allowedAttempts, + criteria: item.criteria, + lesson_questionset: matchingQuestionSet + ? matchingQuestionSet.id + : "", + }); + } + }); + }); + + // Create the final response + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: responseData, + }); + } } diff --git a/src/adapters/hasura/altStudent.adapter.ts b/src/adapters/hasura/altStudent.adapter.ts index 210300fa..d06a09ca 100644 --- a/src/adapters/hasura/altStudent.adapter.ts +++ b/src/adapters/hasura/altStudent.adapter.ts @@ -10,7 +10,7 @@ import { GroupMembershipDtoById } from "src/groupMembership/dto/groupMembership. import { HasuraGroupService } from "./group.adapter"; @Injectable() -export class ALTStudentService { +export class ALTStudentService { constructor( private groupService: HasuraGroupService, private userService: ALTHasuraUserService, @@ -489,7 +489,7 @@ export class ALTStudentService { ) { return new ErrorResponse({ errorCode: "400", - errorMessage: "Please provide 'schoolName' when 'class' is specified.", + errorMessage: "Please provide 'udiseCode' when 'class' is specified.", }); } let classFilter = ""; diff --git a/src/altProgramAssociation/altProgramAssociation.controller.ts b/src/altProgramAssociation/altProgramAssociation.controller.ts index f60c5d80..11655236 100644 --- a/src/altProgramAssociation/altProgramAssociation.controller.ts +++ b/src/altProgramAssociation/altProgramAssociation.controller.ts @@ -141,5 +141,12 @@ export class ALTProgramAssociationController { limit ); } - + @Post("/contentSearch") + @ApiBasicAuth("access-token") + public async contentSearch( + @Req() request: Request, + @Body() body: any + ){ + return this.altProgramAssociationService.contentSearch(request,body); + } } From a5b75f1656f4401fbf7f20edc5730769707c7d91 Mon Sep 17 00:00:00 2001 From: manasiTayade Date: Wed, 27 Nov 2024 17:12:27 +0530 Subject: [PATCH 2/2] added subject filter in contentSearch --- .../hasura/altProgramAssociation.adapter.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/adapters/hasura/altProgramAssociation.adapter.ts b/src/adapters/hasura/altProgramAssociation.adapter.ts index 0b7f4600..0bcaad41 100644 --- a/src/adapters/hasura/altProgramAssociation.adapter.ts +++ b/src/adapters/hasura/altProgramAssociation.adapter.ts @@ -503,9 +503,10 @@ export class ALTProgramAssociationService { return data.slice(startIndex, endIndex); } public async contentSearch(request, body) { - //search the content - //always search for that class only where user is enrolled for format should be same as that of the content return API const programId = body.programId; + const subjectCondition = body.subject + ? `subject: {_eq: "${body.subject}"}, ` + : ""; console.log(programId); const requestBody = { @@ -542,7 +543,7 @@ export class ALTProgramAssociationService { //get the programData from programTermAssoc const data = { query: `query MyQuery { - ProgramTermAssoc(where: {programId: {_eq: "${programId}"}}) { + ProgramTermAssoc(where: {programId: {_eq: "${programId}"}, ${subjectCondition}}) { programId rules subject @@ -553,6 +554,7 @@ export class ALTProgramAssociationService { } `, }; + console.log(data.query); const config_data = { method: "post", @@ -564,6 +566,13 @@ export class ALTProgramAssociationService { data: data, }; const response = await this.axios(config_data); + + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, + }); + } const rulesData = response.data.data.ProgramTermAssoc; //Parse the rules field in rulesData @@ -580,6 +589,7 @@ export class ALTProgramAssociationService { subject: item.subject[0], }) ); + // Process each rule and match contentId const responseData = []; rulesData.forEach((rule) => { @@ -588,7 +598,6 @@ export class ALTProgramAssociationService { const matchingQuestionSet = questionSetIdentifiers.find( (qSet) => qSet.subject === rule.subject ); - console.log(rule); responseData.push({ contentId: item.contentId,