5 Commits
20 changed files with 20913 additions and 83 deletions

No files matched your search

@@ -3,6 +3,7 @@ using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity; using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth; using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence; using Jiaowu.Api.Infrastructure.Persistence;
using Jiaowu.Api.Infrastructure.Timetables;
using Jiaowu.Api.Infrastructure.Scheduling; using Jiaowu.Api.Infrastructure.Scheduling;
using Jiaowu.Api.Infrastructure.Teaching; using Jiaowu.Api.Infrastructure.Teaching;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@@ -286,6 +287,8 @@ public sealed class CourseAdjustmentsController(
db.CourseAdjustments.Add(adj); db.CourseAdjustments.Add(adj);
await db.SaveChangesAsync(cancellationToken); await db.SaveChangesAsync(cancellationToken);
await new PublishedTimetableProjectionService(db)
.RebuildPublishedPlansForTaskAsync(adj.TeachingTaskId, cancellationToken);
if (request.Submit) if (request.Submit)
{ {
@@ -105,6 +105,7 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
var constraints = await db.TeachingTaskScheduleConstraints.AsNoTracking() var constraints = await db.TeachingTaskScheduleConstraints.AsNoTracking()
.WhereIn(taskIds, x => x.TeachingTaskId) .WhereIn(taskIds, x => x.TeachingTaskId)
.Include(x => x.AllowedClassrooms) .Include(x => x.AllowedClassrooms)
.Include(x => x.AllowedExperimentClassrooms)
.ToDictionaryAsync(x => x.TeachingTaskId, cancellationToken); .ToDictionaryAsync(x => x.TeachingTaskId, cancellationToken);
return Ok(tasks.Select(task => return Ok(tasks.Select(task =>
{ {
@@ -132,11 +133,15 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
: constraint?.RequiresClassroom ?? true, : constraint?.RequiresClassroom ?? true,
constraint?.RequiredCampusId, constraint?.RequiredCampusId,
constraint?.RequiredBuildingId, constraint?.RequiredBuildingId,
constraint?.ExperimentRequiredCampusId,
constraint?.ExperimentRequiredBuildingId,
AllowedDayOfWeeks = ParseDays(constraint?.AllowedDayOfWeeks), AllowedDayOfWeeks = ParseDays(constraint?.AllowedDayOfWeeks),
constraint?.EarliestPeriod, constraint?.EarliestPeriod,
constraint?.LatestPeriod, constraint?.LatestPeriod,
AllowedClassroomIds = constraint?.AllowedClassrooms AllowedClassroomIds = constraint?.AllowedClassrooms
.Select(x => x.ClassroomId) ?? [], .Select(x => x.ClassroomId) ?? [],
AllowedExperimentClassroomIds = constraint?.AllowedExperimentClassrooms
.Select(x => x.ClassroomId) ?? [],
AllowedExperimentVenueNatures = constraint?.AllowedExperimentVenueNatures ?? 0 AllowedExperimentVenueNatures = constraint?.AllowedExperimentVenueNatures ?? 0
}; };
})); }));
@@ -168,6 +173,7 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
{ {
var flexibleConstraint = await db.TeachingTaskScheduleConstraints var flexibleConstraint = await db.TeachingTaskScheduleConstraints
.Include(x => x.AllowedClassrooms) .Include(x => x.AllowedClassrooms)
.Include(x => x.AllowedExperimentClassrooms)
.FirstOrDefaultAsync(x => x.TeachingTaskId == teachingTaskId, cancellationToken); .FirstOrDefaultAsync(x => x.TeachingTaskId == teachingTaskId, cancellationToken);
if (flexibleConstraint is not null) if (flexibleConstraint is not null)
{ {
@@ -195,6 +201,22 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
cancellationToken)) cancellationToken))
return ValidationProblem("指定校区不存在或已停用。"); return ValidationProblem("指定校区不存在或已停用。");
Building? experimentBuilding = null;
if (request.ExperimentRequiredBuildingId.HasValue)
{
experimentBuilding = await db.Buildings.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == request.ExperimentRequiredBuildingId && x.IsEnabled,
cancellationToken);
if (experimentBuilding is null) return ValidationProblem("指定实验教学楼不存在或已停用。");
if (request.ExperimentRequiredCampusId.HasValue &&
experimentBuilding.CampusId != request.ExperimentRequiredCampusId)
return ValidationProblem("指定实验教学楼不属于所选校区。");
}
if (request.ExperimentRequiredCampusId.HasValue &&
!await db.Campuses.AnyAsync(x => x.Id == request.ExperimentRequiredCampusId && x.IsEnabled,
cancellationToken))
return ValidationProblem("指定实验校区不存在或已停用。");
var allowedRooms = await db.Classrooms.AsNoTracking() var allowedRooms = await db.Classrooms.AsNoTracking()
.Where(x => x.IsEnabled) .Where(x => x.IsEnabled)
.WhereIn(request.AllowedClassroomIds, x => x.Id) .WhereIn(request.AllowedClassroomIds, x => x.Id)
@@ -208,8 +230,23 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
allowedRooms.Any(x => x.Building!.CampusId != request.RequiredCampusId)) allowedRooms.Any(x => x.Building!.CampusId != request.RequiredCampusId))
return ValidationProblem("指定教室必须位于所选校区。"); return ValidationProblem("指定教室必须位于所选校区。");
var allowedExperimentRoomIds = request.AllowedExperimentClassroomIds?.Distinct().ToArray() ?? [];
var allowedExperimentRooms = await db.Classrooms.AsNoTracking()
.Where(x => x.IsEnabled)
.WhereIn(allowedExperimentRoomIds, x => x.Id)
.Include(x => x.Building)
.ToListAsync(cancellationToken);
if (allowedExperimentRooms.Count != allowedExperimentRoomIds.Length)
return ValidationProblem("部分指定实验场地不存在或已停用。");
if (experimentBuilding is not null && allowedExperimentRooms.Any(x => x.BuildingId != experimentBuilding.Id))
return ValidationProblem("指定实验场地必须位于所选实验教学楼。");
if (request.ExperimentRequiredCampusId.HasValue &&
allowedExperimentRooms.Any(x => x.Building!.CampusId != request.ExperimentRequiredCampusId))
return ValidationProblem("指定实验场地必须位于所选实验校区。");
var constraint = await db.TeachingTaskScheduleConstraints var constraint = await db.TeachingTaskScheduleConstraints
.Include(x => x.AllowedClassrooms) .Include(x => x.AllowedClassrooms)
.Include(x => x.AllowedExperimentClassrooms)
.FirstOrDefaultAsync(x => x.TeachingTaskId == teachingTaskId, cancellationToken); .FirstOrDefaultAsync(x => x.TeachingTaskId == teachingTaskId, cancellationToken);
if (constraint is null) if (constraint is null)
{ {
@@ -223,6 +260,12 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
constraint.RequiredBuildingId = request.RequiresClassroom constraint.RequiredBuildingId = request.RequiresClassroom
? request.RequiredBuildingId ? request.RequiredBuildingId
: null; : null;
constraint.ExperimentRequiredCampusId = request.RequiresClassroom
? request.ExperimentRequiredCampusId
: null;
constraint.ExperimentRequiredBuildingId = request.RequiresClassroom
? request.ExperimentRequiredBuildingId
: null;
constraint.AllowedDayOfWeeks = request.AllowedDayOfWeeks.Count == 0 constraint.AllowedDayOfWeeks = request.AllowedDayOfWeeks.Count == 0
? null ? null
: string.Join(',', request.AllowedDayOfWeeks.Distinct().Order()); : string.Join(',', request.AllowedDayOfWeeks.Distinct().Order());
@@ -230,10 +273,15 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
constraint.LatestPeriod = request.LatestPeriod; constraint.LatestPeriod = request.LatestPeriod;
constraint.AllowedExperimentVenueNatures = request.AllowedExperimentVenueNatures; constraint.AllowedExperimentVenueNatures = request.AllowedExperimentVenueNatures;
db.TeachingTaskAllowedClassrooms.RemoveRange(constraint.AllowedClassrooms); db.TeachingTaskAllowedClassrooms.RemoveRange(constraint.AllowedClassrooms);
db.TeachingTaskAllowedExperimentClassrooms.RemoveRange(constraint.AllowedExperimentClassrooms);
constraint.AllowedClassrooms = request.RequiresClassroom constraint.AllowedClassrooms = request.RequiresClassroom
? request.AllowedClassroomIds.Distinct().Select(classroomId => ? request.AllowedClassroomIds.Distinct().Select(classroomId =>
new TeachingTaskAllowedClassroom { ClassroomId = classroomId }).ToList() new TeachingTaskAllowedClassroom { ClassroomId = classroomId }).ToList()
: []; : [];
constraint.AllowedExperimentClassrooms = request.RequiresClassroom
? allowedExperimentRoomIds.Select(classroomId =>
new TeachingTaskAllowedExperimentClassroom { ClassroomId = classroomId }).ToList()
: [];
await db.SaveChangesAsync(cancellationToken); await db.SaveChangesAsync(cancellationToken);
return NoContent(); return NoContent();
} }
@@ -256,21 +304,26 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
!Enum.IsDefined(request.SchedulingMode.Value)) !Enum.IsDefined(request.SchedulingMode.Value))
return ValidationProblem("授课方式无效。"); return ValidationProblem("授课方式无效。");
if (!request.SchedulingMode.HasValue && if (!request.SchedulingMode.HasValue &&
!request.RequiresClassroom.HasValue && !request.RequiresClassroom.HasValue &&
request.AllowedDayOfWeeks is null && request.AllowedDayOfWeeks is null &&
!request.UpdateClassroomScope && !request.UpdateClassroomScope &&
!request.UpdatePeriodRange && !request.UpdateExperimentClassroomScope &&
!request.AllowedExperimentVenueNatures.HasValue &&
!request.UpdatePeriodRange &&
!request.EarliestPeriod.HasValue && !request.EarliestPeriod.HasValue &&
!request.LatestPeriod.HasValue) !request.LatestPeriod.HasValue)
return ValidationProblem("请至少选择一项需要批量修改的设置。"); return ValidationProblem("请至少选择一项需要批量修改的设置。");
if (request.UpdateClassroomScope && request.RequiresClassroom == false) if (request.UpdateClassroomScope && request.RequiresClassroom == false)
return ValidationProblem("批量指定教室范围时,场地要求不能设置为不占用教室。"); return ValidationProblem("批量指定教室范围时,场地要求不能设置为不占用教室。");
if (request.UpdateExperimentClassroomScope && request.RequiresClassroom == false)
return ValidationProblem("批量指定实验场地时,场地要求不能设置为不占用教室。");
var tasks = await db.TeachingTasks var tasks = await db.TeachingTasks
.Where(x => .Where(x =>
x.AcademicTermId == request.AcademicTermId && x.AcademicTermId == request.AcademicTermId &&
x.Status == TeachingTaskStatus.Published) x.Status == TeachingTaskStatus.Published)
.WhereIn(taskIds, x => x.Id) .WhereIn(taskIds, x => x.Id)
.Include(x => x.Course)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
if (tasks.Count != taskIds.Length) if (tasks.Count != taskIds.Length)
return ValidationProblem("部分教学任务不存在、未发布或不属于当前学期。"); return ValidationProblem("部分教学任务不存在、未发布或不属于当前学期。");
@@ -281,9 +334,15 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
(request.SchedulingMode ?? task.SchedulingMode) == (request.SchedulingMode ?? task.SchedulingMode) ==
TeachingTaskSchedulingMode.Flexible)) TeachingTaskSchedulingMode.Flexible))
return ConflictProblem("非排时课程不能指定教室,请先将当前筛选结果限定为正常排课课程。"); return ConflictProblem("非排时课程不能指定教室,请先将当前筛选结果限定为正常排课课程。");
if (request.UpdateExperimentClassroomScope && tasks.Any(task =>
(request.SchedulingMode ?? task.SchedulingMode) ==
TeachingTaskSchedulingMode.Flexible))
return ConflictProblem("非排时课程不能指定实验场地,请先将当前筛选结果限定为正常排课课程。");
Building? building = null; Building? building = null;
List<Classroom> allowedRooms = []; List<Classroom> allowedRooms = [];
var experimentRoomIds = request.AllowedExperimentClassroomIds?.Distinct().ToArray() ?? [];
List<Classroom> allowedExperimentRooms = [];
if (request.UpdateClassroomScope) if (request.UpdateClassroomScope)
{ {
if (request.RequiredBuildingId.HasValue) if (request.RequiredBuildingId.HasValue)
@@ -320,10 +379,20 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
x.Building!.CampusId != request.RequiredCampusId)) x.Building!.CampusId != request.RequiredCampusId))
return ValidationProblem("指定教室必须位于所选校区。"); return ValidationProblem("指定教室必须位于所选校区。");
} }
if (request.UpdateExperimentClassroomScope)
{
allowedExperimentRooms = await db.Classrooms.AsNoTracking()
.Where(x => x.IsEnabled)
.WhereIn(experimentRoomIds, x => x.Id)
.ToListAsync(cancellationToken);
if (allowedExperimentRooms.Count != experimentRoomIds.Length)
return ValidationProblem("部分指定实验场地不存在或已停用。");
}
var constraints = await db.TeachingTaskScheduleConstraints var constraints = await db.TeachingTaskScheduleConstraints
.WhereIn(taskIds, x => x.TeachingTaskId) .WhereIn(taskIds, x => x.TeachingTaskId)
.Include(x => x.AllowedClassrooms) .Include(x => x.AllowedClassrooms)
.Include(x => x.AllowedExperimentClassrooms)
.ToDictionaryAsync(x => x.TeachingTaskId, cancellationToken); .ToDictionaryAsync(x => x.TeachingTaskId, cancellationToken);
foreach (var task in tasks) foreach (var task in tasks)
{ {
@@ -342,6 +411,8 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
request.RequiresClassroom.HasValue || request.RequiresClassroom.HasValue ||
request.AllowedDayOfWeeks is not null || request.AllowedDayOfWeeks is not null ||
request.UpdateClassroomScope || request.UpdateClassroomScope ||
request.UpdateExperimentClassroomScope ||
request.AllowedExperimentVenueNatures.HasValue ||
request.UpdatePeriodRange; request.UpdatePeriodRange;
if (!changesConstraint) continue; if (!changesConstraint) continue;
constraint = new TeachingTaskScheduleConstraint { TeachingTaskId = task.Id }; constraint = new TeachingTaskScheduleConstraint { TeachingTaskId = task.Id };
@@ -357,7 +428,10 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
constraint.RequiredCampusId = null; constraint.RequiredCampusId = null;
constraint.RequiredBuildingId = null; constraint.RequiredBuildingId = null;
db.TeachingTaskAllowedClassrooms.RemoveRange(constraint.AllowedClassrooms); db.TeachingTaskAllowedClassrooms.RemoveRange(constraint.AllowedClassrooms);
db.TeachingTaskAllowedExperimentClassrooms.RemoveRange(
constraint.AllowedExperimentClassrooms);
constraint.AllowedClassrooms = []; constraint.AllowedClassrooms = [];
constraint.AllowedExperimentClassrooms = [];
} }
} }
if (request.AllowedDayOfWeeks is not null) if (request.AllowedDayOfWeeks is not null)
@@ -379,6 +453,15 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
ClassroomId = room.Id ClassroomId = room.Id
}).ToList(); }).ToList();
} }
if (task.Course?.PracticeHours > 0 && request.AllowedExperimentVenueNatures.HasValue)
constraint.AllowedExperimentVenueNatures = request.AllowedExperimentVenueNatures.Value;
if (task.Course?.PracticeHours > 0 && request.UpdateExperimentClassroomScope)
{
db.TeachingTaskAllowedExperimentClassrooms.RemoveRange(
constraint.AllowedExperimentClassrooms);
constraint.AllowedExperimentClassrooms = allowedExperimentRooms.Select(room =>
new TeachingTaskAllowedExperimentClassroom { ClassroomId = room.Id }).ToList();
}
if (request.UpdatePeriodRange) if (request.UpdatePeriodRange)
{ {
constraint.EarliestPeriod = request.EarliestPeriod; constraint.EarliestPeriod = request.EarliestPeriod;
@@ -402,11 +485,15 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
constraint.RequiresClassroom = false; constraint.RequiresClassroom = false;
constraint.RequiredCampusId = null; constraint.RequiredCampusId = null;
constraint.RequiredBuildingId = null; constraint.RequiredBuildingId = null;
constraint.ExperimentRequiredCampusId = null;
constraint.ExperimentRequiredBuildingId = null;
constraint.AllowedDayOfWeeks = null; constraint.AllowedDayOfWeeks = null;
constraint.EarliestPeriod = null; constraint.EarliestPeriod = null;
constraint.LatestPeriod = null; constraint.LatestPeriod = null;
db.TeachingTaskAllowedClassrooms.RemoveRange(constraint.AllowedClassrooms); db.TeachingTaskAllowedClassrooms.RemoveRange(constraint.AllowedClassrooms);
db.TeachingTaskAllowedExperimentClassrooms.RemoveRange(constraint.AllowedExperimentClassrooms);
constraint.AllowedClassrooms = []; constraint.AllowedClassrooms = [];
constraint.AllowedExperimentClassrooms = [];
} }
private ActionResult ConflictProblem(string detail) => private ActionResult ConflictProblem(string detail) =>
@@ -441,7 +528,10 @@ public sealed record TeachingTaskScheduleConstraintRequest(
IReadOnlyList<int> AllowedDayOfWeeks, IReadOnlyList<int> AllowedDayOfWeeks,
[Range(1, 30)] int? EarliestPeriod, [Range(1, 30)] int? EarliestPeriod,
[Range(1, 30)] int? LatestPeriod, [Range(1, 30)] int? LatestPeriod,
TeachingVenueNature AllowedExperimentVenueNatures = 0); TeachingVenueNature AllowedExperimentVenueNatures = 0,
IReadOnlyList<Guid>? AllowedExperimentClassroomIds = null,
Guid? ExperimentRequiredCampusId = null,
Guid? ExperimentRequiredBuildingId = null);
public sealed record TeachingTaskScheduleConstraintBatchRequest( public sealed record TeachingTaskScheduleConstraintBatchRequest(
Guid AcademicTermId, Guid AcademicTermId,
@@ -455,4 +545,7 @@ public sealed record TeachingTaskScheduleConstraintBatchRequest(
IReadOnlyList<Guid>? AllowedClassroomIds, IReadOnlyList<Guid>? AllowedClassroomIds,
bool UpdatePeriodRange, bool UpdatePeriodRange,
[Range(1, 30)] int? EarliestPeriod, [Range(1, 30)] int? EarliestPeriod,
[Range(1, 30)] int? LatestPeriod); [Range(1, 30)] int? LatestPeriod,
bool UpdateExperimentClassroomScope = false,
TeachingVenueNature? AllowedExperimentVenueNatures = null,
IReadOnlyList<Guid>? AllowedExperimentClassroomIds = null);
@@ -552,6 +552,7 @@ public sealed class SchedulesController(
var constraint = await db.TeachingTaskScheduleConstraints.AsNoTracking() var constraint = await db.TeachingTaskScheduleConstraints.AsNoTracking()
.Include(x => x.AllowedClassrooms) .Include(x => x.AllowedClassrooms)
.Include(x => x.AllowedExperimentClassrooms)
.FirstOrDefaultAsync( .FirstOrDefaultAsync(
x => x.TeachingTaskId == request.TeachingTaskId, x => x.TeachingTaskId == request.TeachingTaskId,
cancellationToken); cancellationToken);
@@ -580,20 +581,26 @@ public sealed class SchedulesController(
x => x.Id == request.ClassroomId && x.IsEnabled, x => x.Id == request.ClassroomId && x.IsEnabled,
cancellationToken); cancellationToken);
if (classroom is null) return ValidationProblem("所选教室不存在或已停用。"); if (classroom is null) return ValidationProblem("所选教室不存在或已停用。");
if (request.Kind == ScheduleEntryKind.Experiment && if (request.Kind != ScheduleEntryKind.Experiment &&
!TeachingVenueNatureRules.SupportsExperiment(classroom.TeachingVenueNature)) constraint?.RequiredCampusId is Guid campusId &&
return ValidationProblem(
$"实验课必须安排在具有实验教学性质的场地;“{classroom.Name}”未标注实验室、实训室、机房或语音室性质。 ");
if (constraint?.RequiredCampusId is Guid campusId &&
classroom.Building!.CampusId != campusId) classroom.Building!.CampusId != campusId)
return ValidationProblem("所选教室不在该课程指定的校区。"); return ValidationProblem("所选教室不在该课程指定的校区。");
if (constraint?.RequiredBuildingId is Guid buildingId && if (request.Kind != ScheduleEntryKind.Experiment &&
constraint?.RequiredBuildingId is Guid buildingId &&
classroom.BuildingId != buildingId) classroom.BuildingId != buildingId)
return ValidationProblem("所选教室不在该课程指定的教学楼。"); return ValidationProblem("所选教室不在该课程指定的教学楼。");
if (request.Kind == ScheduleEntryKind.Experiment &&
constraint?.ExperimentRequiredCampusId is Guid experimentCampusId &&
classroom.Building!.CampusId != experimentCampusId)
return ValidationProblem("所选场地不在该实验课指定的校区。");
if (request.Kind == ScheduleEntryKind.Experiment &&
constraint?.ExperimentRequiredBuildingId is Guid experimentBuildingId &&
classroom.BuildingId != experimentBuildingId)
return ValidationProblem("所选场地不在该实验课指定的教学楼。");
var allowedClassroomIds = constraint?.AllowedClassrooms var allowedClassroomIds = constraint?.AllowedClassrooms
.Select(x => x.ClassroomId) .Select(x => x.ClassroomId)
.ToHashSet() ?? []; .ToHashSet() ?? [];
if (allowedClassroomIds.Count > 0 && if (request.Kind != ScheduleEntryKind.Experiment && allowedClassroomIds.Count > 0 &&
!allowedClassroomIds.Contains(classroom.Id)) !allowedClassroomIds.Contains(classroom.Id))
return ValidationProblem("所选教室不在该课程指定的教室范围内。"); return ValidationProblem("所选教室不在该课程指定的教室范围内。");
if (request.Kind == ScheduleEntryKind.Experiment && if (request.Kind == ScheduleEntryKind.Experiment &&
@@ -601,6 +608,13 @@ public sealed class SchedulesController(
allowedNatures != 0 && allowedNatures != 0 &&
(classroom.TeachingVenueNature & allowedNatures) == 0) (classroom.TeachingVenueNature & allowedNatures) == 0)
return ValidationProblem("所选场地不在该实验课允许的教学场地性质范围内。"); return ValidationProblem("所选场地不在该实验课允许的教学场地性质范围内。");
var allowedExperimentClassroomIds = constraint?.AllowedExperimentClassrooms
.Select(x => x.ClassroomId)
.ToHashSet() ?? [];
if (request.Kind == ScheduleEntryKind.Experiment &&
allowedExperimentClassroomIds.Count > 0 &&
!allowedExperimentClassroomIds.Contains(classroom.Id))
return ValidationProblem("所选场地不在该实验课指定的场地范围内。");
} }
var studentCount = task.Classes.Sum(x => var studentCount = task.Classes.Sum(x =>
x.AdministrativeClass!.Students.Count(student => x.AdministrativeClass!.Students.Count(student =>
Loaded 3 of 20 files, more files were not shown because too many files have changed in this diff. Show more