-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Backend logic to track statistics
- Loading branch information
1 parent
df3c6ea
commit 34d6b10
Showing
26 changed files
with
2,923 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export type ActivityHistory = { | ||
[date: string]: boolean; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
export class CourseStartedEvent { | ||
constructor( | ||
public readonly userId: string, | ||
public readonly courseId: string, | ||
) {} | ||
} | ||
|
||
export class CourseCompletedEvent { | ||
constructor( | ||
public readonly userId: string, | ||
public readonly courseId: string, | ||
) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Global, Module } from "@nestjs/common"; | ||
import { CqrsModule } from "@nestjs/cqrs"; | ||
|
||
@Global() | ||
@Module({ | ||
imports: [CqrsModule], | ||
exports: [CqrsModule], | ||
}) | ||
export class EventsModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from "./course/course-activity.event"; | ||
export * from "./lesson/lesson-completed.event"; | ||
export * from "./quiz/quiz-completed.event"; | ||
export * from "./user/user-activity.event"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export class LessonCompletedEvent { | ||
constructor( | ||
public readonly userId: string, | ||
public readonly courseId: string, | ||
public readonly lessonId: string, | ||
) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export class QuizCompletedEvent { | ||
constructor( | ||
public readonly userId: string, | ||
public readonly courseId: string, | ||
public readonly lessonId: string, | ||
public readonly correctAnswers: number, | ||
public readonly wrongAnswers: number, | ||
public readonly score: number, | ||
) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export type UserActivityType = "LOGIN" | "LESSON_PROGRESS" | "COURSE_PROGRESS"; | ||
|
||
export class UserActivityEvent { | ||
constructor( | ||
public readonly userId: string, | ||
public readonly activityType: UserActivityType, | ||
public readonly metadata?: Record<string, any>, | ||
) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Controller, Get } from "@nestjs/common"; | ||
import { Validate } from "nestjs-typebox"; | ||
|
||
import { baseResponse, BaseResponse, UUIDType } from "src/common"; | ||
|
||
import { CurrentUser } from "../../common/decorators/user.decorator"; | ||
import { UserStatsSchema } from "../schemas/userStats.schema"; | ||
import { StatisticsService } from "../statistics.service"; | ||
|
||
import type { UserStats } from "../schemas/userStats.schema"; | ||
|
||
@Controller("statistics") | ||
export class StatisticsController { | ||
constructor(private statisticsService: StatisticsService) {} | ||
|
||
@Get() | ||
@Validate({ | ||
response: baseResponse(UserStatsSchema), | ||
}) | ||
async getUserStatistics( | ||
@CurrentUser("userId") currentUserId: UUIDType, | ||
): Promise<BaseResponse<UserStats>> { | ||
return new BaseResponse(await this.statisticsService.getUserStats(currentUserId)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
import { EventsHandler } from "@nestjs/cqrs"; | ||
import { match } from "ts-pattern"; | ||
|
||
import { QuizCompletedEvent, UserActivityEvent, CourseStartedEvent } from "src/events"; | ||
|
||
import { StatisticsRepository } from "../repositories/statistics.repository"; | ||
|
||
import type { IEventHandler } from "@nestjs/cqrs"; | ||
|
||
type StatisticsEvent = QuizCompletedEvent | UserActivityEvent | CourseStartedEvent; | ||
|
||
@Injectable() | ||
@EventsHandler(QuizCompletedEvent, UserActivityEvent, CourseStartedEvent) | ||
export class StatisticsHandler implements IEventHandler<QuizCompletedEvent | UserActivityEvent> { | ||
constructor(private readonly statisticsRepository: StatisticsRepository) {} | ||
|
||
async handle(event: StatisticsEvent) { | ||
try { | ||
match(event) | ||
.when( | ||
(e): e is QuizCompletedEvent => e instanceof QuizCompletedEvent, | ||
async (quizEvent) => { | ||
await this.handleQuizCompleted(quizEvent); | ||
}, | ||
) | ||
.when( | ||
(e): e is UserActivityEvent => e instanceof UserActivityEvent, | ||
async (activityEvent) => { | ||
await this.handleUserActivity(activityEvent); | ||
}, | ||
) | ||
.otherwise(() => { | ||
throw new Error("Unknown event type"); | ||
}); | ||
} catch (error) { | ||
console.error("Error handling event:", error); | ||
} | ||
} | ||
|
||
private async handleQuizCompleted(event: QuizCompletedEvent) { | ||
await this.statisticsRepository.createQuizAttempt({ | ||
userId: event.userId, | ||
courseId: event.courseId, | ||
lessonId: event.lessonId, | ||
correctAnswers: event.correctAnswers, | ||
wrongAnswers: event.wrongAnswers, | ||
score: event.score, | ||
}); | ||
} | ||
|
||
private async handleUserActivity(event: UserActivityEvent) { | ||
await this.statisticsRepository.updateUserActivity(event.userId); | ||
} | ||
} |
Oops, something went wrong.