Add server-side ELO ratings

This commit is contained in:
hz
2026-07-11 02:05:43 -05:00
parent bdd32c9dac
commit c716b16c53
21 changed files with 688 additions and 47 deletions
+63 -1
View File
@@ -13,12 +13,17 @@ public class QuestionResponse
public Dictionary<string, int> ChoiceSelectionPercentages { get; set; } = [];
public string Discipline { get; set; } = "";
public string System { get; set; } = "";
public string PerformanceCategory { get; set; } = "";
public int Difficulty { get; set; }
public int QuestionElo { get; set; } = 1000;
public List<QuestionLab> Labs { get; set; } = [];
public List<string> Media { get; set; } = [];
public List<string> Tags { get; set; } = [];
public static QuestionResponse FromModel(Question question, Dictionary<string, int>? liveChoiceSelectionPercentages = null) => new()
public static QuestionResponse FromModel(
Question question,
Dictionary<string, int>? liveChoiceSelectionPercentages = null,
int questionElo = 1000) => new()
{
Id = question.Id ?? "",
ExternalId = question.ExternalId,
@@ -31,7 +36,9 @@ public class QuestionResponse
ChoiceSelectionPercentages = BuildChoiceSelectionPercentages(question, liveChoiceSelectionPercentages),
Discipline = question.Discipline,
System = question.System,
PerformanceCategory = RatingCategory.Normalize(question.PerformanceCategory, question.System, question.Discipline),
Difficulty = question.Difficulty,
QuestionElo = questionElo,
Labs = question.Labs,
Media = question.Media,
Tags = question.Tags
@@ -52,4 +59,59 @@ public class FirstQuestionAttemptResponse
{
public string QuestionExternalId { get; set; } = "";
public Dictionary<string, int> ChoiceSelectionPercentages { get; set; } = [];
public int QuestionElo { get; set; } = 1000;
public RatingChangeResponse? Rating { get; set; }
}
public class SkipQuestionResponse
{
public string QuestionExternalId { get; set; } = "";
public string CorrectChoiceId { get; set; } = "";
public Dictionary<string, int> ChoiceSelectionPercentages { get; set; } = [];
public int QuestionElo { get; set; } = 1000;
public RatingChangeResponse? Rating { get; set; }
}
public static class RatingCategory
{
public const string Uncategorized = "Uncategorized";
public static readonly string[] Categories =
[
"Renal/Urinary & Male/Female Systems & Pregnancy",
"Behavioral Health & Social Sciences: Communication, Ethics, Patient Safety",
"Immune/Blood & Lymphoreticular/Endocrine/Multisystem",
"Biostatistics & Epidemiology/Population Health",
"Musculoskeletal/Skin & Subcutaneous Tissue",
"Cardiovascular System",
"Nervous System & Special Senses",
"Respiratory System",
"Gastrointestinal System",
Uncategorized
];
public static string Normalize(string? explicitCategory, string? system, string? discipline)
{
foreach (var value in new[] { explicitCategory, system, discipline })
{
if (string.IsNullOrWhiteSpace(value))
continue;
var normalized = Categories.FirstOrDefault(c => string.Equals(c, value.Trim(), StringComparison.OrdinalIgnoreCase));
if (normalized is not null)
return normalized;
var lower = value.ToLowerInvariant();
if (lower.Contains("cardio")) return "Cardiovascular System";
if (lower.Contains("resp")) return "Respiratory System";
if (lower.Contains("gastro") || lower.Contains("hepato")) return "Gastrointestinal System";
if (lower.Contains("neuro") || lower.Contains("special sense")) return "Nervous System & Special Senses";
if (lower.Contains("renal") || lower.Contains("urinary") || lower.Contains("pregnan") || lower.Contains("reproductive")) return "Renal/Urinary & Male/Female Systems & Pregnancy";
if (lower.Contains("behavior") || lower.Contains("ethic") || lower.Contains("communication") || lower.Contains("safety")) return "Behavioral Health & Social Sciences: Communication, Ethics, Patient Safety";
if (lower.Contains("immune") || lower.Contains("blood") || lower.Contains("endocrine") || lower.Contains("multisystem")) return "Immune/Blood & Lymphoreticular/Endocrine/Multisystem";
if (lower.Contains("biostat") || lower.Contains("epidemi") || lower.Contains("population")) return "Biostatistics & Epidemiology/Population Health";
if (lower.Contains("musculo") || lower.Contains("skin") || lower.Contains("derm")) return "Musculoskeletal/Skin & Subcutaneous Tissue";
}
return Uncategorized;
}
}