diff --git a/.gitignore b/.gitignore index 2559a2b5..18023cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -463,6 +463,10 @@ $RECYCLE.BIN/ /.trae /TEMP +# Local diagnostic tools and crash dumps +.tools/ +*.dmp + # omo .sisyphus /.mimocode diff --git a/SecRandom/Services/SecAgent/SecAgentHttpHostedService.cs b/SecRandom/Services/SecAgent/SecAgentHttpHostedService.cs index 3863c987..01c3f98f 100644 --- a/SecRandom/Services/SecAgent/SecAgentHttpHostedService.cs +++ b/SecRandom/Services/SecAgent/SecAgentHttpHostedService.cs @@ -188,24 +188,41 @@ private async Task DrawStudentsAsync(JsonObject arguments, Cancellat var requestedCount = Math.Clamp(arguments["count"]?.GetValue() ?? 1, 1, 100); if (mode == "flash") requestedCount = 1; + var gender = StringArgument(arguments, "gender"); var includeTags = StringArray(arguments, "include_tags"); var excludeTags = StringArray(arguments, "exclude_tags"); var includeIds = StringArray(arguments, "include_ids"); var includeNames = StringArray(arguments, "include_names"); var listName = profileService.CurrentStudentList?.Name ?? string.Empty; - var temporaryCounts = temporaryRecordService.GetStudentCounts(listName, string.Empty, string.Empty); + var temporaryCounts = temporaryRecordService.GetStudentCounts(listName, gender, string.Empty); + var hasMatchingStudents = (profileService.CurrentStudentList?.Students ?? []) + .Any(student => student.IsCandidate && Matches(student, gender, includeTags, excludeTags, includeIds, includeNames)); + var mayResetExhaustedRound = configHandler.Data.QuickDrawSettings.DrawMode != DrawMode.Repeat + && hasMatchingStudents; var result = await InvokeAuthorizedAsync(SecurityOperation.QuickDrawStart, () => { - var draw = drawEngine.DrawStudent(requestedCount, student => Matches(student, includeTags, excludeTags, includeIds, includeNames) - && !HasReachedTemporaryLimit(student, temporaryCounts), DrawSettingsType.QuickDraw, linkageDrawCoordinator.GetCourseName()); + DrawResult DrawFromRemainingStudents() + => drawEngine.DrawStudent(requestedCount, + student => Matches(student, gender, includeTags, excludeTags, includeIds, includeNames) + && !HasReachedTemporaryLimit(student, temporaryCounts), + DrawSettingsType.QuickDraw, linkageDrawCoordinator.GetCourseName()); + + var draw = DrawFromRemainingStudents(); + if (!draw.IsSuccess && mayResetExhaustedRound && draw.Status == DrawStatus.RepeatLimitExhausted) + { + temporaryRecordService.ResetStudentList(listName); + temporaryCounts = temporaryRecordService.GetStudentCounts(listName, gender, string.Empty); + draw = DrawFromRemainingStudents(); + } + if (!draw.IsSuccess || draw.Result.Count == 0) return Task.FromResult(draw); profileService.RecordStudentHistory(draw.Result, DateTime.Now, requestedCount, drawMethod: (int)configHandler.Data.QuickDrawSettings.DrawType, courseName: linkageDrawCoordinator.GetCourseName()); - temporaryRecordService.RecordStudents(listName, string.Empty, string.Empty, draw.Result); + temporaryRecordService.RecordStudents(listName, gender, string.Empty, draw.Result); if (mode == "flash") notificationService.QueueStudents(NotificationSettingsType.QuickDraw, linkageDrawCoordinator.GetCourseName(), draw.Result); return Task.FromResult(draw); @@ -242,11 +259,12 @@ private bool HasReachedTemporaryLimit(Student student, IReadOnlyDictionary 0 && temporaryCounts.GetValueOrDefault(ProfileRecordIdentity.EnsureRecordId(student)) >= threshold; } - private static bool Matches(Student student, IReadOnlyCollection includeTags, IReadOnlyCollection excludeTags, + private static bool Matches(Student student, string gender, IReadOnlyCollection includeTags, IReadOnlyCollection excludeTags, IReadOnlyCollection includeIds, IReadOnlyCollection includeNames) { var tags = student.Tags.Split([',', ';', ' '], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - return includeTags.All(tag => tags.Contains(tag, StringComparer.OrdinalIgnoreCase)) + return (string.IsNullOrWhiteSpace(gender) || string.Equals(student.Gender, gender, StringComparison.OrdinalIgnoreCase)) + && includeTags.All(tag => tags.Contains(tag, StringComparer.OrdinalIgnoreCase)) && excludeTags.All(tag => !tags.Contains(tag, StringComparer.OrdinalIgnoreCase)) && (includeIds.Count == 0 || includeIds.Contains(student.Id, StringComparer.OrdinalIgnoreCase)) && (includeNames.Count == 0 || includeNames.Contains(student.Name, StringComparer.OrdinalIgnoreCase));