apps/recallassess/recallassess-api/src/api/client/my-course/my-course.controller.ts
api/client/my-course
Methods |
|
| Async downloadPostBatHtml | ||||||||||||
downloadPostBatHtml(learningGroupParticipantId: number, auth: CLAuthData, reply: FastifyReply)
|
||||||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||||||||
|
Download post-BAT HTML for a course GET /api/client/my-course/:id/post-bat-download
Parameters :
Returns :
Promise<void>
|
| Async downloadPreBatHtml | ||||||||||||
downloadPreBatHtml(learningGroupParticipantId: number, auth: CLAuthData, reply: FastifyReply)
|
||||||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||||||||
|
Download pre-BAT HTML for a course GET /api/client/my-course/:id/pre-bat-download
Parameters :
Returns :
Promise<void>
|
| Async getMyCourseByCode | |||||||||
getMyCourseByCode(courseCode: string, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Get a single enrolled course by course_code with sequential validation Returns redirect info if not accessible (soft redirect approach) GET /api/client/my-course/by-code/:courseCode Example: /api/client/my-course/by-code/communication-skills-101
Parameters :
Returns :
Promise<literal type>
|
| Async getMyCourseById | |||||||||
getMyCourseById(learningGroupParticipantId: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Get a single enrolled course by LearningGroupParticipant ID GET /api/client/my-course/:id
Parameters :
Returns :
Promise<CLMyCourseDto>
|
| Async getMyCourses | ||||||
getMyCourses(auth: CLAuthData)
|
||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||
|
Get all courses the current participant is enrolled in GET /api/client/my-course
Parameters :
Returns :
Promise<CLMyCourseListResponseDto>
|
| Async getMyCoursesSequential | ||||||
getMyCoursesSequential(auth: CLAuthData)
|
||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||
|
Get all courses ordered sequentially with accessibility validation GET /api/client/my-course/sequential
Parameters :
Returns :
Promise<CLMyCourseDto[]>
|
| Async getPostBatPdfUrl | |||||||||
getPostBatPdfUrl(learningGroupParticipantId: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Get post-BAT PDF download URL GET /api/client/my-course/:id/post-bat-pdf-url
Parameters :
Returns :
Promise<literal type>
|
| Async getPreBatPdfUrl | |||||||||
getPreBatPdfUrl(learningGroupParticipantId: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Get pre-BAT PDF download URL GET /api/client/my-course/:id/pre-bat-pdf-url
Parameters :
Returns :
Promise<literal type>
|
| Async getResumeCourse | ||||||
getResumeCourse(auth: CLAuthData)
|
||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||
|
Get resume course information (last accessed course) GET /api/client/my-course/resume
Parameters :
Returns :
Promise<literal type | null>
|
| Async regeneratePostBatAnalysis | |||||||||
regeneratePostBatAnalysis(learningGroupParticipantId: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Regenerate POST-BAT analysis by resending data to CR API POST /api/client/my-course/:id/regenerate-post-bat-analysis
Parameters :
Returns :
Promise<literal type>
|
| Async regeneratePreBatAnalysis | |||||||||
regeneratePreBatAnalysis(learningGroupParticipantId: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Regenerate PRE-BAT analysis by resending data to CR API POST /api/client/my-course/:id/regenerate-pre-bat-analysis
Parameters :
Returns :
Promise<literal type>
|
import { CLAuthData, ClientAuth } from "@api/shared/decorators";
import {
Controller,
Get,
HttpCode,
HttpStatus,
NotFoundException,
Param,
ParseIntPipe,
Post,
Res,
} from "@nestjs/common";
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { FastifyReply } from "fastify";
import { CLMyCourseDto, CLMyCourseListResponseDto } from "./dto";
import { CLMyCourseService } from "./my-course.service";
@ApiTags("Client - My Courses")
@Controller("api/client/my-course")
export class CLMyCourseController {
constructor(private myCourseService: CLMyCourseService) {}
/**
* Get all courses the current participant is enrolled in
* GET /api/client/my-course
*/
@HttpCode(HttpStatus.OK)
@Get()
@ApiOperation({ summary: "Get my enrolled courses with progress" })
@ApiResponse({
status: 200,
description: "Returns courses and company_active flag (for expired-license banner)",
type: CLMyCourseListResponseDto,
})
async getMyCourses(@ClientAuth() auth: CLAuthData): Promise<CLMyCourseListResponseDto> {
return this.myCourseService.getMyCourses(auth.participantId);
}
/**
* Get all courses ordered sequentially with accessibility validation
* GET /api/client/my-course/sequential
*/
@HttpCode(HttpStatus.OK)
@Get("sequential")
@ApiOperation({ summary: "Get my enrolled courses ordered sequentially with accessibility" })
@ApiResponse({
status: 200,
description: "Returns array of enrolled courses with sequential position and accessibility",
type: [CLMyCourseDto],
})
async getMyCoursesSequential(@ClientAuth() auth: CLAuthData): Promise<CLMyCourseDto[]> {
return this.myCourseService.getMyCoursesSequential(auth.participantId);
}
/**
* Get a single enrolled course by course_code with sequential validation
* Returns redirect info if not accessible (soft redirect approach)
* GET /api/client/my-course/by-code/:courseCode
* Example: /api/client/my-course/by-code/communication-skills-101
*/
@HttpCode(HttpStatus.OK)
@Get("by-code/:courseCode")
@ApiOperation({
summary: "Get my enrolled course by course code with sequential validation",
description:
"Returns course data. If not accessible, includes redirect information to redirect to the first incomplete course.",
})
@ApiResponse({
status: 200,
description: "Returns enrolled course with redirect info if not accessible",
schema: {
type: "object",
properties: {
course: { type: "object" },
is_accessible: { type: "boolean" },
requires_redirect: { type: "boolean" },
redirect_to_course: { type: "object" },
message: { type: "string" },
},
},
})
@ApiResponse({
status: 404,
description: "Course not found or not enrolled",
})
async getMyCourseByCode(
@Param("courseCode") courseCode: string,
@ClientAuth() auth: CLAuthData,
): Promise<{
course: CLMyCourseDto;
is_accessible: boolean;
requires_redirect?: boolean;
redirect_to_course?: CLMyCourseDto;
message?: string;
}> {
return this.myCourseService.getMyCourseByCode(auth.participantId, courseCode);
}
/**
* Get resume course information (last accessed course)
* GET /api/client/my-course/resume
*/
@HttpCode(HttpStatus.OK)
@Get("resume")
@ApiOperation({ summary: "Get resume course information (last accessed course)" })
@ApiResponse({
status: 200,
description: "Returns resume course information with last page accessed",
})
@ApiResponse({
status: 404,
description: "No course to resume",
})
async getResumeCourse(@ClientAuth() auth: CLAuthData): Promise<{
course: CLMyCourseDto;
lastPageId: number;
lastPageTitle: string;
progress: number;
daysSinceLastActivity: number;
} | null> {
return this.myCourseService.getResumeCourseInfo(auth.participantId);
}
/**
* Get a single enrolled course by LearningGroupParticipant ID
* GET /api/client/my-course/:id
*/
@HttpCode(HttpStatus.OK)
@Get(":id")
@ApiOperation({ summary: "Get my enrolled course by LearningGroupParticipant ID" })
@ApiResponse({
status: 200,
description: "Returns enrolled course with progress data",
type: CLMyCourseDto,
})
@ApiResponse({
status: 404,
description: "Course not found or not enrolled",
})
async getMyCourseById(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
): Promise<CLMyCourseDto> {
const course = await this.myCourseService.getMyCourseById(auth.participantId, learningGroupParticipantId);
if (!course) {
throw new NotFoundException(
`Course enrollment with ID ${learningGroupParticipantId} not found or not enrolled`,
);
}
return course;
}
/**
* Download pre-BAT HTML for a course
* GET /api/client/my-course/:id/pre-bat-download
*/
@HttpCode(HttpStatus.OK)
@Get(":id/pre-bat-download")
@ApiOperation({ summary: "Download pre-BAT analysis HTML for a course" })
@ApiResponse({
status: 200,
description: "HTML content returned successfully",
content: {
"text/html": {
schema: {
type: "string",
},
},
},
})
@ApiResponse({
status: 404,
description: "Course not found or pre-BAT analysis not available",
})
async downloadPreBatHtml(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
@Res() reply: FastifyReply,
): Promise<void> {
const html = await this.myCourseService.generatePreBatHtml(auth.participantId, learningGroupParticipantId);
reply.header("Content-Type", "text/html; charset=utf-8");
reply.send(html);
}
/**
* Get pre-BAT PDF download URL
* GET /api/client/my-course/:id/pre-bat-pdf-url
*/
@HttpCode(HttpStatus.OK)
@Get(":id/pre-bat-pdf-url")
@ApiOperation({ summary: "Get pre-BAT PDF download URL (presigned S3 URL)" })
@ApiResponse({
status: 200,
description: "Presigned URL returned successfully",
schema: {
type: "object",
properties: {
url: { type: "string", example: "https://s3.amazonaws.com/..." },
},
},
})
@ApiResponse({
status: 404,
description: "Course not found or PDF not available",
})
async getPreBatPdfUrl(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
): Promise<{ url: string }> {
const url = await this.myCourseService.getPreBatPdfDownloadUrl(auth.participantId, learningGroupParticipantId);
return { url };
}
/**
* Download post-BAT HTML for a course
* GET /api/client/my-course/:id/post-bat-download
*/
@HttpCode(HttpStatus.OK)
@Get(":id/post-bat-download")
@ApiOperation({ summary: "Download post-BAT analysis HTML for a course with learning recommendations" })
@ApiResponse({
status: 200,
description: "HTML content returned successfully",
content: {
"text/html": {
schema: {
type: "string",
},
},
},
})
@ApiResponse({
status: 404,
description: "Course not found or post-BAT analysis not available",
})
async downloadPostBatHtml(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
@Res() reply: FastifyReply,
): Promise<void> {
const html = await this.myCourseService.generatePostBatHtml(auth.participantId, learningGroupParticipantId);
reply.header("Content-Type", "text/html; charset=utf-8");
reply.send(html);
}
/**
* Get post-BAT PDF download URL
* GET /api/client/my-course/:id/post-bat-pdf-url
*/
@HttpCode(HttpStatus.OK)
@Get(":id/post-bat-pdf-url")
@ApiOperation({ summary: "Get post-BAT PDF download URL (presigned S3 URL)" })
@ApiResponse({
status: 200,
description: "Presigned URL returned successfully",
schema: {
type: "object",
properties: {
url: { type: "string", example: "https://s3.amazonaws.com/..." },
},
},
})
@ApiResponse({
status: 404,
description: "Course not found or PDF not available",
})
async getPostBatPdfUrl(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
): Promise<{ url: string }> {
const url = await this.myCourseService.getPostBatPdfDownloadUrl(auth.participantId, learningGroupParticipantId);
return { url };
}
/**
* Regenerate PRE-BAT analysis by resending data to CR API
* POST /api/client/my-course/:id/regenerate-pre-bat-analysis
*/
@HttpCode(HttpStatus.OK)
@Post(":id/regenerate-pre-bat-analysis")
@ApiOperation({ summary: "Regenerate PRE-BAT analysis by resending data to CR API" })
@ApiResponse({
status: 200,
description: "PRE-BAT analysis regeneration triggered successfully",
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
message: { type: "string", example: "PRE-BAT analysis regeneration triggered successfully" },
},
},
})
@ApiResponse({
status: 404,
description: "Course enrollment not found",
})
async regeneratePreBatAnalysis(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
): Promise<{ success: boolean; message: string }> {
return this.myCourseService.regeneratePreBatAnalysis(auth.participantId, learningGroupParticipantId);
}
/**
* Regenerate POST-BAT analysis by resending data to CR API
* POST /api/client/my-course/:id/regenerate-post-bat-analysis
*/
@HttpCode(HttpStatus.OK)
@Post(":id/regenerate-post-bat-analysis")
@ApiOperation({ summary: "Regenerate POST-BAT analysis by resending data to CR API" })
@ApiResponse({
status: 200,
description: "POST-BAT analysis regeneration triggered successfully",
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
message: { type: "string", example: "POST-BAT analysis regeneration triggered successfully" },
},
},
})
@ApiResponse({
status: 404,
description: "Course enrollment not found",
})
async regeneratePostBatAnalysis(
@Param("id", ParseIntPipe) learningGroupParticipantId: number,
@ClientAuth() auth: CLAuthData,
): Promise<{ success: boolean; message: string }> {
return this.myCourseService.regeneratePostBatAnalysis(auth.participantId, learningGroupParticipantId);
}
}