Skip to content

Commit

Permalink
Merge pull request #51 from SAIG-KMITL/feat/connect-with-roadmap-ai
Browse files Browse the repository at this point in the history
Feat/connect with roadmap ai
  • Loading branch information
Ganthepro authored Nov 28, 2024
2 parents f95050f + 1e6d090 commit cfb02d3
Show file tree
Hide file tree
Showing 13 changed files with 3,793 additions and 4,600 deletions.
8,130 changes: 3,570 additions & 4,560 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions src/course/course.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ export class Course {
@ManyToOne(() => User)
@JoinColumn({ name: 'teacher_id' })
teacher: User;
@Column({ name: 'teacher_id' })
teacherId: string;

@ManyToOne(() => Category)
@JoinColumn({ name: 'category_id' })
Expand Down
14 changes: 14 additions & 0 deletions src/pretest/dtos/evaluate.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';

export class EvaluateResponseDto {
@ApiProperty({
description: 'The result of the evaluation',
type: String,
example: 'Passed',
})
result: string;

constructor(result: string) {
this.result = result;
}
}
2 changes: 1 addition & 1 deletion src/pretest/dtos/pretest.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ export class PretestDto {
],
})
data: QuestionAiDto[];
}
}
5 changes: 5 additions & 0 deletions src/pretest/interfaces/create-evaluate.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface CreateEvaluate {
question: string[];
correct_answer: string[];
user_answer: string[];
}
36 changes: 29 additions & 7 deletions src/pretest/pretest.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ import {
Req,
} from '@nestjs/common';
import { ApiBearerAuth, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
import { PretestService } from './pretest.service';
import {
PaginatedPretestResponseDto,
PretestResponseDto,
} from './dtos/pretest-response.dto';
import { AuthenticatedRequest } from 'src/auth/interfaces/authenticated-request.interface';
import { PaginateQueryDto } from 'src/shared/pagination/dtos/paginate-query.dto';
import { Roles } from 'src/shared/decorators/role.decorator';
import { Role } from 'src/shared/enums';
import { PaginateQueryDto } from 'src/shared/pagination/dtos/paginate-query.dto';
import { CreatePretestDto } from './dtos/create-pretest.dto';
import { EvaluateResponseDto } from './dtos/evaluate.dto';
import {
PaginatedPretestResponseDto,
PretestResponseDto,
} from './dtos/pretest-response.dto';
import { UpdatePretestDto } from './dtos/update-pretest.dto';

import { PretestService } from './pretest.service';
@Controller('pretest')
@ApiTags('Pretest')
@ApiBearerAuth()
Expand Down Expand Up @@ -99,6 +99,28 @@ export class PretestController {
return new PretestResponseDto(pretest);
}

@Post('/evaluate/:id')
@Roles(Role.STUDENT)
@ApiResponse({
status: HttpStatus.OK,
description: 'Evaluate a pretest',
type: EvaluateResponseDto,
})
async evaluatePretest(
@Req() request: AuthenticatedRequest,
@Param(
'id',
new ParseUUIDPipe({
version: '4',
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
}),
)
id: string,
) {
const pretest = await this.pretestService.evaluatePretest(request.user, id);
return new EvaluateResponseDto(pretest);
}

@Post()
@Roles(Role.STUDENT)
@ApiResponse({
Expand Down
2 changes: 2 additions & 0 deletions src/pretest/pretest.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { HttpModule } from '@nestjs/axios';
import { ConfigModule } from '@nestjs/config';
import { QuestionModule } from 'src/question/question.module';
import { QuestionOptionModule } from 'src/question-option/question-option.module';
import { ExamAnswerModule } from 'src/exam-answer/exam-answer.module';

@Module({
imports: [
Expand All @@ -22,6 +23,7 @@ import { QuestionOptionModule } from 'src/question-option/question-option.module
ConfigModule,
QuestionModule,
QuestionOptionModule,
ExamAnswerModule,
],
controllers: [PretestController],
providers: [...pretestProviders, PretestService],
Expand Down
69 changes: 56 additions & 13 deletions src/pretest/pretest.service.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import { HttpService } from '@nestjs/axios';
import {
BadRequestException,
Inject,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { Pretest } from './pretest.entity';
import { ConfigService } from '@nestjs/config';
import { JwtPayloadDto } from 'src/auth/dtos/jwt-payload.dto';
import { ExamAnswerService } from 'src/exam-answer/exam-answer.service';
import { QuestionOptionService } from 'src/question-option/question-option.service';
import { QuestionService } from 'src/question/question.service';
import { GLOBAL_CONFIG } from 'src/shared/constants/global-config.constant';
import { QuestionType, Role } from 'src/shared/enums';
import { createPagination } from 'src/shared/pagination';
import { UserBackgroundService } from 'src/user-background/user-background.service';
import { User } from 'src/user/user.entity';
import {
FindOneOptions,
FindOptionsSelect,
FindOptionsWhere,
ILike,
Repository,
} from 'typeorm';
import { PaginatedPretestResponseDto } from './dtos/pretest-response.dto';
import { createPagination } from 'src/shared/pagination';
import { User } from 'src/user/user.entity';
import { QuestionType, Role } from 'src/shared/enums';
import { CreatePretestDto } from './dtos/create-pretest.dto';
import { UpdatePretestDto } from './dtos/update-pretest.dto';
import { UserService } from 'src/user/user.service';
import { UserBackgroundService } from 'src/user-background/user-background.service';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { PaginatedPretestResponseDto } from './dtos/pretest-response.dto';
import { PretestDto } from './dtos/pretest.dto';
import { GLOBAL_CONFIG } from 'src/shared/constants/global-config.constant';
import { QuestionService } from 'src/question/question.service';
import { QuestionOptionService } from 'src/question-option/question-option.service';
import { UpdatePretestDto } from './dtos/update-pretest.dto';
import { CreateEvaluate } from './interfaces/create-evaluate';
import { Pretest } from './pretest.entity';

@Injectable()
export class PretestService {
Expand All @@ -39,6 +41,7 @@ export class PretestService {
private readonly configService: ConfigService,
private readonly questionService: QuestionService,
private readonly questionOptionService: QuestionOptionService,
private readonly examAnswerService: ExamAnswerService,
) {}
async findAll(
userId: string,
Expand Down Expand Up @@ -203,6 +206,14 @@ export class PretestService {
};
}

async fetchEvaluation(requestBody: CreateEvaluate) {
const response = await this.httpService.axiosRef.post(
`https://ai.edusaig.com/ai/evaluate`,
requestBody,
);
return { data: response.data };
}

async fetchData(userId: string, pretestId: string): Promise<PretestDto> {
const userBackground = await this.userBackgroundService.findOneByUserId(
userId,
Expand Down Expand Up @@ -248,10 +259,42 @@ export class PretestService {
);
return { data: response.data };
} catch (error) {
console.log(error);
throw new Error('Failed to fetch data or process request');
}
}

async evaluatePretest(
user: JwtPayloadDto,
pretestId: string,
): Promise<string> {
const preTestExam =
await this.examAnswerService.findExamAnswerPretestByPretestId(
user.id,
user.role,
pretestId,
{
page: 1,
limit: 10,
search: '',
},
);

const requestBody: CreateEvaluate = {
question: preTestExam.data.map((data) => data.question.question),
correct_answer: preTestExam.data.map(
(data) => data.correctAnswer.optionText,
),
user_answer: preTestExam.data.map(
(data) => data.selectedOption.optionText,
),
};

const response = await this.fetchEvaluation(requestBody);

return response.data;
}

async createQuestionAndChoice(
pretestId: string,
userId: string,
Expand Down
13 changes: 13 additions & 0 deletions src/roadmap/dtos/create-roadmp-ai.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';

export class CreateRoadmapAiDto {
@IsString()
@IsNotEmpty()
@ApiProperty({
description: 'Roadmap AI Pretest Description',
type: String,
example: 'AI Roadmap',
})
preTestDescription: string;
}
3 changes: 2 additions & 1 deletion src/roadmap/roadmap.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Role } from 'src/shared/enums';
import { PaginateQueryDto } from 'src/shared/pagination/dtos/paginate-query.dto';
import { CreateRoadmapDto, RoadmapResponseDto } from './dtos';
import { RoadmapService } from './roadmap.service';
import { CreateRoadmapAiDto } from './dtos/create-roadmp-ai.dto';

@Controller('roadmap')
@Injectable()
Expand Down Expand Up @@ -90,7 +91,7 @@ export class RoadmapController {
@HttpCode(HttpStatus.CREATED)
async create(
@Req() request: AuthenticatedRequest,
@Body() createRoadmapDto: CreateRoadmapDto,
@Body() createRoadmapDto: CreateRoadmapAiDto,
) {
return await this.roadmapService.create(request.user.id, createRoadmapDto);
}
Expand Down
13 changes: 12 additions & 1 deletion src/roadmap/roadmap.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CourseModule } from 'src/course-module/course-module.entity';
import { UserBackgroundModule } from 'src/user-background/user-background.module';
import { RoadmapController } from './roadmap.controller';
import { Roadmap } from './roadmap.entity';
import { RoadmapService } from './roadmap.service';
import { Course } from 'src/course/course.entity';

@Module({
imports: [TypeOrmModule.forFeature([Roadmap])],
imports: [
TypeOrmModule.forFeature([Roadmap, Course]),
HttpModule,
ConfigModule,
UserBackgroundModule,
CourseModule,
],
controllers: [RoadmapController],
providers: [RoadmapService],
})
Expand Down
102 changes: 88 additions & 14 deletions src/roadmap/roadmap.service.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,109 @@
import { HttpService } from '@nestjs/axios';
import {
Inject,
Injectable,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Course } from 'src/course/course.entity';
import { GLOBAL_CONFIG } from 'src/shared/constants/global-config.constant';
import { createPagination } from 'src/shared/pagination';
import { UserBackgroundService } from 'src/user-background/user-background.service';
import { FindOneOptions, FindOptionsWhere, Repository } from 'typeorm';
import {
CreateRoadmapDto,
PaginatedRoadmapResponseDto,
UpdateRoadmapDto,
} from './dtos';
import { PaginatedRoadmapResponseDto, UpdateRoadmapDto } from './dtos';
import { CreateRoadmapAiDto } from './dtos/create-roadmp-ai.dto';
import { Roadmap } from './roadmap.entity';

@Injectable()
export class RoadmapService {
constructor(
@Inject('RoadmapRepository')
private readonly roadmapRepository: Repository<Roadmap>,
@Inject('CourseRepository')
private readonly courseRepository: Repository<Course>,
private readonly httpService: HttpService,
private readonly configService: ConfigService,
private readonly userBackgroundService: UserBackgroundService,
) {}

async create(
private readonly defaultRelations = {
teacher: true,
category: true,
};

async fetchRoadmapData(
userId: string,
createRoadmapDto: CreateRoadmapDto,
): Promise<Roadmap> {
createRoadmapAiDto: CreateRoadmapAiDto,
) {
const userBackground = await this.userBackgroundService.findOneByUserId(
userId,
);
const course = await this.courseRepository.find({
relations: this.defaultRelations,
});
try {
return await this.roadmapRepository.save({
...createRoadmapDto,
user: { id: userId },
courses: createRoadmapDto.courses.map((courseId) => ({ id: courseId })),
});
const requestBody = {
courses: course,
user_data: {
age: '39',
department: 'Computer Science',
interest: userBackground.topics.map((topic) => topic.title),
name: userBackground.user.fullname,
preTestDescription: createRoadmapAiDto.preTestDescription,
preTestScore: 70,
university: 'Yale',
userID: userId,
},
};
console.log('Request body', requestBody);
const response = await this.httpService.axiosRef.post(
`https://ai.edusaig.com/ai/generate-roadmap`,
requestBody,
);
return { data: response.data };
} catch (error) {
throw new InternalServerErrorException(error);
}
}

async create(userId: string, createRoadmapAiDto: CreateRoadmapAiDto) {
try {
const roadmap = await this.fetchRoadmapData(userId, createRoadmapAiDto);

await Promise.all(
roadmap.data.validated_roadmap.recommended_courses.map(
async (course) => {
const courseData = await this.courseRepository.findOne({
where: { id: course.id },
});

if (courseData) {
const existingRoadmap = await this.roadmapRepository.findOne({
where: {
user: { id: userId },
courses: { id: course.id },
},
});

if (!existingRoadmap) {
const requestBody = {
duration: course.duration.toString(),
priority: course.priority,
courses: [course.id],
};

await this.roadmapRepository.save({
...requestBody,
user: { id: userId },
courses: requestBody.courses.map((courseId) => ({
id: courseId,
})),
});
}
}
},
),
);
} catch (error) {
if (error instanceof NotFoundException) throw error;
throw new InternalServerErrorException(error.message);
Expand Down
Loading

0 comments on commit cfb02d3

Please sign in to comment.