Course Project
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpException,
HttpStatus,
Param,
Post,
Put,
Patch,
Query,
Request,
UseGuards,
SetMetadata,
Logger,
} from "@nestjs/common";
import { AdminRoleGuard } from "src/admin-role.guard";
import { JwtAuthGuard } from "src/guard/jwt-auth.guard";
import { Roles } from "src/roles.decorator";
import { CourseService } from "./course.service";
@Controller("api/v1/courses")
export class CourseController {
private readonly logger = new Logger(CourseController.name);
constructor(private readonly courseService: CourseService) { }
@Get()
@HttpCode(200)
async findCourses(@Request() request, @Query("orgId") orgId: string, @Query("category") category: string) {
console.log("OrgId:" , orgId);
const orgIds = orgId != null ? orgId.split(',') :[];
let courses;
if (category) {
courses = await this.courseService.getCoursesByCategory(orgIds, category);
} else {
courses = await this.courseService.getCourses(orgIds);
}
return courses;
}
@UseGuards(JwtAuthGuard)
@Get('topicsCount')
@HttpCode(200)
async findCourseCount(@Request() request, @Query("category") category: string) {
const orgId = request.user.orgId;
const orgIds = [orgId];
let courses;
if (category) {
courses = await this.courseService.getCoursesByCategory(orgIds, category);
} else {
courses = await this.courseService.getCourses(orgIds);
}
return courses;
}
@Get(":id")
@HttpCode(200)
async getCourse(@Param("id") courseId: string) {
this.logger.debug(courseId);
const data = await this.courseService.getCourse(courseId);
return data;
}
@UseGuards(JwtAuthGuard)
@Post()
@HttpCode(201)
async addCourse(@Request() request, @Body() course: any) {
const updatedBy = parseInt(request.user.userId);
this.logger.debug(course);
course.createdBy = updatedBy;
const data = await this.courseService.addCourse(course);
return data;
}
@Get(":courseId/modules")
@HttpCode(200)
async findCourseModules(@Param("courseId") courseId) {
const data = await this.courseService.getCourseModules(courseId);
return data;
}
@UseGuards(JwtAuthGuard)
@Post(":courseId/modules")
@HttpCode(201)
async addCourseModule(
@Request() request,
@Param("courseId") courseId: string,
@Body() courseModule: any
) {
courseModule.courseId = courseId;
const updatedBy = parseInt(request.user.userId);
courseModule.createdBy = updatedBy;
const result = await this.courseService.addCourseModule(courseId, courseModule);
return result;
}
@UseGuards(JwtAuthGuard)
@Post(":courseId/modules/:moduleId/topics")
@HttpCode(201)
async addCourseTopic(
@Request() request,
@Param("courseId") courseId: string,
@Param("moduleId") moduleId: string,
@Body() courseTopic: any
) {
courseTopic.courseId = courseId;
courseTopic.moduleId = moduleId;
const updatedBy = parseInt(request.user.userId);
courseTopic.createdBy = updatedBy;
const result = await this.courseService.addCourseTopic(courseTopic);
return result;
}
@UseGuards(JwtAuthGuard)
@Delete(":courseId/modules/:moduleId")
@HttpCode(200)
async deleteCourseModule(
@Param("courseId") courseId: string,
@Param("moduleId") moduleId: string
) {
const result = await this.courseService.deleteCourseModules(
courseId,
moduleId
);
return result;
}
@UseGuards(JwtAuthGuard)
@Delete(":courseId/modules/:moduleId/topics/:topicId")
@HttpCode(200)
async deleteCourseTopic(
@Param("courseId") courseId: string,
@Param("moduleId") moduleId: string,
@Param("topicId") topicId: string
) {
const result = await this.courseService.deleteCourseTopic(
courseId,
moduleId,
topicId
);
return result;
}
@Get(":courseId/modules/:moduleId")
@HttpCode(200)
async getCourseModuleDetails(
@Param("courseId") courseId: string,
@Param("moduleId") moduleId: string
) {
const result = await this.courseService.getCourseModule(
courseId,
moduleId
);
return result;
}
@Get(":courseId/modules/:moduleId/topics")
@HttpCode(200)
async getCourseModuleTopics(
@Param("courseId") courseId: string,
@Param("moduleId") moduleId: string
) {
const result = await this.courseService.getCourseModuleTopics(
courseId,
moduleId
);
return result;
}
@Get(":courseId/topics")
@HttpCode(200)
async findCourseTopics(@Param("courseId") courseId) {
const data = await this.courseService.getCourseTopics(courseId);
return data;
}
@Get(":courseId/users")
@HttpCode(200)
async findCourseUsers(@Param("courseId") courseId) {
const data = await this.courseService.getCourseUsers(courseId);
return data;
}
@UseGuards(JwtAuthGuard)
@Put(":courseId/modules/:moduleId")
@HttpCode(200)
async updateCourseModule(@Param("courseId") courseId, @Param("moduleId") moduleId: string, @Body() courseModule: any) {
courseModule.courseId = courseId;
courseModule.moduleId = moduleId;
const result = await this.courseService.updateCourseModule(courseId, courseModule);
return result;
}
@UseGuards(JwtAuthGuard)
@Put(":courseId/publish")
@HttpCode(200)
async publishCourse(@Param("courseId") courseId) {
const status = "PUBLISHED";
const result = await this.courseService.publishCourse(
courseId,
status
);
return result;
}
@UseGuards(JwtAuthGuard)
@Patch(":courseId/unpublish")
@HttpCode(200)
async unPublishCourse(@Param("courseId") courseId) {
const status = "DRAFT";
const result = await this.courseService.updateCourseStatus(
courseId,
status
);
return result;
}
@UseGuards(JwtAuthGuard)
@Patch(":courseId/sync")
@HttpCode(200)
async syncCourse(@Param("courseId") courseId) {
const result = await this.courseService.syncCourse(courseId);
return result;
}
@Get(":courseId/contents")
@HttpCode(200)
async getCourseContentFromS3(@Param("courseId") courseId) {
const result = await this.courseService.getContent(courseId);
return result;
}
@Get(":courseId/slides")
@HttpCode(200)
async getCourseSlides(@Param("courseId") courseId) {
const result = await this.courseService.getSlides(courseId);
return result;
}
@Post(":courseId/slides")
@UseGuards(JwtAuthGuard)
@HttpCode(200)
async addCourseSlide(@Param("courseId") courseId, @Request() request, @Body() slide: any) {
const result = await this.courseService.addSlide(courseId, slide);
return result;
}
@Get(":courseId/content")
@HttpCode(200)
async getCourseContentFromS3Bucket(@Param("courseId") courseId) {
const result = await this.courseService.getContent(courseId);
return result;
}
@Roles('T', 'SA')
@UseGuards(JwtAuthGuard, AdminRoleGuard)
@Delete(":courseId")
@HttpCode(200)
async deleteCourse(@Param("courseId") courseId) {
const result = await this.courseService.deleteCourse(courseId);
return result;
}
@Get(":courseId/questions")
@HttpCode(200)
async findCourseQuestions(@Param("courseId") courseId) {
const data = await this.courseService.findQuestions(courseId);
return data;
}
}
import { Get, Injectable, Logger, Param } from '@nestjs/common';
import { Validator } from 'src/validator';
import { CourseDAO } from './course.dao';
import * as _ from 'lodash';
import { CourseModuleDAO } from './coursemodule.dao';
import { MCourseDAO } from './mcourse.dao';
import { CourseTopicDAO } from './coursetopic.dao';
import axios from 'axios';
import { ValidationException } from 'src/exception/validation-exception';
@Injectable()
export class CourseService {
private readonly logger = new Logger(CourseService.name);
constructor(private readonly courseDAO: CourseDAO, private readonly mcourseDAO: MCourseDAO, private readonly courseModuleDAO: CourseModuleDAO,
private readonly courseTopicDAO: CourseTopicDAO) { }
async addCourse(course: any) {
Validator.isValidString(course.code, "Invalid Course Code");
Validator.isValidString(course.title, "Invalid Course Title");
Validator.isValidNumber(course.category, "Invalid Course Category");
Validator.isValidString(course.orgId, "Invalid Organization");
const mcourseId = await this.mcourseDAO.save(course);
return mcourseId;
}
async getContent(courseId: any) {
const url = `https://coursetracker-courses.s3.ap-south-1.amazonaws.com/spinsoft/${courseId}.json`;
let { data } = await axios.get(url);
return data;
}
async getSlides(courseId: any) {
const url = `https://coursetracker-courses.s3.ap-south-1.amazonaws.com/spinsoft/${courseId}.json`;
let { data } = await axios.get(url);
return data;
}
async addSlide(courseId: any, slide:any) {
const url = `https://coursetracker-courses.s3.ap-south-1.amazonaws.com/spinsoft/${courseId}.json`;
let { data } = await axios.get(url);
return data;
}
async getCourses(orgId: string[]) {
const courses = await this.mcourseDAO.findAll(orgId);
return courses;
}
async getCoursesByCategory(orgId: string[], category: string) {
Validator.isValidString(category, "Invalid Category");
const courses = await this.mcourseDAO.findByCategory(orgId, category);
return courses;
}
async getCourse(courseId: string) {
Validator.isValidString(courseId, "Invalid Course Id");
const course = await this.mcourseDAO.findById(courseId);
if (course == null) {
throw new ValidationException("Invalid CourseId");
}
return course;
}
async getMCourse(courseId: string) {
Validator.isValidString(courseId, "Invalid Course Id");
const course = await this.mcourseDAO.findById(courseId);
if (course == null) {
throw new ValidationException("Invalid CourseId");
}
return course;
}
async getCourseModules(courseId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const courseModules = await this.courseModuleDAO.findModules(courseId);
return courseModules;
}
async getCourseModule(courseId: string, moduleId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
Validator.isValidString(moduleId, "Invalid ModuleId");
const courseModule = await this.courseModuleDAO.findModule(courseId, moduleId);
if (!courseModule) {
throw new ValidationException("Invalid ModuleId");
}
return courseModule;
}
async addCourseModule(courseId: string, courseModule: any) {
Validator.isValidString(courseId, "Invalid CourseId");
Validator.isValidString(courseModule.name, "Invalid Module Name");
const courseObj: any = await this.mcourseDAO.findById(courseId);
this.logger.debug("CourseObj ", courseObj);
if (!courseObj) {
throw new ValidationException("Invalid CourseId");
}
courseObj.modules = courseObj.modules ? courseObj.modules : [];
let exists = courseObj.modules.some((obj: any) => obj.name == courseModule.name);
if (exists) {
throw new ValidationException("Module Name already exists");
}
const course = await this.mcourseDAO.addModule(courseId, courseModule);
return course;
}
async updateCourseModule(courseId: string, courseModule: any) {
Validator.isValidString(courseId, "Invalid CourseId");
const courseObj: any = await this.mcourseDAO.findById(courseModule.courseId);
this.logger.debug("CourseObj ", courseObj);
if (!courseObj) {
throw new ValidationException("Invalid CourseId");
}
courseObj.modules = courseObj.modules ? courseObj.modules : [];
let exists = courseObj.modules.some((obj: any) => obj.name == courseModule.moduleId);
if (!exists) {
throw new ValidationException("Module Name does not exists");
}
const course = await this.mcourseDAO.updateModule(courseId, courseModule);
return course;
}
async deleteCourseModules(courseId: string, moduleId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
Validator.isValidString(moduleId, "Invalid Module Id");
const courseObj: any = await this.mcourseDAO.findById(courseId);
this.logger.debug("CourseObj ", courseObj);
this.logger.debug("moduleId ", moduleId);
if (!courseObj) {
throw new ValidationException("Invalid CourseId");
}
courseObj.modules = courseObj.modules ? courseObj.modules : [];
let exists = courseObj.modules.some((obj: any) => obj._id == moduleId);
if (!exists) {
throw new ValidationException("Module does not exists");
}
const result = await this.mcourseDAO.deleteModule(courseId, moduleId);
return result;
}
async deleteCourseTopic(courseId: string, moduleId: string, topicId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
Validator.isValidString(moduleId, "Invalid Module Id");
Validator.isValidString(topicId, "Invalid Topic Id");
const courseObj: any = await this.mcourseDAO.findById(courseId);
this.logger.debug("CourseObj ", courseObj);
if (!courseObj) {
throw new ValidationException("Invalid CourseId");
}
courseObj.modules = courseObj.modules ? courseObj.modules : [];
let moduleObj = courseObj.modules.find((obj: any) => obj._id == moduleId);
if (!moduleObj) {
throw new ValidationException("Module Name not exists");
}
const topics = moduleObj.topics ?? [];
let exists = topics.some((obj: any) => obj._id == topicId);
if (!exists) {
throw new ValidationException("Topic not exists");
}
const result = await this.courseTopicDAO.deleteTopic(courseId, moduleId, topicId);
return result;
}
async getCourseTopics(courseId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const course = await this.courseDAO.getCourseTopics(courseId);
const moduleTopics = _.groupBy(course, 'moduleName');
return moduleTopics;
}
async getCoursesForIds(courseIds: string[]) {
return await this.mcourseDAO.findByCourseIds(courseIds);
}
async getCourseModuleTopics(courseId: string, moduleId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
Validator.isValidString(moduleId, "Invalid ModuleId");
const course = await this.courseDAO.getCourseModuleTopics(courseId, moduleId);
return course;
}
async findQuestions(courseId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const topics = await this.courseDAO.findQuestions(courseId);
return topics;
}
async getCourseUsers(courseId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const topics = await this.courseDAO.findUsers(courseId);
return topics;
}
async updateCourseStatus(courseId: string, status: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const result = await this.courseDAO.updateStatus(courseId, status);
return result;
}
async publishCourse(courseId: string, status: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const result = await this.mcourseDAO.publish(courseId);
return result;
}
async syncCourse(courseId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const result = await this.courseDAO.updateCourseStats(courseId);
return result;
}
async deleteCourse(courseId: string) {
Validator.isValidString(courseId, "Invalid CourseId");
const result1 = await this.mcourseDAO.delete(courseId);
return result1;
}
async deleteCourseModuleTopics(courseId: string, moduleId: string) {
await this.courseTopicDAO.deleteTopics(courseId, moduleId);
}
async addCourseTopic(courseTopic: any) {
Validator.isValidString(courseTopic.courseId, "Invalid CourseId");
Validator.isValidString(courseTopic.moduleName, "Invalid Module Name");
Validator.isValidString(courseTopic.name, "Invalid Topic Name");
const courseObj: any = await this.mcourseDAO.findById(courseTopic.courseId);
this.logger.debug("CourseObj ", courseObj);
if (!courseObj) {
throw new ValidationException("Invalid CourseId");
}
courseObj.modules = courseObj.modules ? courseObj.modules : [];
const moduleObj = courseObj.modules.find((obj: any) => obj.name == courseTopic.moduleName);
if (!moduleObj) {
throw new ValidationException("Module Name not found");
}
const topics = moduleObj.topics ? moduleObj.topics : [];
const exists = topics.some(obj => obj.name == courseTopic.name);
if (exists) {
throw new ValidationException("Topic already exists");
}
const course = await this.courseTopicDAO.addTopic(courseTopic);
return course;
}
}
import { Injectable, Logger } from "@nestjs/common";
import { Connection } from "typeorm";
@Injectable()
export class CourseDAO {
private readonly logger = new Logger(CourseDAO.name);
constructor(private connection: Connection) { }
async save(course: any) {
const sql =
"insert into ct_courses ( code, title, category,created_by,modified_by) values (?,?,?,?,?)";
const results = await this.connection.query(sql, [
course.code,
course.title,
course.categoryId,
course.createdBy,
course.createdBy
]);
return results.insertId;
}
async exists(courseCode: string) {
const sql =
"select 1 from ct_courses where code = ? ";
const results = await this.connection.query(sql, [
courseCode
]);
return results.length > 0;
}
baseQuery = 'select c.id, c.code,c.title, ct.name as category, c.category as categoryId, c.display_order as displayOrder,created_date as createdDate,c.modified_date as modifiedDate, c.status,c.no_of_topics as noOfTopics, c.no_of_questions as noOfQuestions,c.org_id as orgId from ct_courses c, ct_categories ct where c.category = ct.id and c.active=1 '
async findAll(orgId: string[]) {
const sql =
this.baseQuery + " and org_id in (?) order by c.display_order,c.created_date";
const results = await this.connection.query(sql, [orgId]);
return results;
}
async findByCategory(category: string) {
const sql =
this.baseQuery + " and ct.name = ? order by c.display_order,c.created_date";
const results = await this.connection.query(sql, [category]);
return results;
}
async findById(courseCode: string) {
const sql =
this.baseQuery + ' and c.code = ?'
const results = await this.connection.query(sql, [courseCode]);
return results.length > 0 ? results[0] : null;
}
async findQuestions(courseId: string) {
const sql =
"select id, course_id as courseId,title, display_order as displayOrder,created_date from ct_course_questions where course_id = ? order by display_order,created_date";
const results = await this.connection.query(sql, [courseId]);
return results;
}
async findUsers(courseId: string) {
const sql =
"select u.id,u.name,u.username,u.email,uc.start_date as startDate, uc.completion_date as completionDate, uc.status, uc.modified_date as modifiedDate, c.no_of_topics as noOfTopics, c.no_of_questions as noOfQuestions, (SELECT COUNT(*) FROM user_course_topics uct WHERE uct.user_id = uc.user_id AND uct.course_id =uc.course_id AND uct.STATUS='C') as topicsCompleted from users u , org_users ou , ct_courses c, user_course_enrollment uc where u.username =uc.user_id and u.id = ou.user_id and c.code = uc.course_id and uc.course_id = ? order by topicsCompleted desc";
const results = await this.connection.query(sql, [courseId]);
return results;
}
async getCourseTopics(courseId: string) {
const sql = "SELECT ct.id, ct.code AS topicId, ct.name AS topicName, c.title AS courseName, cm.name AS moduleName, cm.code AS moduleId, ct.core, ct.duration, ct.topic_level as topicLevel, ct.display_order as displayOrder,ct.active "
+ " FROM ct_courses c, ct_course_modules cm, ct_course_topics ct "
+ "WHERE c.code = cm.course_id AND cm.code = ct.course_module_id " + "AND c.code = ?";
const results = await this.connection.query(sql, [courseId]);
return results;
}
async getCourseModuleTopics(courseId: string, moduleId: string) {
const sql = "SELECT ct.id, ct.code AS topicId, ct.name AS topicName, c.title AS courseName, cm.name AS moduleName, cm.code AS moduleId, ct.core, ct.duration, ct.topic_level as topicLevel, ct.display_order as displayOrder,ct.active "
+ " FROM ct_courses c, ct_course_modules cm, ct_course_topics ct "
+ "WHERE c.code = cm.course_id AND cm.code = ct.course_module_id AND c.code = ? and cm.code = ?";
this.logger.debug(sql);
const results = await this.connection.query(sql, [courseId, moduleId]);
return results;
}
async updateCourseStats(courseId: string) {
const sql = 'update ct_courses c set no_of_topics=(select count(*) from ct_course_topics where course_module_id in (select code from ct_course_modules where course_id = c.code)) where code = ? ';
const result = await this.connection.query(sql, [courseId]);
return result.affectedRows;
}
async updateStatus(courseId: string, status: string) {
const sql = 'update ct_courses c set status=?,version=version+1, no_of_topics=(select count(*) from ct_course_topics where course_module_id in (select code from ct_course_modules where course_id = c.code)) where code = ? ';
const result = await this.connection.query(sql, [status, courseId]);
return result.affectedRows;
}
async delete(courseId: string) {
const sql = 'delete from ct_courses c where code = ? and no_of_topics=0';
const result = await this.connection.query(sql, [courseId]);
return result.affectedRows;
}
}
import { Injectable, Logger } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
import { courseModuleDoc } from "src/schema/course-module.schema";
import {
courseTopicDoc,
CourseTopicSchema,
} from "src/schema/course-topic.schema";
import { Course, courseDoc } from "src/schema/course.schema";
import { Connection } from "typeorm";
@Injectable()
export class MCourseDAO {
private readonly logger = new Logger(MCourseDAO.name);
constructor(
private connection: Connection,
@InjectModel("Course") private Course: Model<courseDoc>,
@InjectModel("CourseModule") private CourseModule: Model<courseModuleDoc>,
@InjectModel("CourseTopic") private CourseTopic: Model<courseTopicDoc>
) { }
async save(course: any) {
const courseObj = new this.Course(course);
courseObj["status"] = "DRAFT";
courseObj["category"] = course.category;
courseObj["noOfModules"] = 0;
courseObj["noOfTopics"] = 0;
courseObj["noOfQuestions"] = 0;
courseObj["version"] = 1;
return courseObj.save();
}
async addModule(courseId: string, courseModule: any) {
let courseObj: any = await this.Course.findOne({
code: courseId,
}).exec();
let courseModules = courseObj.modules;
const courseModuleObj = new this.CourseModule(courseModule);
courseModules.push(courseModuleObj);
courseObj["noOfModules"] += 1;
return courseObj.save();
}
async addSlide(courseId: string, slide: any) {
let courseObj: any = await this.Course.findOne({
code: courseId,
}).exec();
let courseActivities = courseObj.activities;
const slideObj = { type:'SLIDES', url: slide.url , name: slide.name};
courseActivities.push(slideObj);
courseObj["noOfActivities"] += 1;
return courseObj.save();
}
async publish(courseId: string) {
let courseObj: any = await this.Course.findOne({
code: courseId,
}).exec();
let versionNo = courseObj["version"];
courseObj["version"] = courseObj["version"] + 1;
courseObj.modules.forEach(m => {
m.topics.forEach(t => {
t.version = versionNo;
})
})
courseObj["status"] = 'PUBLISHED';
return courseObj.save();
}
async incrementVersion(courseId: string) {
let courseObj: any = await this.Course.findOne({
code: courseId,
}).exec();
courseObj["version"] = 1;
courseObj["status"] = 'PUBLISHED';
return courseObj.save();
}
async updateModule(courseId: string, courseModule: any) {
let courseObj: any = await this.Course.findOne({
code: courseId,
}).exec();
let moduleIndex = courseObj.modules.findIndex(obj => obj.name == courseModule.moduleId);
let courseModuleObj = courseObj.modules[moduleIndex];
courseModuleObj["name"] = courseModule.name;
courseObj.modules[moduleIndex] = courseModuleObj;
return this.Course.findOneAndUpdate({ _id: courseObj._id }, { modules: courseObj.modules }).exec();
}
async deleteModule(courseId: string, moduleId: string) {
let courseObj: any = await this.Course.findOne({ code: courseId }).exec();
let moduleIndex = courseObj.modules.findIndex(
(obj: any) => obj._id == moduleId
);
const moduleObj = courseObj.modules[moduleIndex];
courseObj.modules.splice(moduleIndex, 1);
courseObj["noOfModules"] -= 1;
courseObj["noOfTopics"] -= moduleObj.topics.length;
return this.Course.findOneAndUpdate({ _id: courseObj._id },
{
noOfModules: courseObj.noOfModules,
noOfTopics: courseObj.noOfTopics, modules: courseObj.modules
}).exec();
}
async findAll(orgIds: string[]) {
let courses: any = await this.Course.find({
orgId: {
'$in': orgIds
},
}).exec();
return courses;
}
async delete(courseId: string) {
const result = await this.Course.deleteOne({ code: courseId }).exec();
return result.deletedCount;
}
async findByCategory(orgIds: string[], category: string) {
let courses: any = await this.Course.find({
orgId: {
'$in': orgIds
},
category: category
}).exec();
return courses;
}
async findByCourseIds(courseIds) {
let courses: any = await this.Course.find({
code: {
'$in': courseIds
},
}).exec();
return courses;
}
async findById(courseCode: string) {
this.logger.debug("findById", courseCode);
let courseObj: any = await this.Course.findOne({ code: courseCode }).exec();
return courseObj;
}
}