Back to Skills
Education Safe Moderate Evidence
Medical Board Exam Preparation
by Open Medical Skills Community
Description
USMLE/COMLEX board exam preparation tool with practice questions, spaced repetition, and high-yield topic reviews. Tracks performance and identifies weak areas.
Installation
npx skills add Open-Medica/open-medical-skills --skill board-exam-prepSkill Files
Python
#!/usr/bin/env python3
"""
Medical Board Exam Preparation Tool
====================================
USMLE/COMLEX preparation assistant implementing spaced repetition (SM-2
algorithm), performance tracking, and high-yield topic identification.
Clinical Purpose:
Assists medical students and residents in systematic board exam
preparation by tracking knowledge gaps, optimizing review schedules
using evidence-based spaced repetition, and generating practice
questions across all USMLE content categories.
References:
- Deng F, et al. "Spaced Repetition in Medical Education." Med Educ. 2015.
- NBME Content Outline for USMLE Step 1, Step 2 CK, Step 3.
- Wozniak PA. "SuperMemo 2 Algorithm." 1990.
DISCLAIMER: This tool is for educational purposes only. Question content
is illustrative and does not represent actual NBME exam material.
"""
import json
import math
import hashlib
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import Optional
from enum import Enum
class ExamType(Enum):
STEP1 = "usmle_step1"
STEP2CK = "usmle_step2ck"
STEP3 = "usmle_step3"
COMLEX1 = "comlex_level1"
COMLEX2CE = "comlex_level2ce"
class Discipline(Enum):
ANATOMY = "anatomy"
BIOCHEMISTRY = "biochemistry"
PHYSIOLOGY = "physiology"
PATHOLOGY = "pathology"
PHARMACOLOGY = "pharmacology"
MICROBIOLOGY = "microbiology"
IMMUNOLOGY = "immunology"
BEHAVIORAL_SCIENCE = "behavioral_science"
BIOSTATISTICS = "biostatistics"
INTERNAL_MEDICINE = "internal_medicine"
SURGERY = "surgery"
PEDIATRICS = "pediatrics"
OBSTETRICS_GYNECOLOGY = "obstetrics_gynecology"
PSYCHIATRY = "psychiatry"
PREVENTIVE_MEDICINE = "preventive_medicine"
NEUROSCIENCE = "neuroscience"
GENETICS = "genetics"
class OrganSystem(Enum):
CARDIOVASCULAR = "cardiovascular"
RESPIRATORY = "respiratory"
GASTROINTESTINAL = "gastrointestinal"
RENAL = "renal"
ENDOCRINE = "endocrine"
NERVOUS = "nervous"
MUSCULOSKELETAL = "musculoskeletal"
HEMATOLOGIC = "hematologic"
REPRODUCTIVE = "reproductive"
IMMUNE = "immune"
INTEGUMENTARY = "integumentary"
MULTISYSTEM = "multisystem"
class Difficulty(Enum):
EASY = 1
MEDIUM = 2
HARD = 3
@dataclass
class Question:
"""Represents a single board-style practice question."""
question_id: str
stem: str
answer_choices: dict # {"A": "...", "B": "...", ...}
correct_answer: str
explanation: str
discipline: Discipline
organ_system: OrganSystem
difficulty: Difficulty
high_yield: bool = True
tags: list = field(default_factory=list)
@dataclass
class ReviewCard:
"""SM-2 spaced repetition card for a topic or question."""
card_id: str
question_id: str
ease_factor: float = 2.5 # SM-2 default
interval_days: int = 1
repetitions: int = 0
next_review: str = ""
last_quality: int = 0 # 0-5 scale
def __post_init__(self):
if not self.next_review:
self.next_review = datetime.now().isoformat()
@dataclass
class StudentProfile:
"""Tracks a student's performance and progress."""
student_id: str
exam_target: ExamType
total_questions_attempted: int = 0
total_correct: int = 0
discipline_scores: dict = field(default_factory=dict)
system_scores: dict = field(default_factory=dict)
review_cards: list = field(default_factory=list)
study_sessions: list = field(default_factory=list)
weak_areas: list = field(default_factory=list)
def sm2_algorithm(card: ReviewCard, quality: int) -> ReviewCard:
"""
Implement the SM-2 spaced repetition algorithm.
Args:
card: The review card to update
quality: Response quality (0-5):
5 - perfect response
4 - correct after hesitation
3 - correct with difficulty
2 - incorrect but remembered after seeing answer
1 - incorrect, vaguely remembered
0 - complete blackout
Returns:
Updated ReviewCard with new interval and ease factor
"""
quality = max(0, min(5, quality))
if quality >= 3:
# Correct response
if card.repetitions == 0:
card.interval_days = 1
elif card.repetitions == 1:
card.interval_days = 6
else:
card.interval_days = round(card.interval_days * card.ease_factor)
card.repetitions += 1
else:
# Incorrect - reset
card.repetitions = 0
card.interval_days = 1
# Update ease factor (minimum 1.3)
card.ease_factor = max(1.3,
card.ease_factor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
)
card.last_quality = quality
next_date = datetime.now() + timedelta(days=card.interval_days)
card.next_review = next_date.isoformat()
return card
def calculate_performance(profile: StudentProfile) -> dict:
"""Analyze student performance across disciplines and organ systems."""
if profile.total_questions_attempted == 0:
return {"overall_percentage": 0, "weak_areas": [], "strong_areas": []}
overall_pct = (profile.total_correct / profile.total_questions_attempted) * 100
# Identify weak areas (< 60% correct with >= 5 attempts)
weak_areas = []
strong_areas = []
for discipline, scores in profile.discipline_scores.items():
attempted = scores.get("attempted", 0)
correct = scores.get("correct", 0)
if attempted >= 5:
pct = (correct / attempted) * 100
entry = {"topic": discipline, "percentage": round(pct, 1), "attempted": attempted}
if pct < 60:
weak_areas.append(entry)
elif pct >= 80:
strong_areas.append(entry)
for system, scores in profile.system_scores.items():
attempted = scores.get("attempted", 0)
correct = scores.get("correct", 0)
if attempted >= 5:
pct = (correct / attempted) * 100
entry = {"topic": system, "percentage": round(pct, 1), "attempted": attempted}
if pct < 60:
weak_areas.append(entry)
elif pct >= 80:
strong_areas.append(entry)
# Sort weak areas by percentage (worst first)
weak_areas.sort(key=lambda x: x["percentage"])
strong_areas.sort(key=lambda x: x["percentage"], reverse=True)
return {
"overall_percentage": round(overall_pct, 1),
"total_attempted": profile.total_questions_attempted,
"total_correct": profile.total_correct,
"weak_areas": weak_areas,
"strong_areas": strong_areas,
"recommendation": _generate_study_recommendation(overall_pct, weak_areas),
}
def _generate_study_recommendation(overall_pct: float, weak_areas: list) -> str:
"""Generate personalized study recommendations based on performance."""
if overall_pct >= 80:
base = "Strong overall performance. Focus on maintaining knowledge with spaced repetition."
elif overall_pct >= 65:
base = "Solid foundation. Target specific weak areas to push into the high-performance range."
elif overall_pct >= 50:
base = "Developing understanding. Increase study intensity and focus on foundational concepts."
else:
base = "Significant knowledge gaps identified. Prioritize core concepts and high-yield topics."
if weak_areas:
topics = [w["topic"] for w in weak_areas[:3]]
base += f" Priority review areas: {', '.join(topics)}."
return base
def get_due_reviews(profile: StudentProfile) -> list:
"""Get all review cards that are due for review today."""
now = datetime.now()
due = []
for card in profile.review_cards:
if isinstance(card, dict):
review_date = datetime.fromisoformat(card.get("next_review", now.isoformat()))
else:
review_date = datetime.fromisoformat(card.next_review)
if review_date <= now:
due.append(card)
return due
def record_answer(profile: StudentProfile, question: Question,
selected_answer: str) -> dict:
"""
Record a student's answer to a question and update tracking.
Returns result with correctness, explanation, and updated stats.
"""
is_correct = selected_answer.upper() == question.correct_answer.upper()
profile.total_questions_attempted += 1
if is_correct:
profile.total_correct += 1
# Update discipline scores
disc = question.discipline.value
if disc not in profile.discipline_scores:
profile.discipline_scores[disc] = {"attempted": 0, "correct": 0}
profile.discipline_scores[disc]["attempted"] += 1
if is_correct:
profile.discipline_scores[disc]["correct"] += 1
# Update organ system scores
sys_name = question.organ_system.value
if sys_name not in profile.system_scores:
profile.system_scores[sys_name] = {"attempted": 0, "correct": 0}
profile.system_scores[sys_name]["attempted"] += 1
if is_correct:
profile.system_scores[sys_name]["correct"] += 1
return {
"correct": is_correct,
"selected": selected_answer,
"correct_answer": question.correct_answer,
"explanation": question.explanation,
"discipline": disc,
"organ_system": sys_name,
"running_score": f"{profile.total_correct}/{profile.total_questions_attempted}",
}
# High-yield topic bank for demonstration
HIGH_YIELD_TOPICS = {
"step1": [
{"topic": "First Aid: Rapid Review", "discipline": "multidiscipline", "priority": 1},
{"topic": "Pathoma: Fundamentals of Pathology", "discipline": "pathology", "priority": 1},
{"topic": "Sketchy: Antimicrobials", "discipline": "pharmacology", "priority": 1},
{"topic": "Biochemistry: Metabolic Pathways", "discipline": "biochemistry", "priority": 2},
{"topic": "Biostatistics: Study Design & Bias", "discipline": "biostatistics", "priority": 1},
{"topic": "Embryology: High-Yield Associations", "discipline": "anatomy", "priority": 2},
{"topic": "Autonomic Pharmacology", "discipline": "pharmacology", "priority": 1},
{"topic": "Renal Physiology: Nephron Function", "discipline": "physiology", "priority": 1},
{"topic": "Cardiac Pathology: Valvular Disease", "discipline": "pathology", "priority": 1},
{"topic": "Immunology: Hypersensitivity Reactions", "discipline": "immunology", "priority": 1},
],
}
def get_study_plan(exam_type: str, weeks_until_exam: int) -> dict:
"""
Generate a structured study plan based on exam type and time remaining.
"""
topics = HIGH_YIELD_TOPICS.get(exam_type, HIGH_YIELD_TOPICS["step1"])
if weeks_until_exam <= 2:
phase = "Final Review"
strategy = "Focus exclusively on high-yield rapid review and weak areas. Practice questions only."
hours_per_day = 10
elif weeks_until_exam <= 6:
phase = "Intensive Review"
strategy = "Organ system-based review with heavy question practice (40+ questions/day)."
hours_per_day = 8
elif weeks_until_exam <= 12:
phase = "Content Review"
strategy = "Systematic content review with integrated questions. Build foundation."
hours_per_day = 6
else:
phase = "Early Preparation"
strategy = "Begin content review. Focus on understanding concepts, not memorization."
hours_per_day = 4
return {
"exam": exam_type,
"weeks_remaining": weeks_until_exam,
"phase": phase,
"strategy": strategy,
"recommended_hours_per_day": hours_per_day,
"priority_topics": [t for t in topics if t["priority"] == 1],
"secondary_topics": [t for t in topics if t["priority"] == 2],
"daily_structure": {
"morning": "Content review (2-3 hours)",
"midday": "Question blocks (2-3 hours, 40 questions per block)",
"afternoon": "Review incorrect questions and weak areas (1-2 hours)",
"evening": "Spaced repetition flashcard review (1 hour)",
},
"disclaimer": "Study plan is a general recommendation. Adjust based on individual needs.",
}
def run_board_prep(action: str, **kwargs) -> str:
"""
Main entry point for the Board Exam Prep tool.
Actions:
study_plan: Generate study plan (exam_type, weeks_until_exam)
performance: Analyze performance (student_profile dict)
due_reviews: Get due spaced repetition cards
"""
if action == "study_plan":
result = get_study_plan(
kwargs.get("exam_type", "step1"),
kwargs.get("weeks_until_exam", 12),
)
elif action == "performance":
profile = StudentProfile(
student_id="demo",
exam_target=ExamType.STEP1,
**{k: v for k, v in kwargs.items() if k != "action"},
)
result = calculate_performance(profile)
else:
result = {
"error": f"Unknown action: {action}",
"available_actions": ["study_plan", "performance", "due_reviews"],
}
return json.dumps(result, indent=2)
if __name__ == "__main__":
print("=== USMLE Step 1 Study Plan (8 weeks out) ===")
print(run_board_prep("study_plan", exam_type="step1", weeks_until_exam=8))
Version
1.0.0
License
MIT
Status
Published
Reviewer
Pending Review
Date Added
2026-03-02
Specialties
Medical Education
Tags
usmle board-exams medical-education test-prep