Skip to content

Commit

Permalink
add function to record leaderboard data
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-sidhdhi-p committed Dec 13, 2024
1 parent a40c3a6 commit d6c46ea
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 5 deletions.
4 changes: 2 additions & 2 deletions khelo/functions/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default [
google,
{
languageOptions: {
ecmaVersion: 2018,
ecmaVersion: 2021,
globals: {
...globals.browser,
...globals.node,
Expand All @@ -21,7 +21,7 @@ export default [
"quotes": ["error", "double"],
"import/no-unresolved": 0,
"indent": ["error", 2],
"max-len": ["error", {"code": 140}],
"max-len": ["error", {"code": 200}],
"new-cap": 0,
"require-jsdoc": 0,
"no-extend-native": 0,
Expand Down
2 changes: 1 addition & 1 deletion khelo/functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion khelo/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"logs": "firebase functions:log"
},
"engines": {
"node": "18"
"node": "20"
},
"main": "src/index.js",
"dependencies": {
Expand Down
23 changes: 22 additions & 1 deletion khelo/functions/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

Object.defineProperty(exports, "__esModule", {value: true});
exports.fiveMinuteCron = exports.apiv1 = exports.teamPlayerChangeObserver = exports.TIMEZONE = void 0;
exports.fiveMinuteCron = exports.apiv1 = exports.teamPlayerChangeObserver = exports.userStatWriteObserver = exports.TIMEZONE = void 0;

const express = require("express");

Expand All @@ -15,11 +15,13 @@ const logger = require("firebase-functions/logger");
const team_repository = require("./team/team_repository");
const user_repository = require("./user/user_repository");
const match_repository = require("./match/match_repository");
const leaderboard_repository = require("./leaderboard/leaderboard_repository");

const notification_service = require("./notification/notification_service");
const team_service = require("./team/team_service");
const match_service = require("./match/match_service");
const auth_service = require("./auth/auth_service");
const leaderboard_service = require("./leaderboard/leaderboard_service");

exports.TIMEZONE = "Asia/Kolkata";
const REGION = "asia-south1";
Expand All @@ -28,11 +30,13 @@ const db = (0, firestore_1.getFirestore)(app);

const userRepository = new user_repository.UserRepository(db);
const teamRepository = new team_repository.TeamRepository(db);
const leaderboardRepository = new leaderboard_repository.LeaderboardRepository(db);

const notificationService = new notification_service.NotificationService(userRepository);
const teamService = new team_service.TeamService(userRepository, notificationService);
const matchService = new match_service.MatchService(userRepository, teamRepository, notificationService);
const authService = new auth_service.AuthService(userRepository);
const leaderboardService = new leaderboard_service.LeaderboardService(leaderboardRepository);

const matchRepository = new match_repository.MatchRepository(db, matchService);

Expand Down Expand Up @@ -66,6 +70,23 @@ exports.teamPlayerChangeObserver = (0, firestore_2.onDocumentUpdated)({region: R
await teamService.notifyOnAddedToTeam(oldTeam, newTeam);
});

exports.userStatWriteObserver = (0, firestore_2.onDocumentWritten)({region: REGION, document: "users/{userId}/user_stat/{type}"}, async (event) => {
const snapshot = event.data;
if (!snapshot) {
(0, logger.error)("No data associated with the event");
return;
}

const oldStat = snapshot?.before?.data();
const newStat = snapshot?.after?.data();

const {userId} = event.params;

if (newStat) {
await leaderboardService.updateLeaderboard(userId, oldStat, newStat);
}
});

exports.fiveMinuteCron = (0, scheduler.onSchedule)({timeZone: exports.TIMEZONE, schedule: "*/5 * * * *", region: REGION}, async () => {
await matchRepository.processUpcomingMatches();
});
Expand Down
90 changes: 90 additions & 0 deletions khelo/functions/src/leaderboard/leaderboard_repository.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
exports.LeaderboardRepository = void 0;

class LeaderboardRepository {
constructor(db) {
this.db = db;
}
leaderboardRef() {
return this.db.collection("leaderboard");
}
timeBasedLeaderboardRef(timeRange) {
return this.leaderboardRef()
.doc(timeRange)
.collection("data");
}

async getWeeklyLeaderboardOfUser(userId) {
try {
const userRef = this.timeBasedLeaderboardRef("weekly").doc(userId);
const weeklyDoc = await userRef.get();
if (!weeklyDoc.exists) {
return null;
}
return weeklyDoc.data();
} catch (e) {
console.error("LeaderboardRepository: Error getting weekly data of user:", e);
return null;
}
}

async getMonthlyLeaderboardOfUser(userId) {
try {
const userRef = this.timeBasedLeaderboardRef("monthly").doc(userId);
const monthlyDoc = await userRef.get();
if (!monthlyDoc.exists) {
return null;
}
return monthlyDoc.data();
} catch (e) {
console.error("LeaderboardRepository: Error getting monthly data of user:", e);
return null;
}
}

async getAllTimeLeaderboardOfUser(userId) {
try {
const userRef = this.timeBasedLeaderboardRef("all_time").doc(userId);
const allTimeDoc = await userRef.get();
if (!allTimeDoc.exists) {
return null;
}
return allTimeDoc.data();
} catch (e) {
console.error("LeaderboardRepository: Error getting all time data of user:", e);
return null;
}
}

async updateWeeklyLeaderboardOfUser(stats) {
try {
const userRef = this.timeBasedLeaderboardRef("weekly").doc(stats.id);
await userRef.set(stats);
} catch (e) {
console.error("LeaderboardRepository: Error in updating weekly data of user:", e);
return null;
}
}

async updateMonthlyLeaderboardOfUser(stats) {
try {
const userRef = this.timeBasedLeaderboardRef("monthly").doc(stats.id);
await userRef.set(stats);
} catch (e) {
console.error("LeaderboardRepository: Error in updating monthly data of user:", e);
return null;
}
}

async updateAllTimeLeaderboardOfUser(stats) {
try {
const userRef = this.timeBasedLeaderboardRef("all_time").doc(stats.id);
await userRef.set(stats);
} catch (e) {
console.error("LeaderboardRepository: Error in updating all time data of user:", e);
return null;
}
}
}
exports.LeaderboardRepository=LeaderboardRepository;
94 changes: 94 additions & 0 deletions khelo/functions/src/leaderboard/leaderboard_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
exports.LeaderboardService = void 0;
const firestore_1 = require("firebase-admin/firestore");

class LeaderboardService {
constructor(leaderboardRepository) {
this.leaderboardRepository = leaderboardRepository;
}

async updateLeaderboard(userId, oldStat, newStat) {
let weekStat = await this.leaderboardRepository.getWeeklyLeaderboardOfUser(userId);
let monthStat = await this.leaderboardRepository.getMonthlyLeaderboardOfUser(userId);
let allTimeStat = await this.leaderboardRepository.getAllTimeLeaderboardOfUser(userId);

const runDiff = (newStat.batting?.run_scored ?? 0) - (oldStat?.batting?.run_scored ?? 0);
const wicketDiff = (newStat.bowling?.wicket_taken ?? 0) - (oldStat?.bowling?.wicket_taken ?? 0);
const catchDiff = (newStat.fielding?.catches ?? 0) - (oldStat?.fielding?.catches ?? 0);

const runs = runDiff >= 0 ? runDiff : newStat.batting?.run_scored ?? 0;
const wickets = wicketDiff >= 0 ? wicketDiff : newStat.bowling?.wicket_taken ?? 0;
const catches = catchDiff >= 0 ? catchDiff : newStat.fielding?.catches ?? 0;

if (!weekStat) {
weekStat = {id: userId, runs: runs, wickets: wickets, catches: catches, date: new Date()};
} else {
if (weekStat.date instanceof firestore_1.Timestamp && this.isTimeInCurrentWeek(weekStat.date.toDate())) {
weekStat.runs += runs;
weekStat.wickets += wickets;
weekStat.catches += catches;
} else {
weekStat.runs = runs;
weekStat.wickets = wickets;
weekStat.catches = catches;
}
weekStat.date = new Date();
}

// Check if monthStat exists, otherwise create a new object
if (!monthStat) {
monthStat = {id: userId, runs: runs, wickets: wickets, catches: catches, date: new Date()};
} else {
// Update the monthStat if it's within the current month, else reset it
if (monthStat.date instanceof firestore_1.Timestamp && this.isTimeInCurrentMonth(monthStat.date.toDate())) {
monthStat.runs += runs;
monthStat.wickets += wickets;
monthStat.catches += catches;
} else {
monthStat.runs = runs;
monthStat.wickets = wickets;
monthStat.catches = catches;
}
monthStat.date = new Date();
}

if (!allTimeStat) {
allTimeStat = {id: userId, runs: runs, wickets: wickets, catches: catches, date: new Date()};
} else {
allTimeStat.runs += runs;
allTimeStat.wickets += wickets;
allTimeStat.catches += catches;
allTimeStat.date = new Date();
}

await this.leaderboardRepository.updateWeeklyLeaderboardOfUser(weekStat);
await this.leaderboardRepository.updateMonthlyLeaderboardOfUser(monthStat);
await this.leaderboardRepository.updateAllTimeLeaderboardOfUser(allTimeStat);
}

isTimeInCurrentWeek(date) {
// Note: Current week starts from Monday and ends on Sunday
const givenDate = new Date(date);
const currentDate = new Date();

const startOfWeek = new Date(currentDate);
startOfWeek.setDate(currentDate.getDate() - currentDate.getDay() + 1);

const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(startOfWeek.getDate() + 6);

return givenDate >= startOfWeek && givenDate <= endOfWeek;
}

isTimeInCurrentMonth(date) {
const givenDate = new Date(date);
const currentDate = new Date();

return (
givenDate.getMonth() === currentDate.getMonth() &&
givenDate.getFullYear() === currentDate.getFullYear()
);
}
}
exports.LeaderboardService = LeaderboardService;

0 comments on commit d6c46ea

Please sign in to comment.