using Microsoft.Extensions.Configuration; using MongoDB.Driver; using QuestionsApi.Models; using QuestionsApi.Services; const string accessedOn = "2026-07-31"; var apply = args.Contains("--apply", StringComparer.OrdinalIgnoreCase); var connectionString = Environment.GetEnvironmentVariable("STEP3_MONGODB_CONNECTION_STRING") ?? "mongodb://192.168.86.50:27017"; var databaseName = Environment.GetEnvironmentVariable("STEP3_MONGODB_DATABASE") ?? new MongoUrl(connectionString).DatabaseName ?? "promiscuity"; var questions = SeedCatalog.All.Select(Build).ToList(); var errors = questions.Select(q => (q.ExternalId, Error: Validate(q))).Where(x => x.Error is not null).ToList(); foreach (var error in errors) Console.Error.WriteLine($"{error.ExternalId}: {error.Error}"); if (errors.Count > 0) return 1; if (questions.Count != 50 || questions.Select(q => q.ExternalId).Distinct().Count() != 50) { Console.Error.WriteLine("Expected exactly 50 unique questions."); return 1; } Console.WriteLine($"Validated {questions.Count} difficulty-4 gap questions."); foreach (var group in questions.GroupBy(q => q.System).OrderBy(g => g.Key)) Console.WriteLine($"{group.Key}: {group.Count()}"); if (!apply) { Console.WriteLine("Dry run only. Pass --apply to upsert into the configured database."); return 0; } var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["MongoDB:ConnectionString"] = connectionString, ["MongoDB:DatabaseName"] = databaseName }).Build(); var store = new QuestionStore(new MongoDatabaseProvider(configuration)); var upserted = await store.UpsertManyByExternalIdAsync(questions); var saved = await store.GetByExternalIdsAsync(questions.Select(q => q.ExternalId)); var invalid = saved.Values.Count(q => q.Difficulty != 4 || !q.IsPublished || q.Media.Count != 0 || q.References.Count == 0 || q.FlaggedForReviewUtc is not null); if (upserted != 50 || saved.Count != 50 || invalid != 0) { Console.Error.WriteLine($"Verification failed: upserted={upserted}, found={saved.Count}, invalid={invalid}."); return 1; } Console.WriteLine($"Upserted and verified 50 questions in database '{databaseName}'."); return 0; string? Validate(UpsertQuestionRequest q) { var error = QuestionValidator.Validate(q); if (error is not null) return error; if (!q.ExternalId.StartsWith("gap-d4-20260731-", StringComparison.Ordinal)) return "incorrect externalId prefix"; if (q.Difficulty != 4 || !q.IsPublished) return "question must be published at difficulty 4"; if (q.Choices.Count < 5) return "one correct answer and at least four distractors are required"; if (q.Media.Count != 0) return "unreviewed media is prohibited"; if (q.Tags.Count is < 3 or > 6) return "3-6 tags required"; if (q.ChoiceSelectionPercentages.Count != q.Choices.Count || q.ChoiceSelectionPercentages.Values.Any(v => v != 0)) return "choice percentages must initialize to zero"; if (!q.ChoiceExplanations[q.CorrectChoiceId].StartsWith("Correct.")) return "correct explanation prefix required"; if (q.ChoiceExplanations.Where(x => x.Key != q.CorrectChoiceId).Any(x => !x.Value.StartsWith("Incorrect."))) return "incorrect explanation prefix required"; return null; } UpsertQuestionRequest Build(Seed s) { var ids = Enumerable.Range(0, s.Answers.Length).Select(i => ((char)('A' + i)).ToString()).ToArray(); var choices = ids.Zip(s.Answers, (id, text) => new QuestionChoice { Id = id, Text = text }).ToList(); var explanations = ids.ToDictionary(id => id, id => id == "A" ? $"Correct. {s.Rationale}" : $"Incorrect. {choices.Single(c => c.Id == id).Text} does not fit the decisive finding: {s.KeyFact}. {s.Takeaway}"); var distractorIds = ids.Skip(1).ToArray(); var wrong = distractorIds.ToDictionary(id => id, id => { var index = Array.IndexOf(distractorIds, id); var alternatives = new[] { distractorIds[(index + 1) % distractorIds.Length], distractorIds[(index + 2) % distractorIds.Length] }; return new WrongAnswerFollowup { Prompt = $"Which limitation of option {id} should have ruled it out in this case?", Choices = [ new() { Id = "A", Text = explanations[id] }, new() { Id = "B", Text = explanations[alternatives[0]] }, new() { Id = "C", Text = explanations[alternatives[1]] } ], CorrectChoiceId = "A", TeachingPoint = explanations[id] }; }); var reference = References.For(s.Reference); return new UpsertQuestionRequest { ExternalId = $"gap-d4-20260731-{s.Slug}", Stem = s.Stem, LeadIn = s.LeadIn, Choices = choices, CorrectChoiceId = "A", Explanation = $"Correct. {s.Rationale}", KeyTakeaway = s.Takeaway, UsmlePearl = s.Takeaway, ReviewFollowups = [new() { Prompt = "Which principle best generalizes from this case?", Choices = [new() { Id = "A", Text = s.Takeaway }, new() { Id = "B", Text = "Delay action until every possible diagnosis is excluded." }, new() { Id = "C", Text = "Base management on a nondispositive finding alone." }], CorrectChoiceId = "A", TeachingPoint = s.Takeaway }], ChoiceExplanations = explanations, WrongAnswerFollowups = wrong, ChoiceSelectionPercentages = ids.ToDictionary(id => id, _ => 0), Discipline = s.Discipline, System = s.System, PerformanceCategory = s.Task, Difficulty = 4, Labs = [], Media = [], Tags = [s.Topic, "difficulty 4", "gap coverage", s.Discipline.ToLowerInvariant()], References = [new() { Title = reference.Title, Publisher = reference.Publisher, Url = reference.Url, Supports = reference.Supports, AccessedOn = accessedOn, Reviewer = "OpenAI Codex medical content review" }], IsPublished = true }; } internal sealed record Seed(string Slug, string System, string Discipline, string Task, string Topic, string Stem, string LeadIn, string[] Answers, string Rationale, string Takeaway, string KeyFact, string ContextFact, string NeutralFact, string Reference); internal sealed record ReferenceSeed(string Title, string Publisher, string Url, string Supports); internal static class References { internal static ReferenceSeed For(string key) => key switch { "APA" => new("Clinical Practice Guidelines", "American Psychiatric Association", "https://www.psychiatry.org/psychiatrists/practice/clinical-practice-guidelines", "Evidence-based assessment and treatment of psychiatric disorders."), "SAMHSA" => new("National Guidelines for Behavioral Health Crisis Care", "Substance Abuse and Mental Health Services Administration", "https://www.samhsa.gov/find-help/implementing-behavioral-health-crisis-care", "Crisis assessment, stabilization, and linkage to care."), "CDC-MEC" => new("U.S. Medical Eligibility Criteria for Contraceptive Use, 2024", "Centers for Disease Control and Prevention", "https://www.cdc.gov/mmwr/volumes/73/rr/rr7304a1.htm", "Safe contraceptive selection in patients with medical comorbidities."), "CDC-STI" => new("Sexually Transmitted Infections Treatment Guidelines", "Centers for Disease Control and Prevention", "https://www.cdc.gov/std/treatment-guidelines/default.htm", "Diagnosis, treatment, partner management, and prevention of sexually transmitted infections."), "USPSTF" => new("Recommendation Topics", "U.S. Preventive Services Task Force", "https://www.uspreventiveservicestaskforce.org/uspstf/recommendation-topics", "Evidence-based screening and preventive service recommendations."), "ACOG" => new("Clinical Guidance", "American College of Obstetricians and Gynecologists", "https://www.acog.org/clinical", "Evidence-based obstetric and gynecologic clinical management."), "AAO-HNS" => new("Clinical Practice Guidelines", "American Academy of Otolaryngology-Head and Neck Surgery", "https://www.entnet.org/quality-practice/quality-products/clinical-practice-guidelines/", "Evidence-based evaluation and treatment of otolaryngologic disease."), "AAAAI" => new("Statements and Practice Parameters", "American Academy of Allergy, Asthma & Immunology", "https://www.aaaai.org/practice-management/statements-practice-parameters", "Diagnosis and management of allergic and immunologic disease."), "ENDO" => new("Clinical Practice Guidelines", "Endocrine Society", "https://www.endocrine.org/clinical-practice-guidelines", "Evidence-based diagnosis and management of endocrine emergencies and disorders."), "AUA" => new("Clinical Guidelines", "American Urological Association", "https://www.auanet.org/guidelines-and-quality/guidelines", "Evidence-based emergency management of urologic conditions."), "IDSA-SSTI" => new("Practice Guidelines for Skin and Soft Tissue Infections", "Infectious Diseases Society of America", "https://www.idsociety.org/practice-guideline/skin-and-soft-tissue-infections/", "Recognition and urgent surgical treatment of necrotizing soft-tissue infection."), "FDA-LAM" => new("Lamotrigine Drug Safety Communication", "U.S. Food and Drug Administration", "https://www.fda.gov/drugs/drug-safety-and-availability/fda-warns-serious-immune-system-reaction-seizure-and-mental-health-medicine-lamotrigine-lamictal", "Serious cutaneous and systemic reactions associated with lamotrigine."), "AHRQ" => new("Patient Safety Network", "Agency for Healthcare Research and Quality", "https://psnet.ahrq.gov/", "Systems approaches to patient safety and quality improvement."), "AMA" => new("Code of Medical Ethics", "American Medical Association", "https://code-medical-ethics.ama-assn.org/", "Ethical duties involving consent, confidentiality, capacity, and professionalism."), _ => throw new ArgumentOutOfRangeException(nameof(key), key, null) }; }