From c0f668daba31c1591b5e5d32ad2132ddf5cb1623 Mon Sep 17 00:00:00 2001 From: Scott Weaver Date: Sat, 30 Nov 2024 18:54:28 -0600 Subject: [PATCH 1/2] minor tweaks to months end actions --- convex/campaigns.ts | 170 +++++++++++++++++++++++--------------------- convex/crons.ts | 6 +- convex/users.ts | 53 +++++++++----- 3 files changed, 130 insertions(+), 99 deletions(-) diff --git a/convex/campaigns.ts b/convex/campaigns.ts index 644612d..e9bcee0 100644 --- a/convex/campaigns.ts +++ b/convex/campaigns.ts @@ -4,9 +4,11 @@ import { api, internal } from "./_generated/api"; import { v } from "convex/values"; export const createMonthlyCampaign = internalAction({ - args: {}, + args: { + dryRun: v.optional(v.boolean()), + }, returns: v.string(), - handler: async (ctx) => { + handler: async (ctx, { dryRun }) => { //get the current active global campaign const activeGlobalCampaign = await ctx.runQuery( api.campaigns.getActiveGlobalCampaign, @@ -42,34 +44,35 @@ export const createMonthlyCampaign = internalAction({ if (!chains || chains.length === 0) { console.log("No chains found for active global campaign"); - //throw new Error("No chains found for active global campaign"); } //get chain with highest chain number const highestChain = chains.reduce((prev, current) => { - if (prev.chain > current.chain) return prev; - if (prev.chain < current.chain) return current; - // If chains are equal, check wins - if (prev.wins > current.wins) return prev; - if (prev.wins < current.wins) return current; - // If wins are equal, check pushes - if (prev.pushes > current.pushes) return prev; - if (prev.pushes < current.pushes) return current; - // If everything is equal, keep the first one - return prev; + // First compare chains + if (prev.chain !== current.chain) { + return prev.chain > current.chain ? prev : current; + } + // If chains are equal, compare wins + if (prev.wins !== current.wins) { + return prev.wins > current.wins ? prev : current; + } + // If wins are equal, compare pushes (higher pushes wins) + return prev.pushes >= current.pushes ? prev : current; }); //get chain with highest win number const highestWin = chains.reduce((prev, current) => { - if (prev.wins > current.wins) return prev; - if (prev.wins < current.wins) return current; - // If wins are equal, check pushes - if (prev.pushes > current.pushes) return prev; - if (prev.pushes < current.pushes) return current; - // If everything is equal, keep the first one - return prev; + // First compare wins + if (prev.wins !== current.wins) { + return prev.wins > current.wins ? prev : current; + } + // If wins are equal, compare pushes (higher pushes wins) + return prev.pushes >= current.pushes ? prev : current; }); + console.log("Highest Chain", highestChain); + console.log("Highest Win", highestWin); + if (!highestChain) { console.log("No Chain Winner"); //throw new Error("No Chain Winner"); @@ -84,74 +87,81 @@ export const createMonthlyCampaign = internalAction({ activeGlobalCampaign.chainWinnerId = highestChain.userId; activeGlobalCampaign.winnerId = highestWin.userId; - //save the completed global campaign - await ctx.runMutation(internal.campaigns.saveCompletedCampaign, { - active: activeGlobalCampaign.active, - chainWinnerId: activeGlobalCampaign.chainWinnerId, - winnerId: activeGlobalCampaign.winnerId, - campaignId: activeGlobalCampaign._id, - }); - - //deactivate all chains - await ctx.runMutation(internal.chains.deactivateAllChains, { - campaignId: activeGlobalCampaign._id, - }); - - //todo notifications + if (!dryRun) { + //save the completed global campaign + await ctx.runMutation(internal.campaigns.saveCompletedCampaign, { + active: activeGlobalCampaign.active, + chainWinnerId: activeGlobalCampaign.chainWinnerId, + winnerId: activeGlobalCampaign.winnerId, + campaignId: activeGlobalCampaign._id, + }); - //award highest chain winner - if (monthlyChainWinAchievement) { - await ctx.scheduler.runAfter(0, api.achievements.awardAchievement, { - userId: highestChain.userId, - achievementId: monthlyChainWinAchievement._id, + //deactivate all chains + await ctx.runMutation(internal.chains.deactivateAllChains, { + campaignId: activeGlobalCampaign._id, }); + + //todo notifications + + //award highest chain winner + if (monthlyChainWinAchievement) { + await ctx.scheduler.runAfter(0, api.achievements.awardAchievement, { + userId: highestChain.userId, + achievementId: monthlyChainWinAchievement._id, + }); + } + + //award highest win winner + if (monthlyWinAchievement) { + await ctx.scheduler.runAfter(0, api.achievements.awardAchievement, { + userId: highestWin.userId, + achievementId: monthlyWinAchievement._id, + }); + } } - //award highest win winner - if (monthlyWinAchievement) { - await ctx.scheduler.runAfter(0, api.achievements.awardAchievement, { - userId: highestWin.userId, - achievementId: monthlyWinAchievement._id, + if (!dryRun) { + //create a new global campaign + const date = new Date(); + const month = date.toLocaleString("en-US", { month: "long" }); + const year = date.getFullYear(); + const campaignName = `${month} ${year} Monthly Global Campaign`; + const campaignDescription = `The monthly global campaign for ${month} ${year}, all users participate in this campaign.`; + //start date is first day of the month at 10am utc + const startDate = new Date( + date.getFullYear(), + date.getMonth(), + 1, + 10, + 0, + 0 + ); + //end date is last day of the month at 09:59:30 utc + const endDate = new Date( + date.getFullYear(), + date.getMonth() + 1, + 0, + 9, + 59, + 30 + ); + + await ctx.runMutation(internal.campaigns.createGlobalCampaign, { + name: campaignName, + description: campaignDescription, + active: true, + featured: true, + type: "GLOBAL", + startDate: startDate.getTime(), + endDate: endDate.getTime(), + ownedBy: "SYSTEM", }); + return `Created ${campaignName}`; } + return `Dry Run Completed`; } - //create a new global campaign - const date = new Date(); - const month = date.toLocaleString("en-US", { month: "long" }); - const year = date.getFullYear(); - const campaignName = `${month} ${year} Monthly Global Campaign`; - const campaignDescription = `The monthly global campaign for ${month} ${year}, all users participate in this campaign.`; - //start date is first day of the month at 10am utc - const startDate = new Date( - date.getFullYear(), - date.getMonth(), - 1, - 10, - 0, - 0 - ); - //end date is last day of the month at 09:59:30 utc - const endDate = new Date( - date.getFullYear(), - date.getMonth() + 1, - 0, - 9, - 59, - 30 - ); - - await ctx.runMutation(internal.campaigns.createGlobalCampaign, { - name: campaignName, - description: campaignDescription, - active: true, - featured: true, - type: "GLOBAL", - startDate: startDate.getTime(), - endDate: endDate.getTime(), - ownedBy: "SYSTEM", - }); - return `Created ${campaignName}`; + return "Campaign processing completed"; }, }); diff --git a/convex/crons.ts b/convex/crons.ts index e27a285..05d418c 100644 --- a/convex/crons.ts +++ b/convex/crons.ts @@ -18,13 +18,15 @@ crons.daily( crons.monthly( "Monthly Campaign Finalization, Achievement Awards, and New Campaign Creation", { day: 1, hourUTC: 10, minuteUTC: 0 }, // 1st of the month at 10:00 UTC, Midnight HST - internal.campaigns.createMonthlyCampaign + internal.campaigns.createMonthlyCampaign, + { dryRun: false } ); crons.monthly( "Record monthly statistics for all players", { day: 1, hourUTC: 9, minuteUTC: 45 }, // last day of the month at 11:45 PM HST - internal.users.monthlyStatsRecord + internal.users.monthlyStatsRecord, + { dryRun: false } ); export default crons; diff --git a/convex/users.ts b/convex/users.ts index 5cb328c..074a398 100644 --- a/convex/users.ts +++ b/convex/users.ts @@ -347,19 +347,28 @@ export const updateUserMonthlyStats = internalMutation({ _id: v.id("users"), monthlyStats: v.any(), }), + dryRun: v.optional(v.boolean()), }, - handler: async (ctx, { user }) => { - await ctx.db.patch(user._id, { - monthlyStats: { - ...user.monthlyStats, - }, - }); + handler: async (ctx, { user, dryRun }) => { + if (dryRun) { + console.log("UPDATING MONTHLY STATS"); + console.log(user.monthlyStats); + } + if (!dryRun) { + await ctx.db.patch(user._id, { + monthlyStats: { + ...user.monthlyStats, + }, + }); + } }, }); export const monthlyStatsRecord = internalAction({ - args: {}, - handler: async (ctx) => { + args: { + dryRun: v.optional(v.boolean()), + }, + handler: async (ctx, { dryRun }) => { const users = await ctx.runQuery(api.users.getAllUsers); for (const user of users) { if (!user.monthlyStats) { @@ -376,7 +385,9 @@ export const monthlyStatsRecord = internalAction({ currentStats.totalGames = user.stats.wins + user.stats.losses + user.stats.pushes; if (currentStats.totalGames > 0) { - currentStats.winRate = user.stats.wins / currentStats.totalGames; + currentStats.winRate = Number( + (user.stats.wins / currentStats.totalGames).toFixed(2) + ); } if (currentStats.totalGames === 0) { currentStats.winRate = 0; @@ -384,16 +395,24 @@ export const monthlyStatsRecord = internalAction({ currentStats.coins = user.coins; currentStats.statsByLeague = user.stats.statsByLeague; - await ctx.runMutation(internal.users.updateUserMonthlyStats, { - user: { - _id: user._id, - monthlyStats: { - ...user.monthlyStats, - [currentMonth]: currentStats, + if (dryRun) { + console.log("CURRENT STATS", currentMonth, user.name); + console.log(currentStats); + } + + if (!dryRun) { + await ctx.runMutation(internal.users.updateUserMonthlyStats, { + user: { + _id: user._id, + monthlyStats: { + ...user.monthlyStats, + [currentMonth]: currentStats, + }, }, - }, - }); + }); + } } + return "Monthly stats processing completed"; }, }); From b95d042c00a64c886d9562cc3e041cc0b01a930b Mon Sep 17 00:00:00 2001 From: Scott Weaver Date: Sat, 30 Nov 2024 19:03:10 -0600 Subject: [PATCH 2/2] adjust leaderboard sort --- convex/leaderboards.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/convex/leaderboards.ts b/convex/leaderboards.ts index 5bbb284..fd6193a 100644 --- a/convex/leaderboards.ts +++ b/convex/leaderboards.ts @@ -9,8 +9,19 @@ export const getChainLeaderboard = query({ .withIndex("by_active", (q) => q.eq("active", true)) .collect(); - //sort chains by best chain - chains.sort((a, b) => b.chain - a.chain); + //sort chains by best chain, then wins, then pushes + chains.sort((a, b) => { + // First compare chains + if (a.chain !== b.chain) { + return b.chain - a.chain; + } + // If chains are equal, compare wins + if (a.wins !== b.wins) { + return b.wins - a.wins; + } + // If wins are equal, compare pushes + return b.pushes - a.pushes; + }); return Promise.all( (chains as any[]).map(async (chain, index) => {