OpenMedica by IntelMedica.ai
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.

Quick Install

Run in Manus
View Source

Installation

npx skills add Open-Medica/open-medical-skills --skill board-exam-prep

Skill 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