Enroll a batch of users in a course. Returns summary of results.
(
token_user: APITokenUser,
course_uuid: str,
user_ids: List[int],
request: Request,
db_session: AsyncSession,
)
| 1356 | |
| 1357 | |
| 1358 | async def bulk_enroll_users( |
| 1359 | token_user: APITokenUser, |
| 1360 | course_uuid: str, |
| 1361 | user_ids: List[int], |
| 1362 | request: Request, |
| 1363 | db_session: AsyncSession, |
| 1364 | ) -> dict: |
| 1365 | """Enroll a batch of users in a course. Returns summary of results.""" |
| 1366 | |
| 1367 | course = (await db_session.execute( |
| 1368 | select(Course).where( |
| 1369 | Course.course_uuid == course_uuid, |
| 1370 | Course.org_id == token_user.org_id, |
| 1371 | ) |
| 1372 | )).scalars().first() |
| 1373 | if not course: |
| 1374 | raise HTTPException(status_code=404, detail="Course not found") |
| 1375 | |
| 1376 | # Pre-fetch memberships, existing enrollments, and trails in 3 queries total |
| 1377 | member_ids = set( |
| 1378 | (await db_session.execute( |
| 1379 | select(UserOrganization.user_id).where( |
| 1380 | UserOrganization.user_id.in_(user_ids), |
| 1381 | UserOrganization.org_id == token_user.org_id, |
| 1382 | ) |
| 1383 | )).scalars().all() |
| 1384 | ) |
| 1385 | already_enrolled_ids = set( |
| 1386 | (await db_session.execute( |
| 1387 | select(TrailRun.user_id).where( |
| 1388 | TrailRun.course_id == course.id, |
| 1389 | TrailRun.user_id.in_(user_ids), |
| 1390 | TrailRun.org_id == token_user.org_id, |
| 1391 | ) |
| 1392 | )).scalars().all() |
| 1393 | ) |
| 1394 | |
| 1395 | enrolled: List[int] = [] |
| 1396 | already_enrolled: List[int] = [] |
| 1397 | skipped: List[int] = [] |
| 1398 | to_enroll: List[int] = [] |
| 1399 | |
| 1400 | for user_id in user_ids: |
| 1401 | if user_id not in member_ids: |
| 1402 | skipped.append(user_id) |
| 1403 | elif user_id in already_enrolled_ids: |
| 1404 | already_enrolled.append(user_id) |
| 1405 | else: |
| 1406 | to_enroll.append(user_id) |
| 1407 | |
| 1408 | if not to_enroll: |
| 1409 | return {"enrolled": enrolled, "already_enrolled": already_enrolled, "skipped": skipped} |
| 1410 | |
| 1411 | now = datetime.now() |
| 1412 | trails_by_user: dict[int, Trail] = { |
| 1413 | t.user_id: t |
| 1414 | for t in (await db_session.execute( |
| 1415 | select(Trail).where( |