Add functional simulation question review
This commit is contained in:
@@ -40,6 +40,10 @@ public class ExamSimulationsController(ExamSimulationStore simulations) : Contro
|
||||
return simulation is null ? NoContent() : Ok(await simulations.ToResponseAsync(simulation));
|
||||
}
|
||||
|
||||
[HttpGet("completed")]
|
||||
public async Task<IActionResult> Completed([FromQuery] int limit = 20, [FromQuery] DateTime? before = null) =>
|
||||
Ok(await simulations.GetCompletedAsync(UserId(), limit, before));
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> Get(string id)
|
||||
{
|
||||
|
||||
@@ -119,6 +119,24 @@ public class ExamSimulationReadinessResponse
|
||||
public bool HasActiveSimulation { get; set; }
|
||||
}
|
||||
|
||||
public class CompletedExamSimulationSummary
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public DateTime CompletedUtc { get; set; }
|
||||
public int PredictedScore { get; set; }
|
||||
public int RangeLow { get; set; }
|
||||
public int RangeHigh { get; set; }
|
||||
public int Correct { get; set; }
|
||||
public int Total { get; set; }
|
||||
public int SeenCount { get; set; }
|
||||
}
|
||||
|
||||
public class CompletedExamSimulationPage
|
||||
{
|
||||
public List<CompletedExamSimulationSummary> Items { get; set; } = [];
|
||||
public DateTime? NextBefore { get; set; }
|
||||
}
|
||||
|
||||
public class Step3Outcome
|
||||
{
|
||||
[BsonId]
|
||||
|
||||
@@ -62,6 +62,24 @@ public class QuestionResponse
|
||||
};
|
||||
}
|
||||
|
||||
public static QuestionResponse FromModelWithChoices(
|
||||
Question question,
|
||||
IReadOnlyCollection<string> presentedChoiceIds,
|
||||
int questionElo = 1000)
|
||||
{
|
||||
var response = FromModel(question, questionElo: questionElo, includeChoicePool: true);
|
||||
var ids = presentedChoiceIds.ToHashSet(StringComparer.Ordinal);
|
||||
response.Choices = presentedChoiceIds
|
||||
.Select(id => question.Choices.FirstOrDefault(choice => choice.Id == id))
|
||||
.Where(choice => choice is not null)
|
||||
.Cast<QuestionChoice>()
|
||||
.ToList();
|
||||
response.ChoiceExplanations = response.ChoiceExplanations.Where(entry => ids.Contains(entry.Key)).ToDictionary();
|
||||
response.WrongAnswerFollowups = response.WrongAnswerFollowups.Where(entry => ids.Contains(entry.Key)).ToDictionary();
|
||||
response.ChoiceSelectionPercentages = response.ChoiceSelectionPercentages.Where(entry => ids.Contains(entry.Key)).ToDictionary();
|
||||
return response;
|
||||
}
|
||||
|
||||
private static List<QuestionChoice> SelectLegacyChoices(Question question)
|
||||
{
|
||||
if (question.Choices.Count <= 5)
|
||||
|
||||
@@ -33,6 +33,8 @@ public class ExamSimulationStore
|
||||
_metrics = metrics;
|
||||
_simulations.Indexes.CreateOne(new CreateIndexModel<ExamSimulation>(
|
||||
Builders<ExamSimulation>.IndexKeys.Ascending(item => item.UserId).Ascending(item => item.State)));
|
||||
_simulations.Indexes.CreateOne(new CreateIndexModel<ExamSimulation>(
|
||||
Builders<ExamSimulation>.IndexKeys.Ascending(item => item.UserId).Ascending(item => item.State).Descending(item => item.CompletedUtc)));
|
||||
_simulations.Indexes.CreateOne(new CreateIndexModel<ExamSimulation>(
|
||||
Builders<ExamSimulation>.IndexKeys.Ascending(item => item.UpdatedUtc)));
|
||||
_outcomes.Indexes.CreateOne(new CreateIndexModel<Step3Outcome>(
|
||||
@@ -136,8 +138,8 @@ public class ExamSimulationStore
|
||||
Score = ExamSimulationScoring.Calculate(items, responseMap, questionMap),
|
||||
BlueprintTargets = targets,
|
||||
BlueprintActual = items.GroupBy(item => item.BlueprintCategory).ToDictionary(group => group.Key, group => group.Count()),
|
||||
ReviewQuestions = items.Select(item => QuestionResponse.FromModel(
|
||||
questionMap[item.QuestionExternalId], questionElo: item.QuestionElo, includeChoicePool: true)).ToList()
|
||||
ReviewQuestions = items.Select(item => QuestionResponse.FromModelWithChoices(
|
||||
questionMap[item.QuestionExternalId], item.PresentedChoiceIds, item.QuestionElo)).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,6 +156,33 @@ public class ExamSimulationStore
|
||||
return simulation;
|
||||
}
|
||||
|
||||
public async Task<CompletedExamSimulationPage> GetCompletedAsync(string userId, int limit, DateTime? before)
|
||||
{
|
||||
limit = Math.Clamp(limit, 1, 50);
|
||||
var filter = Builders<ExamSimulation>.Filter.Eq(item => item.UserId, userId) &
|
||||
Builders<ExamSimulation>.Filter.Eq(item => item.State, ExamSimulationStates.Completed);
|
||||
if (before is not null)
|
||||
filter &= Builders<ExamSimulation>.Filter.Lt(item => item.CompletedUtc, before.Value);
|
||||
var simulations = await _simulations.Find(filter).SortByDescending(item => item.CompletedUtc).Limit(limit + 1).ToListAsync();
|
||||
var hasMore = simulations.Count > limit;
|
||||
if (hasMore) simulations.RemoveAt(simulations.Count - 1);
|
||||
return new CompletedExamSimulationPage
|
||||
{
|
||||
Items = simulations.Select(item => new CompletedExamSimulationSummary
|
||||
{
|
||||
Id = item.Id ?? "",
|
||||
CompletedUtc = item.CompletedUtc ?? item.UpdatedUtc,
|
||||
PredictedScore = item.Score?.PredictedScore ?? 0,
|
||||
RangeLow = item.Score?.RangeLow ?? 0,
|
||||
RangeHigh = item.Score?.RangeHigh ?? 0,
|
||||
Correct = item.Score?.Correct ?? 0,
|
||||
Total = item.Score?.Total ?? item.Items.Count,
|
||||
SeenCount = item.Score?.SeenCount ?? 0
|
||||
}).ToList(),
|
||||
NextBefore = hasMore && simulations.Count > 0 ? simulations[^1].CompletedUtc : null
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExamSimulation> StartNextBlockAsync(ExamSimulation simulation)
|
||||
{
|
||||
if (simulation.State != ExamSimulationStates.BetweenBlocks)
|
||||
@@ -244,7 +273,7 @@ public class ExamSimulationStore
|
||||
{
|
||||
response.ReviewQuestions = simulation.Items.OrderBy(item => item.BlockIndex).ThenBy(item => item.Position)
|
||||
.Where(item => questionMap.ContainsKey(item.QuestionExternalId))
|
||||
.Select(item => QuestionResponse.FromModel(questionMap[item.QuestionExternalId], questionElo: item.QuestionElo, includeChoicePool: true)).ToList();
|
||||
.Select(item => QuestionResponse.FromModelWithChoices(questionMap[item.QuestionExternalId], item.PresentedChoiceIds, item.QuestionElo)).ToList();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user