教师成绩报告
This commit is contained in:
@@ -49,6 +49,8 @@ public sealed class CourseGradeStatisticsRefreshJobProcessor(
|
||||
x.GradeSheet.TeachingTask!.CourseId == target.CourseId &&
|
||||
x.GradeSheet.TeachingTask.AcademicTermId == target.AcademicTermId)
|
||||
.Select(x => new ScoreRow(
|
||||
x.GradeSheetId,
|
||||
x.GradeSheet!.TeachingTaskId,
|
||||
x.TotalScore!.Value,
|
||||
x.Student!.AdministrativeClassId,
|
||||
x.Student.AdministrativeClass!.MajorId,
|
||||
@@ -65,6 +67,16 @@ public sealed class CourseGradeStatisticsRefreshJobProcessor(
|
||||
scores.GroupBy(x => x.CollegeId), rebuilt, target, now);
|
||||
AddUniversityStatistic(scores, rebuilt, target, now);
|
||||
|
||||
var rebuiltTeachingTasks = scores
|
||||
.GroupBy(x => new { x.GradeSheetId, x.TeachingTaskId })
|
||||
.Select(group => CreateTeachingTaskStatistic(
|
||||
group.Key.GradeSheetId,
|
||||
group.Key.TeachingTaskId,
|
||||
group.Select(x => x.Score),
|
||||
target,
|
||||
now))
|
||||
.ToList();
|
||||
|
||||
await db.CourseGradeStatistics
|
||||
.Where(x => x.CourseId == target.CourseId &&
|
||||
x.AcademicTermId == target.AcademicTermId)
|
||||
@@ -72,6 +84,24 @@ public sealed class CourseGradeStatisticsRefreshJobProcessor(
|
||||
if (rebuilt.Count > 0)
|
||||
db.CourseGradeStatistics.AddRange(rebuilt);
|
||||
|
||||
var oldTeachingTaskStatisticIds = await db.TeachingTaskGradeStatistics
|
||||
.Where(x => x.CourseId == target.CourseId &&
|
||||
x.AcademicTermId == target.AcademicTermId)
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (oldTeachingTaskStatisticIds.Count > 0)
|
||||
{
|
||||
await db.TeachingTaskGradeScoreBands
|
||||
.Where(x => oldTeachingTaskStatisticIds.Contains(
|
||||
x.TeachingTaskGradeStatisticId))
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
await db.TeachingTaskGradeStatistics
|
||||
.Where(x => oldTeachingTaskStatisticIds.Contains(x.Id))
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
}
|
||||
if (rebuiltTeachingTasks.Count > 0)
|
||||
db.TeachingTaskGradeStatistics.AddRange(rebuiltTeachingTasks);
|
||||
|
||||
job.Status = CourseGradeStatisticsRefreshJobStatus.Succeeded;
|
||||
job.CompletedAt = now;
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
@@ -141,6 +171,80 @@ public sealed class CourseGradeStatisticsRefreshJobProcessor(
|
||||
};
|
||||
}
|
||||
|
||||
private sealed record ScoreRow(decimal Score, Guid ClassId, Guid MajorId, Guid CollegeId);
|
||||
private static TeachingTaskGradeStatistic CreateTeachingTaskStatistic(
|
||||
Guid gradeSheetId,
|
||||
Guid teachingTaskId,
|
||||
IEnumerable<decimal> source,
|
||||
StatisticsTarget targetInfo,
|
||||
DateTime calculatedAt)
|
||||
{
|
||||
var scores = source.OrderBy(x => x).ToArray();
|
||||
var passed = scores.Count(x => x >= 60m);
|
||||
var excellent = scores.Count(x => x >= 90m);
|
||||
var average = scores.Average();
|
||||
var middle = scores.Length / 2;
|
||||
var median = scores.Length % 2 == 0
|
||||
? (scores[middle - 1] + scores[middle]) / 2m
|
||||
: scores[middle];
|
||||
var variance = scores.Average(x =>
|
||||
(double)((x - average) * (x - average)));
|
||||
|
||||
var statistic = new TeachingTaskGradeStatistic
|
||||
{
|
||||
GradeSheetId = gradeSheetId,
|
||||
TeachingTaskId = teachingTaskId,
|
||||
CourseId = targetInfo.CourseId,
|
||||
AcademicTermId = targetInfo.AcademicTermId,
|
||||
StudentCount = scores.Length,
|
||||
PassedCount = passed,
|
||||
ExcellentCount = excellent,
|
||||
HighestScore = scores.Max(),
|
||||
AverageScore = Math.Round(average, 1),
|
||||
MedianScore = Math.Round(median, 1),
|
||||
LowestScore = scores.Min(),
|
||||
StandardDeviation = Math.Round((decimal)Math.Sqrt(variance), 2),
|
||||
PassRate = Math.Round((decimal)passed / scores.Length * 100m, 2),
|
||||
ExcellentRate = Math.Round((decimal)excellent / scores.Length * 100m, 2),
|
||||
CalculatedAt = calculatedAt
|
||||
};
|
||||
statistic.ScoreBands =
|
||||
[
|
||||
CreateBand(statistic.Id, "0–59", 0m, 60m,
|
||||
scores.Count(x => x < 60m), 0),
|
||||
CreateBand(statistic.Id, "60–69", 60m, 70m,
|
||||
scores.Count(x => x >= 60m && x < 70m), 1),
|
||||
CreateBand(statistic.Id, "70–79", 70m, 80m,
|
||||
scores.Count(x => x >= 70m && x < 80m), 2),
|
||||
CreateBand(statistic.Id, "80–89", 80m, 90m,
|
||||
scores.Count(x => x >= 80m && x < 90m), 3),
|
||||
CreateBand(statistic.Id, "90–100", 90m, null,
|
||||
scores.Count(x => x >= 90m), 4)
|
||||
];
|
||||
return statistic;
|
||||
}
|
||||
|
||||
private static TeachingTaskGradeScoreBand CreateBand(
|
||||
Guid statisticId,
|
||||
string label,
|
||||
decimal lowerBound,
|
||||
decimal? upperBound,
|
||||
int count,
|
||||
int sortOrder) => new()
|
||||
{
|
||||
TeachingTaskGradeStatisticId = statisticId,
|
||||
Label = label,
|
||||
LowerBound = lowerBound,
|
||||
UpperBound = upperBound,
|
||||
StudentCount = count,
|
||||
SortOrder = sortOrder
|
||||
};
|
||||
|
||||
private sealed record ScoreRow(
|
||||
Guid GradeSheetId,
|
||||
Guid TeachingTaskId,
|
||||
decimal Score,
|
||||
Guid ClassId,
|
||||
Guid MajorId,
|
||||
Guid CollegeId);
|
||||
private sealed record StatisticsTarget(Guid CourseId, Guid AcademicTermId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user