Add data-driven dynamic question mode

This commit is contained in:
hz
2026-07-23 12:33:23 -05:00
parent 50f07a77a5
commit fcbc288a49
22 changed files with 1247 additions and 21 deletions
@@ -0,0 +1,190 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace QuestionsApi.Models;
public sealed class DynamicQuestionBlueprint
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
public string ExternalId { get; set; } = "";
public int Version { get; set; } = 1;
public string ObjectiveId { get; set; } = "";
public string Title { get; set; } = "";
public string System { get; set; } = "";
public string Discipline { get; set; } = "";
public string PerformanceCategory { get; set; } = "";
public string PhysicianTask { get; set; } = "";
public int Difficulty { get; set; } = 3;
public bool IsPublished { get; set; } = true;
public string StemTemplate { get; set; } = "";
public string LeadIn { get; set; } = "";
public Dictionary<string, DynamicVariableDefinition> Variables { get; set; } = [];
public Dictionary<string, DynamicDerivedDefinition> Derived { get; set; } = [];
public List<DynamicConstraint> Constraints { get; set; } = [];
public List<DynamicLabTemplate> Labs { get; set; } = [];
public DynamicAnswerTemplate CorrectAnswer { get; set; } = new();
public List<DynamicAnswerTemplate> Distractors { get; set; } = [];
public string ExplanationTemplate { get; set; } = "";
public string KeyTakeaway { get; set; } = "";
public string UsmlePearl { get; set; } = "";
public List<QuestionReference> References { get; set; } = [];
public List<string> Tags { get; set; } = [];
}
public sealed class DynamicVariableDefinition
{
public string Type { get; set; } = "integer";
public double Minimum { get; set; }
public double Maximum { get; set; }
public double Step { get; set; } = 1;
public int Precision { get; set; }
public List<string> Values { get; set; } = [];
}
public sealed class DynamicDerivedDefinition
{
public string Operation { get; set; } = "";
public string Left { get; set; } = "";
public string Right { get; set; } = "";
public int Precision { get; set; }
}
public sealed class DynamicConstraint
{
public string Field { get; set; } = "";
public string Operator { get; set; } = "";
public double Value { get; set; }
}
public sealed class DynamicLabTemplate
{
public string Name { get; set; } = "";
public string ValueTemplate { get; set; } = "";
public string Unit { get; set; } = "";
public string Reference { get; set; } = "";
}
public sealed class DynamicAnswerTemplate
{
public string Id { get; set; } = "";
public string TextTemplate { get; set; } = "";
public string ExplanationTemplate { get; set; } = "";
}
public sealed class DynamicGenerationClaims
{
public string UserId { get; set; } = "";
public string BlueprintExternalId { get; set; } = "";
public int BlueprintVersion { get; set; }
public string ObjectiveId { get; set; } = "";
public string Seed { get; set; } = "";
public string Nonce { get; set; } = "";
public int GeneratorVersion { get; set; } = 1;
public DateTime ExpiresUtc { get; set; }
}
public sealed class DynamicRunClaims
{
public string UserId { get; set; } = "";
public List<string> SeenObjectiveIds { get; set; } = [];
public string PreviousObjectiveId { get; set; } = "";
public int Cycle { get; set; }
public DateTime ExpiresUtc { get; set; }
}
public sealed class DynamicGeneratedQuestion
{
public Question Question { get; set; } = new();
public DynamicQuestionBlueprint Blueprint { get; set; } = new();
}
public sealed class DynamicQuestionEnvelope
{
public QuestionResponse Question { get; set; } = new();
public string Source { get; set; } = "dynamic";
public string ObjectiveId { get; set; } = "";
public string ObjectiveTitle { get; set; } = "";
public string GenerationToken { get; set; } = "";
public string RunToken { get; set; } = "";
public int CycleSeen { get; set; }
public int TotalObjectives { get; set; }
}
public sealed class DynamicNextRequest
{
public string RunToken { get; set; } = "";
}
public sealed class DynamicAttemptRequest
{
public string GenerationToken { get; set; } = "";
public string ChoiceId { get; set; } = "";
public bool IsSkip { get; set; }
public int DurationSeconds { get; set; }
}
public sealed class DynamicAttemptResponse
{
public bool Inserted { get; set; }
public bool IsCorrect { get; set; }
public string CorrectChoiceId { get; set; } = "";
public string ObjectiveId { get; set; } = "";
public int ObjectiveElo { get; set; }
public int RatingChange { get; set; }
}
public sealed class DynamicReportRequest
{
public string GenerationToken { get; set; } = "";
public string Category { get; set; } = "";
public string Details { get; set; } = "";
}
public sealed class DynamicQuestionStatusResponse
{
public bool Available { get; set; }
public int PublishedObjectives { get; set; }
public int AttemptedObjectives { get; set; }
public int TotalAttempts { get; set; }
public int AverageObjectiveElo { get; set; } = 1000;
}
public sealed class DynamicQuestionAttempt
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
public string UserId { get; set; } = "";
public string Nonce { get; set; } = "";
public string BlueprintExternalId { get; set; } = "";
public int BlueprintVersion { get; set; }
public string ObjectiveId { get; set; } = "";
public string ChoiceId { get; set; } = "";
public bool IsCorrect { get; set; }
public bool IsSkip { get; set; }
public int DurationSeconds { get; set; }
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
}
public sealed class DynamicObjectiveMastery
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
public string UserId { get; set; } = "";
public string ObjectiveId { get; set; } = "";
public int Elo { get; set; } = 1000;
public int Attempts { get; set; }
public int Correct { get; set; }
public DateTime LastSeenUtc { get; set; }
}
public sealed class DynamicGenerationDescriptor
{
public string BlueprintExternalId { get; set; } = "";
public int BlueprintVersion { get; set; }
public int GeneratorVersion { get; set; }
public string Seed { get; set; } = "";
}