Skip to content

Commit

Permalink
#66 Add new statistics service to compute statistics for every quizlo…
Browse files Browse the repository at this point in the history
…rd user
  • Loading branch information
danielemery committed Nov 4, 2023
1 parent dac6de4 commit e7bb25e
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/service.locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import { S3FileService } from './file/s3.service';
import { SQSQueueService } from './queue/sqs.service';
import { QuizPersistence } from './quiz/quiz.persistence';
import { QuizService } from './quiz/quiz.service';
import { StatisticsService } from './statistics/statistics.service';
import { UserPersistence } from './user/user.persistence';
import { UserService } from './user/user.service';
import { MemoryCache } from './util/cache';

const memoryCache = new MemoryCache();

// auth
export const authenticationService = new AuthenticationService();
Expand All @@ -28,3 +32,6 @@ export const userService = new UserService(userPersistence);

// queue
export const queueService = new SQSQueueService(quizService);

// statistics
export const statisticsService = new StatisticsService(userService, quizService, memoryCache);
87 changes: 87 additions & 0 deletions src/statistics/statistics.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { QuizService } from '../quiz/quiz.service';
import { UserService } from '../user/user.service';
import { Cache } from '../util/cache';
import { IndividualUserStatistic } from './statistics.dto';

const INDIVIDUAL_STATISTICS_CACHE_KEY = 'invidual-user-statistics';

export class StatisticsService {
#userService: UserService;
#quizService: QuizService;
#cache: Cache;
constructor(userService: UserService, quizService: QuizService, cache: Cache) {
this.#userService = userService;
this.#quizService = quizService;
this.#cache = cache;
}

/**
* Gets the individual statistics for all users.
* @returns An array of users with their statistics.
*
* @tags worker
*/
async getIndividualUserStatistics(): Promise<IndividualUserStatistic[]> {
const cachedResult = await this.#cache.getItem<IndividualUserStatistic[]>(INDIVIDUAL_STATISTICS_CACHE_KEY);
if (cachedResult) {
return cachedResult;
}

const results: IndividualUserStatistic[] = [];
let hasMoreRows = true;
let cursor: string | undefined = undefined;
while (hasMoreRows) {
const { data, hasMoreRows: moreRows } = await this.#userService.getUsers({
currentUserId: '1', // Current user id isn't valid here and isn't used for sorting by EMAIL_ASC
first: 100,
afterId: cursor,
sortedBy: 'EMAIL_ASC',
});

for (const user of data) {
const { totalQuizCompletions, averageScorePercentage } = await this.#getStatisticsForUser(user.email);
results.push({
email: user.email,
name: user.name,
totalQuizCompletions,
averageScorePercentage,
});
}

cursor = data[data.length - 1]?.id;
hasMoreRows = moreRows;
}

return results;
}

/**
* Get quiz completion statistics for a single user.
* @param userEmail The email of the user to get statistics for.
* @returns Total quiz completions and average score percentage for the user.
*
* @tags worker
*/
async #getStatisticsForUser(userEmail: string) {
let hasMoreRows = true;
let cursor: string | undefined = undefined;
const completionsScores: number[] = [];
while (hasMoreRows) {
const { stats, cursor: latestCursor } = await this.#quizService.quizScorePercentagesForUser({
email: userEmail,
first: 100,
afterId: cursor,
});

completionsScores.push(...stats);

cursor = latestCursor;
hasMoreRows = !!latestCursor;
}

return {
totalQuizCompletions: completionsScores.length,
averageScorePercentage: completionsScores.reduce((a, b) => a + b, 0) / completionsScores.length,
};
}
}

0 comments on commit e7bb25e

Please sign in to comment.