Track real first attempt question stats

This commit is contained in:
hz
2026-07-11 01:42:18 -05:00
parent e991dccbb2
commit 8a86734a0b
13 changed files with 297 additions and 6 deletions
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using MongoDB.Driver;
using QuestionsApi.Models;
using QuestionsApi.Services;
using System.Security.Claims;
namespace QuestionsApi.Controllers;
@@ -11,10 +12,12 @@ namespace QuestionsApi.Controllers;
public class QuestionsController : ControllerBase
{
private readonly QuestionStore _questions;
private readonly QuestionAttemptStore _attempts;
public QuestionsController(QuestionStore questions)
public QuestionsController(QuestionStore questions, QuestionAttemptStore attempts)
{
_questions = questions;
_attempts = attempts;
}
[HttpGet("systems")]
@@ -30,7 +33,12 @@ public class QuestionsController : ControllerBase
public async Task<IActionResult> Search([FromBody] QuestionSearchRequest req)
{
var questions = await _questions.SearchAsync(req, User.IsInRole("SUPER"));
return Ok(questions.Select(QuestionResponse.FromModel).ToList());
var percentages = await _attempts.GetPercentagesByQuestionAsync(questions.Select(q => q.ExternalId));
return Ok(questions.Select(q =>
{
percentages.TryGetValue(q.ExternalId, out var questionPercentages);
return QuestionResponse.FromModel(q, questionPercentages);
}).ToList());
}
[HttpGet("{id}")]
@@ -41,7 +49,43 @@ public class QuestionsController : ControllerBase
if (question is null || (!question.IsPublished && !User.IsInRole("SUPER")))
return NotFound();
return Ok(QuestionResponse.FromModel(question));
var percentages = await _attempts.GetPercentagesForQuestionAsync(question.ExternalId);
return Ok(QuestionResponse.FromModel(question, percentages));
}
[HttpPost("attempts/first")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> RecordFirstAttempt([FromBody] FirstQuestionAttemptRequest req)
{
if (string.IsNullOrWhiteSpace(req.QuestionExternalId))
return BadRequest("questionExternalId required");
if (string.IsNullOrWhiteSpace(req.ChoiceId))
return BadRequest("choiceId required");
var question = await _questions.GetByExternalIdAsync(req.QuestionExternalId.Trim());
if (question is null || (!question.IsPublished && !User.IsInRole("SUPER")))
return NotFound();
if (!question.Choices.Any(c => c.Id == req.ChoiceId.Trim()))
return BadRequest("choiceId must match a choice id");
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
?? User.FindFirstValue(ClaimTypes.Name)
?? User.Identity?.Name
?? "";
if (string.IsNullOrWhiteSpace(userId))
return Unauthorized();
await _attempts.RecordFirstAttemptAsync(
question.ExternalId,
userId,
req.ChoiceId.Trim(),
req.ChoiceId.Trim() == question.CorrectChoiceId);
return Ok(new FirstQuestionAttemptResponse
{
QuestionExternalId = question.ExternalId,
ChoiceSelectionPercentages = await _attempts.GetPercentagesForQuestionAsync(question.ExternalId)
});
}
[HttpPost]