import { fetchGradesFromScraper } from './scraper.service'; import { syncGradesToDatabase, getAllUsers } from './grades.service'; interface SyncResult { username: string; success: boolean; error?: string; classesProcessed?: number; assignmentsAdded?: number; assignmentsUpdated?: number; } export async function syncUserGrades( username: string, password: string ): Promise { try { console.log(`[Sync] Starting sync for user: ${username}`); // Step 1: Fetch from scraper const gradesData = await fetchGradesFromScraper(username, password); // Step 2: Save to database const result = await syncGradesToDatabase(username, password, gradesData); console.log(`[Sync] ✓ Completed for ${username}: ${result.classesProcessed} classes, ${result.assignmentsAdded} new, ${result.assignmentsUpdated} updated`); return { username, success: true, classesProcessed: result.classesProcessed, assignmentsAdded: result.assignmentsAdded, assignmentsUpdated: result.assignmentsUpdated, }; } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); console.error(`[Sync] ✗ Failed for ${username}: ${errorMsg}`); return { username, success: false, error: errorMsg, }; } } export async function syncMultipleUsers(): Promise<{ total: number; successful: number; failed: number; results: SyncResult[]; duration: number; }> { const startTime = Date.now(); const maxConcurrent = parseInt(process.env.MAX_CONCURRENT_SYNCS || '3'); console.log(`\n[Sync Job] Starting batch sync (max ${maxConcurrent} concurrent)`); // Get all users with their credentials const users = await getAllUsers(); if (users.length === 0) { console.log('[Sync Job] No users to sync'); return { total: 0, successful: 0, failed: 0, results: [], duration: 0, }; } // We need to get passwords, so fetch full user records const { prisma } = await import('../db'); const usersWithPasswords = await prisma.user.findMany({ select: { username: true, password: true, }, }); const results: SyncResult[] = []; // Process in batches for (let i = 0; i < usersWithPasswords.length; i += maxConcurrent) { const batch = usersWithPasswords.slice(i, i + maxConcurrent); console.log(`[Sync Job] Processing batch ${Math.floor(i / maxConcurrent) + 1}: ${batch.map(u => u.username).join(', ')}`); const batchPromises = batch.map(user => syncUserGrades(user.username, user.password) ); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); } const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; const duration = Date.now() - startTime; console.log(`[Sync Job] Completed: ${successful} successful, ${failed} failed (${(duration / 1000).toFixed(1)}s)\n`); return { total: users.length, successful, failed, results, duration, }; }