···
1
+
import { db } from "./db";
2
+
import { users as usersTable, takes as takesTable } from "./schema";
3
+
import { sql, eq, and, ne } from "drizzle-orm";
6
+
* Finds and corrects any drift between the computed user total time and the stored value.
7
+
* This helps ensure time calculations remain accurate even if triggers fail or data gets out of sync.
9
+
export async function validateAndFixUserTotals() {
11
+
console.log("Validating user totals...");
13
+
// First, get the calculated totals per user
14
+
const calculatedTotals = await db
16
+
userId: takesTable.userId,
18
+
sql<number>`COALESCE(SUM(${takesTable.elapsedTime}), 0)::integer`.as(
23
+
.groupBy(takesTable.userId);
25
+
// Convert to a map for easier lookup
26
+
const totalsMap = new Map(
27
+
calculatedTotals.map((item) => [item.userId, item.calculatedTotal]),
31
+
const allUsers = await db
34
+
storedTotal: usersTable.totalTakesTime,
38
+
// Find users with drift
39
+
const driftedUsers = allUsers
41
+
const calculatedTotal = totalsMap.get(user.id) || 0;
42
+
return user.storedTotal !== calculatedTotal;
46
+
storedTotal: user.storedTotal,
47
+
calculatedTotal: totalsMap.get(user.id) || 0,
50
+
if (driftedUsers.length === 0) {
51
+
console.log("✅ All user totals are in sync");
52
+
return { fixed: 0, errors: [] };
56
+
`❌ Found ${driftedUsers.length} users with incorrect totals`,
59
+
// Fix each drifted user
60
+
const errors: string[] = [];
63
+
for (const user of driftedUsers) {
67
+
.set({ totalTakesTime: user.calculatedTotal })
68
+
.where(eq(usersTable.id, user.id));
71
+
`Fixed user ${user.id}: ${user.storedTotal} → ${user.calculatedTotal}`,
75
+
const errorMsg = `Failed to fix user ${user.id}: ${error}`;
76
+
console.error(errorMsg);
77
+
errors.push(errorMsg);
81
+
console.log(`✅ Fixed ${fixed} user totals`);
83
+
return { fixed, errors };
85
+
console.error("Error validating user totals:", error);