// Shared "Prepare Labs for a cohort's enrolled students" logic — used by both the existing // cohort-level admin route and the Phase 5A classroom-scoped one (a classroom's Prepare Labs is // exactly this, just entered from the classroom dashboard instead of the cohort page — see // docs/PHASE5A_STUDENT_IDENTITY_REMOTE_CLASSROOM.md §21). Extracted so there's one place this // logic lives, not two copies that could drift. async function prepareLabsForCohort(pool, provisioner, cohortId) { const { rows: students } = await pool.query( `SELECT s.student_id, s.display_name FROM cohort_enrollments e JOIN students s ON s.student_id = e.student_id WHERE e.cohort_id = $1 AND e.enrollment_status = 'enrolled' AND s.status = 'active'`, [cohortId] ); const results = await Promise.allSettled(students.map(async (s) => { const existing = await provisioner.listForStudent(s.student_id); const active = existing.body?.find((e) => ['requested', 'provisioning', 'running', 'resetting'].includes(e.status)); if (active) return { student_id: s.student_id, display_name: s.display_name, status: active.status }; const { status, body } = await provisioner.provision(s.student_id); if (status === 503 && body?.code === 'CAPACITY_EXCEEDED') { return { student_id: s.student_id, display_name: s.display_name, status: 'capacity_waiting' }; } if (status !== 201) return { student_id: s.student_id, display_name: s.display_name, status: 'error', error: body?.error }; return { student_id: s.student_id, display_name: s.display_name, status: body.status }; })); return results.map((r, i) => r.status === 'fulfilled' ? r.value : { student_id: students[i].student_id, display_name: students[i].display_name, status: 'error', error: r.reason?.message }); } module.exports = { prepareLabsForCohort };