using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Infrastructure.Teaching; public static class TeachingTaskRosterQuery { public static IQueryable ForTask(AppDbContext db, Guid teachingTaskId) => db.Students.Where(student => (student.Status == StudentStatus.Active && db.TeachingTaskClasses.Any(assignment => assignment.TeachingTaskId == teachingTaskId && assignment.AdministrativeClassId == student.AdministrativeClassId)) || db.CourseEnrollments.Any(enrollment => enrollment.StudentId == student.Id && enrollment.Status == CourseEnrollmentStatus.Enrolled && enrollment.CourseSelectionOffering!.TeachingTaskId == teachingTaskId)); public static IQueryable TaskIdsForStudent(AppDbContext db, Guid studentId) { var classTaskIds = db.Students .Where(student => student.Id == studentId && student.Status == StudentStatus.Active) .SelectMany(student => db.TeachingTaskClasses .Where(assignment => assignment.AdministrativeClassId == student.AdministrativeClassId) .Select(assignment => assignment.TeachingTaskId)); var enrolledTaskIds = db.CourseEnrollments .Where(enrollment => enrollment.StudentId == studentId && enrollment.Status == CourseEnrollmentStatus.Enrolled) .Select(enrollment => enrollment.CourseSelectionOffering!.TeachingTaskId); return classTaskIds.Concat(enrolledTaskIds).Distinct(); } public static async Task> LoadForTasksAsync( AppDbContext db, IEnumerable teachingTaskIds, CancellationToken cancellationToken) { var taskIds = teachingTaskIds.Distinct().ToArray(); if (taskIds.Length == 0) return []; var assignments = await db.TeachingTaskClasses.AsNoTracking() .WhereIn(taskIds, x => x.TeachingTaskId) .Select(x => new { x.TeachingTaskId, x.AdministrativeClassId }) .ToListAsync(cancellationToken); var classIds = assignments .Select(x => x.AdministrativeClassId) .Distinct() .ToArray(); var classStudents = classIds.Length == 0 ? [] : await db.Students.AsNoTracking() .Where(x => x.Status == StudentStatus.Active) .WhereIn(classIds, x => x.AdministrativeClassId) .Select(x => new RosterStudent( x.Id, x.StudentNumber, x.Name, x.AdministrativeClassId, x.AdministrativeClass!.Name)) .ToListAsync(cancellationToken); var enrolledStudents = await db.CourseEnrollments.AsNoTracking() .Where(x => x.Status == CourseEnrollmentStatus.Enrolled) .WhereIn(taskIds, x => x.CourseSelectionOffering!.TeachingTaskId) .Select(x => new TeachingTaskRosterEntry( x.CourseSelectionOffering!.TeachingTaskId, x.StudentId, x.Student!.StudentNumber, x.Student.Name, x.Student.AdministrativeClass!.Name)) .ToListAsync(cancellationToken); var result = enrolledStudents.ToDictionary( x => (x.TeachingTaskId, x.StudentId)); var studentsByClass = classStudents.ToLookup(x => x.AdministrativeClassId); foreach (var assignment in assignments) { foreach (var student in studentsByClass[assignment.AdministrativeClassId]) { result.TryAdd( (assignment.TeachingTaskId, student.StudentId), new TeachingTaskRosterEntry( assignment.TeachingTaskId, student.StudentId, student.StudentNumber, student.Name, student.ClassName)); } } return result.Values .OrderBy(x => x.TeachingTaskId) .ThenBy(x => x.StudentNumber) .ToList(); } private sealed record RosterStudent( Guid StudentId, string StudentNumber, string Name, Guid AdministrativeClassId, string ClassName); } public sealed record TeachingTaskRosterEntry( Guid TeachingTaskId, Guid StudentId, string StudentNumber, string Name, string ClassName);