Complete CCS results and rating flow
This commit is contained in:
@@ -9,13 +9,15 @@ public sealed class CcsStore
|
||||
private readonly IMongoCollection<CcsOrderDefinition> _orders;
|
||||
private readonly IMongoCollection<CcsAttempt> _attempts;
|
||||
private readonly ExamSimulationStore _examSimulations;
|
||||
private readonly CcsRatingStore _ratings;
|
||||
|
||||
public CcsStore(MongoDatabaseProvider mongo, ExamSimulationStore examSimulations)
|
||||
public CcsStore(MongoDatabaseProvider mongo, ExamSimulationStore examSimulations, CcsRatingStore ratings)
|
||||
{
|
||||
_cases = mongo.Database.GetCollection<CcsCaseRevision>("CcsCaseRevisions");
|
||||
_orders = mongo.Database.GetCollection<CcsOrderDefinition>("CcsOrderCatalog");
|
||||
_attempts = mongo.Database.GetCollection<CcsAttempt>("CcsAttempts");
|
||||
_examSimulations = examSimulations;
|
||||
_ratings = ratings;
|
||||
_cases.Indexes.CreateOne(new CreateIndexModel<CcsCaseRevision>(
|
||||
Builders<CcsCaseRevision>.IndexKeys.Ascending(value => value.ExternalId).Ascending(value => value.Version),
|
||||
new CreateIndexOptions { Unique = true }));
|
||||
@@ -108,6 +110,7 @@ public sealed class CcsStore
|
||||
CurrentVitals = new Dictionary<string, string>(definition.InitialChart.Vitals),
|
||||
ActiveOrderCodes = definition.InitialChart.InitialOrderCodes.Distinct(StringComparer.OrdinalIgnoreCase).ToList(),
|
||||
DeadlineUtc = request.Mode == CcsAttemptModes.Timed ? now.AddMinutes(definition.RealTimeMinutes) : null,
|
||||
RatingEligible = request.Mode == CcsAttemptModes.Timed,
|
||||
Chart =
|
||||
[
|
||||
new CcsChartEntry { Category = "progress", Text = definition.InitialChart.ReasonForEncounter },
|
||||
@@ -181,7 +184,7 @@ public sealed class CcsStore
|
||||
{
|
||||
Id = value.Id ?? "", CaseExternalId = value.CaseExternalId,
|
||||
CaseTitle = definitions.GetValueOrDefault(value.CaseRevisionId)?.Title ?? value.CaseExternalId,
|
||||
Mode = value.Mode, State = value.State, Score = value.Score?.Percent, CreatedUtc = value.CreatedUtc
|
||||
Mode = value.Mode, State = value.State, Score = value.Score?.Percent, Rating = value.Rating, CreatedUtc = value.CreatedUtc
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
@@ -227,7 +230,8 @@ public sealed class CcsStore
|
||||
PendingResults = attempt.PendingResults.Select(value => new CcsPendingResult { OrderCode = value.OrderCode, DueMinute = value.DueMinute }).ToList(),
|
||||
AvailableExams = attempt.State == CcsAttemptStates.Active ? definition.Exams : [],
|
||||
AllowedLocations = attempt.State == CcsAttemptStates.Active ? definition.AllowedLocations : [],
|
||||
Score = completed ? attempt.Score : null, References = completed ? definition.References : []
|
||||
Score = completed ? attempt.Score : null, Rating = completed ? attempt.Rating : null,
|
||||
References = completed ? definition.References : []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -341,6 +345,7 @@ public sealed class CcsStore
|
||||
attempt.State = CcsAttemptStates.Completed;
|
||||
attempt.CompletedUtc = DateTime.UtcNow;
|
||||
attempt.Score = CalculateScore(attempt, definition);
|
||||
attempt.Rating = await _ratings.ApplyAsync(attempt);
|
||||
attempt.UpdatedUtc = DateTime.UtcNow;
|
||||
await SaveAsync(attempt);
|
||||
}
|
||||
@@ -367,6 +372,11 @@ public sealed class CcsStore
|
||||
results.Add(new CcsCriterionResult
|
||||
{
|
||||
CriterionId = criterion.Id,
|
||||
Title = string.IsNullOrWhiteSpace(criterion.Title) ? Humanize(criterion.Id) : criterion.Title,
|
||||
ActionType = criterion.ActionType,
|
||||
ExpectedAction = DescribeExpected(criterion, targets),
|
||||
ObservedAction = action?.Code ?? "Not performed",
|
||||
ObservedMinute = action?.SimulatedMinute,
|
||||
Status = met ? criterion.Points < 0 ? "harmful" : "credited" : criterion.Points > 0 ? "missed" : "avoided",
|
||||
Points = awarded, Rationale = criterion.Rationale
|
||||
});
|
||||
@@ -384,6 +394,16 @@ public sealed class CcsStore
|
||||
|
||||
private Task SaveAsync(CcsAttempt attempt) => _attempts.ReplaceOneAsync(value => value.Id == attempt.Id, attempt);
|
||||
private static CcsAttemptAction Action(string type, string code, int minute, DateTime now) => new() { Type = type, Code = code, SimulatedMinute = minute, RealUtc = now };
|
||||
private static string Humanize(string value) => string.Join(' ', value.Split('_', StringSplitOptions.RemoveEmptyEntries).Select(word => char.ToUpperInvariant(word[0]) + word[1..]));
|
||||
private static string DescribeExpected(CcsScoringCriterion criterion, List<string> targets)
|
||||
{
|
||||
var text = $"{Humanize(criterion.ActionType)}: {string.Join(" or ", targets.Select(Humanize))}";
|
||||
if (criterion.EarliestMinute is not null && criterion.LatestMinute is not null) text += $" between minute {criterion.EarliestMinute} and {criterion.LatestMinute}";
|
||||
else if (criterion.EarliestMinute is not null) text += $" at or after minute {criterion.EarliestMinute}";
|
||||
else if (criterion.LatestMinute is not null) text += $" by minute {criterion.LatestMinute}";
|
||||
if (!string.IsNullOrWhiteSpace(criterion.AfterTargetCode)) text += $" after {Humanize(criterion.AfterTargetCode)}";
|
||||
return text;
|
||||
}
|
||||
private static CcsCaseSummary ToSummary(CcsCaseRevision value) => new()
|
||||
{
|
||||
ExternalId = value.ExternalId, Title = value.Title, System = value.System,
|
||||
|
||||
Reference in New Issue
Block a user