Add SUPER exam results preview
This commit is contained in:
@@ -25,6 +25,14 @@ public class ExamSimulationsController(ExamSimulationStore simulations) : Contro
|
||||
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
|
||||
}
|
||||
|
||||
[HttpGet("preview-results")]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> PreviewResults()
|
||||
{
|
||||
try { return Ok(await simulations.BuildResultsPreviewAsync(UserId())); }
|
||||
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
|
||||
}
|
||||
|
||||
[HttpGet("active")]
|
||||
public async Task<IActionResult> Active()
|
||||
{
|
||||
|
||||
@@ -93,6 +93,54 @@ public class ExamSimulationStore
|
||||
return simulation;
|
||||
}
|
||||
|
||||
public async Task<ExamSimulationStateResponse> BuildResultsPreviewAsync(string userId)
|
||||
{
|
||||
var bank = (await _questions.GetPublishedAsync()).Where(question => question.Choices.Count >= 5).ToList();
|
||||
if (bank.Count < RequiredQuestions)
|
||||
throw new InvalidOperationException($"At least {RequiredQuestions} published questions are required.");
|
||||
var targets = BuildTargets();
|
||||
var selected = SelectForm(bank, [], targets);
|
||||
var items = selected.Select((question, index) => new ExamSimulationItem
|
||||
{
|
||||
QuestionExternalId = question.ExternalId,
|
||||
BlockIndex = BlockIndexForPosition(index),
|
||||
Position = PositionWithinBlock(index),
|
||||
PresentedChoiceIds = SelectChoices(question),
|
||||
QuestionElo = 1000,
|
||||
WasSeen = false,
|
||||
BlueprintCategory = BlueprintCategory(question)
|
||||
}).ToList();
|
||||
var questionMap = selected.ToDictionary(question => question.ExternalId, StringComparer.Ordinal);
|
||||
var responses = items.Select((item, index) =>
|
||||
{
|
||||
var question = questionMap[item.QuestionExternalId];
|
||||
var choiceId = index % 3 == 0
|
||||
? item.PresentedChoiceIds.First(id => id != question.CorrectChoiceId)
|
||||
: question.CorrectChoiceId;
|
||||
return new ExamSimulationResponse
|
||||
{
|
||||
QuestionExternalId = item.QuestionExternalId,
|
||||
ChoiceId = choiceId,
|
||||
ElapsedSeconds = 75 + index % 45,
|
||||
Flagged = index % 11 == 0
|
||||
};
|
||||
}).ToList();
|
||||
var responseMap = responses.ToDictionary(item => item.QuestionExternalId, StringComparer.Ordinal);
|
||||
return new ExamSimulationStateResponse
|
||||
{
|
||||
Id = "preview",
|
||||
State = ExamSimulationStates.Completed,
|
||||
CurrentBlock = BlockSizes.Length - 1,
|
||||
BreakSecondsRemaining = 3300,
|
||||
Responses = responses,
|
||||
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()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExamSimulation?> FindActiveAsync(string userId) =>
|
||||
await _simulations.Find(item => item.UserId == userId &&
|
||||
(item.State == ExamSimulationStates.BetweenBlocks || item.State == ExamSimulationStates.BlockActive))
|
||||
@@ -261,6 +309,28 @@ public class ExamSimulationStore
|
||||
private Task ReplaceAsync(ExamSimulation simulation) =>
|
||||
_simulations.ReplaceOneAsync(item => item.Id == simulation.Id && item.UserId == simulation.UserId, simulation);
|
||||
|
||||
private static int BlockIndexForPosition(int position)
|
||||
{
|
||||
var consumed = 0;
|
||||
for (var block = 0; block < BlockSizes.Length; block++)
|
||||
{
|
||||
if (position < consumed + BlockSizes[block]) return block;
|
||||
consumed += BlockSizes[block];
|
||||
}
|
||||
return BlockSizes.Length - 1;
|
||||
}
|
||||
|
||||
private static int PositionWithinBlock(int position)
|
||||
{
|
||||
var consumed = 0;
|
||||
foreach (var size in BlockSizes)
|
||||
{
|
||||
if (position < consumed + size) return position - consumed;
|
||||
consumed += size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static ExamSimulationQuestionResponse SafeQuestion(Question question, List<string> presentedIds) => new()
|
||||
{
|
||||
ExternalId = question.ExternalId,
|
||||
|
||||
Reference in New Issue
Block a user