Explain medication titration in icu using the infusion pump

Reading File
Finding Sources
Finding Sources
Searching the Web

medication titration infusion pump ICU critical care nursing principles

Finding Sources
Finding Sources
Reading Web Page

https://www.myamericannurse.com/medication-titration

Finding Sources
Finding Sources
Finding Sources
Reading Web Page

https://www.augusta.edu/mcg/center-for-digital-health/docume…

I now have comprehensive information to deliver a thorough, well-structured answer.

Medication Titration in the ICU Using an Infusion Pump

What is Medication Titration?

Titration is the process of adjusting the rate of an intravenous (IV) infusion - upward or downward - in small, deliberate increments to achieve a specific clinical goal (target blood pressure, sedation score, heart rate, etc.) while staying within a safe dose range. It is one of the most common and high-risk nursing activities in the ICU.

Why Infusion Pumps Are Mandatory in the ICU

All continuous ICU drips must be administered via a dedicated infusion pump - never gravity flow. The reasons are:
  • Precision: Pumps deliver rates in tenths of mL/hr, which is critical when drugs like norepinephrine cause major physiological changes at micro-dose changes.
  • Dose-error prevention: Modern "smart pumps" contain built-in drug libraries with hard and soft dose limits (DERS - Dose Error Reduction Software). Hard limits auto-stop infusions exceeding maximum safe doses; soft limits alert the nurse to confirm before proceeding.
  • Safety during transport: Pumps maintain accurate rates even when moved.
  • Documentation integration: In facilities with electronic health records (EHR), smart pumps automatically upload infusion rates and dosages for charting.

The Three Roles in Titration

The Joint Commission (TJC) Medication Management Standard defines three accountable roles:
RoleResponsibility
Prescriber (Physician/NP/PA)Writes a complete titration order with goal, starting dose, increment, frequency, and maximum dose
PharmacistVerifies order for appropriateness, drug interactions, concentration, and prepares the infusion bag
Critical Care NursePerforms the titration at the bedside, assesses the patient before and after each rate change, and documents

Elements of a Complete Titration Order

An incomplete order is a major safety risk - each nurse may interpret it differently. A complete titration order must include:
  1. Drug name and route
  2. Starting rate (e.g., 0.05 mcg/kg/min)
  3. Titration increment (e.g., increase by 0.05 mcg/kg/min)
  4. Titration interval (e.g., every 5-15 minutes)
  5. Clinical goal (e.g., MAP >65 mmHg, RASS 0 to -2)
  6. Maximum dose (e.g., max 2 mcg/kg/min)
  7. Conditions to hold or notify the provider
Example - Norepinephrine (complete order):
Goal MAP >65 mmHg. Start at 0.05 mcg/kg/min. Titrate by 0.05 mcg/kg/min every 5 minutes. Max dose 2 mcg/kg/min. Notify provider if MAP <55 or >110 mmHg.
Example - Propofol (complete order):
Goal RASS 0 to -2. Start at 5 mcg/kg/min. Adjust by 5 mcg/kg/min every 15 minutes. Max dose 80 mcg/kg/min. Notify provider if RASS < -4.

Infusion Pump Setup: Step-by-Step

1. Drug Preparation & Verification

  • Confirm the drug, concentration, and volume match the order (two-nurse verification is standard for high-alert medications).
  • Calculate the dose rate before programming:
Formula for weight-based drugs:
Rate (mL/hr) = [Dose (mcg/kg/min) × Weight (kg) × 60 min] ÷ Concentration (mcg/mL)
Example: Epinephrine 2 mcg/min, mixed as 1 mg in 250 mL D5W (= 4 mcg/mL):
Rate = 2 mcg/min × 60 min ÷ 4 mcg/mL = 30 mL/hr

2. Select the Drug in the Pump's Drug Library

  • Navigate to the appropriate care area profile (e.g., "Medical ICU," "Cardiac ICU").
  • Select the drug by name - this auto-loads the pre-programmed dose limits from the hospital's drug library.
  • Enter the patient's weight (for weight-based dosing).
  • Enter the bag concentration.
  • Enter the desired dose - the pump calculates and confirms the mL/hr rate.

3. Prime and Connect

  • Prime the tubing to eliminate air.
  • Connect via a dedicated lumen - vasopressors and other vasoactive drips go through central venous access (PICC or central line). Peripheral access may be used temporarily but all vasoactive drugs risk severe tissue necrosis if extravasation occurs.
  • Label the line clearly at the pump and at the patient connection point.

4. Start and Baseline Assessment

  • Document baseline vitals and clinical assessment (pain score, RASS, MAP, heart rate) before starting the infusion.
  • Confirm monitors are active: continuous ECG, blood pressure (arterial line preferred for vasopressors), and pulse oximetry.

The Titration Process

Upward Titration (Uptitration)

Performed when the clinical goal is not met. Steps:
  1. Reassess the patient - confirm the cause is not correctable without a dose increase (e.g., hypovolemia causing low MAP).
  2. Increase the infusion by the ordered increment (e.g., +0.05 mcg/kg/min norepinephrine) by reprogramming the pump dose.
  3. Wait the ordered interval before reassessing.
  4. Document: time, new rate, new dose, clinical assessment, and the parameter prompting the change.
  5. Repeat until goal is met or maximum dose is reached.

Downward Titration (Weaning)

Performed when the patient stabilizes or exceeds the goal. Steps:
  1. Decrease by the ordered increment.
  2. Allow time for physiological equilibration.
  3. Continue weaning at the specified interval until the minimum effective dose is reached.
  4. When discontinuing, most vasopressors and sedatives are weaned - never abruptly stopped (risk of rebound hypotension or withdrawal).

Monitoring During Titration

Drug ClassMonitorFrequency
Vasopressors (norepinephrine, epinephrine, dopamine)MAP, heart rate, urine output, skin perfusionEvery 5-15 min during active titration; hourly once goal achieved
Sedatives (propofol, midazolam, dexmedetomidine)RASS score, respiratory rate, blood pressureHourly
Analgesics (fentanyl, morphine)CPOT or NRS pain score, respiratory rate, RASSHourly
Antihypertensives (nicardipine, labetalol, nitroprusside)SBP/DBP/MAP, HREvery 2-5 min during active titration
Antiarrhythmics (esmolol, amiodarone)HR, BP, rhythm on monitorEvery 2 min during titration

Common ICU Titration Drug Reference

DrugClassStarting DoseTitration IncrementIntervalMax DoseGoal
NorepinephrineVasopressor0.05 mcg/kg/min0.05 mcg/kg/min5 min2 mcg/kg/minMAP >65 mmHg
EpinephrineVasopressor/inotrope0.01 mcg/kg/min0.01 mcg/kg/min5 min0.5 mcg/kg/minMAP, CO target
Vasopressin (septic shock)Vasopressin agonist1.8 units/hrFixed - do not titrate-2.4 units/hrAdjunct (fixed dose)
DopamineInotrope/vasopressor2-5 mcg/kg/min2 mcg/kg/min10 min20 mcg/kg/minMAP, urine output
PropofolSedation5 mcg/kg/min5 mcg/kg/min15-30 min80 mcg/kg/minRASS goal
DexmedetomidineSedation0.2 mcg/kg/hr0.1 mcg/kg/hr30 min1.5 mcg/kg/hrRASS goal
FentanylAnalgesia0.5 mcg/kg/hr0.5 mcg/kg/hr60 min10 mcg/kg/hrCPOT/NRS goal
NicardipineAntihypertensive5 mg/hr2.5 mg/hr5 min15 mg/hrSBP/MAP target
NitroprussideAntihypertensive0.3 mcg/kg/min0.5 mcg/kg/min2 min3 mcg/kg/minSBP/MAP target
EsmololBeta-blocker50 mcg/kg/min50 mcg/kg/min2 min200-300 mcg/kg/minHR goal

Nurse-Titrated vs. Provider-Only Titrated Drips

Not all drugs in the ICU may be nurse-titrated. Most institutions distinguish:
  • Nurse-titrated (protocol-driven): Vasopressors, sedatives, analgesics, most antihypertensives - nurses adjust within a defined range per protocol.
  • Provider-only titrated: Some institutions restrict certain drugs (e.g., neuromuscular blockers like rocuronium, high-dose insulin, thrombolytics) to physician-only adjustment. Every change must be reflected as a new order.
  • When two drugs are titrated to the same goal (e.g., two vasopressors for MAP): the order must explicitly state which to titrate first and when to escalate to the second.

Documentation Requirements

After each titration change, the nurse must document:
  • Time of the change
  • Previous rate and new rate (in mL/hr and dose units)
  • Clinical assessment prompting the change (e.g., MAP was 55, increased norepinephrine)
  • Reassessment after the change (e.g., MAP now 68 after 10 minutes)
  • Any provider notifications

Safety Considerations

  • Dedicate a lumen: Never infuse vasopressors and routine fluids through the same port - bolus flushes can cause sudden surges of vasoactive drug.
  • No abrupt discontinuation: Wean vasopressors gradually; stopping norepinephrine abruptly can cause sudden cardiovascular collapse.
  • Propofol infusion syndrome (PRIS): Watch for metabolic acidosis, rhabdomyolysis, and cardiac dysrhythmias when propofol exceeds 4 mg/kg/hr for >48 hours.
  • Cyanide toxicity: Nitroprusside at high doses or prolonged use can cause cyanide accumulation - limit to 72 hours and monitor thiocyanate levels.
  • Avoid dual nurse bypass of pump alerts: Hard limits on smart pumps should never be overridden without provider authorization.
  • Hypovolemia check first: Before uptitrating a vasopressor, always confirm adequate fluid resuscitation - vasopressors do not substitute for volume.

Summary

Medication titration via infusion pump in the ICU is a systematic, protocol-driven process requiring precise drug preparation, correct pump programming, goal-directed adjustment in defined increments, continuous patient monitoring, and thorough documentation. The smart infusion pump is both a precision delivery device and a safety net - but it is only as safe as the order, the preparation, and the nurse's clinical assessment behind it.

Generate a quick-reference protocol poster for ICU titration of vasopressors and sedatives

Generating Image

A professional ICU clinical quick-reference protocol poster titled "ICU TITRATION PROTOCOL: Vasopressors & Sedatives". Dark navy blue background with white and bright accent text. Clean medical infographic layout with two main sections side by side. LEFT SECTION - "VASOPRESSORS" with red/orange accent header: Table with columns: Drug | Start Dose | Increment | Interval | Max Dose | Goal - Norepinephrine | 0.05 mcg/kg/min | +0.05 | 5 min | 2 mcg/kg/min | MAP >65 - Epinephrine | 0.01 mcg/kg/min | +0.01 | 5 min | 0.5 mcg/kg/min | MAP/CO - Dopamine | 2 mcg/kg/min | +2 | 10 min | 20 mcg/kg/min | MAP/UO - Vasopressin | 1.8 units/hr | Fixed | — | 2.4 units/hr | Do not titrate - Phenylephrine | 0.5 mcg/kg/min | +0.1 | 5 min | 3 mcg/kg/min | MAP >65 RIGHT SECTION - "SEDATIVES & ANALGESICS" with blue/teal accent header: Table with columns: Drug | Start Dose | Increment | Interval | Max Dose | Goal - Propofol | 5 mcg/kg/min | +5 | 15 min | 80 mcg/kg/min | RASS goal - Dexmedetomidine | 0.2 mcg/kg/hr | +0.1 | 30 min | 1.5 mcg/kg/hr | RASS goal - Midazolam | 1 mg/hr | +0.5 | 30 min | 20 mg/hr | RASS goal - Fentanyl | 0.5 mcg/kg/hr | +0.5 | 60 min | 10 mcg/kg/hr | CPOT/NRS - Morphine | 2 mg/hr | +1 | 30 min | 30 mg/hr | RASS/NRS BOTTOM SECTION - "SAFETY REMINDERS" with yellow warning accent icons: Five bullet points in a row: 1) All drips via infusion pump only 2) Central line preferred for vasopressors 3) Check volume status before uptitrating 4) Document: rate change + clinical assessment 5) Never abruptly stop vasopressors - wean RASS SCALE sidebar on right: -5 Unarousable to 0 Alert to +4 Combative with color coding green to red. Small footer: "Notify provider if max dose reached or goal unmet after 3 titrations" Clean, clinical, high-contrast design suitable for hospital wall display. Professional medical typography.

ICU Titration Protocol Poster
Here is your quick-reference ICU Titration Protocol poster. It covers:
Vasopressors (left panel)
  • Norepinephrine, Epinephrine, Dopamine, Vasopressin, Phenylephrine
  • Each with starting dose, increment, titration interval, maximum dose, and clinical goal
Sedatives & Analgesics (right panel)
  • Propofol, Dexmedetomidine, Midazolam, Fentanyl, Morphine
  • Titrated to RASS, CPOT, or NRS goals
Safety Reminders (bottom strip)
  • Pump-only administration, central access, volume check before uptitrating, documentation requirements, and vasopressor weaning rules
RASS scale sidebar for quick reference at the bedside.
A few points to keep in mind when adapting this for your unit:
  • Dose ranges can vary by institution protocol - always cross-check against your hospital's approved drug library and formulary.
  • Vasopressin in septic shock is typically fixed at 0.03-0.04 units/min (not titrated), while non-sepsis indications may allow titration - confirm with your pharmacy.
  • Provider notification thresholds (max dose reached, 3+ titrations without goal achievement) should match your unit's policy.

When assessing an older adult's functional status, which tool best evaluates their ability to live independently? Braden Scale Edinburgh Depression Scale Activities of Daily Living (ADL) scale Mini-Mental State Exam (MMSE)

Finding Sources
Finding Sources
Finding Sources

Answer: C - Activities of Daily Living (ADL) Scale ✓

The ADL scale is the correct answer. Here is why each option is right or wrong:

Why ADL Scale is Correct

The Activities of Daily Living (ADL) scale - originally developed by Sidney Katz in 1963 (the "Katz Index") - directly measures a person's ability to perform the fundamental self-care tasks needed for independent living. It assesses six core functions:
ADL DomainWhat It Measures
BathingCan the person bathe without assistance?
DressingCan they select and put on clothing independently?
ToiletingCan they manage toileting and hygiene?
TransferringCan they move from bed to chair independently?
ContinenceDo they have control of bladder and bowel?
FeedingCan they feed themselves?
A fully independent person scores 6/6. Scores of 4 or below indicate significant functional impairment and likely inability to live independently without support.
The Lawton IADL scale (Instrumental ADLs) extends this further by measuring more complex independent living skills - cooking, managing finances, using transportation, medication management, and shopping - making it the go-to tool when assessing capacity for independent community living in older adults.

Why the Other Options Are Wrong

OptionTool's Actual PurposeWhy It Doesn't Answer the Question
A - Braden ScalePredicts pressure ulcer (pressure injury) risk based on sensory perception, moisture, activity, mobility, nutrition, and friction/shearIt tells you skin breakdown risk, not functional independence
B - Edinburgh Postnatal Depression ScaleScreens for postpartum/perinatal depression in new mothersDesigned for a specific population (postpartum); has no role in general geriatric functional assessment
D - MMSE (Mini-Mental State Exam)Screens for cognitive impairment and dementia - tests orientation, recall, attention, language, and visuospatial abilityIt measures cognitive function, not physical functional status; a person can have mild cognitive impairment but still live independently, or have normal cognition but severe physical disability

Clinical Pearl

In geriatric assessment, ADLs and IADLs work together:
  • ADLs = "Can they care for their own body?" (basic survival tasks)
  • IADLs = "Can they manage their own household and life?" (higher-order independent living)
When a patient's ADL score declines, it is a strong predictor of nursing home placement, caregiver burden, and increased mortality risk in older adults. The ADL assessment is a standard component of the Comprehensive Geriatric Assessment (CGA).

Create a study guide on geriatric assessment tools

Reading File
Finding Sources
Finding Sources
Searching the Web

comprehensive geriatric assessment tools MMSE ADL IADL GDS Braden nursing study guide

Finding Sources
Finding Sources
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Reading Web Page

https://www.msdmanuals.com/professional/geriatrics/approach-…


📋 GERIATRIC ASSESSMENT TOOLS - STUDY GUIDE


Overview: What is the Comprehensive Geriatric Assessment (CGA)?

The Comprehensive Geriatric Assessment (CGA) is a multidimensional, interdisciplinary process designed to evaluate the functional ability, physical health, cognitive and mental health, and socioenvironmental situation of older adults. Originally developed in the 1980s-1990s, CGA has been shown to improve independent living, physical function, and long-term mortality.
The CGA covers six core domains:
DomainWhat It Assesses
Functional StatusADLs, IADLs, mobility
Cognitive StatusMemory, orientation, executive function
Psychological/AffectiveDepression, anxiety
Physical HealthComorbidities, nutrition, frailty, pain
Social/EnvironmentalSupport systems, living situation
PolypharmacyMedication burden and appropriateness
Source: Sabiston Textbook of Surgery; MSD Manuals - Comprehensive Geriatric Assessment

DOMAIN 1: Functional Status

🔑 Katz Index of Activities of Daily Living (ADL)

Developer: Sidney Katz, 1963 Purpose: Measures ability to perform basic self-care tasks - the most fundamental level of independent living
ADL ItemIndependent = 1Dependent = 0
BathingBathes self or only needs help with one body partNeeds help with more than one part
DressingDresses without help (except tying shoes)Needs help getting dressed
ToiletingGoes to toilet, cleans selfNeeds help with toileting
TransferringMoves in/out of bed and chair independentlyNeeds help or bedbound
ContinenceControls bladder and bowelPartial or full incontinence
FeedingFeeds self without helpNeeds help feeding or is tube-fed
Scoring:
  • 6 = Fully independent
  • 4 = Moderate impairment
  • 2 or less = Severe functional impairment - likely cannot live independently
  • Declining ADL score = strong predictor of nursing home placement and mortality

🔑 Lawton Instrumental Activities of Daily Living (IADL)

Developer: Lawton & Brody, 1969 Purpose: Measures more complex skills needed for independent community living (higher-order than basic ADLs)
IADL DomainScore 0-1 Each
Using telephone
Shopping
Food preparation
Housekeeping
Laundry
Transportation
Managing medications
Managing finances
Scoring (out of 8):
  • 8 = High independence
  • 0 = Fully dependent
  • Decline in IADLs often precedes decline in ADLs - early warning sign
Exam Tip: ADLs = "Can they care for their body?" IADLs = "Can they manage their life?" When a question asks about independent living, think IADLs (Lawton). When it asks about basic self-care, think ADLs (Katz).

Timed Up and Go (TUG) Test

Purpose: Measures mobility, balance, and fall risk Method: Time how long it takes the patient to:
  1. Rise from a chair
  2. Walk 3 meters
  3. Turn around
  4. Walk back
  5. Sit down again
Interpretation:
  • <12 seconds = Normal, low fall risk
  • 12-14 seconds = Borderline - monitor closely
  • >14 seconds = High fall risk - requires intervention
  • >20 seconds = Suggests significant mobility impairment

DOMAIN 2: Cognitive Status

🔑 Mini-Mental State Examination (MMSE)

Developer: Folstein, Folstein & McHugh, 1975 Time: 5-10 minutes Purpose: Screens for cognitive impairment and tracks progression of dementia
SectionPoints
Orientation to time (year, season, date, day, month)5
Orientation to place (country, state, county, hospital, floor)5
Registration (repeat 3 words)3
Attention & Calculation (serial 7s or spell "WORLD" backward)5
Recall (recall 3 words)3
Language (naming, repetition, 3-stage command)8
Visuospatial (copy intersecting pentagons)1
Total30
Scoring:
  • 27-30 = Normal
  • 24-26 = Mild cognitive impairment (possible)
  • 18-23 = Mild-moderate dementia
  • 10-17 = Moderate dementia
  • <10 = Severe dementia
Limitations: Influenced by education level, language, and motor ability. Not diagnostic - a screening tool only.

🔑 Montreal Cognitive Assessment (MoCA)

Developer: Nasreddine, 2005 Time: 10-15 minutes Purpose: Detects mild cognitive impairment (MCI) - more sensitive than MMSE for early changes
Assesses: Visuospatial/executive function, naming, memory, attention, language, abstraction, delayed recall, orientation
Scoring (out of 30):
  • ≥26 = Normal
  • 18-25 = Mild cognitive impairment
  • 10-17 = Moderate impairment
  • <10 = Severe impairment
  • Add 1 point if patient has ≤12 years of education
MoCA vs MMSE: MoCA catches early/mild cognitive impairment that MMSE misses. MoCA preferred when MCI is suspected.

🔑 Mini-Cog

Developer: Borson et al., 2000 Time: 3 minutes Purpose: Ultra-brief, practical cognitive screen - excellent for busy clinical settings
Two components:
  1. 3-word recall (0-3 points): Say three unrelated words (e.g., banana, sunrise, chair). Ask patient to repeat them after the clock draw.
  2. Clock Draw Test (CDT) (0 or 2 points): Ask patient to draw a clock, insert all numbers, and set hands to 11:10.
Clock scored abnormal if any element missing: all 12 numbers, correct positions, two hands pointing correctly.
Scoring:
  • 0-2 = Possible cognitive impairment (further assessment needed)
  • 3-5 = No impairment suggested
Exam Tip: Mini-Cog is the preferred bedside screening tool per ACS/AGS surgical guidelines due to its speed, ease, and focus on attention and executive function.

DOMAIN 3: Psychological/Affective Status

🔑 Geriatric Depression Scale (GDS)

Developer: Yesavage, 1982 (GDS-30); Sheikh, 1986 (GDS-15 short form) Time: 5-10 minutes Purpose: Screens for depression in older adults using simple yes/no format (avoids somatic items that may be confounded by physical illness)
GDS-15 sample questions:
  • "Are you basically satisfied with your life?"
  • "Do you often feel helpless?"
  • "Do you prefer to stay at home rather than going out and doing new things?"
  • "Do you feel that your life is empty?"
Scoring (GDS-15):
  • 0-4 = Normal
  • 5-8 = Mild depression
  • 9-11 = Moderate depression
  • 12-15 = Severe depression
Not a diagnostic tool - positive screening requires clinical interview and DSM criteria confirmation.

Patient Health Questionnaire-2 (PHQ-2)

Purpose: Ultra-brief 2-question depression screen, recommended by ACS/AGS as a practical preoperative screen for elderly patients
Questions:
  1. "Over the past 2 weeks, how often have you felt down, depressed, or hopeless?"
  2. "Over the past 2 weeks, how often have you had little interest or pleasure in doing things?"
Score ≥3 out of 6 = Positive screen - refer for full PHQ-9 and psychiatric evaluation

Confusion Assessment Method (CAM)

Purpose: Screens for delirium - NOT depression or dementia
4 Features (all required for positive CAM):
  1. Acute onset AND fluctuating course
  2. Inattention
  3. Disorganized thinking
  4. Altered level of consciousness
Interpretation: All 4 features present = Delirium positive Sensitivity: ~94% | Specificity: ~89%
Key Distinction: CAM = delirium screen. GDS = depression screen. MMSE/MoCA = dementia/cognitive impairment screen. These are NOT interchangeable.

DOMAIN 4: Physical Health Sub-Domains

Frailty: FRAIL Scale

Purpose: Quick 5-item frailty screen
ItemQuestion
F - Fatigue"How much of the time did you feel tired over the past 4 weeks?"
R - Resistance"Do you have difficulty climbing a flight of stairs?"
A - Ambulation"Do you have difficulty walking 100 yards?"
I - Illness"Do you have more than 5 illnesses?"
L - Loss of weight"Have you lost more than 5% of your weight in the past year?"
Scoring:
  • 0 = Robust
  • 1-2 = Pre-frail
  • 3-5 = Frail

Frailty: Fried Phenotype (Cardiovascular Health Study Criteria)

5 Components:
  1. Unintentional weight loss (>10 lbs in past year)
  2. Self-reported exhaustion
  3. Weakness (low grip strength)
  4. Slow walking speed
  5. Low physical activity level
Classification:
  • 0 criteria = Non-frail/Robust
  • 1-2 criteria = Pre-frail
  • 3-5 criteria = Frail

Nutrition: Mini Nutritional Assessment - Short Form (MNA-SF)

Purpose: Screens for malnutrition risk in older adults
Scoring:
  • 12-14 = Normal nutritional status
  • 8-11 = At risk of malnutrition
  • 0-7 = Malnourished
Quick screen: "Has the patient lost >4.5 kg (10 lbs) in the past 6 months without trying?"

Pressure Injury Risk: Braden Scale

Purpose: Predicts pressure ulcer/injury risk (NOT functional independence) 6 subscales: Sensory perception, moisture, activity, mobility, nutrition, friction/shear Scoring (6-23):
  • ≤9 = Very high risk
  • 10-12 = High risk
  • 13-14 = Moderate risk
  • 15-18 = Mild risk
  • 19-23 = No risk

DOMAIN 5: Pain Assessment

Pain Assessment in Advanced Dementia (PAINAD)

Purpose: Assesses pain in non-verbal patients with advanced dementia who cannot self-report
5 Behavioral Indicators (each scored 0-2):
  1. Breathing (normal → labored/noisy)
  2. Negative vocalization (none → crying)
  3. Facial expression (relaxed → grimacing)
  4. Body language (relaxed → rigid)
  5. Consolability (no need → unable to console)
Score 0-10: Higher = more pain

DOMAIN 6: Delirium Risk & Polypharmacy

Postoperative Delirium Risk Factors (ACS/AGS Guidelines)

Key risk factors to screen for preoperatively:
  • Age >65
  • Preexisting cognitive impairment
  • Depression
  • Sleep disturbance
  • Polypharmacy (especially anticholinergics, benzodiazepines, antihistamines)
  • Sensory impairment (hearing/vision)
  • Electrolyte abnormalities
  • Poor nutritional status
  • Presence of urinary catheter
Source: Sabiston Textbook of Surgery, p. 294 (ACS/AGS Guidelines)

Beers Criteria

Purpose: Identifies medications that are potentially inappropriate in older adults (>65 years) Examples of high-risk drugs in elderly:
  • Benzodiazepines (fall/delirium risk)
  • First-generation antihistamines (diphenhydramine) - highly anticholinergic
  • Tricyclic antidepressants
  • Muscle relaxants (cyclobenzaprine)
  • NSAIDs with chronic use

Master Reference Table: Quick-Compare All Tools

ToolDomainTimeScoringKey Cut-offUse When
Katz ADLFunction - basic5 min0-6<4 = impairedAssessing basic self-care ability
Lawton IADLFunction - complex10 min0-8Lower = dependentAssessing community living capacity
TUGMobility/Falls5 minSeconds>12 sec = at riskFall risk screening
MMSECognition10 min0-30<24 = impairedDementia tracking, moderate-severe
MoCACognition15 min0-30<26 = impairedEarly/mild cognitive impairment
Mini-CogCognition3 min0-5≤2 = impairedRapid bedside screen
GDS-15Depression5-10 min0-15>5 = depressedDepression in elderly
PHQ-2Depression1 min0-6≥3 = positiveUltra-rapid depression screen
CAMDelirium5 min+/-All 4 = deliriumInpatient delirium detection
FRAIL ScaleFrailty3 min0-5≥3 = frailQuick frailty screen
Fried PhenotypeFrailty15 min0-5≥3 = frailDetailed frailty classification
MNA-SFNutrition5 min0-14<12 = at riskMalnutrition screening
Braden ScalePressure injury5 min6-23≤18 = at riskSkin integrity/pressure ulcer prevention
PAINADPain5 min0-10Higher = more painNon-verbal dementia patients

Exam-Ready Mnemonics

"FACES" - CGA Domains

  • F - Functional status (ADL/IADL)
  • A - Affective/Psychological (depression, delirium)
  • C - Cognitive status (dementia, MCI)
  • E - Environmental/Social support
  • S - Somatic/Physical (nutrition, frailty, medications)

"3 Cognitive Tools by Speed"

  • Mini-Cog = 3 minutes (fastest - for busy wards)
  • MMSE = 10 minutes (standard screen)
  • MoCA = 15 minutes (most sensitive for MCI)

Common Exam Traps

Wrong ChoiceWhy It's WrongCorrect Tool
MMSE for independent livingMMSE tests cognition, not functionKatz ADL / Lawton IADL
Braden for functional statusBraden assesses pressure ulcer riskKatz ADL
GDS for deliriumGDS screens depressionCAM
Edinburgh Scale in general elderlyEdinburgh is for postpartum depressionGDS-15
ADL for community livingADL = basic self-care onlyIADL (Lawton)

Clinical Pearl: Hierarchy of Functional Decline

In older adults, functional decline typically follows this pattern:
IADLs decline first  →  ADLs decline next  →  Basic mobility affected
(managing money,          (bathing, dressing,     (transferring, then
 cooking, meds)            toileting, eating)       feeding last)
This sequence guides care planning: a patient losing IADLs may still live at home with support, while declining ADLs usually signals need for assisted living or more intensive caregiver involvement.

Sources: Sabiston Textbook of Surgery (ACS/AGS Guidelines); MSD Manuals - Comprehensive Geriatric Assessment; SIOG CGA Framework; Augusta University Adult Critical Care Protocol

Teach me the various IV solutions and categorize them in groups based on their tonicity

Reading File
Finding Sources
Finding Sources
Searching the Web

IV intravenous fluids tonicity classification isotonic hypotonic hypertonic nursing

Finding Sources
Finding Sources
Reading Web Page

https://wtcs.pressbooks.pub/nursingfundamentals/chapter/15-3…

Reading File
Reading File
Reading File
I now have comprehensive, textbook-verified data to deliver a complete teaching guide. Let me compile it.

IV Solutions: A Complete Teaching Guide Organized by Tonicity


Foundation: Understanding Tonicity

Tonicity describes the osmotic effect a solution has on cells when compared to normal blood plasma (osmolarity ~275-295 mOsm/L).
TonicityOsmolarityEffect on CellsDirection of Water Movement
Isotonic275-295 mOsm/LNo changeNo net movement
Hypotonic< 275 mOsm/LCells swell (lyse if severe)Water moves INTO cells
Hypertonic> 295 mOsm/LCells shrink (crenate)Water moves OUT of cells
There are two main categories of IV fluids: crystalloids (small molecules that pass through membranes) and colloids (large molecules that stay in the vascular space). Tonicity classification applies primarily to crystalloids.

GROUP 1: ISOTONIC SOLUTIONS

Same osmolarity as blood. No osmotic gradient is created. Fluid stays in the extracellular/intravascular space.
Primary Use: Volume expansion - treating hypovolemia, hemorrhage, and dehydration without shifting fluid into or out of cells.

1. Normal Saline (0.9% NaCl) - "NS"

PropertyValue
Osmolarity308 mOsm/L
Na⁺154 mEq/L
Cl⁻154 mEq/L
pH5.5
Indications:
  • Fluid resuscitation for hemorrhage, hypotension, severe vomiting/diarrhea, GI suction losses
  • Hyponatremia (mild)
  • Blood transfusions (only compatible fluid with blood products)
  • Wound irrigation
  • Traumatic brain injury (TBI) - preferred over balanced solutions due to mortality benefit
Nursing Considerations / Warnings:
  • Risk of hyperchloremic metabolic acidosis with large volumes (high Cl⁻ content of 154 mEq/L exceeds plasma Cl⁻ of ~100 mEq/L)
  • Associated with higher rates of acute kidney injury in large-volume resuscitation
  • Monitor for hypervolemia, especially in heart failure and renal failure patients
  • Do NOT mix with blood - but IS the only compatible fluid for blood administration lines

2. Lactated Ringer's Solution (LR) - "Ringer's Lactate"

PropertyValue
Osmolarity273 mOsm/L
Na⁺130 mEq/L
K⁺4 mEq/L
Ca²⁺2.7 mEq/L
Cl⁻109 mEq/L
Lactate28 mEq/L
pH6.5
Indications:
  • Fluid resuscitation for burns, trauma, and major surgery (preferred by many surgeons)
  • GI tract fluid losses
  • Metabolic acidosis
  • Most closely mimics plasma composition among crystalloids
Nursing Considerations / Warnings:
  • Do NOT use in alkalosis (pH >7.5) - lactate is converted to bicarbonate by the liver, worsening alkalosis
  • Do NOT use in hyperkalemia - contains 4 mEq/L K⁺
  • Do NOT use in renal failure - risk of hyperkalemia
  • Do NOT use with blood products - calcium in LR can cause clotting
  • Do NOT use in TBI - slightly hypotonic compared to plasma (273 mOsm/L), can worsen cerebral edema
  • Preferred over NS for most surgical resuscitation due to lower chloride and reduced acidosis risk

3. Plasma-Lyte A / Plasma-Lyte 148

PropertyValue
Osmolarity294 mOsm/L
Na⁺140 mEq/L
K⁺5 mEq/L
Mg²⁺3 mEq/L
Cl⁻98 mEq/L
Acetate27 mEq/L
Gluconate23 mEq/L
pH7.4 (Plasma-Lyte A)
Indications:
  • Fluid resuscitation - "balanced" alternative to NS
  • Critically ill patients where normal saline-induced acidosis is a concern
  • Most physiologically balanced crystalloid available - pH 7.4 matches blood
Nursing Considerations / Warnings:
  • Most expensive of the crystalloids
  • Avoid in hyperkalemia (contains 5 mEq/L K⁺)
  • Avoid in renal failure

4. Dextrose 5% in Water (D5W) - Isotonic in the bag, hypotonic in the body

PropertyValue
Osmolarity (in bag)252 mOsm/L (effectively isotonic with dextrose)
Dextrose5 g/100 mL
Na⁺/Cl⁻0
Special case: D5W starts as isotonic in the IV bag because the glucose molecules contribute to osmolarity. However, glucose is rapidly metabolized by cells after infusion, leaving only free water behind - making it functionally hypotonic in the body.
Indications:
  • Hypernatremia (dilutes sodium concentration)
  • Provides free water for renal excretion of solutes
  • Caloric supplementation in NPO patients (provides ~170 calories/L)
  • Medication dilution vehicle
Nursing Considerations / Warnings:
  • Do NOT use for fluid resuscitation - once glucose is metabolized, the free water leaves the intravascular space, causing hypovolemia and potentially cerebral edema
  • Avoid in trauma, burns, liver disease
  • Monitor for cerebral edema
  • Monitor blood glucose - can cause hyperglycemia

5. 0.9% NaCl + 5% Dextrose (D5NS)

  • Osmolarity: ~560 mOsm/L - technically hypertonic until dextrose is metabolized
  • Used for maintenance fluid in certain clinical scenarios
  • Provides both sodium and calories

GROUP 2: HYPOTONIC SOLUTIONS

Lower osmolarity than blood. Creates an osmotic gradient pulling water from the bloodstream INTO cells. Expands the intracellular compartment.
Primary Use: Cellular dehydration (water needed inside cells), hypernatremia, chronic dehydration (not acute volume loss).
⚠️ Key Warning: Hypotonic fluids reduce intravascular volume. Never use for acute fluid resuscitation or in hemodynamically unstable patients.

1. Half Normal Saline (0.45% NaCl) - "½NS"

PropertyValue
Osmolarity154 mOsm/L
Na⁺77 mEq/L
Cl⁻77 mEq/L
Indications:
  • Hypernatremia (free water deficit)
  • Maintenance fluid in stable patients
  • Diabetic ketoacidosis (DKA) - after initial isotonic resuscitation, to replace free water
  • Cellular dehydration
Nursing Considerations / Warnings:
  • Can cause hyponatremia with excessive use
  • Can cause cerebral edema (water shifts into brain cells)
  • Do NOT use in trauma, burns, increased ICP, or liver disease
  • Monitor neurological status frequently

2. One-Third Normal Saline (0.33% NaCl) - "⅓NS"

PropertyValue
Osmolarity103 mOsm/L
Na⁺51 mEq/L
Cl⁻51 mEq/L
Indications:
  • Pediatric maintenance fluid (common in children)
  • More aggressive free water replacement than ½NS
Nursing Considerations / Warnings:
  • Greatest risk of hyponatremia and cerebral edema of all NaCl solutions
  • Rarely used in adults

3. D5W (functionally hypotonic after metabolism)

(Covered above under isotonic - classified as both)

4. D5 ½NS (Dextrose 5% in 0.45% NaCl)

PropertyValue
Osmolarity~405 mOsm/L (before dextrose metabolized)
Behaves asHypotonic after dextrose is metabolized
Indications:
  • Standard maintenance fluid (per Fischer's Surgery: "typical maintenance fluid is D5 ½NS")
  • Provides electrolytes + calories in NPO patients
  • Maintains plasma osmolality during fasting
Maintenance Rate (4-2-1 Rule):
[4 mL/kg × first 10 kg] + [2 mL/kg × second 10 kg] + [1 mL/kg × each kg >20 kg]

Example (70 kg patient):
(4×10) + (2×10) + (1×50) = 40 + 20 + 50 = 110 mL/hr
Source: Fischer's Mastery of Surgery, p. 213

GROUP 3: HYPERTONIC SOLUTIONS

Higher osmolarity than blood. Pulls water out of cells and into the bloodstream. Expands intravascular volume rapidly at the expense of intracellular volume.
Primary Use: Severe hyponatremia, cerebral edema (draws water out of brain), severe burns.
⚠️ Key Warning: Must be given slowly through a central line. Rapid infusion causes cellular dehydration, hemolysis, and vascular damage.

1. Hypertonic Saline 3% NaCl

PropertyValue
Osmolarity~1,026 mOsm/L
Na⁺513 mEq/L
Cl⁻513 mEq/L
Indications:
  • Severe symptomatic hyponatremia (seizures, altered mental status from low Na⁺)
  • Cerebral edema / increased intracranial pressure (ICP) - draws water out of brain
  • SIADH refractory to fluid restriction
Nursing Considerations / Warnings:
  • Must infuse slowly - rapid correction of hyponatremia risks osmotic demyelination syndrome (ODS) (previously called central pontine myelinolysis) - irreversible brain damage
  • Rule: Correct Na⁺ by no more than 8-10 mEq/L per 24 hours
  • Administer through central venous access only - causes severe tissue necrosis if it infiltrates
  • Monitor sodium levels every 2-4 hours during infusion
  • Monitor for pulmonary edema and fluid overload

2. Hypertonic Saline 5% NaCl

PropertyValue
Osmolarity~1,710 mOsm/L
Indications:
  • Same as 3% NaCl, used for more severe cases
  • Rarely used; reserved for critical neurological emergencies

3. Dextrose 10% in Water (D10W)

PropertyValue
Osmolarity~505 mOsm/L
Dextrose100 g/L
Indications:
  • Hypoglycemia treatment
  • Neonatal glucose maintenance
  • Provides free water + calories without sodium
  • Used in TPN (total parenteral nutrition) formulations

4. D5 0.9% NaCl (D5NS) and D5LR

  • Hypertonic in the bag (D5NS ~560 mOsm/L; D5LR ~524 mOsm/L)
  • Provide both electrolytes and calories
  • Become isotonic/hypotonic after dextrose is metabolized

GROUP 4: COLLOIDS

Large molecules that remain in the intravascular space and exert oncotic (colloid osmotic) pressure, pulling fluid from interstitial spaces into the bloodstream.
Key principle from Goldman-Cecil Medicine: "There is no clear advantage of colloid over crystalloid solutions, so crystalloids - which are less expensive - are preferred" for initial resuscitation. Hetastarches are never recommended (associated with worse outcomes, including AKI).

1. Human Albumin (5% and 25%)

TypeTonicityUse
5% AlbuminIsotonicVolume expansion in hypovolemia/shock
25% AlbuminHypertonicSevere hypoalbuminemia, hepatorenal syndrome, spontaneous bacterial peritonitis
Indications:
  • Hypoalbuminemia (serum albumin <2.5 g/dL)
  • Liver cirrhosis with ascites (paracentesis >5L - give albumin to prevent circulatory dysfunction)
  • Spontaneous bacterial peritonitis (SBP) - reduces renal failure risk
  • Burns after first 24 hours
  • Gold standard colloid; preferred in neonates and infants as plasma expander
Nursing Considerations / Warnings:
  • Expensive
  • Risk of allergic reactions
  • Monitor for fluid overload
  • 25% albumin is hypertonic - draws fluid from extravascular spaces

2. Fresh Frozen Plasma (FFP)

  • Contains all coagulation factors
  • Used in coagulopathy, massive transfusion, reversal of warfarin
  • Not a volume expander per se - used for clotting factor replacement

3. Hetastarch (HES) / Hydroxyethyl Starch

  • Avoid / Contraindicated in most clinical scenarios
  • Associated with acute kidney injury, coagulopathy, and increased mortality in sepsis
  • No longer recommended per current guidelines

4. Dextran

  • Synthetic polysaccharide colloid
  • Largely replaced by albumin in modern practice
  • Dextran 40: improves microcirculation, anti-platelet aggregation
  • Risk of allergic reactions, coagulopathy

Master Summary Table

SolutionTonicityOsmolarityCompartment ExpandedPrimary UseKey Warning
0.9% NaCl (NS)Isotonic308Extracellular/intravascularResuscitation, blood transfusion, TBIHyperchloremic acidosis with large volumes
Lactated Ringer'sIsotonic273Extracellular/intravascularBurns, trauma, surgeryAvoid in alkalosis, hyperkalemia, TBI, renal failure
Plasma-Lyte AIsotonic294Extracellular/intravascularBalanced resuscitationMost physiologic; avoid in hyperkalemia
D5WIsotonic→Hypotonic252Intracellular (after metabolism)Hypernatremia, free water, medication diluentNever resuscitate; cerebral edema risk
0.45% NaCl (½NS)Hypotonic154IntracellularHypernatremia, cellular dehydration, DKAHyponatremia, cerebral edema
0.33% NaCl (⅓NS)Hypotonic103IntracellularPediatric maintenanceHigh hyponatremia risk
D5 ½NSHypotonic (after)~405→hypotonicIntracellularMaintenance fluid, NPO patientsNot for resuscitation
3% NaClHypertonic1,026Draws water into vesselsSymptomatic hyponatremia, cerebral edemaSlow infusion only; ODS risk; central line required
5% NaClHypertonic1,710Draws water into vesselsCritical hyponatremiaSame as 3% - extreme caution
D10WHypertonic505Intravascular + caloriesHypoglycemia, neonates, TPNMonitor glucose
5% AlbuminIsotonic (colloid)~308IntravascularHypovolemia + low albuminExpensive; monitor for fluid overload
25% AlbuminHypertonic (colloid)~1,500Pulls fluid into vesselsHypoalbuminemia, cirrhosis, SBPHypervolemia risk
HetastarchIsotonic (colloid)~308IntravascularAVOIDAKI, coagulopathy, increased mortality

Key Clinical Mnemonics

"IN-IS-OUT" for Tonicity

  • INtotonic (Hypotonic) → water goes IN to cells
  • ISotonic → stays IS (no movement)
  • OUTertonic (Hypertonic) → water goes OUT of cells

The 3 "Never's"

  1. Never use D5W for fluid resuscitation
  2. Never mix LR with blood products (calcium causes clotting)
  3. Never use hetastarch (associated with worse outcomes)

"BALANCED is BETTER (usually)"

  • Balanced crystalloids (LR, Plasma-Lyte) cause less acidosis than NS
  • Exception: TBI - use NS, not balanced solutions

Sources: Fischer's Mastery of Surgery, 8th ed., p. 211-213 (Table 3.2 - Properties of Crystalloid Solutions); Goldman-Cecil Medicine, p. 1077-1078 (IV Fluids section); Harrison's Principles of Internal Medicine 22e; Nursing Fundamentals 2e (Open RN)

Design a mind map of the basic metabolic panel and correlate how they relate to one another

Generating Image

A detailed, professional medical mind map of the Basic Metabolic Panel (BMP) on a deep navy blue background. The mind map has a central node labeled "BASIC METABOLIC PANEL (BMP)" in bold white text with a bright cyan border. Eight main branches radiate outward from the center, each representing a BMP component with its own color-coded node and sub-branches showing normal range, clinical significance, and key correlations: 1. SODIUM (Na+) - orange node - 135-145 mEq/L - sub-branches: "Hypernatremia → dehydration, DI", "Hyponatremia → SIADH, heart failure, cirrhosis", "Correlates with: Chloride, Osmolality, BUN" 2. POTASSIUM (K+) - yellow node - 3.5-5.0 mEq/L - sub-branches: "Hyperkalemia → renal failure, acidosis", "Hypokalemia → diuretics, vomiting, alkalosis", "Correlates with: pH/CO2 (inverse), Creatinine" 3. CHLORIDE (Cl-) - light green node - 98-106 mEq/L - sub-branches: "Low Cl → metabolic alkalosis", "High Cl → metabolic acidosis (normal saline)", "Correlates with: Anion Gap = Na - (Cl + HCO3)" 4. BICARBONATE/CO2 (HCO3-) - teal node - 22-29 mEq/L - sub-branches: "Low HCO3 → metabolic acidosis, DKA", "High HCO3 → metabolic alkalosis, vomiting", "Correlates with: Chloride (reciprocal), Anion Gap", "Anion Gap = Na - (Cl + HCO3) normal 8-12" 5. BUN (Blood Urea Nitrogen) - red node - 7-20 mg/dL - sub-branches: "Elevated → dehydration, renal failure, GI bleed", "BUN:Creatinine ratio >20:1 = prerenal", "BUN:Creatinine ratio <10:1 = intrinsic renal", "Correlates with: Creatinine, Sodium" 6. CREATININE (Cr) - pink/rose node - 0.6-1.2 mg/dL - sub-branches: "Elevated → AKI, CKD, rhabdomyolysis", "eGFR calculated from creatinine", "Correlates with: BUN, Potassium (renal failure → hyperkalemia)" 7. GLUCOSE - purple node - 70-100 mg/dL fasting - sub-branches: "Hyperglycemia → DM, DKA, HHS, steroids", "Hypoglycemia → insulin excess, liver failure", "DKA: high glucose + low CO2 + high anion gap", "Correlates with: CO2 (DKA), Sodium (pseudohyponatremia)" 8. CALCIUM (Ca2+) - gold/amber node - 8.5-10.2 mg/dL - sub-branches: "Hypercalcemia → malignancy, hyperparathyroidism", "Hypocalcemia → hypoparathyroidism, low vitamin D, pancreatitis", "Correlates with: Phosphate (inverse), Albumin (adjust total Ca)" In the CENTER-BOTTOM area, a special highlighted box labeled "KEY CORRELATIONS" shows three connection arrows: - "↑BUN + ↑Cr = RENAL FAILURE → watch K+ and HCO3-" - "Low HCO3 + High Anion Gap = METABOLIC ACIDOSIS (DKA, lactic acid, toxins)" - "Na/K/Cl/HCO3 = ELECTROLYTE CLUSTER → assess fluid and acid-base status together" Color-coded connecting dotted lines link related nodes: Na-Cl-HCO3 (acid-base cluster in teal), BUN-Creatinine (renal cluster in red), Glucose-CO2 (DKA cluster in purple). Clean sans-serif medical typography. Professional infographic quality suitable for medical education. White and light text on dark background. Organized radial layout, not cluttered.

BMP Mind Map
Here is your BMP mind map. Below is a companion reference explaining the key correlations shown:

How the BMP Components Relate to Each Other

Cluster 1: Electrolytes + Acid-Base (Na, Cl, HCO3, K)

These four work as a unit. Use them to assess fluid status and acid-base balance together.
Anion Gap = Na⁺ - (Cl⁻ + HCO₃⁻) → Normal: 8-12 mEq/L
  • High anion gap metabolic acidosis (MUDPILES): Methanol, Uremia, DKA, Propylene glycol, Isoniazid/Iron, Lactic acidosis, Ethylene glycol, Salicylates
  • Normal anion gap acidosis: Diarrhea (HCO₃ lost), RTA - Cl⁻ rises to compensate (hyperchloremic acidosis)
K⁺ and pH are inversely related:
  • Acidosis → K⁺ shifts OUT of cells → Hyperkalemia
  • Alkalosis → K⁺ shifts INTO cells → Hypokalemia

Cluster 2: Renal Function (BUN + Creatinine)

BUN:Cr RatioInterpretation
>20:1Pre-renal (dehydration, GI bleed, heart failure)
10-20:1Normal
<10:1Intrinsic renal disease or low protein intake
  • Creatinine rises → eGFR falls → watch for K⁺ retention and HCO₃ loss (metabolic acidosis from renal failure)

Cluster 3: Glucose + CO2 (DKA pattern)

Classic DKA triad on BMP:
  • High glucose (>250 mg/dL)
  • Low HCO₃ (<18 mEq/L)
  • High anion gap (bicarbonate replaced by ketoacids)
Glucose also affects Na⁺ (pseudohyponatremia): For every 100 mg/dL rise in glucose above 100, true Na⁺ is actually 1.6-2.4 mEq/L higher than reported.

Cluster 4: Calcium Correlations

  • Calcium and phosphate are inversely regulated (PTH raises Ca, lowers PO₄)
  • Always correct for albumin: Corrected Ca = Measured Ca + 0.8 × (4 - albumin g/dL)
  • Low albumin falsely lowers total calcium - check ionized calcium when albumin is abnormal

Now gere

It looks like your message got cut off! Did you mean to say:
  • "Now generate..." a mind map or visual for something else?
  • "Now here..." followed by more context?
  • Something about the CMP (Comprehensive Metabolic Panel)?
  • A study guide for the BMP components?
Could you finish your thought? I want to make sure I give you exactly what you're looking for!

Yes, mind map for comprehensive metabolic panel and their interrelation to one another

Generating Image

A detailed professional medical mind map of the Comprehensive Metabolic Panel (CMP) on a deep dark navy blue background. The mind map has a large central node labeled "COMPREHENSIVE METABOLIC PANEL (CMP)" in bold white text with a bright gold/yellow glowing border. Subtitle below: "14 Components • Liver + Kidney + Electrolytes + Glucose + Protein" Fourteen color-coded branches radiate outward in a circular arrangement, grouped into 4 visible clusters with subtle background shading: CLUSTER 1 - ELECTROLYTES (blue-teal shading, left side): 1. SODIUM (Na+) - cyan node - "135-145 mEq/L" - sub-text: "Fluid balance • Hypernatremia=dehydration • Hyponatremia=SIADH/HF • Links to Cl, HCO3, Glucose" 2. POTASSIUM (K+) - sky blue node - "3.5-5.0 mEq/L" - sub-text: "Cardiac rhythm • Inverse to pH • ↑ in renal failure/acidosis • ↓ with diuretics/alkalosis" 3. CHLORIDE (Cl-) - light teal node - "98-106 mEq/L" - sub-text: "Acid-base balance • ANION GAP = Na-(Cl+HCO3) • Reciprocal to HCO3" 4. BICARBONATE (HCO3-) - green-teal node - "22-29 mEq/L" - sub-text: "Acid-base buffer • ↓=metabolic acidosis/DKA • ↑=metabolic alkalosis • Links to Cl (reciprocal), AG" CLUSTER 2 - RENAL FUNCTION (red-pink shading, right side): 5. BUN - red node - "7-20 mg/dL" - sub-text: "Protein catabolism • BUN:Cr>20=prerenal • ↑ in dehydration/GI bleed • Links to Creatinine, Na" 6. CREATININE - rose node - "0.6-1.2 mg/dL" - sub-text: "Muscle metabolism • Calculates eGFR • ↑ AKI/CKD → ↑K+, ↓HCO3 • Links to BUN, K+" 7. GLUCOSE - purple node - "70-100 mg/dL" - sub-text: "Energy metabolism • DKA=high glucose+low HCO3+high AG • Pseudohyponatremia when high • Links to Na, HCO3" 8. CALCIUM (Ca2+) - amber/gold node - "8.5-10.2 mg/dL" - sub-text: "Bone/nerve/muscle • Correct for albumin: Ca+0.8×(4-Alb) • Inverse to Phosphate • ↑ malignancy/hyperPTH" CLUSTER 3 - LIVER FUNCTION (orange-yellow shading, bottom): 9. TOTAL PROTEIN - orange node - "6.3-8.2 g/dL" - sub-text: "= Albumin + Globulin • ↓ in liver failure/malnutrition • Links to Albumin, Bilirubin" 10. ALBUMIN - yellow-orange node - "3.5-5.0 g/dL" - sub-text: "Oncotic pressure/transport • ↓ in liver disease/malnutrition/nephrotic • Affects Ca correction • Links to Total Protein, Ca" 11. TOTAL BILIRUBIN - yellow node - "0.1-1.2 mg/dL" - sub-text: "Heme breakdown product • ↑ liver disease/hemolysis/bile obstruction • Links to AST, ALT, Alk Phos" 12. AST (Aspartate Aminotransferase) - lime green node - "10-40 U/L" - sub-text: "Hepatocellular damage • Also in heart/muscle • AST:ALT>2=alcoholic hepatitis • Links to ALT, Bilirubin" 13. ALT (Alanine Aminotransferase) - green node - "7-56 U/L" - sub-text: "Liver-specific enzyme • Best marker hepatocellular injury • Links to AST, Bilirubin, Alk Phos" 14. ALKALINE PHOSPHATASE (ALP) - olive node - "44-147 U/L" - sub-text: "Biliary/bone disease • ↑ cholestasis, bone mets, Paget's • Links to Bilirubin, GGT (not in CMP)" THREE KEY CORRELATION BOXES at the bottom center connected by colored arrows: BOX 1 (red): "RENAL CLUSTER: ↑BUN+↑Cr → eGFR↓ → watch K+↑ and HCO3↓" BOX 2 (orange): "LIVER CLUSTER: ↑AST+↑ALT+↑Bili → hepatocellular injury | ↑ALP+↑Bili → cholestasis" BOX 3 (cyan): "ACID-BASE: Na-(Cl+HCO3)=Anion Gap | DKA: ↑Glucose+↓HCO3+↑AG" Thin dotted colored connector lines link related components across clusters: Albumin↔Calcium (white), BUN↔Sodium (yellow), Glucose↔HCO3 (purple), AST↔ALT (green), Bilirubin↔ALP (orange). Clean medical typography, organized radial mind map layout with clear cluster groupings, professional clinical infographic quality for medical education wall display. All text clearly readable on dark background.

CMP Mind Map
Here is your CMP mind map. The CMP has all 8 BMP components plus 6 liver/protein markers. Here is how the four clusters interconnect:

CMP Cluster Interrelationships

Cluster 1: Electrolytes + Acid-Base (Na, K, Cl, HCO3)

  • Anion Gap = Na - (Cl + HCO3) → Normal: 8-12 mEq/L
  • Cl and HCO3 are reciprocal - when one rises, the other falls
  • K shifts with pH: acidosis → K exits cells (hyperkalemia); alkalosis → K enters cells (hypokalemia)

Cluster 2: Renal (BUN, Creatinine, Glucose, Calcium)

  • BUN:Cr >20:1 = prerenal cause | <10:1 = intrinsic renal disease
  • Rising Cr → eGFR falls → K accumulates, HCO3 drops (uremic acidosis)
  • High glucose falsely lowers Na (pseudohyponatremia) - correct by +1.6 mEq/L Na per 100 mg/dL glucose above 100
  • Calcium must always be corrected for albumin: Corrected Ca = measured Ca + 0.8 × (4 - albumin)

Cluster 3: Liver Enzymes (AST, ALT, ALP, Bilirubin)

PatternLikely Cause
AST + ALT both elevatedHepatocellular injury (hepatitis, cirrhosis)
AST:ALT ratio >2:1Alcoholic liver disease
ALP + Bilirubin elevatedCholestatic/obstructive pattern (bile duct obstruction, PBC)
Isolated ALT elevationEarly/mild hepatocellular damage (most liver-specific enzyme)

Cluster 4: Protein (Total Protein, Albumin)

  • Total Protein = Albumin + Globulin
  • Low albumin → affects drug binding, calcium levels, and oncotic pressure
  • Albumin is a sensitive marker of chronic liver disease and nutritional status (half-life ~20 days)
  • Globulin = Total Protein - Albumin → elevated globulin gap suggests chronic inflammation, multiple myeloma, or autoimmune liver disease

CMP vs BMP - What's Added?

Added in CMPWhat It Tells You
Total ProteinOverall protein synthesis and nutritional status
AlbuminLiver synthetic function, nutritional status, drug binding
Total BilirubinLiver excretory function, hemolysis
ASTHepatocellular damage (also muscle/heart)
ALTLiver-specific damage marker
ALPCholestatic/biliary disease or bone pathology

Generate a diagnostic flowchart for elevated liver enzymes

Generating Image

A professional medical diagnostic flowchart titled "DIAGNOSTIC APPROACH TO ELEVATED LIVER ENZYMES" on a clean white background with dark text and color-coded decision boxes. Vertical top-to-bottom flowchart with branching paths. TOP START BOX (dark navy blue, wide): "ELEVATED LIVER ENZYMES DETECTED" subtitle: "AST, ALT, ALP, GGT, or Bilirubin above normal range" Arrow down to STEP 1 BOX (dark teal): "STEP 1: Identify the Pattern" — two branches side by side: LEFT branch (orange box): "HEPATOCELLULAR PATTERN" — "AST and/or ALT predominantly elevated (>3x ULN)" — "Suggests liver cell damage" RIGHT branch (purple box): "CHOLESTATIC PATTERN" — "ALP and/or GGT predominantly elevated" — "Suggests bile flow obstruction" HEPATOCELLULAR BRANCH continues down: Arrow to grey box: "STEP 2A: Check AST:ALT Ratio" THREE sub-branches: - LEFT (red box): "AST:ALT > 2:1" → "Alcoholic Liver Disease" → "Check: alcohol history, GGT elevated, biopsy" - CENTER (orange box): "ALT > AST" → "Viral Hepatitis / NAFLD / Drug-induced" → "Check: Hep A/B/C serology, ultrasound, medication review, BMI/metabolic syndrome" - RIGHT (yellow box): "Massively elevated AST+ALT (>1000)" → "Ischemic hepatitis / Acute viral hepatitis / Drug toxicity (acetaminophen)" → "Check: Echo, BP history, acetaminophen level, viral panel" Further down from center orange box: "STEP 3A: Additional Workup" box (light orange): "• Hepatitis B surface antigen (HBsAg)" "• Hepatitis C antibody (anti-HCV)" "• ANA, ASMA → Autoimmune hepatitis" "• Ferritin, transferrin saturation → Hemochromatosis" "• Ceruloplasmin → Wilson disease (age <40)" "• Alpha-1 antitrypsin level" "• TSH (hypothyroid can elevate AST)" "• Liver ultrasound" CHOLESTATIC BRANCH continues down: Arrow to grey box: "STEP 2B: Is ALP Elevated?" TWO sub-branches: - LEFT (purple box): "ALP + GGT both elevated" → "TRUE HEPATIC/BILIARY cause" → "Liver or bile duct origin confirmed" - RIGHT (lavender box): "ALP elevated, GGT normal" → "BONE SOURCE of ALP" → "Consider: Paget's disease, bone mets, healing fracture, pregnancy" From TRUE HEPATIC box: "STEP 3B: Imaging" (blue box): "RIGHT UPPER QUADRANT ULTRASOUND" TWO branches: - LEFT (dark blue box): "DUCTAL DILATION seen" → "EXTRAHEPATIC OBSTRUCTION" → "Gallstones (choledocholithiasis), Cholangiocarcinoma, Pancreatic cancer, PSC/PBC" → "→ MRCP or ERCP" - RIGHT (medium blue box): "NO DUCTAL DILATION" → "INTRAHEPATIC CHOLESTASIS" → "Primary Biliary Cholangitis (PBC): check AMA, IgM" → "Primary Sclerosing Cholangitis (PSC): check p-ANCA, MRCP" → "Drug-induced cholestasis" → "Infiltrative disease (lymphoma, sarcoid)" BOTTOM SECTION - wide box spanning full width (dark green): "STEP 4: SEVERITY ASSESSMENT — When to refer/biopsy?" Four columns: Column 1: "MILD (<3x ULN)" → "Repeat LFTs in 3-6 months, lifestyle modification, stop offending drugs" Column 2: "MODERATE (3-10x ULN)" → "Full workup, imaging, hepatology referral" Column 3: "SEVERE (>10x ULN)" → "Urgent hepatology consult, assess for acute liver failure" Column 4: "ACUTE LIVER FAILURE signs" → "INR >1.5 + encephalopathy → ICU admission" BOTTOM FOOTER BOX (dark red): "RED FLAGS — Immediate Action Required: Jaundice + coagulopathy + encephalopathy = Acute Liver Failure | Painless jaundice + weight loss = Rule out malignancy | Fever + RUQ pain + jaundice = Charcot's Triad = Ascending Cholangitis" Color-coded arrows: orange for hepatocellular path, purple for cholestatic path, green for final management. Clean sans-serif medical font. Professional clinical quality. All text clearly readable.

Elevated Liver Enzymes Diagnostic Flowchart
Here is your diagnostic flowchart. Below is a companion reference to walk through each decision point:

How to Use This Flowchart

Step 1 - Identify the Pattern First

PatternDominant ElevationThink
HepatocellularAST and/or ALTLiver cell death/injury
CholestaticALP and/or GGTBile flow impairment
MixedAll enzymes elevatedOverlap - needs full workup
GGT is the key differentiator: if ALP is elevated but GGT is normal, the ALP is coming from bone, not liver.

Step 2A - AST:ALT Ratio (Hepatocellular Path)

RatioDiagnosis to Consider
>2:1 (AST much higher)Alcoholic hepatitis (especially if GGT also elevated)
ALT > ASTViral hepatitis, NAFLD/NASH, drug-induced liver injury
Both >1000 U/LIschemic hepatitis ("shock liver"), acute viral hepatitis, acetaminophen toxicity

Step 2B - Cholestatic Workup

  • ALP + GGT elevated together = confirmed liver/biliary source → get RUQ ultrasound
  • Ductal dilation on ultrasound = extrahepatic obstruction → MRCP or ERCP to find the block
  • No ductal dilation = intrahepatic cholestasis → check AMA (PBC), p-ANCA/MRCP (PSC), medication list

Step 3 - Additional Lab Workup (Hepatocellular)

TestRules In
HBsAg, anti-HCVHepatitis B or C
ANA, anti-smooth muscle Ab (ASMA)Autoimmune hepatitis
Ferritin + transferrin saturation >45%Hemochromatosis
Ceruloplasmin (if age <40)Wilson's disease
Alpha-1 antitrypsin levelAlpha-1 antitrypsin deficiency
TSHHypothyroidism (can raise AST mildly)

Severity and Red Flags

SeverityAction
<3x ULNRepeat in 3-6 months, remove offending drugs, lifestyle changes
3-10x ULNFull workup + imaging + hepatology referral
>10x ULNUrgent hepatology consult
Coagulopathy + encephalopathyAcute liver failure - ICU admission
Three classic emergency patterns:
  • Charcot's Triad (fever + RUQ pain + jaundice) = Ascending cholangitis - urgent ERCP
  • Painless jaundice + weight loss = Pancreatic or biliary malignancy until proven otherwise
  • Jaundice + INR >1.5 + encephalopathy = Acute liver failure - transplant evaluation

Create a clinical case study showing how to interprete an abnormal CMP

Finding Sources
Finding Sources
Finding Sources
Finding Sources
I have enough authoritative medical knowledge to create a rich, realistic clinical case study. Let me build it now.

Clinical Case Study: Interpreting an Abnormal CMP


THE PATIENT

Name: Mr. J.T. Age: 58-year-old male Presenting complaint: "I haven't felt right for 3 days. I'm nauseated, confused, and my legs are swollen."
History of Present Illness:
  • 3-day history of nausea, decreased appetite, and progressive confusion
  • Wife reports he has been drinking more water than usual and urinating less
  • Bilateral lower extremity swelling noticed over the past week
  • Mild right upper quadrant discomfort
Past Medical History:
  • Type 2 Diabetes Mellitus (on metformin, poorly controlled)
  • Hypertension (on lisinopril)
  • Chronic alcohol use (6-8 drinks/day for 20+ years)
Physical Exam:
  • BP: 92/58 mmHg (hypotensive), HR: 108 bpm (tachycardic)
  • Confused (oriented to person only)
  • Jaundice (yellow sclera and skin)
  • Ascites (fluid wave positive)
  • 3+ pitting edema bilateral lower extremities
  • Spider angiomata on chest
  • Palmar erythema

THE CMP RESULTS

ComponentPatient ValueNormal RangeStatus
Sodium (Na⁺)128 mEq/L135-145🔴 LOW
Potassium (K⁺)5.8 mEq/L3.5-5.0🔴 HIGH
Chloride (Cl⁻)94 mEq/L98-106🟡 LOW
Bicarbonate (HCO₃⁻)14 mEq/L22-29🔴 LOW
BUN68 mg/dL7-20🔴 HIGH
Creatinine4.2 mg/dL0.6-1.2🔴 HIGH
Glucose312 mg/dL70-100🔴 HIGH
Calcium (Ca²⁺)7.4 mg/dL8.5-10.2🔴 LOW
Total Protein4.8 g/dL6.3-8.2🔴 LOW
Albumin1.9 g/dL3.5-5.0🔴 LOW
Total Bilirubin8.6 mg/dL0.1-1.2🔴 HIGH
AST312 U/L10-40🔴 HIGH
ALT148 U/L7-56🔴 HIGH
Alkaline Phosphatase (ALP)198 U/L44-147🔴 HIGH

STEP-BY-STEP INTERPRETATION


STEP 1 - Calculate the Anion Gap

Formula: Anion Gap = Na⁺ - (Cl⁻ + HCO₃⁻)
AG = 128 - (94 + 14)
AG = 128 - 108
AG = 20 mEq/L   ← ELEVATED (normal 8-12)
Conclusion: This patient has a high anion gap metabolic acidosis (HAGMA)
The low bicarbonate (14 mEq/L) + elevated anion gap (20) = unmeasured anions accumulating in the blood. Given the elevated creatinine and renal failure, uremic acids are the most likely culprit, with possible lactic acidosis from hypotension also contributing.

STEP 2 - Interpret the Renal Function

CalculationValueMeaning
BUN68 mg/dLSeverely elevated
Creatinine4.2 mg/dLSeverely elevated
BUN:Cr Ratio68 ÷ 4.2 = 16:1Normal ratio despite both high
eGFR (estimated)~14 mL/min/1.73m²Stage 5 - severe renal impairment
Interpretation: Both BUN and Cr are markedly elevated. The BUN:Cr ratio of 16:1 falls in the intermediate range, suggesting a mixed picture - likely a combination of:
  • Pre-renal component (dehydration/hypotension - BP 92/58)
  • Intrinsic renal disease (diabetic nephropathy from poorly controlled DM)
The kidneys cannot excrete K⁺ effectively → explains the hyperkalemia (5.8 mEq/L). The kidneys cannot excrete acid → contributes to the low HCO₃⁻ (14 mEq/L).
⚠️ Potassium 5.8 mEq/L is a medical emergency. At K⁺ >6.0, fatal cardiac arrhythmias (ventricular fibrillation) can occur. This patient needs an immediate ECG.

STEP 3 - Interpret the Liver Panel

AST:ALT Ratio:
AST ÷ ALT = 312 ÷ 148 = 2.1:1   ← > 2:1
AST:ALT > 2:1 is the hallmark of alcoholic liver disease.
FindingValueClinical Significance
AST312 U/L (~8x ULN)Hepatocellular damage
ALT148 U/L (~3x ULN)AST > ALT = alcohol pattern
ALP198 U/L (~1.3x ULN)Mildly elevated - some cholestatic component
Total Bilirubin8.6 mg/dL (~7x ULN)Significant - jaundice visible at >2.5 mg/dL
Pattern: Hepatocellular-predominant injury (AST/ALT much higher than ALP) with moderate cholestatic involvement (elevated ALP + bilirubin).
Combined with the 20+ year alcohol history, ascites, spider angiomata, and palmar erythema → alcoholic cirrhosis with acute alcoholic hepatitis.

STEP 4 - Interpret the Protein/Albumin

FindingValueClinical Significance
Albumin1.9 g/dL (severely low)Liver can no longer synthesize albumin
Total Protein4.8 g/dL (low)Reflects low albumin + decreased synthesis
Why albumin matters here:
  1. Ascites - low albumin → reduced oncotic pressure → fluid leaks into peritoneal cavity
  2. Edema - same mechanism causes fluid to leak into interstitial tissues (the leg swelling)
  3. Calcium must be corrected:
Corrected Ca = Measured Ca + 0.8 × (4 - Albumin)
Corrected Ca = 7.4 + 0.8 × (4 - 1.9)
Corrected Ca = 7.4 + 0.8 × 2.1
Corrected Ca = 7.4 + 1.68
Corrected Ca = 9.1 mg/dL   ← NORMAL
Conclusion: The apparent hypocalcemia (7.4 mg/dL) is pseudohypocalcemia - it is entirely explained by the low albumin. The corrected calcium is 9.1 mg/dL (normal). No true calcium disorder exists.

STEP 5 - Interpret the Electrolytes

FindingValueCause
Na⁺ 128 mEq/LHyponatremiaDilutional - fluid retention in cirrhosis + heart failure physiology; also SIADH from liver disease
K⁺ 5.8 mEq/LHyperkalemiaAKI - kidneys not excreting K⁺; also acidosis shifts K⁺ out of cells
Cl⁻ 94 mEq/LLow-normalConsistent with metabolic acidosis and volume changes
HCO₃⁻ 14 mEq/LMetabolic acidosisUremic acid accumulation (AKI) + possible lactic acidosis from hypotension
Glucose 312 mg/dL - significantly elevated in a known diabetic. Hyperglycemia also causes a further shift of water into the intravascular space, which can lower Na⁺ additionally:
Corrected Na for hyperglycemia:
Add 1.6 mEq/L Na per 100 mg/dL glucose >100
Glucose above 100 = 312 - 100 = 212 mg/dL above normal
Correction = (212/100) × 1.6 = +3.4 mEq/L
True Na⁺ ≈ 128 + 3.4 = ~131.4 mEq/L (still hyponatremic, but less severe)

SYNTHESIS: PUTTING IT ALL TOGETHER

ProblemCMP EvidenceClinical Correlation
Alcoholic cirrhosisAST:ALT >2:1, high bilirubin, low albumin, high ALPJaundice, ascites, spider angiomata, palmar erythema
Acute Kidney Injury (AKI)Cr 4.2, BUN 68, BUN:Cr 16:1Hypotension (pre-renal) + diabetic nephropathy
High Anion Gap Metabolic AcidosisHCO₃ 14, AG = 20Uremia from AKI; possible lactic acidosis
HyperkalemiaK⁺ 5.8 mEq/LAKI + acidosis → K⁺ exits cells
Dilutional hyponatremiaNa⁺ 128 mEq/LCirrhosis + fluid overload
PseudohypocalcemiaCa 7.4 → corrected 9.1Low albumin, not true calcium deficiency
HyperglycemiaGlucose 312 mg/dLPoorly controlled DM2

IMMEDIATE MANAGEMENT PRIORITIES

Based on the CMP interpretation, these actions are needed urgently:
  1. ECG immediately - K⁺ 5.8 mEq/L; peaked T-waves or widened QRS = life-threatening
  2. Treat hyperkalemia - Calcium gluconate (cardioprotection), insulin + dextrose (shift K into cells), sodium bicarbonate, consider dialysis if refractory
  3. IV fluid resuscitation - Cautiously (NS or albumin, not LR); target MAP >65 mmHg
  4. Hold metformin - Contraindicated in AKI (risk of lactic acidosis)
  5. Nephrology consult - Cr 4.2 + oliguria may require dialysis (AEIOU indications)
  6. Hepatology consult - Acute alcoholic hepatitis + decompensated cirrhosis
  7. Insulin + fluid for hyperglycemia
  8. Recheck electrolytes every 2-4 hours

KEY LEARNING POINTS FROM THIS CASE

PrincipleLesson
Always calculate the anion gapElevated AG changed the entire diagnostic approach
AST:ALT ratio directs diagnosis>2:1 = alcohol; don't just note "transaminitis"
Correct calcium for albuminNever treat "low calcium" without this correction first
Correct sodium for glucoseHyperglycemia artificially lowers reported Na⁺
BUN + Cr + K⁺ + HCO₃ move togetherAKI creates a predictable 4-value pattern
Albumin is a window into the liverHypoalbuminemia explains ascites, edema, AND pseudohypocalcemia

Generate a quick reference PDF for normal CMP ranges and interpretations

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || mkdir -p /home/daytona/workspace …

Running Command

mkdir -p /home/daytona/workspace/cmp-reference && python3 -c "import reportlab; …

Writing File

~/cmp-reference/generate_cmp_pdf.py

from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import PageBreak

OUTPUT = "/home/daytona/workspace/cmp-reference/CMP_Quick_Reference.pdf"

# ── Colour palette ──────────────────────────────────────────────────────────
NAVY      = colors.HexColor("#1A2645")
TEAL      = colors.HexColor("#0D7377")
ORANGE    = colors.HexColor("#E07B39")
RED       = colors.HexColor("#C0392B")
GREEN     = colors.HexColor("#1E8449")
PURPLE    = colors.HexColor("#6C3483")
GOLD      = colors.HexColor("#B7950B")
LIGHT_BG  = colors.HexColor("#F4F6F9")
MID_GREY  = colors.HexColor("#BDC3C7")
WHITE     = colors.white
BLACK     = colors.black
DARK_GREY = colors.HexColor("#2C3E50")

# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def PS(name, parent="Normal", **kw):
    return ParagraphStyle(name, parent=styles[parent], **kw)

title_style  = PS("Title2", fontSize=22, textColor=WHITE,
                  alignment=TA_CENTER, spaceAfter=4, fontName="Helvetica-Bold")
sub_style    = PS("Sub",    fontSize=11, textColor=colors.HexColor("#A8D8EA"),
                  alignment=TA_CENTER, spaceAfter=2, fontName="Helvetica")
section_hdr  = PS("SecHdr", fontSize=12, textColor=WHITE,
                  alignment=TA_CENTER, fontName="Helvetica-Bold", spaceAfter=0)
body         = PS("Body2",  fontSize=8.5, textColor=DARK_GREY, leading=12)
bold_body    = PS("BoldB",  fontSize=8.5, textColor=DARK_GREY,
                  fontName="Helvetica-Bold", leading=12)
footer_style = PS("Footer", fontSize=7.5, textColor=MID_GREY,
                  alignment=TA_CENTER)
note_style   = PS("Note",   fontSize=8, textColor=DARK_GREY,
                  backColor=colors.HexColor("#FEF9E7"), leading=11)
red_note     = PS("RedNote",fontSize=8, textColor=RED,
                  fontName="Helvetica-Bold", leading=11)

# ── Helper: coloured section header row ─────────────────────────────────────
def section_row(label, col, span=5):
    return ([Paragraph(label, section_hdr)],
            [("BACKGROUND", (0,0),(-1,-1), col),
             ("TEXTCOLOR",  (0,0),(-1,-1), WHITE),
             ("SPAN",       (0,0),(span-1,0)),
             ("TOPPADDING", (0,0),(-1,-1), 5),
             ("BOTTOMPADDING",(0,0),(-1,-1),5),
             ("LEFTPADDING",(0,0),(-1,-1), 6)])

def cell(txt, bold=False, color=DARK_GREY, bg=None, align=TA_LEFT, size=8.2):
    st = ParagraphStyle("c", fontSize=size, textColor=color,
                        fontName="Helvetica-Bold" if bold else "Helvetica",
                        alignment=align, leading=11,
                        backColor=bg if bg else colors.white)
    return Paragraph(txt, st)

# ── Page canvas (header banner + footer) ────────────────────────────────────
def on_page(canvas, doc):
    W, H = letter
    # Top banner
    canvas.setFillColor(NAVY)
    canvas.rect(0, H-54, W, 54, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, H-58, W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 18)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(W/2, H-34, "COMPREHENSIVE METABOLIC PANEL (CMP)")
    canvas.setFont("Helvetica", 10)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(W/2, H-48,
        "Quick Reference Guide  •  Normal Ranges, Interpretations & Clinical Pearls")
    # Footer
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(MID_GREY)
    canvas.drawCentredString(W/2, 18,
        "For educational purposes only  •  Always correlate with clinical context  "
        f"•  Page {doc.page}")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 28, W, 2, fill=1, stroke=0)

# ── Build document ───────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=letter,
    topMargin=0.85*inch,
    bottomMargin=0.5*inch,
    leftMargin=0.45*inch,
    rightMargin=0.45*inch,
)

story = []
COL_W = [1.35*inch, 0.85*inch, 0.75*inch, 1.65*inch, 2.35*inch]
total_w = sum(COL_W)

BASE_STYLE = [
    ("FONTNAME",      (0,0),(-1,-1), "Helvetica"),
    ("FONTSIZE",      (0,0),(-1,-1), 8.2),
    ("ROWBACKGROUND", (0,0),(-1,-1), [LIGHT_BG, WHITE]),
    ("GRID",          (0,0),(-1,-1), 0.4, colors.HexColor("#D5D8DC")),
    ("VALIGN",        (0,0),(-1,-1), "TOP"),
    ("TOPPADDING",    (0,0),(-1,-1), 4),
    ("BOTTOMPADDING", (0,0),(-1,-1), 4),
    ("LEFTPADDING",   (0,0),(-1,-1), 5),
    ("RIGHTPADDING",  (0,0),(-1,-1), 5),
]

HDR_STYLE = BASE_STYLE + [
    ("BACKGROUND",    (0,0),(-1,0), DARK_GREY),
    ("TEXTCOLOR",     (0,0),(-1,0), WHITE),
    ("FONTNAME",      (0,0),(-1,0), "Helvetica-Bold"),
    ("FONTSIZE",      (0,0),(-1,0), 8.5),
    ("ALIGN",         (0,0),(-1,0), "CENTER"),
]

def hdr_row():
    return [cell("COMPONENT", bold=True, color=WHITE, bg=DARK_GREY),
            cell("NORMAL RANGE", bold=True, color=WHITE, bg=DARK_GREY, align=TA_CENTER),
            cell("UNIT", bold=True, color=WHITE, bg=DARK_GREY, align=TA_CENTER),
            cell("LOW  ▼  MEANS…", bold=True, color=WHITE, bg=DARK_GREY),
            cell("HIGH  ▲  MEANS…", bold=True, color=WHITE, bg=DARK_GREY)]

def mk_row(name, rng, unit, low, high, bg=None):
    bg = bg or WHITE
    return [cell(name, bold=True, color=NAVY),
            cell(rng, align=TA_CENTER),
            cell(unit, align=TA_CENTER),
            cell(low,  color=colors.HexColor("#1A5276")),
            cell(high, color=colors.HexColor("#7B241C"))]

# ═══════════════════════════════════════════════════════════════════════
#  CLUSTER 1: ELECTROLYTES
# ═══════════════════════════════════════════════════════════════════════
story.append(Spacer(1, 0.12*inch))

elec_data = [
    hdr_row(),
    mk_row("Sodium (Na⁺)",      "135 – 145", "mEq/L",
           "Hyponatremia → SIADH, heart failure,\ncirrhosis, hypothyroidism, polydipsia",
           "Hypernatremia → dehydration, DI,\nexcessive Na intake, fever"),
    mk_row("Potassium (K⁺)",    "3.5 – 5.0", "mEq/L",
           "Hypokalemia → diuretics, vomiting,\nalkalosis, diarrhea, low intake",
           "Hyperkalemia → AKI/CKD, acidosis,\nACE inhibitors, Addison's, hemolysis"),
    mk_row("Chloride (Cl⁻)",    "98 – 106",  "mEq/L",
           "Hypochloremia → vomiting, NG suction,\nmetabolic alkalosis, diuretics",
           "Hyperchloremia → diarrhea, RTA,\nnormal saline overload, dehydration"),
    mk_row("Bicarbonate (HCO₃⁻)","22 – 29", "mEq/L",
           "Metabolic acidosis → DKA, lactic\nacidosis, renal failure, diarrhea",
           "Metabolic alkalosis → vomiting,\nNG suction, diuretics, antacid excess"),
]

elec_sec_hdr = [cell("ELECTROLYTES  (Acid-Base & Fluid Balance Cluster)", bold=True, color=WHITE)]
elec_tbl = Table([[elec_sec_hdr[0]]], colWidths=[total_w])
elec_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), TEAL),
    ("TOPPADDING",(0,0),(-1,-1),5), ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8)]))
story.append(elec_tbl)

t = Table(elec_data, colWidths=COL_W, repeatRows=1)
t.setStyle(TableStyle(HDR_STYLE + [
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LIGHT_BG, WHITE])]))
story.append(t)
story.append(Spacer(1, 0.06*inch))

# Anion Gap callout
ag_txt = (
    "<b>KEY FORMULA — Anion Gap (AG):</b>  AG = Na⁺ − (Cl⁻ + HCO₃⁻)  "
    "| <b>Normal: 8–12 mEq/L</b><br/>"
    "High AG metabolic acidosis (MUDPILES): <b>M</b>ethanol · <b>U</b>remia · "
    "<b>D</b>KA · <b>P</b>ropylene glycol · <b>I</b>soniazid/Iron · "
    "<b>L</b>actic acidosis · <b>E</b>thylene glycol · <b>S</b>alicylates<br/>"
    "Normal AG metabolic acidosis (HARDUPS): diarrhea, RTA, hyperchloremic acidosis"
)
ag_tbl = Table([[Paragraph(ag_txt, PS("ag", fontSize=8, textColor=DARK_GREY,
                leading=12, backColor=colors.HexColor("#EBF5FB")))]],
               colWidths=[total_w])
ag_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#EBF5FB")),
    ("BOX",(0,0),(-1,-1),1, TEAL),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8)]))
story.append(ag_tbl)
story.append(Spacer(1, 0.12*inch))

# ═══════════════════════════════════════════════════════════════════════
#  CLUSTER 2: RENAL + GLUCOSE + CALCIUM
# ═══════════════════════════════════════════════════════════════════════
renal_sec = Table([[cell("RENAL FUNCTION + GLUCOSE + CALCIUM", bold=True, color=WHITE)]],
                  colWidths=[total_w])
renal_sec.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), RED),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8)]))
story.append(renal_sec)

renal_data = [
    hdr_row(),
    mk_row("BUN\n(Blood Urea Nitrogen)", "7 – 20", "mg/dL",
           "Low protein intake, liver failure,\noverhydration, malnutrition",
           "Dehydration, GI bleed, renal failure,\nhigh protein diet, catabolic state"),
    mk_row("Creatinine (Cr)",    "0.6 – 1.2", "mg/dL",
           "Low muscle mass (elderly, malnutrition),\npregnancy, severe liver disease",
           "AKI, CKD, rhabdomyolysis, drugs\n(NSAIDs, aminoglycosides, contrast)"),
    mk_row("Glucose",            "70 – 100",  "mg/dL",
           "Hypoglycemia → insulin excess,\nliver failure, Addison's, sepsis",
           "Hyperglycemia → DM, DKA, HHS,\nsteroids, pancreatitis, stress response"),
    mk_row("Calcium (Ca²⁺)",    "8.5 – 10.2","mg/dL",
           "Hypocalcemia → hypoparathyroidism,\nvit D deficiency, pancreatitis, CKD",
           "Hypercalcemia → malignancy, primary\nhyperPTH, sarcoid, vit D toxicity"),
]
t2 = Table(renal_data, colWidths=COL_W, repeatRows=1)
t2.setStyle(TableStyle(HDR_STYLE + [
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LIGHT_BG, WHITE])]))
story.append(t2)
story.append(Spacer(1, 0.06*inch))

# BUN:Cr ratio box
bun_txt = (
    "<b>BUN : Creatinine Ratio  (Normal 10–20:1)</b><br/>"
    "  &gt;20:1 → Pre-renal (dehydration, GI bleed, heart failure)   |   "
    "  10–20:1 → Normal or intrinsic renal   |   "
    "  &lt;10:1 → Liver failure, low protein intake, ATN<br/>"
    "<b>Calcium Correction for Low Albumin:</b>  "
    "Corrected Ca = Measured Ca + 0.8 × (4 − Albumin g/dL)<br/>"
    "<b>Glucose ↔ Sodium:</b>  For every 100 mg/dL glucose &gt;100, "
    "add 1.6 mEq/L to reported Na⁺ to get true Na⁺"
)
bun_tbl = Table([[Paragraph(bun_txt, PS("bun", fontSize=8, textColor=DARK_GREY,
                 leading=12, backColor=colors.HexColor("#FDEDEC")))]], colWidths=[total_w])
bun_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#FDEDEC")),
    ("BOX",(0,0),(-1,-1),1, RED),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8)]))
story.append(bun_tbl)
story.append(Spacer(1, 0.12*inch))

# ═══════════════════════════════════════════════════════════════════════
#  CLUSTER 3: LIVER / PROTEIN
# ═══════════════════════════════════════════════════════════════════════
liv_sec = Table([[cell("LIVER FUNCTION + PROTEIN  (Hepatic Synthetic & Excretory Cluster)",
                       bold=True, color=WHITE)]], colWidths=[total_w])
liv_sec.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), ORANGE),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8)]))
story.append(liv_sec)

liver_data = [
    hdr_row(),
    mk_row("Total Protein",      "6.3 – 8.2", "g/dL",
           "Liver failure, malnutrition,\nnephrotic syndrome, malabsorption",
           "Chronic inflammation, dehydration,\nmultiple myeloma, sarcoidosis"),
    mk_row("Albumin",            "3.5 – 5.0", "g/dL",
           "Liver disease (cirrhosis), malnutrition,\nnephrotic syndrome, chronic illness",
           "Dehydration (relative elevation)\n— rarely clinically significant"),
    mk_row("Total Bilirubin",    "0.1 – 1.2", "mg/dL",
           "Rarely significant when mildly low\n(normal variation)",
           "Jaundice >2.5; hepatitis, cirrhosis,\nhemolysis, bile duct obstruction"),
    mk_row("AST\n(Aspartate Aminotransferase)", "10 – 40", "U/L",
           "Rarely clinically significant\n(low muscle mass)",
           "Liver injury, MI, muscle damage,\nalcoholic hepatitis (AST:ALT >2:1)"),
    mk_row("ALT\n(Alanine Aminotransferase)",   "7 – 56",  "U/L",
           "Rarely significant",
           "Hepatocellular injury (most liver-\nspecific enzyme), viral hepatitis, NAFLD"),
    mk_row("Alkaline Phosphatase (ALP)", "44 – 147", "U/L",
           "Hypothyroidism, pernicious anemia,\nzinc deficiency (rare)",
           "Cholestasis, bile obstruction, bone\ndisease, Paget's, bone mets, pregnancy"),
]
t3 = Table(liver_data, colWidths=COL_W, repeatRows=1)
t3.setStyle(TableStyle(HDR_STYLE + [
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LIGHT_BG, WHITE])]))
story.append(t3)
story.append(Spacer(1, 0.06*inch))

# Liver pattern box
liv_txt = (
    "<b>LIVER ENZYME PATTERNS:</b><br/>"
    "  Hepatocellular (AST+ALT &gt;&gt; ALP):  "
    "viral hepatitis, NAFLD, alcoholic hepatitis, drug-induced, ischemia<br/>"
    "  Cholestatic (ALP+GGT &gt;&gt; AST/ALT):  "
    "gallstones, cholangiocarcinoma, pancreatic cancer, PBC, PSC, drugs<br/>"
    "  <b>AST:ALT &gt;2:1</b> → alcoholic liver disease  |  "
    "<b>AST or ALT &gt;1000</b> → ischemic hepatitis / acute viral / acetaminophen toxicity<br/>"
    "  <b>ALP elevated + GGT normal</b> → BONE source (Paget's, mets, healing fracture)"
)
liv_box = Table([[Paragraph(liv_txt, PS("lb", fontSize=8, textColor=DARK_GREY,
                 leading=12))]], colWidths=[total_w])
liv_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#FEF5E7")),
    ("BOX",(0,0),(-1,-1),1, ORANGE),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8)]))
story.append(liv_box)
story.append(Spacer(1, 0.14*inch))

# ═══════════════════════════════════════════════════════════════════════
#  CRITICAL VALUES & RED FLAGS
# ═══════════════════════════════════════════════════════════════════════
crit_sec = Table([[cell("CRITICAL VALUES  —  Immediate Action Required", bold=True, color=WHITE)]],
                 colWidths=[total_w])
crit_sec.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#922B21")),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8)]))
story.append(crit_sec)

crit_data = [
    [cell("VALUE", bold=True, color=WHITE, bg=DARK_GREY),
     cell("CRITICAL THRESHOLD", bold=True, color=WHITE, bg=DARK_GREY, align=TA_CENTER),
     cell("IMMEDIATE CONCERN", bold=True, color=WHITE, bg=DARK_GREY),
     cell("PRIORITY ACTION", bold=True, color=WHITE, bg=DARK_GREY)],
    [cell("Sodium"),       cell("< 120  or  > 160", align=TA_CENTER),
     cell("Seizures, cerebral edema, coma"),
     cell("Neurology/nephrology consult; correct slowly")],
    [cell("Potassium"),    cell("< 2.5  or  > 6.5", align=TA_CENTER),
     cell("Fatal cardiac arrhythmia (V-fib)"),
     cell("STAT ECG; calcium gluconate if >6.5")],
    [cell("Bicarbonate"),  cell("< 10  or  > 40", align=TA_CENTER),
     cell("Severe acid-base crisis"),
     cell("ABG, ICU consult, treat underlying cause")],
    [cell("Creatinine"),   cell("> 10 (acute rise)", align=TA_CENTER),
     cell("Acute renal failure, dialysis needed"),
     cell("Nephrology consult; assess AEIOU indications")],
    [cell("Glucose"),      cell("< 40  or  > 500", align=TA_CENTER),
     cell("Neuroglycopenia / DKA / HHS"),
     cell("D50W IV for hypoglycemia; insulin protocol for hyper")],
    [cell("Calcium"),      cell("< 6.0  or  > 13.0", align=TA_CENTER),
     cell("Tetany, cardiac arrest / hypercalcemic crisis"),
     cell("IV calcium (if low); IV fluids + bisphosphonate (if high)")],
    [cell("Total Bilirubin"),cell("> 15 in adults", align=TA_CENTER),
     cell("Severe liver failure or biliary obstruction"),
     cell("Hepatology consult; rule out obstruction urgently")],
]
crit_col = [1.25*inch, 1.1*inch, 1.9*inch, 2.5*inch]
tc = Table(crit_data, colWidths=crit_col)
tc.setStyle(TableStyle([
    ("FONTSIZE",(0,0),(-1,-1),8),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[colors.HexColor("#FDEDEC"), WHITE]),
    ("GRID",(0,0),(-1,-1),0.4,MID_GREY),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("BACKGROUND",(0,0),(-1,0),DARK_GREY),
    ("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
]))
story.append(tc)
story.append(Spacer(1, 0.14*inch))

# ═══════════════════════════════════════════════════════════════════════
#  CLINICAL PATTERNS QUICK TABLE
# ═══════════════════════════════════════════════════════════════════════
pat_sec = Table([[cell("COMMON CLINICAL PATTERNS ON CMP", bold=True, color=WHITE)]],
                colWidths=[total_w])
pat_sec.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), PURPLE),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8)]))
story.append(pat_sec)

pat_col = [1.6*inch, 2.2*inch, 2.95*inch]
pat_data = [
    [cell("CONDITION", bold=True, color=WHITE, bg=DARK_GREY),
     cell("CMP PATTERN", bold=True, color=WHITE, bg=DARK_GREY),
     cell("KEY CLUES", bold=True, color=WHITE, bg=DARK_GREY)],
    [cell("DKA"),
     cell("↑ Glucose, ↓ HCO₃, ↑ Anion Gap, ↑ K⁺ (initially)"),
     cell("Type 1 DM; fruity breath; Kussmaul respirations")],
    [cell("AKI (Acute Kidney Injury)"),
     cell("↑ Cr, ↑ BUN, ↑ K⁺, ↓ HCO₃, ↑ Anion Gap"),
     cell("Oliguria; recent nephrotoxin/hypotension; rising Cr")],
    [cell("Liver Cirrhosis"),
     cell("↑ AST/ALT, ↑ Bili, ↓ Albumin, ↓ Na⁺, ↑ ALP"),
     cell("Jaundice, ascites, spider angiomata, AST:ALT >2:1")],
    [cell("Heart Failure"),
     cell("↓ Na⁺ (dilutional), ↑ BUN, ↑ Cr, ↓ HCO₃"),
     cell("Edema, JVD, crackles; BNP elevated (not on CMP)")],
    [cell("Dehydration"),
     cell("↑ Na⁺, ↑ BUN, ↑ Cr, BUN:Cr >20:1"),
     cell("Dry mucosa, tachycardia, concentrated urine")],
    [cell("Primary Hyperparathyroidism"),
     cell("↑ Ca²⁺, ↓ Phosphate (not on CMP), ↑ ALP"),
     cell("'Bones, stones, groans, moans'; ↑ PTH confirms")],
    [cell("Alcoholic Hepatitis"),
     cell("↑ AST > ALT (ratio >2:1), ↑ Bili, ↓ Albumin, ↑ ALP"),
     cell("Heavy alcohol use; GGT markedly elevated (not on CMP)")],
    [cell("SIADH"),
     cell("↓ Na⁺, normal BUN/Cr, normal glucose"),
     cell("Euvolemic hyponatremia; concentrated urine")],
    [cell("Nephrotic Syndrome"),
     cell("↓ Albumin, ↓ Total Protein, normal/↑ Cr"),
     cell("Heavy proteinuria; edema; hyperlipidemia; frothy urine")],
    [cell("Rhabdomyolysis"),
     cell("↑ Cr (disproportionate), ↑ K⁺, ↑ Ca²⁺ then ↓ Ca²⁺"),
     cell("Dark urine; muscle pain; ↑ CK >10,000 (not on CMP)")],
]
tp = Table(pat_data, colWidths=pat_col, repeatRows=1)
tp.setStyle(TableStyle([
    ("FONTSIZE",(0,0),(-1,-1),8),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[colors.HexColor("#F4ECF7"), WHITE]),
    ("GRID",(0,0),(-1,-1),0.4,MID_GREY),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("BACKGROUND",(0,0),(-1,0),DARK_GREY),
    ("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
]))
story.append(tp)
story.append(Spacer(1, 0.14*inch))

# ═══════════════════════════════════════════════════════════════════════
#  STEP-BY-STEP CMP INTERPRETATION GUIDE
# ═══════════════════════════════════════════════════════════════════════
steps_sec = Table([[cell("5-STEP CMP INTERPRETATION FRAMEWORK", bold=True, color=WHITE)]],
                  colWidths=[total_w])
steps_sec.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), GREEN),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8)]))
story.append(steps_sec)

steps_txt = (
    "<b>Step 1 — Scan for critical values first.</b>  "
    "K⁺ &gt;6.5 or &lt;2.5 mEq/L, Na⁺ &lt;120 mEq/L, glucose &lt;40 mg/dL → act immediately.<br/><br/>"
    "<b>Step 2 — Assess acid-base status.</b>  "
    "Look at HCO₃⁻. Low = acidosis. High = alkalosis. "
    "Then calculate Anion Gap = Na − (Cl + HCO₃). AG &gt;12 = high gap metabolic acidosis (MUDPILES).<br/><br/>"
    "<b>Step 3 — Evaluate renal function.</b>  "
    "Check BUN and Creatinine together. Calculate BUN:Cr ratio. "
    "Elevated Cr → also expect ↑K⁺ and ↓HCO₃. Correct calcium for albumin level.<br/><br/>"
    "<b>Step 4 — Interpret the liver panel.</b>  "
    "Is the pattern hepatocellular (AST/ALT dominant) or cholestatic (ALP/Bili dominant)? "
    "Calculate AST:ALT ratio. Check albumin and total protein for synthetic function.<br/><br/>"
    "<b>Step 5 — Synthesize the full picture.</b>  "
    "No value exists in isolation. Combine all clusters: "
    "low albumin → correct calcium; high glucose → correct sodium; "
    "AKI → expect hyperkalemia + acidosis; cirrhosis → expect hyponatremia + low albumin + coagulopathy."
)
steps_tbl = Table([[Paragraph(steps_txt, PS("st", fontSize=8.5, textColor=DARK_GREY,
                   leading=13))]], colWidths=[total_w])
steps_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#EAFAF1")),
    ("BOX",(0,0),(-1,-1),1, GREEN),
    ("TOPPADDING",(0,0),(-1,-1),8),("BOTTOMPADDING",(0,0),(-1,-1),8),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10)]))
story.append(steps_tbl)

# ── Build ────────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print("PDF generated:", OUTPUT)
Running Command

cd /home/daytona/workspace/cmp-reference && python3 generate_cmp_pdf.py

Running Command

ls -lh /home/daytona/workspace/cmp-reference/CMP_Quick_Reference.pdf

Your PDF is ready!

CMP Quick Reference

PDF Document · PDF


Here is what the PDF contains across its color-coded sections:

PDF Structure

1. Electrolytes Cluster (Teal header)
  • Na⁺, K⁺, Cl⁻, HCO₃⁻ with normal ranges, low/high interpretations
  • Anion Gap formula callout box with MUDPILES mnemonic
2. Renal + Glucose + Calcium Cluster (Red header)
  • BUN, Creatinine, Glucose, Calcium with clinical interpretations
  • BUN:Cr ratio guide, calcium correction formula, and glucose-sodium correction formula
3. Liver Function + Protein Cluster (Orange header)
  • Total Protein, Albumin, Bilirubin, AST, ALT, ALP
  • Hepatocellular vs. cholestatic pattern key + AST:ALT ratio guide
4. Critical Values Table (Dark red header)
  • Threshold values requiring immediate action for all 7 affected components
  • Immediate concerns and priority actions for each
5. Common Clinical Patterns (Purple header)
  • 11 conditions (DKA, AKI, cirrhosis, HF, dehydration, SIADH, etc.) with their CMP fingerprints
6. 5-Step Interpretation Framework (Green header)
  • Systematic approach: critical values → acid-base → renal → liver → synthesize

Create a list of normal lab values of Commonly asked NCLEX test

Searching the Web

NCLEX normal lab values 2024 2025 commonly tested reference ranges

Finding Sources
Reading Web Page

https://simplenursing.com/nclex-lab-values-nclex-practice-qu…

I have all the data I need. Here is the comprehensive list:

🩺 Normal Lab Values: NCLEX Quick Reference

Next Gen NCLEX Note: The current NCLEX now provides reference ranges with lab questions. However, knowing these values deeply helps you recognize abnormals instantly and choose the correct nursing action - which is what the exam actually tests.

1. COMPLETE BLOOD COUNT (CBC)

Lab TestNormal RangeCritical ValuesNCLEX Pearl
WBC5,000–10,000 /µL<2,000 or >30,000>10,000 = infection/inflammation; <2,500 = neutropenia precautions
RBC (male)4.5–6.2 million/µLLow = anemia; high = polycythemia
RBC (female)4.0–5.5 million/µL
Hemoglobin (male)14–18 g/dL<7 or >20Transfuse typically when Hgb <7-8 g/dL
Hemoglobin (female)12–16 g/dL<7 or >20
Hematocrit (male)42–52%<21% or >66%Hct ≈ 3× Hgb (quick check)
Hematocrit (female)37–47%<21% or >66%
Platelets150,000–400,000 /mm³<50,000 or >1,000,000<50,000 = bleeding risk; <20,000 = spontaneous bleed risk
MCV80–100 fLLow = microcytic (iron def); High = macrocytic (B12/folate def)
MCH27–33 pgLow = hypochromic
Reticulocytes0.5–2%High = active RBC production (hemolysis, treatment response)

2. METABOLIC PANEL (BMP/CMP)

Electrolytes

Lab TestNormal RangeCritical ValuesNCLEX Pearl
Sodium (Na⁺)135–145 mEq/L<120 or >160Low = SIADH/overhydration; High = dehydration/DI
Potassium (K⁺)3.5–5.0 mEq/L<2.5 or >6.5ECG changes at extremes; hold digoxin if K⁺ <3.5
Chloride (Cl⁻)98–106 mEq/L<80 or >115Reciprocal to HCO₃; low Cl = metabolic alkalosis
Bicarbonate (HCO₃⁻)22–29 mEq/L<10 or >40Low = acidosis (DKA, renal failure); High = alkalosis (vomiting)
Calcium (Ca²⁺)8.5–10.2 mg/dL<6.0 or >13.0Low = Chvostek's/Trousseau's sign; High = "bones, stones, groans, moans"
Magnesium (Mg²⁺)1.5–2.5 mEq/L<1.0 or >4.0Low = cardiac arrhythmia, seizures; toxic = respiratory depression (esp. eclampsia Mg drip)
Phosphorus (PO₄)3.0–4.5 mg/dL<1.0 or >8.0Inverse to calcium; low in hyperPTH, DKA treatment

Renal Function

Lab TestNormal RangeCritical ValuesNCLEX Pearl
BUN7–20 mg/dL>100BUN:Cr >20:1 = pre-renal; hold contrast and metformin if elevated
Creatinine0.6–1.2 mg/dL>10 (acute rise)Best marker of kidney function; check before contrast dye
eGFR≥60 mL/min/1.73m²<15 = kidney failure<30 = avoid metformin; <60 = modify drug doses
Uric Acid3.5–8.0 mg/dLElevated in gout, tumor lysis syndrome

Glucose & Diabetes

Lab TestNormal RangeNCLEX Pearl
Fasting Blood Glucose70–100 mg/dL<70 = hypoglycemia (give 15g carbs); >126 = diabetes diagnosis
2-hr Glucose Tolerance<140 mg/dL140–199 = prediabetes; ≥200 = diabetes
HbA1c<5.7%5.7–6.4% = prediabetes; ≥6.5% = diabetes; target <7% for diabetics
Critical glucose<40 or >500 = critical action needed

3. ARTERIAL BLOOD GASES (ABGs)

ComponentNormal RangeLow MeansHigh Means
pH7.35–7.45Acidosis (<7.35)Alkalosis (>7.45)
PaO₂80–100 mmHgHypoxemiaHyperoxia
PaCO₂35–45 mmHgRespiratory alkalosis (hyperventilation)Respiratory acidosis (hypoventilation)
HCO₃⁻22–26 mEq/LMetabolic acidosisMetabolic alkalosis
SaO₂>95%Inadequate oxygenation
Base Excess-2 to +2Metabolic acidosisMetabolic alkalosis

ABG Quick Interpretation Rule (ROME):

Respiratory Opposite — pH and PaCO₂ move in opposite directions Metabolic Equal — pH and HCO₃ move in the same direction
PatternpHPaCO₂HCO₃Common Cause
Respiratory AcidosisNormalCOPD, hypoventilation, opioids
Respiratory AlkalosisNormalHyperventilation, anxiety, PE
Metabolic AcidosisNormalDKA, renal failure, diarrhea
Metabolic AlkalosisNormalVomiting, NG suction, diuretics

4. COAGULATION STUDIES

Lab TestNormal RangeTherapeutic RangeDrug MonitoredNCLEX Pearl
PT (Prothrombin Time)11–12.5 seconds16–31 sec (1.5–2.5× normal)WarfarinAntidote: Vitamin K (slow) or FFP (fast)
INR0.8–1.12.0–3.0 (most); 2.5–3.5 (mechanical valves)WarfarinHold warfarin if INR >3.0; reverse with Vitamin K
aPTT30–40 seconds45–80 sec (1.5–2× normal)Heparin (unfractionated)Antidote: Protamine sulfate
Fibrinogen200–400 mg/dLLow in DIC, liver failure
D-dimer<0.5 µg/mLElevated in DVT, PE, DIC (high sensitivity, low specificity)
Bleeding Time2–9 minutesMeasures platelet function

5. LIVER FUNCTION TESTS (LFTs)

Lab TestNormal RangeElevated Suggests
AST10–40 U/LLiver cell damage, MI, muscle injury
ALT7–56 U/LLiver-specific damage (most sensitive)
ALP44–147 U/LCholestasis, bone disease
Total Bilirubin0.1–1.2 mg/dLJaundice appears >2.5; hepatitis, obstruction, hemolysis
Direct Bilirubin0–0.3 mg/dLObstructive jaundice (post-hepatic)
Albumin3.5–5.0 g/dLLow = liver failure, malnutrition, nephrotic syndrome
Total Protein6.3–8.2 g/dLLow = liver disease, malnutrition
Ammonia15–45 µg/dLElevated in hepatic encephalopathy; diet restriction + lactulose

6. CARDIAC MARKERS

Lab TestNormal RangeNCLEX Pearl
Troponin I<0.04 ng/mLRises 3–6 hrs after MI; peaks 14–18 hrs; most specific cardiac marker
Troponin T<0.01 ng/mLAny elevation is abnormal; remains elevated 7–10 days
CK-MB<5% of total CKRises 4–8 hrs; peaks 12–24 hrs; returns to normal in 48–72 hrs
BNP<100 pg/mL>100 = heart failure likely; >400 = strong indicator
NT-proBNP<125 pg/mL (<75 yrs)Used to diagnose and monitor heart failure
Myoglobin<90 µg/LEarliest MI marker (rises 1–3 hrs) but not specific

7. LIPID PANEL

Lab TestDesirable RangeNCLEX Pearl
Total Cholesterol<200 mg/dL200–239 = borderline; ≥240 = high
LDL<100 mg/dL (optimal)"Bad" cholesterol; target <70 in high-risk patients
HDL (male)35–65 mg/dL"Good" cholesterol — HIGHER is better
HDL (female)35–80 mg/dL<40 (male) or <50 (female) = cardiovascular risk factor
Triglycerides<150 mg/dL≥200 = high; ≥500 = pancreatitis risk

8. THYROID FUNCTION TESTS

Lab TestNormal RangeLow SuggestsHigh Suggests
TSH0.4–4.0 mIU/LHyperthyroidism (T4 suppressing TSH)Hypothyroidism
Free T40.8–1.8 ng/dLHypothyroidismHyperthyroidism
Total T45–12 µg/dLHypothyroidismHyperthyroidism
Total T370–205 ng/dLHypothyroidismHyperthyroidism
NCLEX Tip: TSH is the best initial screening test for thyroid disorders. TSH and T4 move in opposite directions in primary thyroid disease.

9. DRUG THERAPEUTIC LEVELS (High-Alert Medications)

DrugTherapeutic RangeToxic LevelNCLEX Action if Toxic
Digoxin0.5–2.0 ng/mL>2.0 ng/mLHold drug; check K⁺ (hypokalemia worsens toxicity); monitor ECG
Lithium0.6–1.2 mEq/L>1.5 mEq/LHold; push fluids; check renal function
Phenytoin (Dilantin)10–20 µg/mL>20 µg/mLHold; check for nystagmus, ataxia, confusion
Theophylline10–20 µg/mL>20 µg/mLHold; monitor cardiac rhythm
Vancomycin (trough)10–20 µg/mL>20 µg/mLNephrotoxic + ototoxic; check BUN/Cr
Carbamazepine4–12 µg/mL>12 µg/mLHold; monitor CBC (aplastic anemia risk)
Valproic Acid50–100 µg/mL>100 µg/mLHold; check liver function
Cyclosporine100–400 ng/mL>400 ng/mLNephrotoxic; check Cr before dose

10. URINE STUDIES

Lab TestNormal RangeNCLEX Pearl
Urine Specific Gravity1.010–1.030<1.010 = dilute (overhydrated, DI); >1.030 = concentrated (dehydrated, SIADH)
Urine pH4.5–8.0Acidic urine (<5) in acidosis, starvation; alkaline in UTI, vegetarian diet
Creatinine Clearance85–135 mL/minMeasures true GFR; decreases with renal disease and aging
Urine Protein<150 mg/24 hr>300 mg/24 hr in pregnancy = preeclampsia; heavy proteinuria = nephrotic syndrome
Urine GlucoseNegativePresent when blood glucose >180 mg/dL (renal threshold)
Urine KetonesNegativePresent in DKA, starvation, low-carb diet

11. IRON STUDIES

Lab TestNormal RangeLow SuggestsHigh Suggests
Serum Iron50–150 µg/dLIron-deficiency anemiaHemochromatosis, hemolysis
TIBC (Total Iron Binding Capacity)250–370 µg/dLAnemia of chronic diseaseIron-deficiency anemia
Ferritin12–300 ng/mLIron-deficiencyHemochromatosis, inflammation, liver disease
Transferrin Saturation20–50%Iron deficiency if <20%Hemochromatosis if >50%

12. MISCELLANEOUS HIGH-YIELD VALUES

Lab TestNormal RangeNCLEX Pearl
ESR (male)0–15 mm/hrNonspecific inflammatory marker; elevated in RA, lupus, infection
ESR (female)0–20 mm/hr
CRP<1.0 mg/dLMore sensitive/faster than ESR; used for cardiac risk stratification
Prealbumin15–36 mg/dLBetter short-term nutritional marker than albumin (half-life 2 days)
Serum Osmolality285–295 mOsm/kgLow = overhydration/SIADH; High = dehydration/hyperglycemia/DI
Lactic Acid0.7–2.1 mmol/L>2 = tissue hypoperfusion; >4 = severe sepsis/shock
Ammonia15–45 µg/dLElevated in hepatic encephalopathy
Lipase0–160 U/LElevated in pancreatitis (more specific than amylase)
Amylase25–125 U/LElevated in pancreatitis, parotitis

NCLEX STRATEGY SUMMARY

When you see…Think…Do…
K⁺ >5.5 or <2.5Cardiac arrhythmia riskECG first; notify provider
Platelets <50,000Bleeding precautionsAvoid invasive procedures; no IM injections
INR >3.0 on warfarinSupratherapeutic anticoagulationHold warfarin; notify provider
aPTT >80 on heparinBleeding riskHold heparin; check for bleeding
Na⁺ <120Severe hyponatremiaSeizure precautions; careful correction (not faster than 8-10 mEq/L/24hr)
Troponin elevatedAcute myocardial injury12-lead ECG; notify provider immediately
Digoxin >2.0 + low K⁺Digoxin toxicityHold dig; replace K⁺; ECG monitoring
pH <7.35AcidosisDetermine if respiratory (↑PaCO₂) or metabolic (↓HCO₃)
Glucose <40Critical hypoglycemiaD50W IV (conscious: OJ/glucose tabs)
WBC >30,000Severe infection or leukemiaBlood cultures before antibiotics

Generate a printable NCLEX lab values cheat sheet PDF

Running Command

mkdir -p /home/daytona/workspace/nclex-labs && python3 -c "from reportlab.lib.pa…

Writing File

~/nclex-labs/generate_nclex_cheatsheet.py

from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Lab_Values_Cheat_Sheet.pdf"

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY     = colors.HexColor("#1B2A4A")
TEAL     = colors.HexColor("#0B7A75")
ORANGE   = colors.HexColor("#D9622B")
RED      = colors.HexColor("#B03A2E")
GREEN    = colors.HexColor("#1D6A39")
PURPLE   = colors.HexColor("#5B2C6F")
GOLD     = colors.HexColor("#9A7D0A")
BLUE     = colors.HexColor("#1A5276")
PINK     = colors.HexColor("#7B2D8B")
LBLUE    = colors.HexColor("#EAF4FB")
LGREEN   = colors.HexColor("#EAFAF1")
LORANGE  = colors.HexColor("#FEF5E7")
LPURPLE  = colors.HexColor("#F5EEF8")
LRED     = colors.HexColor("#FDEDEC")
LTEAL    = colors.HexColor("#E8F8F5")
LGOLD    = colors.HexColor("#FDFAE5")
WHITE    = colors.white
DGREY    = colors.HexColor("#2C3E50")
MGREY    = colors.HexColor("#BDC3C7")
LGREY    = colors.HexColor("#F4F6F9")

def PS(name, **kw):
    defaults = dict(fontName="Helvetica", fontSize=7.5, textColor=DGREY,
                    leading=10, spaceAfter=0, spaceBefore=0)
    defaults.update(kw)
    return ParagraphStyle(name, **defaults)

hdr_ps   = PS("h", fontName="Helvetica-Bold", fontSize=7.8, textColor=WHITE)
bold_ps  = PS("b", fontName="Helvetica-Bold", fontSize=7.5, textColor=NAVY)
red_ps   = PS("r", fontName="Helvetica-Bold", fontSize=7.3, textColor=RED)
body_ps  = PS("bd", fontSize=7.3, textColor=DGREY)
ctr_ps   = PS("c", fontSize=7.3, textColor=DGREY, alignment=TA_CENTER)
ctr_b_ps = PS("cb", fontName="Helvetica-Bold", fontSize=7.3,
              textColor=DGREY, alignment=TA_CENTER)

def C(txt, ps=None, **kw):
    if ps is None:
        ps = PS("_", **kw)
    return Paragraph(str(txt), ps)

def sec_hdr(label, bg):
    tbl = Table([[Paragraph(label,
                  PS("sh", fontName="Helvetica-Bold", fontSize=8.5,
                     textColor=WHITE, alignment=TA_CENTER))]],
                colWidths=[7.5*inch])
    tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("TOPPADDING",(0,0),(-1,-1),4),
        ("BOTTOMPADDING",(0,0),(-1,-1),4),
    ]))
    return tbl

BASE = [
    ("FONTNAME",       (0,0),(-1,-1), "Helvetica"),
    ("FONTSIZE",       (0,0),(-1,-1), 7.3),
    ("ROWBACKGROUNDS", (0,1),(-1,-1), [LGREY, WHITE]),
    ("GRID",           (0,0),(-1,-1), 0.35, MGREY),
    ("VALIGN",         (0,0),(-1,-1), "TOP"),
    ("TOPPADDING",     (0,0),(-1,-1), 3),
    ("BOTTOMPADDING",  (0,0),(-1,-1), 3),
    ("LEFTPADDING",    (0,0),(-1,-1), 4),
    ("RIGHTPADDING",   (0,0),(-1,-1), 4),
    ("BACKGROUND",     (0,0),(-1,0),  DGREY),
    ("TEXTCOLOR",      (0,0),(-1,0),  WHITE),
    ("FONTNAME",       (0,0),(-1,0),  "Helvetica-Bold"),
    ("FONTSIZE",       (0,0),(-1,0),  7.5),
    ("ALIGN",          (0,0),(-1,0),  "CENTER"),
]

def mktbl(data, col_w, extra=None):
    t = Table(data, colWidths=col_w, repeatRows=1)
    style = list(BASE)
    if extra:
        style += extra
    t.setStyle(TableStyle(style))
    return t

def row(vals, ps_list=None):
    if ps_list is None:
        ps_list = [body_ps]*len(vals)
    return [C(v, p) for v, p in zip(vals, ps_list)]

def hrow(vals):
    return [C(v, hdr_ps) for v in vals]

# ── Page callbacks ────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = letter

def page_cb(canvas, doc):
    # Top banner
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-50, PAGE_W, 50, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-54, PAGE_W, 4, fill=1, stroke=0)

    canvas.setFont("Helvetica-Bold", 17)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-30,
        "NCLEX LAB VALUES CHEAT SHEET")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-44,
        "Quick Reference  •  Normal Ranges  •  Critical Values  •  Nursing Actions")

    # Footer
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 6.8)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"For educational/NCLEX prep purposes only  •  Always verify with institutional reference ranges  •  Page {doc.page}")

# ── Build story ───────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.75*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
story = []
SP = lambda n=0.07: Spacer(1, n*inch)

# ╔══════════════════════════════════════════════════════════╗
#   TWO-COLUMN LAYOUT via side-by-side tables
# ╚══════════════════════════════════════════════════════════╝
LW = 3.60*inch   # left column width (total inner)
RW = 3.60*inch   # right column
GAP = 0.30*inch

# ── Helper to place two blocks side-by-side ───────────────
def two_col(left_items, right_items):
    lt = Table([[item] for item in left_items],
               colWidths=[LW])
    lt.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),0),
                             ("BOTTOMPADDING",(0,0),(-1,-1),2),
                             ("LEFTPADDING",(0,0),(-1,-1),0),
                             ("RIGHTPADDING",(0,0),(-1,-1),0)]))
    rt = Table([[item] for item in right_items],
               colWidths=[RW])
    rt.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),0),
                             ("BOTTOMPADDING",(0,0),(-1,-1),2),
                             ("LEFTPADDING",(0,0),(-1,-1),0),
                             ("RIGHTPADDING",(0,0),(-1,-1),0)]))
    outer = Table([[lt, Spacer(GAP, 1), rt]],
                  colWidths=[LW, GAP, RW])
    outer.setStyle(TableStyle([
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("TOPPADDING",(0,0),(-1,-1),0),
        ("BOTTOMPADDING",(0,0),(-1,-1),0),
        ("LEFTPADDING",(0,0),(-1,-1),0),
        ("RIGHTPADDING",(0,0),(-1,-1),0),
    ]))
    return outer

# ──────────────────────────────────────────────────────────────────────────────
# LEFT COL BLOCK 1 — CBC
# ──────────────────────────────────────────────────────────────────────────────
cbc_sh = Table([[C("🔴  COMPLETE BLOOD COUNT (CBC)", PS("s",fontName="Helvetica-Bold",
                fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
               colWidths=[LW])
cbc_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),RED),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

CBC_CW = [1.08*inch, 0.82*inch, 0.78*inch, 0.92*inch]
cbc_data = [
    hrow(["TEST","NORMAL","CRITICAL","PEARL"]),
    row(["WBC",            "5,000–10,000 /µL",   "<2K or >30K",   "↑=infection; ↓=neutropenia precautions"]),
    row(["Hgb (M)",        "14–18 g/dL",          "<7 g/dL",       "Transfuse threshold ~7-8 g/dL"]),
    row(["Hgb (F)",        "12–16 g/dL",          "<7 g/dL",       "Hgb × 3 ≈ Hct (quick check)"]),
    row(["Hct (M)",        "42–52%",               "<21% or >66%",  "Low=anemia; High=polycythemia"]),
    row(["Hct (F)",        "37–47%",               "<21% or >66%",  ""]),
    row(["Platelets",      "150K–400K /mm³",       "<50K or >1M",   "<50K=bleed risk; <20K=spontaneous bleed"]),
    row(["MCV",            "80–100 fL",            "—",             "Low=iron def; High=B12/folate def"]),
    row(["Reticulocytes",  "0.5–2%",               "—",             "↑=active RBC production"]),
]
cbc_tbl = mktbl(cbc_data, CBC_CW)

# ──────────────────────────────────────────────────────────────────────────────
# LEFT COL BLOCK 2 — ABGs
# ──────────────────────────────────────────────────────────────────────────────
abg_sh = Table([[C("🫁  ARTERIAL BLOOD GASES (ABGs)", PS("s",fontName="Helvetica-Bold",
                fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
               colWidths=[LW])
abg_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),TEAL),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

ABG_CW = [0.72*inch, 0.82*inch, 1.08*inch, 0.98*inch]
abg_data = [
    hrow(["VALUE","NORMAL","LOW = ACIDOSIS","HIGH = ALKALOSIS"]),
    row(["pH",      "7.35–7.45",   "Acidosis (<7.35)",           "Alkalosis (>7.45)"]),
    row(["PaO₂",    "80–100 mmHg", "Hypoxemia",                  "Hyperoxia"]),
    row(["PaCO₂",   "35–45 mmHg",  "Resp. Alkalosis (hypervent)", "Resp. Acidosis (hypovent)"]),
    row(["HCO₃⁻",   "22–26 mEq/L", "Metabolic Acidosis",         "Metabolic Alkalosis"]),
    row(["SaO₂",    ">95%",        "Hypoxia — intervene",        "—"]),
]
abg_tbl = mktbl(abg_data, ABG_CW)

rome_txt = ("<b>ROME:</b>  <b>R</b>esp <b>O</b>pposite (pH↓ + PaCO₂↑)  |  "
            "<b>M</b>etabolic <b>E</b>qual (pH↓ + HCO₃↓)<br/>"
            "Partially compensated = both values abnormal  |  "
            "Fully compensated = pH normal, both others abnormal")
rome_box = Table([[Paragraph(rome_txt, PS("rm", fontSize=7.1, textColor=BLUE,
                   leading=10, backColor=LBLUE))]],
                 colWidths=[LW])
rome_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LBLUE),
    ("BOX",(0,0),(-1,-1),0.8,TEAL),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5)]))

# ──────────────────────────────────────────────────────────────────────────────
# LEFT COL BLOCK 3 — COAGULATION
# ──────────────────────────────────────────────────────────────────────────────
coag_sh = Table([[C("🩸  COAGULATION STUDIES", PS("s",fontName="Helvetica-Bold",
                fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
               colWidths=[LW])
coag_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),PURPLE),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

COAG_CW = [0.72*inch, 0.72*inch, 0.80*inch, 1.36*inch]
coag_data = [
    hrow(["TEST","NORMAL","THERAPEUTIC","NURSING ACTION"]),
    row(["PT",        "11–12.5 sec",  "16–31 sec\n(warfarin)",   "Antidote: Vit K (slow) or FFP (fast)"]),
    row(["INR",       "0.8–1.1",      "2.0–3.0\n(2.5–3.5 valves)","Hold warfarin if INR >3.0"]),
    row(["aPTT",      "30–40 sec",    "45–80 sec\n(heparin)",     "Antidote: Protamine sulfate"]),
    row(["Fibrinogen","200–400 mg/dL","—",                        "Low in DIC & liver failure"]),
    row(["D-dimer",   "<0.5 µg/mL",  "—",                        "↑ in DVT/PE/DIC (sensitive not specific)"]),
    row(["Platelets", "150K–400K",   "—",                        "<50K: no IM injections or invasive procedures"]),
]
coag_tbl = mktbl(coag_data, COAG_CW)

# ──────────────────────────────────────────────────────────────────────────────
# LEFT COL BLOCK 4 — LIPIDS
# ──────────────────────────────────────────────────────────────────────────────
lip_sh = Table([[C("🫀  LIPID PANEL", PS("s",fontName="Helvetica-Bold",
                fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
               colWidths=[LW])
lip_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),GOLD),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

LIP_CW = [1.0*inch, 0.90*inch, 1.70*inch]
lip_data = [
    hrow(["TEST","DESIRABLE","NCLEX PEARL"]),
    row(["Total Cholesterol","<200 mg/dL",    "200–239=borderline; ≥240=high risk"]),
    row(["LDL",             "<100 mg/dL",     "\"Bad\" chol; target <70 in high-risk; statins lower LDL"]),
    row(["HDL (M)",         "35–65 mg/dL",    "\"Good\" chol — HIGHER IS BETTER"]),
    row(["HDL (F)",         "35–80 mg/dL",    "<40M or <50F = CV risk factor"]),
    row(["Triglycerides",   "<150 mg/dL",     "≥500 = pancreatitis risk; fasting test"]),
]
lip_tbl = mktbl(lip_data, LIP_CW)

# ──────────────────────────────────────────────────────────────────────────────
# RIGHT COL BLOCK 1 — ELECTROLYTES
# ──────────────────────────────────────────────────────────────────────────────
elec_sh = Table([[C("⚡  ELECTROLYTES  (BMP/CMP)", PS("s",fontName="Helvetica-Bold",
                 fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
                colWidths=[RW])
elec_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),BLUE),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

EL_CW = [0.80*inch, 0.78*inch, 0.78*inch, 1.24*inch]
elec_data = [
    hrow(["TEST","NORMAL","CRITICAL","NCLEX PEARL"]),
    row(["Na⁺",   "135–145 mEq/L","<120 or >160","Low=SIADH/HF; High=dehydration/DI"]),
    row(["K⁺",    "3.5–5.0 mEq/L","<2.5 or >6.5","Cardiac arrhythmia at extremes; hold digoxin if <3.5"]),
    row(["Cl⁻",   "98–106 mEq/L", "<80 or >115",  "Reciprocal to HCO₃; low=metabolic alkalosis"]),
    row(["HCO₃⁻", "22–29 mEq/L",  "<10 or >40",   "Low=acidosis (DKA/renal); High=alkalosis (vomiting)"]),
    row(["Ca²⁺",  "8.5–10.2 mg/dL","<6 or >13",   "Low=Chvostek/Trousseau; High=bones/stones/groans"]),
    row(["Mg²⁺",  "1.5–2.5 mEq/L","<1 or >4",    "Low=arrhythmia; Toxic=resp depression (eclampsia)"]),
    row(["PO₄",   "3.0–4.5 mg/dL", "<1.0",        "Inverse to Ca²⁺; low in hyperPTH & DKA treatment"]),
    row(["BUN",   "7–20 mg/dL",    ">100",         "BUN:Cr >20:1 = pre-renal; hold metformin if elevated"]),
    row(["Creatinine","0.6–1.2 mg/dL",">10 (acute)","Check before contrast; rising Cr=AKI"]),
    row(["Glucose","70–100 mg/dL", "<40 or >500",  "HbA1c <5.7% normal; ≥6.5%=DM diagnosis"]),
]
elec_tbl = mktbl(elec_data, EL_CW)

ag_box = Table([[Paragraph(
    "<b>Anion Gap = Na − (Cl + HCO₃)   Normal: 8–12 mEq/L</b><br/>"
    "High AG = MUDPILES: Methanol · Uremia · DKA · Propylene glycol · "
    "Isoniazid · Lactic acidosis · Ethylene glycol · Salicylates",
    PS("ag", fontSize=7, textColor=BLUE, leading=10, backColor=LBLUE))]],
    colWidths=[RW])
ag_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LBLUE),
    ("BOX",(0,0),(-1,-1),0.8,BLUE),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5)]))

# ──────────────────────────────────────────────────────────────────────────────
# RIGHT COL BLOCK 2 — LIVER + CARDIAC
# ──────────────────────────────────────────────────────────────────────────────
liv_sh = Table([[C("🟡  LIVER FUNCTION TESTS", PS("s",fontName="Helvetica-Bold",
                fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
               colWidths=[RW])
liv_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),ORANGE),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

LV_CW = [0.82*inch, 0.70*inch, 1.68*inch]
liv_data = [
    hrow(["TEST","NORMAL","NCLEX PEARL"]),
    row(["AST",          "10–40 U/L",     "Liver, MI, muscle; AST:ALT >2:1 = alcoholic hepatitis"]),
    row(["ALT",          "7–56 U/L",      "Most liver-specific; best marker hepatocellular injury"]),
    row(["ALP",          "44–147 U/L",    "Cholestasis or bone disease; ALP↑ + GGT normal = bone source"]),
    row(["Total Bili",   "0.1–1.2 mg/dL", "Jaundice visible >2.5; hepatitis/obstruction/hemolysis"]),
    row(["Albumin",      "3.5–5.0 g/dL",  "Low=liver failure/malnutrition; corrects Ca²⁺ calculation"]),
    row(["Total Protein","6.3–8.2 g/dL",  "= Albumin + Globulin; low=malnutrition/liver disease"]),
    row(["Ammonia",      "15–45 µg/dL",   "↑=hepatic encephalopathy → lactulose; low-protein diet"]),
]
liv_tbl = mktbl(liv_data, LV_CW)

card_sh = Table([[C("❤️  CARDIAC MARKERS", PS("s",fontName="Helvetica-Bold",
                 fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
                colWidths=[RW])
card_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),RED),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

CARD_CW = [0.85*inch, 0.70*inch, 1.65*inch]
card_data = [
    hrow(["TEST","NORMAL","NCLEX PEARL"]),
    row(["Troponin I",   "<0.04 ng/mL",  "Rises 3–6hr post-MI; peaks 14–18hr; stays elevated 7–10d"]),
    row(["Troponin T",   "<0.01 ng/mL",  "Any elevation = abnormal; most specific for cardiac damage"]),
    row(["CK-MB",        "<5% total CK", "Rises 4–8hr; peaks 12–24hr; returns normal in 48–72hr"]),
    row(["BNP",          "<100 pg/mL",   ">100=HF likely; >400=strong HF indicator"]),
    row(["Myoglobin",    "<90 µg/L",     "Earliest MI marker (1–3hr) but NOT specific"]),
]
card_tbl = mktbl(card_data, CARD_CW)

# ──────────────────────────────────────────────────────────────────────────────
# RIGHT COL BLOCK 3 — THYROID + DRUG LEVELS
# ──────────────────────────────────────────────────────────────────────────────
thy_sh = Table([[C("🦋  THYROID FUNCTION", PS("s",fontName="Helvetica-Bold",
                fontSize=8,textColor=WHITE,alignment=TA_CENTER))]],
               colWidths=[RW])
thy_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),GREEN),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

THY_CW = [0.62*inch, 0.75*inch, 1.08*inch, 1.15*inch]
thy_data = [
    hrow(["TEST","NORMAL","LOW SUGGESTS","HIGH SUGGESTS"]),
    row(["TSH",   "0.4–4.0 mIU/L", "Hyperthyroidism",    "Hypothyroidism → levothyroxine"]),
    row(["Free T4","0.8–1.8 ng/dL","Hypothyroidism",    "Hyperthyroidism → PTU/methimazole"]),
    row(["Total T4","5–12 µg/dL",  "Hypothyroidism",    "Hyperthyroidism"]),
    row(["Total T3","70–205 ng/dL","Hypothyroidism",    "Hyperthyroidism"]),
]
thy_tbl = mktbl(thy_data, THY_CW)

thy_note = Table([[Paragraph(
    "<b>TSH is the BEST initial thyroid screening test.</b>  "
    "TSH and T4 move in OPPOSITE directions in primary thyroid disease.",
    PS("tn", fontSize=7, textColor=GREEN, leading=10, backColor=LGREEN))]],
    colWidths=[RW])
thy_note.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LGREEN),
    ("BOX",(0,0),(-1,-1),0.8,GREEN),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("RIGHTPADDING",(0,0),(-1,-1),5)]))

# ──────────────────────────────────────────────────────────────────────────────
# FULL-WIDTH — DRUG THERAPEUTIC LEVELS
# ──────────────────────────────────────────────────────────────────────────────
drug_sh = Table([[C("💊  DRUG THERAPEUTIC LEVELS  (High-Alert Medications)",
                PS("s",fontName="Helvetica-Bold",fontSize=8.5,
                   textColor=WHITE,alignment=TA_CENTER))]],
                colWidths=[7.5*inch])
drug_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),PURPLE),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

DR_CW = [0.90*inch, 0.88*inch, 0.85*inch, 1.30*inch, 3.57*inch]
drug_data = [
    hrow(["DRUG","THERAPEUTIC","TOXIC LEVEL","ANTIDOTE","NURSING ACTION IF TOXIC"]),
    row(["Digoxin",       "0.5–2.0 ng/mL",  ">2.0 ng/mL",   "Digibind (Digoxin Immune Fab)",
         "Hold drug; check K⁺ (hypoK worsens toxicity); ECG monitoring; signs: N/V, yellow-green halos"]),
    row(["Lithium",       "0.6–1.2 mEq/L",  ">1.5 mEq/L",   "None (supportive)",
         "Hold lithium; push fluids; check renal function; signs: tremor, ataxia, confusion"]),
    row(["Phenytoin",     "10–20 µg/mL",    ">20 µg/mL",     "None (supportive)",
         "Hold drug; monitor for nystagmus, ataxia, diplopia; check serum level"]),
    row(["Theophylline",  "10–20 µg/mL",    ">20 µg/mL",     "None (supportive)",
         "Hold drug; ECG monitoring; signs: tachycardia, nausea, seizures"]),
    row(["Vancomycin",    "10–20 µg/mL\n(trough)", ">20 µg/mL","None (supportive)",
         "Hold dose; nephrotoxic + ototoxic; check BUN/Cr; notify prescriber"]),
    row(["Heparin (UFH)", "aPTT 45–80 sec", "aPTT >80 sec",  "Protamine sulfate",
         "Hold infusion; check for bleeding; 1 mg protamine per 100 units heparin given"]),
    row(["Warfarin",      "INR 2.0–3.0",    "INR >3.0",      "Vitamin K / FFP",
         "Hold warfarin; Vit K for slow reversal; FFP for immediate reversal; monitor for bleeding"]),
    row(["Carbamazepine", "4–12 µg/mL",     ">12 µg/mL",     "None (supportive)",
         "Hold drug; CBC (aplastic anemia risk); liver function tests"]),
]
drug_tbl = mktbl(drug_data, DR_CW)

# ──────────────────────────────────────────────────────────────────────────────
# FULL-WIDTH — CRITICAL VALUES + NURSING ACTIONS
# ──────────────────────────────────────────────────────────────────────────────
crit_sh = Table([[C("🚨  CRITICAL VALUES — IMMEDIATE NURSING ACTION REQUIRED",
                PS("s",fontName="Helvetica-Bold",fontSize=8.5,
                   textColor=WHITE,alignment=TA_CENTER))]],
                colWidths=[7.5*inch])
crit_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#922B21")),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

CR_CW = [0.85*inch, 1.05*inch, 0.80*inch, 4.80*inch]
crit_data = [
    hrow(["LAB","CRITICAL VALUE","CONCERN","PRIORITY NURSING ACTION"]),
    row(["K⁺",           ">6.5 or <2.5",  "Cardiac arrest",   "STAT ECG; calcium gluconate IV (if >6.5); insulin+dextrose; notify MD immediately"]),
    row(["Na⁺",          "<120 or >160",  "Seizures/coma",    "Seizure precautions; slow correction (max 8–10 mEq/L/24hr); frequent neuro checks"]),
    row(["Glucose",      "<40 or >500",   "Brain damage/DKA", "<40: D50W IV or OJ/glucose tabs if conscious; >500: insulin protocol + fluids"]),
    row(["Ca²⁺",         "<6.0 or >13",   "Tetany/arrest",    "Low: IV calcium gluconate; High: IV fluids + loop diuretic + bisphosphonate"]),
    row(["Troponin",     "Any elevation", "Acute MI",         "12-lead ECG immediately; notify MD; prepare for ACS protocol; aspirin if ordered"]),
    row(["Hgb",          "<7 g/dL",       "Severe anemia",    "Assess symptoms; anticipate transfusion order; type & screen; O₂ therapy"]),
    row(["Platelets",    "<50,000",        "Bleeding risk",    "Bleeding precautions; no IM injections; soft toothbrush; avoid aspirin/NSAIDs"]),
    row(["INR",          ">3.0",           "Hemorrhage risk",  "Hold warfarin; assess for active bleeding; notify MD; prepare Vit K or FFP"]),
    row(["pH",           "<7.20 or >7.60","Organ failure",    "Notify MD immediately; ABG confirmation; prepare for ICU transfer"]),
    row(["Creatinine",   "Acute >10",     "Renal failure",    "I&O every hour; hold nephrotoxic drugs; nephrology consult; assess dialysis need"]),
    row(["Ammonia",      ">100 µg/dL",    "Hepatic enceph.",  "Neuro checks; lactulose; low-protein diet; fall/seizure precautions"]),
]
crit_tbl = mktbl(crit_data, CR_CW, extra=[
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LRED, WHITE])])

# ──────────────────────────────────────────────────────────────────────────────
# FULL-WIDTH — URINE + MISC
# ──────────────────────────────────────────────────────────────────────────────
misc_sh = Table([[C("🔬  URINE STUDIES + MISCELLANEOUS HIGH-YIELD VALUES",
                PS("s",fontName="Helvetica-Bold",fontSize=8.5,
                   textColor=WHITE,alignment=TA_CENTER))]],
                colWidths=[7.5*inch])
misc_sh.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),colors.HexColor("#1A5276")),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4)]))

# Two sub-tables side by side
MISC_L_CW = [1.1*inch, 0.85*inch, 1.65*inch]
urine_data = [
    hrow(["URINE TEST","NORMAL","NCLEX PEARL"]),
    row(["Specific Gravity","1.010–1.030","<1.010=dilute(DI/overhydrated); >1.030=concentrated(SIADH/dehydrated)"]),
    row(["pH",              "4.5–8.0",    "Acidic in acidosis/starvation; alkaline in UTI/vegetarian diet"]),
    row(["Protein",         "<150 mg/24h","≥300mg=preeclampsia; heavy proteinuria=nephrotic syndrome"]),
    row(["Glucose",         "Negative",   "Present when blood glucose >180 mg/dL (renal threshold)"]),
    row(["Ketones",         "Negative",   "Present in DKA, starvation, low-carb diet"]),
    row(["Creat. Clearance","85–135 mL/min","Decreases with age and renal disease; true GFR measure"]),
]
urine_tbl = mktbl(urine_data, MISC_L_CW)

MISC_R_CW = [1.1*inch, 0.82*inch, 1.68*inch]
misc_data = [
    hrow(["MISC TEST","NORMAL","NCLEX PEARL"]),
    row(["HbA1c",      "<5.7%",          "5.7–6.4%=prediabetes; ≥6.5%=DM; therapeutic target <7%"]),
    row(["Serum Osmol.","285–295 mOsm/kg","Low=SIADH/overhydration; High=dehydration/hypernatremia"]),
    row(["Lactic Acid", "0.7–2.1 mmol/L", ">2=tissue hypoperfusion; >4=severe sepsis/shock"]),
    row(["Lipase",      "0–160 U/L",      "Best marker for pancreatitis (more specific than amylase)"]),
    row(["Amylase",     "25–125 U/L",     "Pancreatitis, parotitis; less specific than lipase"]),
    row(["Prealbumin",  "15–36 mg/dL",    "Short-term nutritional marker (half-life 2 days)"]),
    row(["BNP",         "<100 pg/mL",     "Best marker for heart failure; guides diuretic therapy"]),
]
misc_tbl = mktbl(misc_data, MISC_R_CW)

misc_combined = Table([[urine_tbl, Spacer(0.30*inch, 1), misc_tbl]],
                      colWidths=[3.60*inch, 0.30*inch, 3.60*inch])
misc_combined.setStyle(TableStyle([
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("TOPPADDING",(0,0),(-1,-1),0),
    ("BOTTOMPADDING",(0,0),(-1,-1),0),
    ("LEFTPADDING",(0,0),(-1,-1),0),
    ("RIGHTPADDING",(0,0),(-1,-1),0),
]))

# ──────────────────────────────────────────────────────────────────────────────
# ASSEMBLE STORY
# ──────────────────────────────────────────────────────────────────────────────
story.append(SP(0.05))

# Two-column: CBC + electrolytes
story.append(two_col(
    [cbc_sh, cbc_tbl, SP(), abg_sh, abg_tbl, SP(0.04), rome_box,
     SP(), coag_sh, coag_tbl, SP(), lip_sh, lip_tbl],
    [elec_sh, elec_tbl, SP(0.04), ag_box,
     SP(), liv_sh, liv_tbl,
     SP(), card_sh, card_tbl,
     SP(), thy_sh, thy_tbl, SP(0.04), thy_note]
))

story.append(SP(0.08))
story.append(drug_sh)
story.append(drug_tbl)

story.append(SP(0.08))
story.append(crit_sh)
story.append(crit_tbl)

story.append(SP(0.08))
story.append(misc_sh)
story.append(misc_combined)

# ── Build ─────────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=page_cb, onLaterPages=page_cb)
print("SUCCESS:", OUTPUT)
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_nclex_cheatsheet.py 2>…

Editing File

~/nclex-labs/generate_nclex_cheatsheet.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/nclex-labs/generate_nclex_cheatsheet.py'
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_nclex_cheatsheet.py 2>…

Running Command

ls -lh /home/daytona/workspace/nclex-labs/NCLEX_Lab_Values_Cheat_Sheet.pdf

Your cheat sheet is ready to download and print!

NCLEX Lab Values Cheat Sheet

PDF Document · PDF


What's Inside (4-page printable PDF)

Page 1 - Two-column layout:
  • 🔴 CBC (WBC, Hgb, Hct, Platelets, MCV, Retics) with critical values + pearls
  • Electrolytes/BMP (Na, K, Cl, HCO₃, Ca, Mg, PO₄, BUN, Creatinine, Glucose) with Anion Gap formula box
Page 2 - Two-column layout:
  • 🫁 ABGs with ROME mnemonic callout box
  • 🩸 Coagulation (PT/INR/aPTT + therapeutic ranges + antidotes)
  • 💛 Lipid Panel
  • 🟡 Liver Function Tests (AST, ALT, ALP, Bilirubin, Albumin, Ammonia)
  • ❤️ Cardiac Markers (Troponin I/T, CK-MB, BNP, Myoglobin)
  • 🦋 Thyroid (TSH, T3, T4) with TSH screening tip
Page 3 - Full-width:
  • 💊 Drug Therapeutic Levels - 8 high-alert drugs (Digoxin, Lithium, Phenytoin, Theophylline, Vancomycin, Heparin, Warfarin, Carbamazepine) with antidotes and nursing actions
Page 4 - Full-width:
  • 🚨 Critical Values - 11 lab emergencies with exact thresholds and priority nursing actions
  • 🔬 Urine Studies + Miscellaneous (HbA1c, lactic acid, lipase, BNP, serum osmolality)
Print tip: Print on letter-size paper, double-sided, for a compact 2-sheet reference set.

Create flash cards using these lab values

Writing File

~/nclex-labs/generate_flashcards.py

"""
NCLEX Lab Values Flashcards — ReportLab
Layout: 3 × 3 grid per page, each card 2.5" × 3.3"
Pages alternate FRONT (question) then BACK (answer) so they can be
printed double-sided (flip on short edge) and cut out.
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import PageBreak

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Lab_Flashcards.pdf"

# ── Dimensions ────────────────────────────────────────────────────────────────
CARD_W  = 2.50 * inch
CARD_H  = 3.30 * inch
COLS    = 3
ROWS    = 3
PER_PAGE = COLS * ROWS   # 9 cards per page

# ── Colour palette ────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#1B2A4A")
TEAL   = colors.HexColor("#0B7A75")
RED    = colors.HexColor("#B03A2E")
ORANGE = colors.HexColor("#D9622B")
GREEN  = colors.HexColor("#1D6A39")
PURPLE = colors.HexColor("#5B2C6F")
GOLD   = colors.HexColor("#9A7D0A")
BLUE   = colors.HexColor("#1A5276")
PINK   = colors.HexColor("#7B2D8B")
WHITE  = colors.white
LGREY  = colors.HexColor("#F4F6F9")
DGREY  = colors.HexColor("#2C3E50")
MGREY  = colors.HexColor("#BDC3C7")

# Category → accent colour
CAT_COLOR = {
    "CBC":       RED,
    "ELECTRO":   BLUE,
    "ABG":       TEAL,
    "COAG":      PURPLE,
    "LIVER":     ORANGE,
    "CARDIAC":   RED,
    "THYROID":   GREEN,
    "DRUG":      PINK,
    "LIPID":     GOLD,
    "URINE":     NAVY,
    "MISC":      DGREY,
}

def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY,
                leading=11, alignment=TA_CENTER)
    base.update(kw)
    return ParagraphStyle(name, **base)

# ── Raw flashcard data ────────────────────────────────────────────────────────
# Each entry: (category, question_front, answer_back)
CARDS = [
    # ── CBC ──────────────────────────────────────────────────────────────────
    ("CBC",
     "What is the normal\nWBC count?",
     "5,000 – 10,000 /µL\n\n"
     "↑ WBC = infection / inflammation\n"
     "↓ WBC < 2,500 = neutropenia\n"
     "→ protective isolation precautions"),

    ("CBC",
     "Normal Hemoglobin\n(male vs female)?",
     "Male:   14 – 18 g/dL\n"
     "Female: 12 – 16 g/dL\n\n"
     "Critical: < 7 g/dL\n"
     "Transfusion threshold ≈ 7–8 g/dL\n"
     "Quick check: Hgb × 3 ≈ Hct"),

    ("CBC",
     "Normal Hematocrit\n(male vs female)?",
     "Male:   42 – 52%\n"
     "Female: 37 – 47%\n\n"
     "Critical: < 21% or > 66%\n"
     "Low = anemia\n"
     "High = polycythemia / dehydration"),

    ("CBC",
     "Normal Platelet count\nand critical values?",
     "Normal: 150,000 – 400,000 /mm³\n\n"
     "Critical: < 50,000 or > 1,000,000\n\n"
     "< 50,000 → bleeding precautions;\n"
     "no IM injections\n"
     "< 20,000 → spontaneous bleeding risk"),

    ("CBC",
     "What does MCV tell you\nand what is normal?",
     "Normal MCV: 80 – 100 fL\n\n"
     "↓ Low MCV (< 80) = Microcytic\n"
     "→ Iron-deficiency anemia, thalassemia\n\n"
     "↑ High MCV (> 100) = Macrocytic\n"
     "→ B12 or Folate deficiency"),

    # ── ELECTROLYTES ─────────────────────────────────────────────────────────
    ("ELECTRO",
     "Normal Sodium (Na⁺)\nand critical levels?",
     "Normal: 135 – 145 mEq/L\n\n"
     "Critical: < 120 or > 160\n\n"
     "↓ Hyponatremia → SIADH, HF, cirrhosis\n"
     "↑ Hypernatremia → dehydration, DI\n"
     "Correct Na max 8–10 mEq/L per 24 hr"),

    ("ELECTRO",
     "Normal Potassium (K⁺)\nand critical levels?",
     "Normal: 3.5 – 5.0 mEq/L\n\n"
     "Critical: < 2.5 or > 6.5\n\n"
     "↓ Hypokalemia → diuretics, vomiting\n"
     "↑ Hyperkalemia → AKI, acidosis\n"
     "Hold digoxin if K⁺ < 3.5\n"
     "> 6.5 → STAT ECG!"),

    ("ELECTRO",
     "Normal Calcium (Ca²⁺)\nand clinical signs?",
     "Normal: 8.5 – 10.2 mg/dL\n\n"
     "Critical: < 6.0 or > 13.0\n\n"
     "↓ Hypocalcemia → Chvostek's sign,\n"
     "Trousseau's sign, tetany\n"
     "↑ Hypercalcemia → 'Bones, Stones,\n"
     "Groans & Moans'; malignancy, hyperPTH"),

    ("ELECTRO",
     "Normal Magnesium (Mg²⁺)\nand nursing concern?",
     "Normal: 1.5 – 2.5 mEq/L\n\n"
     "Critical: < 1.0 or > 4.0\n\n"
     "↓ Low Mg → arrhythmia, seizures\n"
     "↑ High Mg (toxicity) → loss of DTRs,\n"
     "respiratory depression\n"
     "(esp. Mg sulfate in eclampsia)"),

    ("ELECTRO",
     "What is the Anion Gap\nformula and normal?",
     "AG = Na⁺ − (Cl⁻ + HCO₃⁻)\n\n"
     "Normal: 8 – 12 mEq/L\n\n"
     "High AG = MUDPILES:\n"
     "Methanol · Uremia · DKA\n"
     "Propylene glycol · Isoniazid\n"
     "Lactic acidosis · EG · Salicylates"),

    ("ELECTRO",
     "Normal BUN and\nBUN:Cr ratio significance?",
     "Normal BUN: 7 – 20 mg/dL\n\n"
     "BUN:Cr Ratio:\n"
     "> 20:1 → Pre-renal (dehydration,\n"
     "         GI bleed, heart failure)\n"
     "10–20:1 → Normal\n"
     "< 10:1 → Intrinsic renal / liver failure\n"
     "Hold metformin if BUN ↑"),

    ("ELECTRO",
     "Normal Creatinine\nand its clinical use?",
     "Normal: 0.6 – 1.2 mg/dL\n\n"
     "Critical: acute rise > 10 mg/dL\n\n"
     "Best routine marker of renal function\n"
     "Calculates eGFR\n"
     "Always check BEFORE contrast dye\n"
     "Rising Cr → hold nephrotoxic drugs"),

    # ── ABGs ─────────────────────────────────────────────────────────────────
    ("ABG",
     "Normal ABG values\n(all 5 components)?",
     "pH:      7.35 – 7.45\n"
     "PaO₂:    80 – 100 mmHg\n"
     "PaCO₂:   35 – 45 mmHg\n"
     "HCO₃⁻:  22 – 26 mEq/L\n"
     "SaO₂:    > 95%\n\n"
     "pH < 7.35 = Acidosis\n"
     "pH > 7.45 = Alkalosis"),

    ("ABG",
     "What causes Respiratory\nAcidosis and Alkalosis?",
     "Resp. Acidosis (↓pH, ↑PaCO₂):\n"
     "COPD, hypoventilation, opioids,\n"
     "sedation, airway obstruction\n\n"
     "Resp. Alkalosis (↑pH, ↓PaCO₂):\n"
     "Hyperventilation, anxiety, PE,\n"
     "mechanical over-ventilation, fever"),

    ("ABG",
     "What causes Metabolic\nAcidosis and Alkalosis?",
     "Metab. Acidosis (↓pH, ↓HCO₃):\n"
     "DKA, renal failure, diarrhea,\n"
     "lactic acidosis (MUDPILES)\n\n"
     "Metab. Alkalosis (↑pH, ↑HCO₃):\n"
     "Vomiting, NG suction, diuretics,\n"
     "antacid excess, Cushing's"),

    ("ABG",
     "What is the ROME rule\nfor ABG interpretation?",
     "R — Respiratory = Opposite\n"
     "    pH ↓ and PaCO₂ ↑  (acidosis)\n"
     "    pH ↑ and PaCO₂ ↓  (alkalosis)\n\n"
     "M — Metabolic = Equal\n"
     "    pH ↓ and HCO₃ ↓  (acidosis)\n"
     "    pH ↑ and HCO₃ ↑  (alkalosis)"),

    # ── COAGULATION ──────────────────────────────────────────────────────────
    ("COAG",
     "Normal PT, INR\nand therapeutic ranges?",
     "PT:    11 – 12.5 seconds\n"
     "INR:   0.8 – 1.1 (normal)\n\n"
     "Therapeutic INR: 2.0 – 3.0\n"
     "(2.5 – 3.5 for mechanical heart valves)\n\n"
     "Drug monitored: WARFARIN\n"
     "Antidote: Vitamin K (slow)\n"
     "          FFP (rapid reversal)"),

    ("COAG",
     "Normal aPTT and\ntherapeutic range?",
     "Normal aPTT: 30 – 40 seconds\n\n"
     "Therapeutic: 45 – 80 seconds\n"
     "(1.5 – 2× normal)\n\n"
     "Drug monitored: HEPARIN (UFH)\n"
     "Antidote: Protamine sulfate\n"
     "(1 mg per 100 units heparin given)"),

    ("COAG",
     "What does D-dimer\nindicate?",
     "Normal D-dimer: < 0.5 µg/mL\n\n"
     "Elevated in:\n"
     "• DVT / Pulmonary Embolism (PE)\n"
     "• DIC (Disseminated Intravascular\n"
     "  Coagulation)\n"
     "• Post-surgery / trauma\n\n"
     "HIGH sensitivity, LOW specificity\n"
     "(good to RULE OUT PE if negative)"),

    # ── LIVER ────────────────────────────────────────────────────────────────
    ("LIVER",
     "Normal AST and ALT\nand significance?",
     "AST: 10 – 40 U/L\n"
     "ALT: 7 – 56 U/L\n\n"
     "ALT = most liver-specific enzyme\n\n"
     "AST:ALT > 2:1 → Alcoholic hepatitis\n"
     "AST or ALT > 1000 → Ischemic hepatitis,\n"
     "acute viral hepatitis, or\n"
     "acetaminophen toxicity"),

    ("LIVER",
     "Normal ALP and Bilirubin\nand what elevation means?",
     "ALP: 44 – 147 U/L\n"
     "Total Bilirubin: 0.1 – 1.2 mg/dL\n\n"
     "↑ ALP + ↑ Bili = Cholestatic pattern\n"
     "→ gallstones, PBC, PSC, obstruction\n\n"
     "↑ ALP + normal GGT = BONE source\n"
     "→ Paget's, bone mets, fracture\n\n"
     "Jaundice visible when Bili > 2.5"),

    ("LIVER",
     "Normal Albumin\nand clinical significance?",
     "Normal: 3.5 – 5.0 g/dL\n\n"
     "Low albumin causes:\n"
     "• Ascites (↓ oncotic pressure)\n"
     "• Edema\n"
     "• Pseudohypocalcemia\n\n"
     "Ca correction formula:\n"
     "Corrected Ca = Measured Ca\n"
     "+ 0.8 × (4 − albumin)"),

    # ── CARDIAC ──────────────────────────────────────────────────────────────
    ("CARDIAC",
     "Normal Troponin levels\nand timing after MI?",
     "Troponin I: < 0.04 ng/mL\n"
     "Troponin T: < 0.01 ng/mL\n\n"
     "Timing after MI:\n"
     "• Rises: 3 – 6 hours\n"
     "• Peaks: 14 – 18 hours\n"
     "• Returns normal: 7 – 10 days\n\n"
     "Most specific cardiac marker\n"
     "Any elevation = ABNORMAL"),

    ("CARDIAC",
     "Normal BNP and\nwhat it indicates?",
     "Normal BNP: < 100 pg/mL\n\n"
     "100–400 → Possible heart failure\n"
     "> 400 → Strong HF indicator\n\n"
     "NT-proBNP (< 75 yrs): < 125 pg/mL\n\n"
     "Used to diagnose and monitor\n"
     "heart failure severity\n"
     "Guides diuretic therapy"),

    ("CARDIAC",
     "What is the earliest\nmarker of MI?",
     "MYOGLOBIN rises first:\n"
     "• Rises: 1 – 3 hours after MI\n"
     "• Normal: < 90 µg/L\n\n"
     "BUT: NOT cardiac-specific\n"
     "(also elevated in muscle injury)\n\n"
     "Order of rise:\n"
     "Myoglobin → CK-MB → Troponin\n"
     "(Troponin = most specific & lasts longest)"),

    # ── THYROID ──────────────────────────────────────────────────────────────
    ("THYROID",
     "Normal TSH and\nhow to interpret it?",
     "Normal TSH: 0.4 – 4.0 mIU/L\n\n"
     "TSH = BEST initial screening test\n\n"
     "↓ Low TSH → Hyperthyroidism\n"
     "(T4 suppressing pituitary TSH)\n\n"
     "↑ High TSH → Hypothyroidism\n"
     "(pituitary working harder)\n\n"
     "TSH and T4 move in OPPOSITE directions"),

    ("THYROID",
     "Normal T3 and T4\nand drug treatments?",
     "Free T4: 0.8 – 1.8 ng/dL\n"
     "Total T4: 5 – 12 µg/dL\n"
     "Total T3: 70 – 205 ng/dL\n\n"
     "Hypothyroidism treatment:\n"
     "→ Levothyroxine (Synthroid)\n"
     "Take on empty stomach; check TSH\n\n"
     "Hyperthyroidism treatment:\n"
     "→ PTU or Methimazole"),

    # ── DRUG LEVELS ──────────────────────────────────────────────────────────
    ("DRUG",
     "Therapeutic & toxic level\nfor DIGOXIN?",
     "Therapeutic: 0.5 – 2.0 ng/mL\n"
     "Toxic: > 2.0 ng/mL\n\n"
     "Antidote: Digibind (Digoxin Fab)\n\n"
     "Toxicity signs:\n"
     "N/V, bradycardia, yellow-green halos,\n"
     "dysrhythmias\n\n"
     "HYPOKALEMIA worsens toxicity!\n"
     "Hold if K⁺ < 3.5 or HR < 60"),

    ("DRUG",
     "Therapeutic & toxic level\nfor LITHIUM?",
     "Therapeutic: 0.6 – 1.2 mEq/L\n"
     "Toxic: > 1.5 mEq/L\n"
     "Severe: > 2.0 mEq/L\n\n"
     "Antidote: None (supportive care)\n\n"
     "Toxicity signs:\n"
     "Tremor, ataxia, confusion,\n"
     "slurred speech, seizures\n\n"
     "Maintain adequate fluid/Na intake"),

    ("DRUG",
     "Therapeutic & toxic level\nfor WARFARIN (INR)?",
     "Therapeutic INR: 2.0 – 3.0\n"
     "(2.5–3.5 for mechanical valves)\n\n"
     "Toxic: INR > 3.0\n\n"
     "Antidote:\n"
     "• Vitamin K — slow reversal\n"
     "• FFP — rapid reversal\n"
     "• Andexanet alfa (Factor Xa)\n\n"
     "Monitor for bleeding signs;\n"
     "educate on consistent vitamin K diet"),

    ("DRUG",
     "Therapeutic & toxic level\nfor HEPARIN (aPTT)?",
     "Therapeutic aPTT: 45 – 80 sec\n"
     "(1.5 – 2× normal)\n\n"
     "Toxic: aPTT > 80 sec\n\n"
     "Antidote: Protamine sulfate\n"
     "(1 mg per 100 units heparin)\n\n"
     "Signs of toxicity: bleeding,\n"
     "hematuria, petechiae, hematoma\n"
     "Monitor platelet count (HIT risk)"),

    ("DRUG",
     "Therapeutic & toxic level\nfor PHENYTOIN?",
     "Therapeutic: 10 – 20 µg/mL\n"
     "Toxic: > 20 µg/mL\n\n"
     "Antidote: None (supportive)\n\n"
     "Toxicity signs (SAND):\n"
     "S — Slurred speech\n"
     "A — Ataxia\n"
     "N — Nystagmus\n"
     "D — Diplopia\n\n"
     "Hold dose; check serum level"),

    ("DRUG",
     "Therapeutic & toxic level\nfor VANCOMYCIN?",
     "Therapeutic trough: 10 – 20 µg/mL\n"
     "Toxic: > 20 µg/mL\n\n"
     "Antidote: None (supportive)\n\n"
     "Side effects:\n"
     "• NEPHROTOXIC → check BUN/Cr\n"
     "• OTOTOXIC → monitor hearing\n"
     "• Red Man Syndrome (rapid infusion)\n"
     "→ slow infusion rate; pre-medicate"),

    # ── LIPIDS ───────────────────────────────────────────────────────────────
    ("LIPID",
     "Desirable lipid levels\n(all four values)?",
     "Total Cholesterol: < 200 mg/dL\n"
     "LDL:  < 100 mg/dL (optimal)\n"
     "      < 70 mg/dL (high-risk pts)\n"
     "HDL:  > 60 mg/dL (protective)\n"
     "      < 40M / < 50F = CV risk\n"
     "Triglycerides: < 150 mg/dL\n\n"
     "HDL = 'Good' — HIGHER is BETTER\n"
     "LDL = 'Bad' — LOWER is BETTER"),

    ("LIPID",
     "When do triglycerides\nbecome dangerous?",
     "Normal: < 150 mg/dL\n"
     "Borderline: 150 – 199\n"
     "High: 200 – 499\n"
     "Very High: ≥ 500 mg/dL\n\n"
     "≥ 500 mg/dL = PANCREATITIS RISK\n\n"
     "Causes: DM, alcohol, hypothyroidism,\n"
     "obesity, certain medications\n"
     "Must be FASTING test for accuracy"),

    # ── URINE ────────────────────────────────────────────────────────────────
    ("URINE",
     "Normal urine specific\ngravity and meaning?",
     "Normal: 1.010 – 1.030\n\n"
     "< 1.010 = DILUTE urine\n"
     "→ Overhydration, DI (diabetes insipidus),\n"
     "  renal failure\n\n"
     "> 1.030 = CONCENTRATED urine\n"
     "→ Dehydration, SIADH,\n"
     "  hypovolemia, fever"),

    ("URINE",
     "Normal HbA1c and\ndiabetes thresholds?",
     "Normal: < 5.7%\n"
     "Pre-diabetes: 5.7 – 6.4%\n"
     "Diabetes: ≥ 6.5%\n\n"
     "Therapeutic target for diabetics: < 7%\n\n"
     "Represents average blood glucose\n"
     "over the past 2–3 months\n"
     "Not affected by fasting status"),

    # ── MISCELLANEOUS ─────────────────────────────────────────────────────────
    ("MISC",
     "Normal Lactic Acid\nand clinical meaning?",
     "Normal: 0.7 – 2.1 mmol/L\n\n"
     "> 2.0 mmol/L → Tissue hypoperfusion,\n"
     "sepsis, shock, liver failure\n\n"
     "> 4.0 mmol/L → Severe sepsis / shock\n"
     "→ ICU admission; fluid resuscitation;\n"
     "  identify and treat source\n\n"
     "Lactic acidosis = HIGH anion gap"),

    ("MISC",
     "Normal Lipase\nvs Amylase?",
     "Lipase: 0 – 160 U/L\n"
     "Amylase: 25 – 125 U/L\n\n"
     "Lipase = MORE SPECIFIC for pancreatitis\n"
     "(not elevated in parotitis)\n\n"
     "Amylase = less specific\n"
     "(also elevated in parotitis,\n"
     "bowel obstruction, ectopic pregnancy)\n\n"
     "Order LIPASE first for pancreatitis"),

    ("MISC",
     "Normal serum osmolality\nand interpretation?",
     "Normal: 285 – 295 mOsm/kg\n\n"
     "↓ Low (<280) → SIADH, overhydration,\n"
     "hyponatremia\n\n"
     "↑ High (>295) → Dehydration,\n"
     "hypernatremia, hyperglycemia, DI,\n"
     "uremia, ethanol ingestion\n\n"
     "Quick formula:\n"
     "Osm ≈ 2(Na) + Glucose/18 + BUN/2.8"),

    ("MISC",
     "Critical value for Ammonia\nand nursing actions?",
     "Normal: 15 – 45 µg/dL\n\n"
     "Elevated in: Hepatic encephalopathy,\n"
     "liver failure, Reye syndrome\n\n"
     "Nursing actions:\n"
     "• Neuro checks (LOC, asterixis)\n"
     "• Administer lactulose\n"
     "• Low-protein diet (acute)\n"
     "• Fall and seizure precautions\n"
     "• Safety environment"),
]

# ── Card rendering ────────────────────────────────────────────────────────────
def make_card_front(cat, question):
    """Returns a Table cell representing the FRONT of a flashcard."""
    accent = CAT_COLOR.get(cat, NAVY)
    cat_label = {
        "CBC": "CBC", "ELECTRO": "ELECTROLYTES", "ABG": "BLOOD GASES",
        "COAG": "COAGULATION", "LIVER": "LIVER FUNCTION",
        "CARDIAC": "CARDIAC MARKERS", "THYROID": "THYROID",
        "DRUG": "DRUG LEVELS", "LIPID": "LIPID PANEL",
        "URINE": "URINE / MISC", "MISC": "MISCELLANEOUS",
    }.get(cat, cat)

    cat_p = Paragraph(cat_label, ParagraphStyle("cl",
        fontName="Helvetica-Bold", fontSize=7, textColor=WHITE,
        alignment=TA_CENTER, leading=9))
    q_p   = Paragraph(question.replace("\n","<br/>"),
                      ParagraphStyle("qp", fontName="Helvetica-Bold",
                          fontSize=10, textColor=NAVY,
                          alignment=TA_CENTER, leading=14))
    hint  = Paragraph("▼  answer on back  ▼",
                      ParagraphStyle("ht", fontName="Helvetica-Oblique",
                          fontSize=7, textColor=colors.HexColor("#95A5A6"),
                          alignment=TA_CENTER, leading=9))

    inner = Table(
        [[cat_p], [Spacer(1, 0.08*inch)], [q_p], [Spacer(1, 0.1*inch)], [hint]],
        colWidths=[CARD_W - 0.18*inch]
    )
    inner.setStyle(TableStyle([
        ("ALIGN",         (0,0),(-1,-1), "CENTER"),
        ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
        ("TOPPADDING",    (0,0),(-1,-1), 2),
        ("BOTTOMPADDING", (0,0),(-1,-1), 2),
        ("LEFTPADDING",   (0,0),(-1,-1), 4),
        ("RIGHTPADDING",  (0,0),(-1,-1), 4),
        ("BACKGROUND",    (0,0),(0,0),   accent),
        ("TOPPADDING",    (0,0),(0,0),   4),
        ("BOTTOMPADDING", (0,0),(0,0),   4),
    ]))
    return inner

def make_card_back(cat, answer):
    """Returns a Table cell representing the BACK of a flashcard."""
    accent = CAT_COLOR.get(cat, NAVY)
    ans_p  = Paragraph(answer.replace("\n","<br/>"),
                       ParagraphStyle("ap", fontName="Helvetica",
                           fontSize=8.2, textColor=DGREY,
                           alignment=TA_LEFT, leading=12))

    inner = Table(
        [[ans_p]],
        colWidths=[CARD_W - 0.22*inch]
    )
    inner.setStyle(TableStyle([
        ("ALIGN",         (0,0),(-1,-1), "LEFT"),
        ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
        ("TOPPADDING",    (0,0),(-1,-1), 8),
        ("BOTTOMPADDING", (0,0),(-1,-1), 8),
        ("LEFTPADDING",   (0,0),(-1,-1), 8),
        ("RIGHTPADDING",  (0,0),(-1,-1), 6),
    ]))

    # Wrap with coloured left border via outer table
    border = Table([[inner]], colWidths=[CARD_W - 0.10*inch])
    border.setStyle(TableStyle([
        ("LEFTPADDING",   (0,0),(-1,-1), 5),
        ("TOPPADDING",    (0,0),(-1,-1), 0),
        ("BOTTOMPADDING", (0,0),(-1,-1), 0),
        ("RIGHTPADDING",  (0,0),(-1,-1), 0),
        ("LINEBEFORE",    (0,0),(0,-1),  3, accent),
    ]))
    return border

# ── Card cell wrapper (adds border + background) ──────────────────────────────
def card_cell(inner, bg=WHITE):
    t = Table([[inner]], colWidths=[CARD_W], rowHeights=[CARD_H])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0),(-1,-1), bg),
        ("BOX",           (0,0),(-1,-1), 1.2, MGREY),
        ("TOPPADDING",    (0,0),(-1,-1), 6),
        ("BOTTOMPADDING", (0,0),(-1,-1), 6),
        ("LEFTPADDING",   (0,0),(-1,-1), 4),
        ("RIGHTPADDING",  (0,0),(-1,-1), 4),
        ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
        ("ALIGN",         (0,0),(-1,-1), "CENTER"),
    ]))
    return t

def blank_card():
    t = Table([[""]], colWidths=[CARD_W], rowHeights=[CARD_H])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0),(-1,-1), LGREY),
        ("BOX",        (0,0),(-1,-1), 0.5, MGREY),
    ]))
    return t

# ── Build grid pages ──────────────────────────────────────────────────────────
def build_grid_page(cells):
    """Takes a flat list of 9 card-cell Tables, returns a 3×3 Table."""
    while len(cells) < PER_PAGE:
        cells.append(blank_card())
    rows_data = []
    for r in range(ROWS):
        rows_data.append([cells[r*COLS+c] for c in range(COLS)])
    grid = Table(rows_data,
                 colWidths=[CARD_W]*COLS,
                 rowHeights=[CARD_H]*ROWS)
    grid.setStyle(TableStyle([
        ("TOPPADDING",    (0,0),(-1,-1), 2),
        ("BOTTOMPADDING", (0,0),(-1,-1), 2),
        ("LEFTPADDING",   (0,0),(-1,-1), 2),
        ("RIGHTPADDING",  (0,0),(-1,-1), 2),
        ("ALIGN",         (0,0),(-1,-1), "CENTER"),
        ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
    ]))
    return grid

# ── Page callback ─────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = letter

def on_front_page(canvas, doc):
    # tiny top label
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-22, PAGE_W, 22, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 10)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-14,
        "NCLEX LAB VALUES FLASHCARDS  —  FRONT (Questions)")
    canvas.setFont("Helvetica", 7)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 10,
        f"Print double-sided (flip on SHORT edge) then cut along card borders  •  Page {doc.page}")

def on_back_page(canvas, doc):
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-22, PAGE_W, 22, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 10)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-14,
        "NCLEX LAB VALUES FLASHCARDS  —  BACK (Answers)")
    canvas.setFont("Helvetica", 7)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 10,
        f"Print double-sided (flip on SHORT edge) then cut along card borders  •  Page {doc.page}")

# ── Alternate page callback ───────────────────────────────────────────────────
page_types = []   # will be filled: 'F' or 'B'

def on_page(canvas, doc):
    idx = doc.page - 1
    if idx < len(page_types) and page_types[idx] == 'B':
        on_back_page(canvas, doc)
    else:
        on_front_page(canvas, doc)

# ── Assemble story ────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.32*inch, bottomMargin=0.20*inch,
                        leftMargin=0.28*inch, rightMargin=0.28*inch)
story = []

# Chunk cards into pages of 9
chunks = [CARDS[i:i+PER_PAGE] for i in range(0, len(CARDS), PER_PAGE)]

for chunk in chunks:
    # FRONT page
    front_cells = [card_cell(make_card_front(cat, q)) for cat, q, _ in chunk]
    story.append(build_grid_page(front_cells))
    page_types.append('F')
    story.append(PageBreak())

    # BACK page — mirror column order for double-sided printing
    back_cells_raw = [card_cell(make_card_back(cat, a), bg=colors.HexColor("#FDFEFE"))
                      for cat, _, a in chunk]
    # Pad to 9
    while len(back_cells_raw) < PER_PAGE:
        back_cells_raw.append(blank_card())
    # Mirror columns (col 0↔2) for correct flip alignment
    mirrored = []
    for r in range(ROWS):
        row_cells = back_cells_raw[r*COLS:(r+1)*COLS]
        mirrored.extend(reversed(row_cells))
    story.append(build_grid_page(mirrored))
    page_types.append('B')
    story.append(PageBreak())

# Remove final PageBreak
if story and isinstance(story[-1], PageBreak):
    story.pop()

doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}  ({len(CARDS)} cards, {len(chunks)*2} pages)")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_flashcards.py 2>&1

Running Command

ls -lh /home/daytona/workspace/nclex-labs/NCLEX_Lab_Flashcards.pdf

Your flashcard deck is ready!

NCLEX Lab Flashcards

PDF Document · PDF


What's Inside

41 flashcards across 10 pages — organized into color-coded categories:
ColorCategoryCards
🔴 RedCBCWBC, Hgb, Hct, Platelets, MCV
🔵 BlueElectrolytes/BMPNa, K, Ca, Mg, Anion Gap, BUN, Creatinine
🟢 TealABGsNormal values, Resp/Metabolic patterns, ROME rule
🟣 PurpleCoagulationPT/INR, aPTT, D-dimer
🟠 OrangeLiver FunctionAST/ALT, ALP/Bili, Albumin
🔴 RedCardiac MarkersTroponin, BNP, Myoglobin
🌿 GreenThyroidTSH, T3/T4
🩷 PinkDrug LevelsDigoxin, Lithium, Warfarin, Heparin, Phenytoin, Vancomycin
🟡 GoldLipidsCholesterol, LDL/HDL, Triglycerides
🔵 NavyUrine/MiscSpecific gravity, HbA1c, Lactic acid, Lipase, Osmolality, Ammonia

How to Print

  1. Print double-sided, flip on the short edge
  2. Cut along the card borders (3 × 3 grid = 9 cards per sheet)
  3. Each front question lines up with its back answer when printed correctly
  4. Shuffle and test yourself - category color on the front helps if you want to study by topic

Create a pharmacology quick reference guide on common drug-lab value interactions

Finding Sources
Searching the Web

common drug lab value interactions nursing pharmacology monitoring reference 2024

Finding Sources
Finding Sources
Writing File

~/nclex-labs/generate_drug_lab_guide.py

"""
Pharmacology Quick Reference Guide: Drug-Lab Value Interactions
ReportLab PDF — organized by drug class
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, HRFlowable
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT

OUTPUT = "/home/daytona/workspace/nclex-labs/Drug_Lab_Interactions_Guide.pdf"

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#1B2A4A")
TEAL   = colors.HexColor("#0B7A75")
RED    = colors.HexColor("#B03A2E")
ORANGE = colors.HexColor("#D9622B")
GREEN  = colors.HexColor("#1D6A39")
PURPLE = colors.HexColor("#5B2C6F")
GOLD   = colors.HexColor("#9A7D0A")
BLUE   = colors.HexColor("#1A5276")
MAROON = colors.HexColor("#7B241C")
SLATE  = colors.HexColor("#2E4057")
WHITE  = colors.white
DGREY  = colors.HexColor("#2C3E50")
MGREY  = colors.HexColor("#BDC3C7")
LGREY  = colors.HexColor("#F4F6F9")
LRED   = colors.HexColor("#FDEDEC")
LGREEN = colors.HexColor("#EAFAF1")
LBLUE  = colors.HexColor("#EBF5FB")
LORANGE= colors.HexColor("#FEF5E7")
LPURPLE= colors.HexColor("#F5EEF8")

PAGE_W, PAGE_H = letter

def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY, leading=11)
    base.update(kw)
    return ParagraphStyle(name, **base)

hdr_ps  = PS("h", fontName="Helvetica-Bold", fontSize=8, textColor=WHITE)
body_ps = PS("b", fontSize=7.8, textColor=DGREY, leading=11)
bold_ps = PS("bd", fontName="Helvetica-Bold", fontSize=7.8, textColor=NAVY, leading=11)
red_ps  = PS("r", fontName="Helvetica-Bold", fontSize=7.8, textColor=RED, leading=11)
ctr_ps  = PS("c", fontSize=7.8, textColor=DGREY, leading=11,
             alignment=TA_CENTER)

def C(txt, ps=body_ps):
    return Paragraph(str(txt), ps)

BASE_TBL = [
    ("FONTNAME",       (0,0),(-1,-1),  "Helvetica"),
    ("FONTSIZE",       (0,0),(-1,-1),  7.8),
    ("ROWBACKGROUNDS", (0,1),(-1,-1),  [LGREY, WHITE]),
    ("GRID",           (0,0),(-1,-1),  0.35, MGREY),
    ("VALIGN",         (0,0),(-1,-1),  "TOP"),
    ("TOPPADDING",     (0,0),(-1,-1),  3),
    ("BOTTOMPADDING",  (0,0),(-1,-1),  3),
    ("LEFTPADDING",    (0,0),(-1,-1),  5),
    ("RIGHTPADDING",   (0,0),(-1,-1),  5),
    ("BACKGROUND",     (0,0),(-1,0),   DGREY),
    ("TEXTCOLOR",      (0,0),(-1,0),   WHITE),
    ("FONTNAME",       (0,0),(-1,0),   "Helvetica-Bold"),
    ("FONTSIZE",       (0,0),(-1,0),   8),
    ("ALIGN",          (0,0),(-1,0),   "CENTER"),
]

FULL_W = 7.5 * inch
COL_W  = [1.30*inch, 0.95*inch, 0.90*inch, 1.15*inch, 1.55*inch, 1.65*inch]

def hrow(vals):
    return [C(v, hdr_ps) for v in vals]

def mktbl(data, cw=None, extra=None):
    if cw is None: cw = COL_W
    t = Table(data, colWidths=cw, repeatRows=1)
    style = list(BASE_TBL)
    if extra: style += extra
    t.setStyle(TableStyle(style))
    return t

def sec_hdr(label, bg, sub=""):
    lines = [C(label, PS("sh", fontName="Helvetica-Bold", fontSize=10,
                          textColor=WHITE, alignment=TA_CENTER, leading=13))]
    if sub:
        lines.append(C(sub, PS("ss", fontSize=7.5, textColor=colors.HexColor("#D5D8DC"),
                                alignment=TA_CENTER, leading=10)))
    cells = [[item] for item in lines]
    t = Table(cells, colWidths=[FULL_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0),(-1,-1), bg),
        ("TOPPADDING",    (0,0),(-1,-1), 5),
        ("BOTTOMPADDING", (0,0),(-1,-1), 5),
        ("LEFTPADDING",   (0,0),(-1,-1), 8),
    ]))
    return t

def note_box(txt, bg, border_col):
    t = Table([[Paragraph(txt, PS("nb", fontSize=7.5, textColor=DGREY,
                leading=11, backColor=bg))]],
              colWidths=[FULL_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0),(-1,-1), bg),
        ("BOX",           (0,0),(-1,-1), 1, border_col),
        ("TOPPADDING",    (0,0),(-1,-1), 5),
        ("BOTTOMPADDING", (0,0),(-1,-1), 5),
        ("LEFTPADDING",   (0,0),(-1,-1), 8),
        ("RIGHTPADDING",  (0,0),(-1,-1), 8),
    ]))
    return t

SP = lambda n=0.08: Spacer(1, n*inch)

# ── Page banner ───────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-52, PAGE_W, 52, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-56, PAGE_W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 16)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-28,
        "PHARMACOLOGY QUICK REFERENCE: Drug-Lab Value Interactions")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-44,
        "Drugs That Affect Lab Values  •  Required Monitoring  •  Critical Actions & Antidotes")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 7)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"For educational use only  •  Verify with current clinical guidelines & institutional protocols  •  Page {doc.page}")

# ═══════════════════════════════════════════════════════════════════════════════
story = []
SP0 = Spacer(1, 0.10*inch)

MAIN_COLS = ["DRUG / CLASS", "AFFECTED LAB", "DIRECTION", "MECHANISM", "NURSING MONITOR", "ACTION IF ABNORMAL"]

# ╔══════════════════════════════════════════════════════════════════════════╗
#  1. CARDIOVASCULAR DRUGS
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP0)
story.append(sec_hdr("1.  CARDIOVASCULAR DRUGS",
    NAVY, "ACE Inhibitors • ARBs • Diuretics • Digoxin • Statins • Antiarrhythmics"))
story.append(mktbl([
    hrow(MAIN_COLS),
    [C("ACE Inhibitors\n(lisinopril, enalapril,\ncaptopril)", bold_ps),
     C("Potassium (K⁺)\nCreatinine\nBUN"),
     C("↑ K⁺\n↑ Cr\n↑ BUN"),
     C("Block angiotensin II → ↓ aldosterone → K⁺ retention; ↓ GFR in renal artery stenosis"),
     C("K⁺, Cr, BUN at baseline, 1–2 wks after start, then every 3–6 months"),
     C("Hold if K⁺ >5.5 or Cr rises >30% from baseline; notify provider; avoid NSAIDs + ACEi combo")],
    [C("ARBs\n(losartan, valsartan,\nirbesartan)", bold_ps),
     C("Potassium (K⁺)\nCreatinine"),
     C("↑ K⁺\n↑ Cr"),
     C("Same mechanism as ACEi (block AT1 receptor); same renal and K⁺ effects"),
     C("Same as ACEi: K⁺, Cr, BUN at baseline and periodically"),
     C("Do NOT combine ARB + ACEi — doubles hyperkalemia/AKI risk")],
    [C("Loop Diuretics\n(furosemide,\nbumetanide)", bold_ps),
     C("K⁺, Na⁺, Mg²⁺\nBUN, Cr\nUric Acid"),
     C("↓ K⁺, Na⁺, Mg²⁺\n↑ BUN, Cr\n↑ Uric Acid"),
     C("Block Na-K-2Cl in loop of Henle → electrolyte wasting; volume depletion → prerenal azotemia"),
     C("Daily weight, electrolytes, BUN/Cr; watch K⁺ <3.5; magnesium often overlooked"),
     C("Replace K⁺ and Mg²⁺; hold if signs of dehydration; Caution: hypoK worsens digoxin toxicity")],
    [C("Thiazide Diuretics\n(HCTZ,\nchlorthalidone)", bold_ps),
     C("K⁺, Na⁺\nGlucose\nUric Acid, Ca²⁺"),
     C("↓ K⁺, Na⁺\n↑ Glucose\n↑ Uric Acid, ↑ Ca²⁺"),
     C("Na-Cl block in distal tubule; ↑ Ca reabsorption; impair insulin release"),
     C("Electrolytes, glucose, BUN/Cr, uric acid at baseline and periodically"),
     C("Monitor for new/worsening diabetes; hold if K⁺ <3.0; caution in gout")],
    [C("K⁺-Sparing Diuretics\n(spironolactone,\namiloride)", bold_ps),
     C("Potassium (K⁺)\nCreatinine\nTestosterone"),
     C("↑ K⁺\n↑ Cr\n↓ Testosterone (spiro)"),
     C("Block aldosterone receptor or ENaC → K⁺ retention; spironolactone is anti-androgenic"),
     C("K⁺ and Cr at baseline and 1 month after dose change; avoid with ACEi/ARB if possible"),
     C("Hold if K⁺ >5.5; gynecomastia with spironolactone — may need dose adjustment")],
    [C("Digoxin", bold_ps),
     C("Digoxin level\nK⁺\nMg²⁺\nCreatinine"),
     C("Toxic >2.0 ng/mL\n↓ K⁺ worsens\n↓ Mg²⁺ worsens\n↑ Cr → toxicity"),
     C("Narrow therapeutic index; renal clearance — impaired kidneys ↑ drug levels; hypoK/Mg sensitize myocardium"),
     C("Digoxin trough level; K⁺, Mg²⁺, Cr before each dose change and at least q6 months"),
     C("Hold if level >2.0, K⁺ <3.5, HR <60; Antidote: Digibind (Digoxin Immune Fab)")],
    [C("Statins\n(atorvastatin,\nrosuvastatin,\nsimvastatin)", bold_ps),
     C("CK (CPK)\nLFTs (AST/ALT)\nGlucose/HbA1c"),
     C("↑ CK → myopathy\n↑ AST/ALT\n↑ Glucose"),
     C("Inhibit HMG-CoA → ↓ cholesterol; also ↓ CoQ10 → muscle dysfunction; mild hepatotoxic"),
     C("LFTs at baseline; CK if muscle pain reported; HbA1c annually (↑ diabetes risk)"),
     C("Hold if CK >10× ULN (rhabdomyolysis); stop if AST/ALT >3× ULN × 2 readings; drug holiday")],
    [C("Amiodarone", bold_ps),
     C("TFTs (TSH/T4)\nLFTs\nCreatinine\nINR (if on warfarin)"),
     C("↑ or ↓ TSH/T4\n↑ LFTs\n↑ Cr\n↑ INR"),
     C("Contains 37% iodine → thyroid dysfunction (hypo OR hyper); inhibits CYP2C9 → ↑ warfarin effect"),
     C("TFTs every 6 months; LFTs and CXR annually; reduce warfarin dose 30–50% when starting"),
     C("Notify provider for any TSH abnormality; reduce warfarin dose; monitor pulmonary function")],
]))
story.append(SP())
story.append(note_box(
    "<b>KEY CARDIOVASCULAR PEARLS:</b>  "
    "ACEi/ARBs → always check K⁺ and Cr before and after starting.  "
    "Loop diuretics → hypokalemia worsens digoxin toxicity — treat both.  "
    "Amiodarone inhibits CYP2C9/3A4 — can double warfarin effect within days.  "
    "Statins → CK only needed if patient reports muscle pain (not routine).",
    LBLUE, NAVY))

# ╔══════════════════════════════════════════════════════════════════════════╗
#  2. ANTICOAGULANTS & ANTIPLATELETS
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP())
story.append(sec_hdr("2.  ANTICOAGULANTS & ANTIPLATELETS",
    MAROON, "Warfarin • Heparin • DOACs • Aspirin • Clopidogrel"))
story.append(mktbl([
    hrow(MAIN_COLS),
    [C("Warfarin (Coumadin)", bold_ps),
     C("INR\nPT"),
     C("↑ INR, ↑ PT"),
     C("Inhibits Vit K-dependent factors (II, VII, IX, X); many drug and food interactions via CYP2C9"),
     C("INR at baseline, daily while loading, then weekly until stable, then monthly"),
     C("INR >3.0: hold warfarin; >4.5: Vit K oral; >5.0 + bleeding: IV Vit K + FFP; Antidote: Vit K / FFP")],
    [C("Heparin (UFH)", bold_ps),
     C("aPTT\nPlatelet count"),
     C("↑ aPTT\n↓ Platelets (HIT)"),
     C("Activates antithrombin III; aPTT reflects anticoagulation; HIT = immune-mediated platelet destruction"),
     C("aPTT every 6 hrs until therapeutic (45–80 sec), then daily; platelet count every 2–3 days for HIT"),
     C("Antidote: Protamine sulfate. If platelets drop >50% → STOP heparin; suspect HIT; use argatroban/bivalirudin")],
    [C("LMWH\n(enoxaparin,\ndalteparin)", bold_ps),
     C("Anti-Xa level\nCreatinine\nPlatelet count"),
     C("Anti-Xa 0.5–1.0\n(therapeutic)\n↑ Cr → drug accum."),
     C("Predictable pharmacokinetics; no routine aPTT needed; renally cleared — accumulates in CKD"),
     C("Anti-Xa level in renal impairment, obesity, or pregnancy; Cr before starting; platelets for HIT"),
     C("Partial antidote: Protamine sulfate (reverses ~60%); dose-reduce or switch to UFH if eGFR <30")],
    [C("DOACs\n(rivaroxaban,\napixaban,\ndabigatran)", bold_ps),
     C("Creatinine/eGFR\nLFTs\n(PT/aPTT not\nreliable for DOACs)"),
     C("↑ Cr → accumulation\n↑ LFTs (rare)"),
     C("Renally cleared (dabigatran >rivaroxaban >apixaban); liver-metabolized; PT/aPTT NOT reliable monitors"),
     C("Cr/eGFR at baseline and every 6–12 months; hold if eGFR <15; LFTs at baseline"),
     C("Antidotes: Idarucizumab (dabigatran); Andexanet alfa (Factor Xa inhibitors); activated charcoal if recent ingestion")],
    [C("Aspirin", bold_ps),
     C("Bleeding time\nHgb / Hct\nUric Acid"),
     C("↑ Bleeding time\n↓ Hgb (chronic)\n↑ Uric Acid (low dose)"),
     C("Irreversibly inhibits COX-1 → platelet dysfunction for 7–10 days; GI bleeding risk; competes with uric acid excretion"),
     C("Hgb/Hct annually (GI bleed risk); stool guaiac; renal function if high-dose use"),
     C("Hold 7–10 days before elective surgery; monitor for GI bleeding signs; avoid in gout")],
    [C("Clopidogrel\n(Plavix)", bold_ps),
     C("CBC (Hgb, Plt)\nLFTs (rare TTP)"),
     C("↓ Hgb (bleeding)\n↓ Plt (TTP — rare)"),
     C("P2Y12 inhibitor; prodrug requiring CYP2C19 activation — poor metabolizers get less effect"),
     C("Baseline CBC and LFTs; monitor for unusual bruising, petechiae, or neurological changes (TTP)"),
     C("Hold 5 days before surgery; TTP = rare emergency — STOP drug + plasmapheresis")],
]))
story.append(SP())
story.append(note_box(
    "<b>ANTICOAGULANT ANTIDOTES SUMMARY:</b>  "
    "Warfarin → Vitamin K (slow) / FFP (fast)  |  "
    "UFH → Protamine sulfate  |  "
    "LMWH → Protamine (partial)  |  "
    "Dabigatran → Idarucizumab (Praxbind)  |  "
    "Factor Xa inhibitors → Andexanet alfa  |  "
    "HIT: STOP all heparin products immediately — switch to argatroban or bivalirudin.",
    LRED, RED))

# ╔══════════════════════════════════════════════════════════════════════════╗
#  3. ANTIBIOTICS & ANTI-INFECTIVES
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP())
story.append(sec_hdr("3.  ANTIBIOTICS & ANTI-INFECTIVES",
    GREEN, "Vancomycin • Aminoglycosides • Fluoroquinolones • Antifungals • Antitubercular"))
story.append(mktbl([
    hrow(MAIN_COLS),
    [C("Vancomycin", bold_ps),
     C("Vancomycin trough\nBUN / Creatinine"),
     C("Toxic >20 µg/mL\n↑ BUN, Cr"),
     C("Nephrotoxic (proximal tubule damage); ototoxic at high levels; narrow therapeutic index"),
     C("Trough before 4th dose (target 10–20 µg/mL); BUN/Cr 2–3× per week; hold if Cr ↑ significantly"),
     C("Reduce dose/extend interval in renal impairment; Audiology consult for prolonged therapy; AUC-guided dosing preferred")],
    [C("Aminoglycosides\n(gentamicin,\ntobramycin,\namikacin)", bold_ps),
     C("Drug trough level\nBUN / Creatinine\nAudiology"),
     C("↑ Cr, ↑ BUN\nOtotoxicity"),
     C("Proximal tubule damage; free radical injury to cochlear hair cells; accumulate in renal failure"),
     C("Peak AND trough levels; BUN/Cr daily; urine output hourly in critically ill; baseline audiogram"),
     C("Hold if Cr rising; extended-interval dosing (once daily) reduces nephrotoxicity; avoid loop diuretics combo")],
    [C("Fluoroquinolones\n(ciprofloxacin,\nlevofloxacin)", bold_ps),
     C("BUN / Creatinine\nBlood glucose\nQTc interval"),
     C("↑ Cr (mild)\n↑ or ↓ Glucose\n↑ QTc"),
     C("Rare nephrotoxicity; alter glucose metabolism (hypoglycemia in diabetics); prolong QT interval"),
     C("Monitor blood glucose (especially in diabetics on sulfonylureas); ECG if QTc concern; Cr baseline"),
     C("Avoid with other QT-prolonging drugs (amiodarone, antipsychotics); hold in hypoglycemia")],
    [C("Azole Antifungals\n(fluconazole,\nvoriconazole)", bold_ps),
     C("LFTs (AST/ALT)\nINR (if on warfarin)\nCreatinine"),
     C("↑ LFTs\n↑ INR\n↑ Cr (voriconazole IV)"),
     C("Hepatotoxic; strong CYP2C9 inhibitors → ↑ warfarin dramatically; IV voriconazole vehicle (SBECD) is nephrotoxic"),
     C("LFTs weekly; INR frequently if on warfarin (reduce warfarin 50%); Cr for IV voriconazole"),
     C("Switch IV voriconazole to oral if Cr ↑; adjust warfarin dose proactively; stop if LFTs >5× ULN")],
    [C("Antitubercular Drugs\n(rifampin, isoniazid,\npyrazinamide)", bold_ps),
     C("LFTs (AST/ALT)\nUric Acid\nINR, drug levels"),
     C("↑ LFTs\n↑ Uric Acid (PZA)\n↓ INR (rifampin)"),
     C("Isoniazid/pyrazinamide = hepatotoxic; rifampin = potent CYP inducer → ↓ warfarin, OCP, many drugs"),
     C("LFTs monthly (or more if symptomatic); uric acid if gout history; warn about drug interactions"),
     C("Hold if AST/ALT >5× ULN or symptoms; rifampin reduces efficacy of contraceptives — counsel patients")],
]))

# ╔══════════════════════════════════════════════════════════════════════════╗
#  4. DIABETES MEDICATIONS
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP())
story.append(sec_hdr("4.  DIABETES MEDICATIONS",
    PURPLE, "Metformin • Sulfonylureas • Insulin • GLP-1 Agonists • SGLT-2 Inhibitors"))
story.append(mktbl([
    hrow(MAIN_COLS),
    [C("Metformin", bold_ps),
     C("Creatinine/eGFR\nB12 level\nLactic Acid"),
     C("Monitor Cr\n↓ B12 (chronic)\n↑ Lactic acid (rare)"),
     C("Not nephrotoxic but renally cleared; causes lactic acidosis if Cr ↑; impairs B12 absorption in ileum"),
     C("Cr/eGFR before start and every 3–6 months; B12 annually if long-term use; HOLD before contrast dye"),
     C("HOLD if eGFR <30; hold 48 hrs before and after contrast; resume only when Cr stable; supplement B12")],
    [C("Sulfonylureas\n(glipizide, glyburide,\nglimepiride)", bold_ps),
     C("Blood Glucose\nHbA1c\nCreatinine"),
     C("↓ Glucose\n↓ HbA1c\n↑ Cr → drug accum."),
     C("Stimulate insulin release regardless of glucose → hypoglycemia risk; renally cleared (glyburide worst)"),
     C("Blood glucose frequently; HbA1c every 3 months until stable; renal function monitoring"),
     C("Avoid glyburide in elderly/CKD → switch to glipizide; treat hypoglycemia with 15g fast-acting carbs")],
    [C("Insulin\n(all types)", bold_ps),
     C("Blood Glucose\nK⁺\nHbA1c"),
     C("↓ Glucose\n↓ K⁺ (insulin drives\nK into cells)\n↓ HbA1c"),
     C("Glucose uptake; used therapeutically to shift K⁺ intracellularly in hyperkalemia treatment"),
     C("Blood glucose per protocol (AC/HS or q1–4h in ICU); K⁺ monitoring in DKA/hyperkalemia treatment"),
     C("Hypoglycemia (<70): give D50W IV (unconscious) or 15g carbs (conscious); always give dextrose with insulin for hyperkalemia")],
    [C("GLP-1 Agonists\n(semaglutide,\nliraglutide)", bold_ps),
     C("Amylase/Lipase\nCreatinine\nHbA1c"),
     C("↑ Amylase/Lipase\n↑ Cr (rarely)\n↓ HbA1c"),
     C("GLP-1 receptor activation; associated with pancreatitis; slows GI motility; weight loss"),
     C("Amylase/lipase if abdominal pain develops; Cr/eGFR baseline; HbA1c every 3 months"),
     C("HOLD and evaluate if severe abdominal pain develops (pancreatitis); avoid in personal/family history of medullary thyroid cancer")],
    [C("SGLT-2 Inhibitors\n(empagliflozin,\ndapagliflozin)", bold_ps),
     C("Cr/eGFR\nHbA1c\nK⁺\nUrine glucose"),
     C("↑ Cr (initial, transient)\n↓ HbA1c\n↑ K⁺ (mild)\n+ Urine glucose"),
     C("Block glucose reabsorption in proximal tubule; osmotic diuresis → volume depletion; euglycemic DKA risk"),
     C("Cr/eGFR at baseline and periodically; HbA1c; urine glucose will be positive (expected effect)"),
     C("HOLD before surgery/fasting (euglycemic DKA risk); avoid if eGFR <20–30; monitor for UTI/genital yeast infections")],
]))

# ╔══════════════════════════════════════════════════════════════════════════╗
#  5. NEUROLOGICAL / PSYCHIATRIC DRUGS
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP())
story.append(sec_hdr("5.  NEUROLOGICAL & PSYCHIATRIC DRUGS",
    SLATE, "Anticonvulsants • Lithium • Antipsychotics • SSRIs • Opioids"))
story.append(mktbl([
    hrow(MAIN_COLS),
    [C("Phenytoin (Dilantin)", bold_ps),
     C("Phenytoin level\nAlbumin\nCBC\nLFTs"),
     C("Toxic >20 µg/mL\nFalse low if albumin↓\n↓ CBC (aplastic)\n↑ LFTs"),
     C("Narrow TI; highly protein-bound (albumin) — low albumin → more free drug; induces CYP enzymes"),
     C("Trough level (target 10–20); albumin (correct level if low); CBC and LFTs annually"),
     C("Correct phenytoin for albumin: Corrected level = measured ÷ (0.2 × albumin + 0.1); hold if SAND signs appear")],
    [C("Valproic Acid\n(Depakote)", bold_ps),
     C("Valproate level\nLFTs\nNH₃ (Ammonia)\nPlatelet count"),
     C("Toxic >100 µg/mL\n↑ LFTs\n↑ Ammonia\n↓ Platelets"),
     C("Hepatotoxic (especially in children <2); elevates ammonia even without liver disease; inhibits platelet aggregation"),
     C("Drug level (target 50–100 µg/mL); LFTs and ammonia at baseline and every 3–6 months; CBC for platelets"),
     C("Stop if severe LFT elevation; encephalopathy despite normal LFTs → check ammonia; supplement carnitine")],
    [C("Carbamazepine\n(Tegretol)", bold_ps),
     C("Carbamazepine level\nCBC\nNa⁺\nLFTs"),
     C("Toxic >12 µg/mL\n↓ WBC (aplastic anemia)\n↓ Na⁺ (SIADH effect)\n↑ LFTs"),
     C("Autoinduces own metabolism; can cause SIADH-like hyponatremia; agranulocytosis/aplastic anemia risk"),
     C("Drug level (target 4–12 µg/mL); CBC with diff at baseline and monthly × 3, then annually; Na⁺ and LFTs"),
     C("Hold if WBC <3,000 or ANC <1,500; treat hyponatremia cautiously; Black Box: aplastic anemia risk")],
    [C("Lithium", bold_ps),
     C("Lithium level\nCreatinine/eGFR\nTSH / T4\nNa⁺"),
     C("Toxic >1.5 mEq/L\n↑ Cr (nephrotoxic)\n↑ TSH (hypothyroid)\n↑ Li if Na↓"),
     C("Narrow TI; renally cleared; competes with Na⁺ — low Na diet/dehydration/diuretics ↑ lithium toxicity; causes hypothyroidism"),
     C("Lithium level every 5–7 days until stable, then every 3–6 months; Cr, TFTs, Na every 6 months"),
     C("HOLD if Cr rises; maintain adequate Na/fluid intake; hold diuretics if possible; antidote: none — supportive + dialysis if severe")],
    [C("Antipsychotics\n(haloperidol,\nolanzapine,\nquetiapine)", bold_ps),
     C("Glucose / HbA1c\nLipids\nProlactin\nQTc (ECG)\nCBC (clozapine)"),
     C("↑ Glucose\n↑ Lipids\n↑ Prolactin\n↑ QTc\n↓ WBC (clozapine)"),
     C("Metabolic syndrome; dopamine block → ↑ prolactin; QT prolongation (especially haloperidol IV, ziprasidone)"),
     C("Fasting glucose and lipids at baseline and annually; ECG for QTc; prolactin if symptoms; CBC weekly for clozapine"),
     C("Clozapine: HOLD if ANC <1,500 — registered REMS program; use ECG to monitor QTc; counsel on metabolic syndrome")],
    [C("SSRIs\n(fluoxetine,\nsertraline,\ncitalopram)", bold_ps),
     C("Na⁺ (SIADH)\nINR (if on warfarin)\nPlatelet function"),
     C("↓ Na⁺\n↑ INR\n↓ Platelet function"),
     C("SIADH (especially in elderly); inhibit serotonin uptake in platelets → ↑ bleeding; inhibit CYP2C9 → ↑ warfarin"),
     C("Na⁺ in elderly within 2 weeks of starting; INR if on warfarin; monitor for unusual bruising"),
     C("Hyponatremia → fluid restrict; seizure precautions if severe; adjust warfarin dose; serotonin syndrome if combined with MAOIs")],
]))

# ╔══════════════════════════════════════════════════════════════════════════╗
#  6. IMMUNOSUPPRESSANTS & BIOLOGICS
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP())
story.append(sec_hdr("6.  IMMUNOSUPPRESSANTS, CHEMOTHERAPY & BIOLOGICS",
    TEAL, "Methotrexate • Cyclosporine • Corticosteroids • TNF Inhibitors"))
story.append(mktbl([
    hrow(MAIN_COLS),
    [C("Methotrexate", bold_ps),
     C("CBC\nLFTs\nCreatinine\nAlbumin"),
     C("↓ WBC, Plt, RBC\n↑ LFTs\n↑ Cr\n↓ Albumin"),
     C("Folate antagonist → bone marrow suppression and mucositis; hepatotoxic; renally excreted"),
     C("CBC, LFTs, Cr at baseline and every 1–3 months; hold 1 week before elective surgery"),
     C("Leucovorin rescue for toxicity; hold if WBC <3,000 or Cr ↑; avoid NSAIDs (↑ methotrexate levels); supplement folic acid")],
    [C("Cyclosporine /\nTacrolimus", bold_ps),
     C("Drug level (trough)\nCreatinine/eGFR\nK⁺, Mg²⁺\nGlucose (tacrolimus)\nLFTs"),
     C("Toxic levels vary\n↑ Cr\n↑ K⁺, ↓ Mg²⁺\n↑ Glucose\n↑ LFTs"),
     C("Calcineurin inhibitors; nephrotoxic; tacrolimus causes new-onset DM post-transplant (NODAT)"),
     C("Trough levels 2× per week initially, then monthly; Cr, K⁺, Mg²⁺ weekly; fasting glucose; LFTs monthly"),
     C("Reduce dose if Cr ↑ >25% from baseline; replace Mg²⁺; adjust for drug interactions (CYP3A4 inducers/inhibitors)")],
    [C("Corticosteroids\n(prednisone,\nmethylprednisolone,\ndexamethasone)", bold_ps),
     C("Blood Glucose\nK⁺\nBone density\nCBC (WBC ↑)\nLFTs (NASH)"),
     C("↑ Glucose\n↓ K⁺\n↓ Bone density\n↑ WBC (false)\n↑ LFTs (long-term)"),
     C("Gluconeogenesis ↑; mineralocorticoid effect → Na retention, K⁺ loss; suppress bone formation; ↑ WBC by demargination"),
     C("Blood glucose AC/HS; electrolytes; bone density (DEXA) if long-term; BP; infection surveillance"),
     C("Sliding scale insulin for steroid-induced hyperglycemia; K⁺ replacement; calcium/Vit D supplementation; taper — never abrupt stop")],
    [C("TNF Inhibitors\n(infliximab,\nadalimumab,\netanercept)", bold_ps),
     C("CBC\nLFTs\nTB testing\n(QuantiFERON)\nANA"),
     C("↓ WBC (rare)\n↑ LFTs (rare)\n+ TB reactivation\n↑ ANA (drug-induced lupus)"),
     C("Immunosuppression → TB reactivation, opportunistic infections; rare hepatotoxicity; can unmask autoimmune conditions"),
     C("CBC and LFTs at baseline and every 3–6 months; TB test before initiating; chest X-ray; monitor for infection signs"),
     C("HOLD and evaluate for any active infection; STOP if active TB — treat TB before restarting biologic; monitor for lupus-like symptoms")],
]))

# ╔══════════════════════════════════════════════════════════════════════════╗
#  7. CRITICAL INTERACTIONS SUMMARY TABLE
# ╚══════════════════════════════════════════════════════════════════════════╝
story.append(SP())
story.append(sec_hdr("7.  CRITICAL DRUG-LAB INTERACTIONS AT A GLANCE",
    RED, "High-Alert Combinations Requiring Immediate Action"))
CRIT_CW = [1.55*inch, 1.55*inch, 1.50*inch, 2.90*inch]
story.append(mktbl([
    hrow(["DRUG", "LAB VALUE", "CRITICAL THRESHOLD", "IMMEDIATE NURSING ACTION"]),
    [C("Digoxin", bold_ps),      C("Digoxin level"),  C("> 2.0 ng/mL"),      C("Hold drug; STAT ECG; check K⁺ and Mg²⁺; Antidote: Digibind")],
    [C("Warfarin", bold_ps),     C("INR"),             C("> 3.0 (or >5.0)"),  C("Hold warfarin; Vit K PO (>3.0) or IV + FFP (>5.0 + bleeding)")],
    [C("Heparin UFH", bold_ps),  C("aPTT"),            C("> 80 seconds"),     C("Hold infusion; assess for bleeding; reduce dose per protocol")],
    [C("Heparin (all)", bold_ps),C("Platelets"),       C("Drop >50% from baseline"), C("STOP all heparin; HIT panel; switch to argatroban or bivalirudin")],
    [C("Lithium", bold_ps),      C("Lithium level"),   C("> 1.5 mEq/L"),      C("Hold lithium; IV fluids; dialysis if >2.5; neuro monitoring")],
    [C("Phenytoin", bold_ps),    C("Phenytoin level"), C("> 20 µg/mL"),       C("Hold dose; assess for SAND signs (nystagmus, ataxia); correct for albumin")],
    [C("Vancomycin", bold_ps),   C("Trough level"),    C("> 20 µg/mL"),       C("Hold dose; extend interval; check BUN/Cr; adjust per renal function")],
    [C("Metformin", bold_ps),    C("Creatinine/eGFR"), C("eGFR < 30 mL/min"),C("HOLD metformin; assess for lactic acidosis; do not restart until Cr stable")],
    [C("ACEi / ARBs", bold_ps),  C("K⁺"),              C("> 5.5 mEq/L"),      C("Hold drug; dietary K⁺ restriction; ECG; treat hyperkalemia")],
    [C("Statins", bold_ps),      C("CK (CPK)"),        C("> 10× ULN"),        C("STOP statin; aggressive IV hydration (rhabdomyolysis); monitor Cr and urine output")],
    [C("Amiodarone", bold_ps),   C("TSH"),             C("< 0.1 or > 10"),    C("Notify provider; endocrinology consult; treat thyroid dysfunction")],
    [C("Clozapine", bold_ps),    C("ANC (WBC)"),       C("ANC < 1,500 /µL"), C("STOP clozapine immediately; do not rechallenge until ANC recovers")],
    [C("Corticosteroids", bold_ps),C("Blood Glucose"), C("> 300 mg/dL"),      C("Insulin per sliding scale; notify provider; monitor for DKA symptoms")],
    [C("Loop Diuretics", bold_ps),C("K⁺"),             C("< 3.0 mEq/L"),      C("K⁺ replacement IV or oral; hold digoxin if also prescribed; ECG")],
], cw=CRIT_CW, extra=[("ROWBACKGROUNDS",(0,1),(-1,-1),[LRED, WHITE])]))

story.append(SP())
story.append(note_box(
    "<b>UNIVERSAL MONITORING RULES:</b>  "
    "(1) Always check a drug level BEFORE giving the next dose (trough level = just before next dose).  "
    "(2) Narrow Therapeutic Index (NTI) drugs require more frequent monitoring: "
    "Digoxin · Lithium · Phenytoin · Theophylline · Vancomycin · Warfarin · Cyclosporine/Tacrolimus.  "
    "(3) HOLD nephrotoxic drugs (aminoglycosides, NSAIDs, contrast dye, metformin) if Cr is rising.  "
    "(4) Any drug that inhibits CYP2C9 (amiodarone, azoles, SSRIs) will INCREASE warfarin effect — reduce warfarin dose proactively.  "
    "(5) CYP3A4 inducers (rifampin, carbamazepine, phenytoin) DECREASE levels of many drugs including warfarin, cyclosporine, and oral contraceptives.",
    LGREEN, GREEN))

# ── Build ──────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.80*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_drug_lab_guide.py 2>&1

Running Command

ls -lh /home/daytona/workspace/nclex-labs/Drug_Lab_Interactions_Guide.pdf

Your pharmacology reference guide is ready!

Drug Lab Interactions Guide

PDF Document · PDF


What's Inside (7 color-coded sections)

1. Cardiovascular Drugs (Navy) ACEi/ARBs, Loop diuretics, Thiazides, K-sparing diuretics, Digoxin, Statins, Amiodarone — with CYP interaction notes
2. Anticoagulants & Antiplatelets (Dark Red) Warfarin, UFH, LMWH, DOACs, Aspirin, Clopidogrel — with full antidote summary box
3. Antibiotics & Anti-infectives (Green) Vancomycin, Aminoglycosides, Fluoroquinolones, Azole antifungals, Antitubercular drugs
4. Diabetes Medications (Purple) Metformin, Sulfonylureas, Insulin, GLP-1 agonists, SGLT-2 inhibitors — including contrast dye and eGFR cutoffs
5. Neurological & Psychiatric Drugs (Slate) Phenytoin (albumin correction formula included), Valproic acid, Carbamazepine, Lithium, Antipsychotics, SSRIs
6. Immunosuppressants & Biologics (Teal) Methotrexate, Cyclosporine/Tacrolimus, Corticosteroids, TNF inhibitors — transplant monitoring included
7. Critical Interactions at a Glance (Red) 14 highest-risk drug-lab combinations with exact thresholds and immediate nursing actions, plus a universal monitoring rules box covering NTI drugs and CYP2C9/3A4 interactions

Make a printable study schedule template for NCLEX prep

Writing File

~/nclex-labs/generate_study_schedule.py

"""
NCLEX Prep Study Schedule Template — ReportLab PDF
8-week planner with daily study blocks, topic rotation, and progress tracking
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import HRFlowable

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Study_Schedule.pdf"

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY    = colors.HexColor("#1B2A4A")
TEAL    = colors.HexColor("#0B7A75")
RED     = colors.HexColor("#B03A2E")
ORANGE  = colors.HexColor("#D9622B")
GREEN   = colors.HexColor("#1D6A39")
PURPLE  = colors.HexColor("#5B2C6F")
GOLD    = colors.HexColor("#9A7D0A")
BLUE    = colors.HexColor("#1A5276")
SLATE   = colors.HexColor("#2E4057")
ROSE    = colors.HexColor("#922B21")

WHITE   = colors.white
DGREY   = colors.HexColor("#2C3E50")
MGREY   = colors.HexColor("#BDC3C7")
LGREY   = colors.HexColor("#F4F6F9")
XLGREY  = colors.HexColor("#FAFAFA")

LTEAL   = colors.HexColor("#E8F8F5")
LBLUE   = colors.HexColor("#EBF5FB")
LGREEN  = colors.HexColor("#EAFAF1")
LORANGE = colors.HexColor("#FEF5E7")
LPURPLE = colors.HexColor("#F5EEF8")
LRED    = colors.HexColor("#FDEDEC")
LNAVY   = colors.HexColor("#EAF0FB")
LGOLD   = colors.HexColor("#FDFAE5")

PAGE_W, PAGE_H = letter

# ── Styles ────────────────────────────────────────────────────────────────────
def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY, leading=11)
    base.update(kw)
    return ParagraphStyle(name, **base)

def C(txt, **kw):
    ps = PS("_", **kw)
    return Paragraph(str(txt), ps)

def CB(txt, **kw):
    kw.setdefault("fontName","Helvetica-Bold")
    return C(txt, **kw)

def CW(txt, **kw):
    kw["textColor"] = WHITE
    kw.setdefault("fontName","Helvetica-Bold")
    return C(txt, **kw)

def CC(txt, **kw):
    kw["alignment"] = TA_CENTER
    return C(txt, **kw)

def CCB(txt, **kw):
    kw["alignment"] = TA_CENTER
    kw.setdefault("fontName","Helvetica-Bold")
    return C(txt, **kw)

BASE = [
    ("FONTNAME",      (0,0),(-1,-1), "Helvetica"),
    ("FONTSIZE",      (0,0),(-1,-1), 8),
    ("GRID",          (0,0),(-1,-1), 0.4, MGREY),
    ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
    ("TOPPADDING",    (0,0),(-1,-1), 3),
    ("BOTTOMPADDING", (0,0),(-1,-1), 3),
    ("LEFTPADDING",   (0,0),(-1,-1), 5),
    ("RIGHTPADDING",  (0,0),(-1,-1), 5),
]

SP = lambda n=0.07: Spacer(1, n*inch)
FULL = 7.5*inch

def banner(title, subtitle, bg):
    rows = [[CW(title, fontSize=10, alignment=TA_CENTER)]]
    if subtitle:
        rows.append([C(subtitle, fontSize=7.5, textColor=colors.HexColor("#D5D8DC"),
                       alignment=TA_CENTER, leading=10)])
    t = Table(rows, colWidths=[FULL])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("TOPPADDING",(0,0),(-1,-1), 5),
        ("BOTTOMPADDING",(0,0),(-1,-1), 5),
    ]))
    return t

# ── Page callback ─────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-54, PAGE_W, 54, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-58, PAGE_W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 18)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-32,
        "NCLEX PREP — 8-WEEK STUDY SCHEDULE TEMPLATE")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-48,
        "Customize dates  •  Check off completed topics  •  Track your progress  •  Stay consistent!")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"NCLEX Study Planner  •  Printable Template  •  Page {doc.page}")

# =============================================================================
story = []
story.append(SP(0.08))

# ── OVERVIEW / HOW TO USE ─────────────────────────────────────────────────────
story.append(banner("HOW TO USE THIS PLANNER", "", SLATE))
how_data = [
    [CB("✏️  Fill in dates"), CB("📚  Follow the topic plan"), CB("✅  Check off daily"), CB("📊  Track weekly scores")],
    [C("Write your exam date at the top\nof each week. Work backward to\nset weekly goals.",fontSize=7.5,leading=10),
     C("Each week targets a core NCLEX\ncontent area. Rotate topics and\nreview weak areas on weekends.",fontSize=7.5,leading=10),
     C("Check the box each day after\ncompleting your study session\nand practice questions.",fontSize=7.5,leading=10),
     C("Log your practice test score\neach week. Aim to trend upward.\nTarget ≥ 60% before exam.",fontSize=7.5,leading=10)],
]
t = Table(how_data, colWidths=[FULL/4]*4)
t.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), NAVY),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("BACKGROUND",(0,1),(-1,1), LNAVY),
    ("GRID",(0,0),(-1,-1),0.5,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),5),
    ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("ALIGN",(0,0),(-1,0),"CENTER"),
]))
story.append(t)
story.append(SP(0.06))

# ── DAILY TIME BLOCKS ─────────────────────────────────────────────────────────
story.append(banner("RECOMMENDED DAILY STUDY STRUCTURE", "Adapt to your schedule — consistency matters more than hours", TEAL))
time_data = [
    [CCB("TIME BLOCK", textColor=WHITE), CCB("ACTIVITY", textColor=WHITE),
     CCB("DURATION", textColor=WHITE), CCB("TOOLS / RESOURCES", textColor=WHITE)],
    [CC("Morning\n(6–8 AM)"), C("Content Review — Read / Watch lecture on today's topic"),
     CC("45–60 min"), C("Textbook, ATI, Saunders, Uworld content")],
    [CC("Mid-Morning\n(9–11 AM)"), C("Practice Questions — NCLEX-style Q bank (focused on today's topic)"),
     CC("60–75 min"), C("UWorld, Kaplan, NCLEX Mastery app — 50–75 Qs")],
    [CC("Afternoon\n(1–2 PM)"), C("Review WRONG answers — Read rationales; re-read content for missed Qs"),
     CC("45–60 min"), C("Q bank review mode; highlight key concepts")],
    [CC("Evening\n(7–8 PM)"), C("Flashcards / Lab values / Meds — Quick active recall review"),
     CC("30–45 min"), C("This flashcard deck, Anki, or paper cards")],
    [CC("Before Bed\n(Optional)"), C("Read summary notes only — NO new content; light review"),
     CC("15–20 min"), C("Personal summary notes, mind maps")],
]
t2 = Table(time_data, colWidths=[1.0*inch, 3.0*inch, 0.85*inch, 2.65*inch])
t2.setStyle(TableStyle(BASE + [
    ("BACKGROUND",(0,0),(-1,0), TEAL),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LTEAL, WHITE]),
    ("ALIGN",(0,0),(0,-1),"CENTER"),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))
story.append(t2)
story.append(SP(0.08))

# ── NCLEX CONTENT AREA BREAKDOWN ─────────────────────────────────────────────
story.append(banner("NCLEX-RN CONTENT DISTRIBUTION (Next Gen NCLEX)", "Know where the most points are", BLUE))
content_data = [
    [CCB("CONTENT AREA",textColor=WHITE), CCB("% OF EXAM",textColor=WHITE),
     CCB("KEY TOPICS",textColor=WHITE), CCB("PRIORITY",textColor=WHITE)],
    [C("Safe & Effective Care\nManagement"), CC("26–38%"),
     C("Delegation, prioritization, infection control, safety, legal/ethical, emergency response"),
     CCB("⭐⭐⭐ HIGHEST", textColor=RED)],
    [C("Health Promotion &\nMaintenance"), CC("6–12%"),
     C("Developmental stages, immunizations, health screening, prenatal, newborn care"),
     CCB("⭐⭐ MODERATE", textColor=ORANGE)],
    [C("Psychosocial Integrity"), CC("6–12%"),
     C("Mental health, therapeutic communication, crisis, abuse, coping, addiction"),
     CCB("⭐⭐ MODERATE", textColor=ORANGE)],
    [C("Physiological Integrity\n(Basic Care)"), CC("6–12%"),
     C("Nutrition, mobility, rest, hygiene, assistive devices, elimination, comfort"),
     CCB("⭐⭐ MODERATE", textColor=ORANGE)],
    [C("Physiological Integrity\n(Pharmacological)"), CC("12–18%"),
     C("Drug actions, adverse effects, therapeutic levels, antidotes, client education"),
     CCB("⭐⭐⭐ HIGH", textColor=RED)],
    [C("Physiological Integrity\n(Reduction of Risk)"), CC("9–15%"),
     C("Lab values, diagnostic tests, pre/post-op care, potential complications"),
     CCB("⭐⭐⭐ HIGH", textColor=RED)],
    [C("Physiological Integrity\n(Physiological Adaptation)"), CC("11–17%"),
     C("Acute/chronic illness, fluid balance, medical emergencies, pathophysiology"),
     CCB("⭐⭐⭐ HIGH", textColor=RED)],
]
t3 = Table(content_data, colWidths=[1.7*inch, 0.85*inch, 3.55*inch, 1.40*inch])
t3.setStyle(TableStyle(BASE + [
    ("BACKGROUND",(0,0),(-1,0), BLUE),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LBLUE, WHITE]),
    ("ALIGN",(1,0),(1,-1),"CENTER"),
]))
story.append(t3)

# ── PAGE 2 — 8-WEEK SCHEDULE ─────────────────────────────────────────────────
story.append(PageBreak())
story.append(SP(0.08))

WEEK_COLORS = [NAVY, TEAL, BLUE, GREEN, PURPLE, ORANGE, RED, SLATE]
WEEK_LIGHT  = [LNAVY, LTEAL, LBLUE, LGREEN, LPURPLE, LORANGE, LRED, LGREY]

WEEK_PLANS = [
    ("WEEK 1", "Foundation & Safety", "Safe Care Management • Infection Control • Delegation • Prioritization • Legal/Ethical",
     [("Mon", "Delegation principles + NCLEX priority frameworks (ABC, Maslow, safety first)"),
      ("Tue", "Infection control: Standard precautions, transmission-based precautions, isolation"),
      ("Wed", "Legal & ethical: Informed consent, advance directives, HIPAA, scope of practice"),
      ("Thu", "Emergency response: Triage, disaster nursing, rapid response"),
      ("Fri", "Lab values review: CBC, BMP/CMP, ABGs (use lab flashcards)"),
      ("Sat", "Practice test: 75 questions (safety & management focus) + full review of rationales"),
      ("Sun", "Rest day OR weak area review — ONLY topics you missed this week")]),
    ("WEEK 2", "Cardiovascular & Respiratory", "Cardiac Disorders • Heart Failure • Dysrhythmias • Respiratory Disorders • ABGs",
     [("Mon", "Cardiac: MI, angina, heart failure — S/S, labs (troponin, BNP), interventions"),
      ("Tue", "ECG basics: rhythm identification, ACLS medications, cardioversion vs. defibrillation"),
      ("Wed", "Respiratory: Pneumonia, asthma, COPD, TB, ARDS — priority interventions"),
      ("Thu", "ABG interpretation: ROME method, compensation, nursing actions for each imbalance"),
      ("Fri", "Drug-lab review: Digoxin, amiodarone, loop diuretics, anticoagulants (Weeks 1–2 drugs)"),
      ("Sat", "Practice test: 75 questions (cardio/resp focus) + review rationales"),
      ("Sun", "Weak area review OR rest")]),
    ("WEEK 3", "Neurological & Musculoskeletal", "Stroke • ICP • Spinal Cord • Seizures • Fractures • Orthopedic",
     [("Mon", "Stroke: FAST, tPA criteria, post-stroke care, hemorrhagic vs ischemic"),
      ("Tue", "Increased ICP: S/S, Cushing's triad, positioning, mannitol, monitoring"),
      ("Wed", "Spinal cord injury: Level classifications, autonomic dysreflexia, bladder/bowel care"),
      ("Thu", "Seizures: Types, medications (phenytoin levels!), safety, status epilepticus"),
      ("Fri", "Musculoskeletal: Hip fracture, compartment syndrome, fat embolism, traction, casts"),
      ("Sat", "Practice test: 75 questions (neuro/MSK focus) + review rationales"),
      ("Sun", "Weak area review OR rest")]),
    ("WEEK 4", "Endocrine & GI", "Diabetes • Thyroid • Adrenal • GI Disorders • Liver Disease",
     [("Mon", "Diabetes: DKA vs HHS (compare!), insulin types, hypoglycemia, sick-day rules, HbA1c"),
      ("Tue", "Thyroid: Hypo vs Hyper — S/S, meds (levothyroxine, PTU), thyroid storm, myxedema"),
      ("Wed", "Adrenal: Cushing's vs Addison's — compare labs, meds, crisis management"),
      ("Thu", "GI: Peptic ulcer, Crohn's vs UC, liver cirrhosis, hepatic encephalopathy, CMP review"),
      ("Fri", "Drug-lab: Metformin/sulfonylureas/insulin, antiepileptics, GI drugs, steroids"),
      ("Sat", "Practice test: 75 questions (endocrine/GI focus) + review rationales"),
      ("Sun", "Midpoint check: Review total % correct trend. Adjust weak areas for Weeks 5–8")]),
    ("WEEK 5", "Renal, Fluids & Electrolytes", "AKI • CKD • Dialysis • IV Fluids • Electrolyte Imbalances",
     [("Mon", "AKI vs CKD: Causes, oliguric vs recovery phases, labs (BUN/Cr/K⁺/HCO₃)"),
      ("Tue", "Dialysis: Hemodialysis vs peritoneal — assessment, access care, complications"),
      ("Wed", "IV fluids: Isotonic/hypotonic/hypertonic solutions — when to use each, tonicity effects"),
      ("Thu", "Electrolytes: Hypo/Hypernatremia, Hypo/Hyperkalemia, Hypo/Hypercalcemia — all S/S"),
      ("Fri", "Drug-lab: ACEi/ARBs, diuretics, drug effects on electrolytes, correction formulas"),
      ("Sat", "Practice test: 75 questions (renal/fluids focus) + review rationales"),
      ("Sun", "Weak area review OR rest")]),
    ("WEEK 6", "Pharmacology & Med Administration", "Drug Safety • High-Alert Meds • Therapeutic Levels • Pain Management",
     [("Mon", "Rights of medication administration + high-alert drugs: anticoagulants, insulin, digoxin"),
      ("Tue", "Anticoagulants full review: Warfarin INR, Heparin aPTT, DOACs, antidotes"),
      ("Wed", "Pain management: Opioids, PCA, non-opioid analgesics, equianalgesic dosing"),
      ("Thu", "Psychiatric meds: Lithium, antipsychotics (clozapine!), SSRIs, MAOIs — lab monitoring"),
      ("Fri", "Drug-lab interactions full review (entire Drug-Lab guide — all 7 sections)"),
      ("Sat", "Practice test: 75 questions (pharmacology focus) + review rationales"),
      ("Sun", "Weak area review OR rest")]),
    ("WEEK 7", "Maternal-Newborn & Pediatrics", "Prenatal Care • Labor & Delivery • Postpartum • Pediatric Milestones",
     [("Mon", "Prenatal: Normal vs at-risk pregnancy, pre-eclampsia (Mg sulfate monitoring!), HELLP"),
      ("Tue", "Labor & delivery: Stages of labor, fetal monitoring, shoulder dystocia, emergency delivery"),
      ("Wed", "Postpartum: BUBBLE-HE assessment, PPH, mastitis, postpartum depression"),
      ("Thu", "Newborn: APGAR, normal vs abnormal findings, common conditions, immunizations"),
      ("Fri", "Pediatrics: Developmental milestones, common childhood illnesses, growth parameters"),
      ("Sat", "Practice test: 75 questions (OB/peds focus) + review rationales"),
      ("Sun", "Weak area review OR rest")]),
    ("WEEK 8", "Final Review & Exam Prep", "Comprehensive Review • Full-Length Practice Exams • Test Strategy",
     [("Mon", "Full-length practice exam (150–265 Q) — simulate real NCLEX conditions"),
      ("Tue", "Thorough rationale review of full-length exam. Flag and re-study all missed topics"),
      ("Wed", "High-yield review: ABCs, Maslow, delegation rules, infection precautions, priority Qs"),
      ("Thu", "Lab values final review + Drug-lab interactions cheat sheet + flashcard run-through"),
      ("Fri", "Light content review only — no new material. Trust your preparation."),
      ("Sat", "Exam-day prep: Lay out supplies, confirm location/time, early bedtime. Light review only."),
      ("Sun", "EXAM DAY 🎓 — Arrive early, breathe, trust yourself. You've got this!")]),
]

DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

for i, (week, theme, topics, days_plan) in enumerate(WEEK_PLANS):
    wc  = WEEK_COLORS[i]
    wlc = WEEK_LIGHT[i]

    # Week header
    hdr_tbl = Table([
        [CW(f"{week}:  {theme}", fontSize=11, alignment=TA_CENTER),
         CW("EXAM DATE: ___________________", fontSize=8.5, alignment=TA_RIGHT)]
    ], colWidths=[5.0*inch, 2.5*inch])
    hdr_tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), wc),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]))
    story.append(hdr_tbl)

    # Topics row
    topic_tbl = Table([[C(f"📖 Focus: {topics}", fontSize=7.5, textColor=DGREY,
                           backColor=wlc, leading=10)]],
                      colWidths=[FULL])
    topic_tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),wlc),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
    ]))
    story.append(topic_tbl)

    # Daily plan grid
    day_header = [
        CC("✓", fontName="Helvetica-Bold", textColor=WHITE),
        CC("DAY", fontName="Helvetica-Bold", textColor=WHITE),
        CW("TODAY'S STUDY TOPIC", fontSize=8, alignment=TA_LEFT),
        CC("Qs Done", fontName="Helvetica-Bold", textColor=WHITE, fontSize=7.5),
        CC("% Correct", fontName="Helvetica-Bold", textColor=WHITE, fontSize=7.5),
        CW("Notes / Weak Areas", fontSize=7.5, alignment=TA_LEFT),
    ]
    day_rows = [day_header]
    for j, (day, topic) in enumerate(days_plan):
        bg = wlc if j % 2 == 0 else WHITE
        day_rows.append([
            C("☐", fontSize=11, alignment=TA_CENTER),
            CB(day, alignment=TA_CENTER, textColor=wc),
            C(topic, fontSize=7.5, leading=10),
            C("", alignment=TA_CENTER),
            C("", alignment=TA_CENTER),
            C("", fontSize=7.5),
        ])
    day_tbl = Table(day_rows, colWidths=[0.28*inch, 0.40*inch, 3.45*inch, 0.65*inch, 0.72*inch, 1.95*inch])
    day_tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,0), DGREY),
        ("TEXTCOLOR",(0,0),(-1,0), WHITE),
        ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
        ("FONTSIZE",(0,0),(-1,0),7.8),
        ("ALIGN",(0,0),(0,-1),"CENTER"),
        ("ALIGN",(1,0),(1,-1),"CENTER"),
        ("ALIGN",(3,0),(4,-1),"CENTER"),
        ("ROWBACKGROUNDS",(0,1),(-1,-1),[wlc, WHITE]),
        ("GRID",(0,0),(-1,-1),0.4,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),3),
        ("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),4),
        ("RIGHTPADDING",(0,0),(-1,-1),4),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]))
    story.append(day_tbl)

    # Weekly reflection row
    refl_tbl = Table([[
        CB("Weekly Reflection:", textColor=wc, fontSize=7.5),
        C("Practice Exam Score: _______ / 100     Weak areas this week: _________________________________", fontSize=7.5),
        CB("✓ Week Complete", textColor=GREEN, fontSize=7.5, alignment=TA_RIGHT),
    ]], colWidths=[1.2*inch, 5.0*inch, 1.3*inch])
    refl_tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), LGREY),
        ("GRID",(0,0),(-1,-1),0.4,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]))
    story.append(refl_tbl)
    story.append(SP(0.10))

# ── PAGE 3 — PROGRESS TRACKER + QUICK TIPS ───────────────────────────────────
story.append(PageBreak())
story.append(SP(0.08))

# Progress tracker
story.append(banner("8-WEEK PROGRESS TRACKER", "Log your weekly practice scores — aim for an upward trend", GREEN))
prog_data = [
    [CCB("WEEK",textColor=WHITE), CCB("DATES",textColor=WHITE),
     CCB("TOPIC FOCUS",textColor=WHITE), CCB("Q's COMPLETED",textColor=WHITE),
     CCB("% CORRECT",textColor=WHITE), CCB("WEAK AREAS",textColor=WHITE),
     CCB("✓ DONE",textColor=WHITE)],
    [CC("1"), C("___/___  to  ___/___"), C("Safety & Management"),  CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("2"), C("___/___  to  ___/___"), C("Cardio & Respiratory"), CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("3"), C("___/___  to  ___/___"), C("Neuro & MSK"),          CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("4"), C("___/___  to  ___/___"), C("Endocrine & GI"),        CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("5"), C("___/___  to  ___/___"), C("Renal & Fluids"),        CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("6"), C("___/___  to  ___/___"), C("Pharmacology"),          CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("7"), C("___/___  to  ___/___"), C("OB & Pediatrics"),       CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CC("8"), C("___/___  to  ___/___"), C("Final Review"),          CC("______"), CC("______"), C("_______________________"), CC("☐")],
    [CCB("TOTAL",textColor=NAVY), C(""), C(""), CCB("____________",textColor=NAVY),
     CCB("Avg: ________",textColor=NAVY), C("EXAM DATE: ______________"), CCB("🎓",textColor=GREEN, fontSize=14)],
]
prog_cw = [0.42*inch, 1.25*inch, 1.45*inch, 0.95*inch, 0.90*inch, 2.0*inch, 0.53*inch]
prog_tbl = Table(prog_data, colWidths=prog_cw)
prog_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), GREEN),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("BACKGROUND",(0,-1),(-1,-1), LGREEN),
    ("FONTNAME",(0,-1),(-1,-1),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-2),[LGREEN, WHITE]),
    ("GRID",(0,0),(-1,-1),0.5,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("ALIGN",(0,0),(0,-1),"CENTER"),
    ("ALIGN",(3,0),(4,-1),"CENTER"),
    ("ALIGN",(6,0),(6,-1),"CENTER"),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(prog_tbl)
story.append(SP(0.10))

# ── NCLEX STRATEGY TIPS ───────────────────────────────────────────────────────
story.append(banner("NCLEX QUESTION STRATEGY TIPS", "Apply these every time you answer a question", PURPLE))
tips_data = [
    [CCB("PRIORITY FRAMEWORK",textColor=WHITE), CCB("DELEGATION RULES",textColor=WHITE),
     CCB("WHEN IN DOUBT",textColor=WHITE), CCB("COMMON TRAPS",textColor=WHITE)],
    [C("1. Is it a safety issue? → Act FIRST\n"
       "2. ABC: Airway > Breathing > Circulation\n"
       "3. Maslow: Physiological > Safety > Love\n"
       "4. Acute before chronic\n"
       "5. Unstable before stable\n"
       "6. Assess before intervene", fontSize=7.5, leading=11),
     C("RN delegates to:\n"
       "• LPN: Stable patients, meds (except IV push)\n"
       "• UAP: Basic care, VS on stable patients\n\n"
       "RN CANNOT delegate:\n"
       "• Assessment / Teaching\n"
       "• IV meds / Blood products\n"
       "• Unstable/new admission patients", fontSize=7.5, leading=11),
     C("• Read ALL options before choosing\n"
       "• Eliminate obviously wrong answers first\n"
       "• 'Assess first' is usually right early in a question\n"
       "• 'Notify provider' is usually NOT the best first action\n"
       "• Choose the MOST COMPLETE safe answer\n"
       "• SATA: treat each option as True/False", fontSize=7.5, leading=11),
     C("• 'Always' and 'never' = usually wrong\n"
       "• Reassure without addressing = wrong\n"
       "• 'Tell the client not to worry' = wrong\n"
       "• Normal findings after surgery = expected, don't act\n"
       "• Highest priority ≠ most interesting finding\n"
       "• First action ≠ most important intervention", fontSize=7.5, leading=11)],
]
tips_tbl = Table(tips_data, colWidths=[FULL/4]*4)
tips_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), PURPLE),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("BACKGROUND",(0,1),(-1,1), LPURPLE),
    ("GRID",(0,0),(-1,-1),0.5,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("ALIGN",(0,0),(-1,0),"CENTER"),
]))
story.append(tips_tbl)
story.append(SP(0.10))

# ── EXAM DAY CHECKLIST ────────────────────────────────────────────────────────
story.append(banner("EXAM DAY CHECKLIST", "", RED))
exam_data = [
    [CCB("NIGHT BEFORE",textColor=WHITE), CCB("MORNING OF EXAM",textColor=WHITE),
     CCB("AT THE TEST CENTER",textColor=WHITE), CCB("DURING THE EXAM",textColor=WHITE)],
    [C("☐  Lay out all required ID documents\n"
       "☐  Confirm test center address + parking\n"
       "☐  Pack a healthy snack and water\n"
       "☐  Set two alarms\n"
       "☐  Review summary notes only (no new content)\n"
       "☐  Sleep at least 7–8 hours\n"
       "☐  Avoid cramming — trust your prep", fontSize=7.5, leading=11),
     C("☐  Eat a protein-rich breakfast\n"
       "☐  Arrive 30 minutes early\n"
       "☐  Avoid discussing content with others\n"
       "☐  Use bathroom before entering\n"
       "☐  Take deep breaths — manage anxiety\n"
       "☐  Bring valid photo ID (two forms)\n"
       "☐  Leave phone/smartwatch in car/locker", fontSize=7.5, leading=11),
     C("☐  Follow ALL check-in procedures\n"
       "☐  Use provided scratch paper/whiteboard\n"
       "☐  Read the tutorial carefully\n"
       "☐  Use scheduled breaks (every 2 hours)\n"
       "☐  Stretch + breathe during breaks\n"
       "☐  Do not discuss questions with others\n"
       "☐  Stay until you are sure — no rushing", fontSize=7.5, leading=11),
     C("☐  Read each question COMPLETELY\n"
       "☐  Apply NCLEX priority frameworks\n"
       "☐  Eliminate 2 obviously wrong options\n"
       "☐  For SATA: treat each choice T/F\n"
       "☐  Don't second-guess first instinct\n"
       "☐  Don't count questions — trust the CAT\n"
       "☐  Breathe. You are READY. 🎓", fontSize=7.5, leading=11)],
]
exam_tbl = Table(exam_data, colWidths=[FULL/4]*4)
exam_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), RED),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("BACKGROUND",(0,1),(-1,1), LRED),
    ("GRID",(0,0),(-1,-1),0.5,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),6),("BOTTOMPADDING",(0,0),(-1,-1),6),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("ALIGN",(0,0),(-1,0),"CENTER"),
]))
story.append(exam_tbl)
story.append(SP(0.08))

# Final motivational note
motiv = Table([[Paragraph(
    "<b>\"The secret of getting ahead is getting started.\"</b>  "
    "One focused hour every day beats one all-night cram session.  "
    "You have everything you need.  <b>NOW GO PASS THAT NCLEX! 🎓</b>",
    PS("m", fontSize=9, textColor=NAVY, alignment=TA_CENTER,
       fontName="Helvetica-Bold", leading=13))]],
    colWidths=[FULL])
motiv.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), LNAVY),
    ("BOX",(0,0),(-1,-1), 1.5, NAVY),
    ("TOPPADDING",(0,0),(-1,-1), 8),("BOTTOMPADDING",(0,0),(-1,-1), 8),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10),
]))
story.append(motiv)

# ── Build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.80*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_study_schedule.py 2>&1

Running Command

ls -lh /home/daytona/workspace/nclex-labs/NCLEX_Study_Schedule.pdf

Your study schedule is ready!

NCLEX Study Schedule

PDF Document · PDF


What's Inside (3 pages, fully printable)

Page 1 — Overview & Framework
  • How to use the planner (4-step guide)
  • Recommended daily study structure with time blocks (morning through evening)
  • NCLEX-RN content distribution table showing which areas carry the most exam weight (Safety/Management = 26–38% is the largest)
Page 2 — 8-Week Daily Schedule Each week includes:
  • Color-coded week header with a blank "exam date" field
  • Topic focus and subtopics for the week
  • Day-by-day study plan with checkboxes, Q's done column, % correct column, and a notes field
  • Weekly reflection row to log practice exam scores and weak areas
WeekFocus
1Safety, Delegation, Infection Control, Legal/Ethics
2Cardiovascular & Respiratory, ABGs
3Neurological & Musculoskeletal
4Endocrine & GI (midpoint check-in)
5Renal, Fluids & Electrolytes
6Pharmacology & Drug-Lab Interactions
7Maternal-Newborn & Pediatrics
8Final Review, Full-Length Practice, Exam Prep
Page 3 — Tools & Exam Day
  • 8-week progress score tracker with total Q count and average %
  • NCLEX question strategy quick guide (priority frameworks, delegation rules, common traps)
  • Exam day checklist (night before → morning → test center → during exam)
  • Motivational closing note

Pro tip: Print all 3 pages, put them in a binder or clip them together, and fill in your target exam date working backward from Week 8. A consistent 3–4 hours/day beats sporadic 8-hour sessions every time.

Create a comprehensive NCLEX question bank by topic (with detailed rationales)

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.

Create a comprehensive NCLEX question bank by topic (with detailed rationales)

Create a comprehensive NCLEX question bank by topic (with detailed rationales)

Create a final checklist of the top 100 NCLEX concepts nurse must know before exam day

Writing File

~/nclex-labs/generate_top100_checklist.py

"""
Top 100 NCLEX Concepts Checklist — ReportLab PDF
Organized by category with checkboxes, brief explanations, and exam tips
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Top100_Concepts_Checklist.pdf"

PAGE_W, PAGE_H = letter

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#1B2A4A")
TEAL   = colors.HexColor("#0B7A75")
RED    = colors.HexColor("#B03A2E")
ORANGE = colors.HexColor("#D9622B")
GREEN  = colors.HexColor("#1D6A39")
PURPLE = colors.HexColor("#5B2C6F")
GOLD   = colors.HexColor("#9A7D0A")
BLUE   = colors.HexColor("#1A5276")
SLATE  = colors.HexColor("#2E4057")
ROSE   = colors.HexColor("#7B241C")
WHITE  = colors.white
DGREY  = colors.HexColor("#2C3E50")
MGREY  = colors.HexColor("#BDC3C7")
LGREY  = colors.HexColor("#F4F6F9")
LNAVY  = colors.HexColor("#EAF0FB")
LTEAL  = colors.HexColor("#E8F8F5")
LGREEN = colors.HexColor("#EAFAF1")
LRED   = colors.HexColor("#FDEDEC")
LORANGE= colors.HexColor("#FEF5E7")
LPURPLE= colors.HexColor("#F5EEF8")
LBLUE  = colors.HexColor("#EBF5FB")
LGOLD  = colors.HexColor("#FDFAE5")

FULL = 7.5 * inch

def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY, leading=11)
    base.update(kw)
    return ParagraphStyle(name, **base)

def C(txt, **kw):  return Paragraph(str(txt), PS("_", **kw))
def CB(txt, **kw): kw.setdefault("fontName","Helvetica-Bold"); return C(txt, **kw)
def CW(txt, **kw): kw["textColor"]=WHITE; kw.setdefault("fontName","Helvetica-Bold"); return C(txt, **kw)
def CC(txt, **kw): kw["alignment"]=TA_CENTER; return C(txt, **kw)

SP = lambda n=0.06: Spacer(1, n*inch)

# ── Page callback ─────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-54, PAGE_W, 54, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-58, PAGE_W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 17)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-32,
        "TOP 100 NCLEX CONCEPTS: Pre-Exam Checklist")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-48,
        "Check off each concept as you master it  •  Review any unchecked items before exam day  •  You've got this! 🎓")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"NCLEX Prep Series  •  For educational use only  •  Page {doc.page}")

# ── Section header helper ─────────────────────────────────────────────────────
def sec(num, title, subtitle, bg, light):
    rows = [[CW(f"  SECTION {num}:  {title.upper()}", fontSize=10, alignment=TA_LEFT)]]
    if subtitle:
        rows.append([C(f"  {subtitle}", fontSize=7.5,
                       textColor=colors.HexColor("#D5D8DC"), leading=10)])
    t = Table(rows, colWidths=[FULL])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),bg),
        ("TOPPADDING",(0,0),(-1,-1),5),
        ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ]))
    return t

# ── Checklist item builder ─────────────────────────────────────────────────────
def items(concepts, bg_light):
    """
    concepts = list of (num, concept_title, one_line_explanation)
    Returns a Table with two columns (left half, right half) for compact layout.
    """
    rows = []
    for n, title, explanation in concepts:
        rows.append((n, title, explanation))

    # Split into two columns
    mid = (len(rows) + 1) // 2
    left  = rows[:mid]
    right = rows[mid:]

    # Pad right to same length
    while len(right) < len(left):
        right.append(None)

    table_rows = []
    for l, r in zip(left, right):
        lc = _item_cell(l, bg_light) if l else Paragraph("", PS("e"))
        rc = _item_cell(r, bg_light) if r else Paragraph("", PS("e"))
        table_rows.append([lc, rc])

    COL = (FULL - 0.15*inch) / 2
    t = Table(table_rows, colWidths=[COL, COL], hAlign="LEFT")
    t.setStyle(TableStyle([
        ("TOPPADDING",    (0,0),(-1,-1), 1.5),
        ("BOTTOMPADDING", (0,0),(-1,-1), 1.5),
        ("LEFTPADDING",   (0,0),(-1,-1), 0),
        ("RIGHTPADDING",  (0,0),(-1,-1), 4),
        ("VALIGN",        (0,0),(-1,-1), "TOP"),
    ]))
    return t

def _item_cell(item, bg):
    if item is None:
        return Paragraph("", PS("e"))
    n, title, explanation = item
    content = (
        f'<font name="Helvetica-Bold" size="8" color="#2C3E50">☐  {n}. {title}</font><br/>'
        f'<font name="Helvetica" size="7" color="#5D6D7E">     {explanation}</font>'
    )
    p = Paragraph(content, PS("ic", leading=12, backColor=bg))
    box = Table([[p]], colWidths=[(FULL - 0.15*inch)/2 - 4])
    box.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("TOPPADDING",(0,0),(-1,-1), 3),
        ("BOTTOMPADDING",(0,0),(-1,-1), 3),
        ("LEFTPADDING",(0,0),(-1,-1), 3),
        ("RIGHTPADDING",(0,0),(-1,-1), 3),
        ("BOX",(0,0),(-1,-1), 0.3, MGREY),
    ]))
    return box

# ═════════════════════════════════════════════════════════════════════════════
# QUESTION DATA
# ═════════════════════════════════════════════════════════════════════════════

S1 = [  # SAFETY & MANAGEMENT (1–14)
    (1,  "ABC Priority Framework",
         "Airway > Breathing > Circulation — always address in this order unless cardiac arrest (then CAB)"),
    (2,  "Maslow's Hierarchy in NCLEX",
         "Physiological needs first, then safety, then psychosocial. Physiological needs = priority over emotional needs"),
    (3,  "SATA Strategy",
         "Treat each option independently as True or False. All correct options MUST be selected — partial credit = 0"),
    (4,  "Delegation to LPN",
         "LPN can: give medications (not IV push), care for stable patients, perform wound care, insert urinary catheters"),
    (5,  "Delegation to UAP",
         "UAP can: VS on stable patients, basic ADLs (bathing, feeding), I&O, transport. CANNOT assess or teach"),
    (6,  "What RN Cannot Delegate",
         "Assessment, teaching, IV push medications, care of unstable/new admissions, blood transfusion initiation"),
    (7,  "Infection Control: Standard Precautions",
         "Hand hygiene + gloves for ALL patients. Add mask/gown/goggles when splash risk. Applies to blood, body fluids, non-intact skin"),
    (8,  "Contact Precautions",
         "Gloves + gown. Private room. MRSA, VRE, C. diff, wound infections, scabies, norovirus"),
    (9,  "Droplet Precautions",
         "Surgical mask within 3 feet. Private room. Influenza, meningitis, pertussis, mumps, rubella, Group A strep pharyngitis"),
    (10, "Airborne Precautions",
         "N95 respirator, negative pressure room. TB, measles (rubeola), chickenpox (varicella), disseminated herpes zoster"),
    (11, "Priority Question Strategy",
         "'Most important first action' = ASSESSMENT before INTERVENTION unless patient safety is immediately threatened"),
    (12, "Triage: Most Critical First",
         "Unstable before stable. New symptom before chronic. Life-threatening before non-life-threatening"),
    (13, "Informed Consent",
         "Nurse witnesses signature only. Provider explains procedure, risks, benefits, alternatives. Patient must be competent and not coerced"),
    (14, "Advance Directives / DNR",
         "DNR = no CPR, no intubation. Still provide comfort care, pain meds, oxygen. Honor unless patient revokes"),
]

S2 = [  # CARDIOVASCULAR (15–24)
    (15, "MI: Priority Nursing Actions",
         "MONA: Morphine, Oxygen, Nitroglycerin, Aspirin. 12-lead ECG first. Aspirin 325 mg immediately. Notify provider STAT"),
    (16, "Troponin Timing",
         "Rises 3–6 hrs after MI, peaks 14–18 hrs, stays elevated 7–10 days. Most specific cardiac marker — any elevation is abnormal"),
    (17, "Heart Failure: Left vs Right",
         "Left HF → pulmonary edema (crackles, SOB, pink frothy sputum). Right HF → systemic edema (JVD, peripheral edema, ascites)"),
    (18, "Digoxin Toxicity Signs",
         "Bradycardia, nausea/vomiting, yellow-green halos. Hold if HR <60 or K⁺ <3.5. Antidote: Digibind"),
    (19, "Hypertensive Emergency",
         "BP >180/120 + end-organ damage. IV labetalol or nicardipine. Lower BP gradually (25% in first hour) — NOT rapidly"),
    (20, "Warfarin (INR) Monitoring",
         "Therapeutic INR: 2.0–3.0 (2.5–3.5 for mechanical heart valves). Hold if INR >3.0. Antidote: Vitamin K / FFP"),
    (21, "Heparin (aPTT) Monitoring",
         "Therapeutic aPTT: 45–80 sec (1.5–2× normal). Hold if >80 sec. Antidote: Protamine sulfate"),
    (22, "Peripheral Artery Disease (PAD) vs DVT",
         "PAD: pale, pulseless, pain, paresthesia, paralysis (6 Ps) — ARTERIAL. DVT: warm, red, swollen calf — VENOUS"),
    (23, "Post-Cardiac Cath Care",
         "Check insertion site every 15 min × 1 hr. Keep limb straight 4–6 hrs. Monitor distal pulses. Push fluids to flush contrast dye"),
    (24, "BNP and Heart Failure",
         "BNP >100 pg/mL = HF likely. >400 = strong indicator. Used to diagnose and monitor response to treatment"),
]

S3 = [  # RESPIRATORY (25–32)
    (25, "ABG: ROME Method",
         "Respiratory = Opposite (pH↓/PaCO₂↑). Metabolic = Equal (pH↓/HCO₃↓). Normal: pH 7.35–7.45, PaCO₂ 35–45, HCO₃ 22–26"),
    (26, "COPD: Priority Concern",
         "Low-flow oxygen (1–3 L/min via nasal cannula). High-flow O₂ removes hypoxic drive → respiratory arrest. Target SaO₂ 88–92%"),
    (27, "Asthma: Peak Flow Zones",
         "Green (80–100% personal best) = OK. Yellow (50–80%) = caution, increase meds. Red (<50%) = emergency, call 911"),
    (28, "Chest Tube: Priority Checks",
         "Water-seal chamber: tidaling (rises with inspiration) = normal. Continuous bubbling in water seal = air leak. Clamp only briefly"),
    (29, "Pneumothorax vs Hemothorax",
         "Pneumothorax: absent breath sounds, tracheal deviation AWAY from affected side (tension). Hemothorax: dullness to percussion"),
    (30, "Pulmonary Embolism (PE) Signs",
         "Sudden chest pain, SOB, tachycardia, hemoptysis. Risk factors: Virchow's triad (stasis, hypercoagulability, vessel damage)"),
    (31, "TB: Precautions & Treatment",
         "Airborne precautions (N95, negative pressure room). Treatment: RIPE — Rifampin, Isoniazid, Pyrazinamide, Ethambutol × 2 months; then RI × 4 months"),
    (32, "Ventilator Alarm: High Pressure",
         "First action: assess the patient. Common causes: coughing, biting tube, secretions, bronchospasm, pneumothorax"),
]

S4 = [  # FLUIDS, ELECTROLYTES & RENAL (33–44)
    (33, "Hyponatremia (Na⁺ <135)",
         "Signs: confusion, seizures, cerebral edema. Causes: SIADH, HF, cirrhosis, overhydration. Correct SLOWLY (max 8–10 mEq/L/24 hr)"),
    (34, "Hypernatremia (Na⁺ >145)",
         "Signs: thirst, dry mucosa, confusion, lethargy. Causes: DI, dehydration, excess Na intake. Treatment: hypotonic fluids slowly"),
    (35, "Hypokalemia (K⁺ <3.5)",
         "Signs: muscle weakness, leg cramps, dysrhythmias, U waves on ECG. Causes: diuretics, vomiting, NG suction. Hold digoxin if K⁺ <3.5"),
    (36, "Hyperkalemia (K⁺ >5.0)",
         "Signs: peaked T waves, wide QRS, bradycardia → V-fib. STAT ECG. Calcium gluconate (cardiac protection), insulin+dextrose (shift K in)"),
    (37, "Hypocalcemia Signs",
         "Chvostek's sign (tap cheek → facial twitch), Trousseau's sign (BP cuff → carpal spasm), tetany, seizures. Give IV calcium gluconate"),
    (38, "Magnesium Toxicity (Eclampsia Drip)",
         "Loss of DTRs (first sign), respiratory depression (fatal). Antidote: Calcium gluconate. Keep at bedside during Mg sulfate infusion"),
    (39, "IV Fluid Selection",
         "Isotonic (NS, LR): volume expansion. Hypotonic (½NS, D5W): cellular dehydration/hypernatremia. Hypertonic (3% NaCl): severe hyponatremia, cerebral edema"),
    (40, "AKI vs CKD",
         "AKI: sudden rise in Cr/BUN, oliguria, reversible. CKD: chronic Cr >1.2, GFR <60 × 3 months, irreversible. Both → ↑K⁺, ↓HCO₃"),
    (41, "Dialysis Indications (AEIOU)",
         "A-Acidosis, E-Electrolytes (K⁺ >6.5), I-Intoxication, O-Volume Overload, U-Uremia. AV fistula = no BP/sticks on that arm"),
    (42, "Anion Gap Formula",
         "AG = Na − (Cl + HCO₃). Normal 8–12. High AG = MUDPILES: Methanol, Uremia, DKA, Propylene glycol, Isoniazid, Lactic acidosis, Ethylene glycol, Salicylates"),
    (43, "Dehydration vs SIADH",
         "Dehydration: ↑Na, ↑BUN, ↑specific gravity, ↓urine output. SIADH: ↓Na, ↓serum osmolality, ↑urine specific gravity (concentrated urine despite ↓serum Na)"),
    (44, "Pre/Post-Renal Differentiation",
         "BUN:Cr >20:1 = pre-renal (dehydration, HF, GI bleed). <10:1 = intrinsic renal (ATN). BUN:Cr 10–20:1 = normal or mixed"),
]

S5 = [  # ENDOCRINE (45–52)
    (45, "DKA vs HHS — Key Differences",
         "DKA: Type 1 DM, rapid onset, ketones, pH <7.3, glucose 250–600. HHS: Type 2 DM, insidious, NO ketones, pH normal, glucose >600"),
    (46, "DKA Management Priority",
         "1. IV normal saline (fluid first). 2. Insulin drip (AFTER fluids). 3. K⁺ replacement (check K⁺ before insulin — if <3.5, hold insulin)"),
    (47, "Hypoglycemia Treatment (15-15 Rule)",
         "If glucose <70 and conscious: 15g fast-acting carbs (4 oz juice, glucose tabs). Recheck in 15 min. If unconscious: D50W IV"),
    (48, "Insulin Types & Peak Times",
         "Lispro/Aspart (rapid): peak 1–2 hr. Regular (short): peak 2–4 hr. NPH (intermediate): peak 4–12 hr. Glargine/Detemir: NO peak (basal)"),
    (49, "Hypothyroid vs Hyperthyroid",
         "Hypo (↑TSH, ↓T4): cold, constipated, weight gain, bradycardia, myxedema. Hyper (↓TSH, ↑T4): hot, diarrhea, weight loss, tachycardia, exophthalmos"),
    (50, "Cushing's Syndrome Signs",
         "Moon face, buffalo hump, purple striae, central obesity, hyperglycemia, hypertension, hypokalemia, thin skin, immunosuppression"),
    (51, "Addison's Disease Crisis",
         "Hypotension, ↓Na, ↑K, hypoglycemia, hyperpigmentation. Addisonian crisis = shock. Treatment: hydrocortisone IV + NS + dextrose"),
    (52, "SIADH vs DI",
         "SIADH: too much ADH → concentrated urine, dilute blood (↓Na, ↓osmolality). DI: no ADH → dilute urine (specific gravity <1.005), concentrated blood (↑Na)"),
]

S6 = [  # NEUROLOGICAL (53–60)
    (53, "Stroke: FAST & tPA Criteria",
         "FAST: Face drooping, Arm weakness, Speech difficulty, Time to call 911. tPA within 3–4.5 hrs of symptom onset. Contraindication: recent bleeding/surgery"),
    (54, "Increased ICP: Cushing's Triad",
         "Hypertension + bradycardia + irregular respirations = late sign of herniation. HOB 30°, avoid neck flexion, NO Trendelenburg"),
    (55, "Glasgow Coma Scale (GCS)",
         "Eyes (1–4) + Verbal (1–5) + Motor (1–6) = 3–15. Score ≤8 = intubate. Best possible = 15. Score 13–15 = mild TBI"),
    (56, "Seizure Nursing Priority",
         "Do NOT restrain. Turn to side (aspiration prevention). Time the seizure. Protect from injury. O₂ and suction ready. Post-ictal: reorient"),
    (57, "Spinal Cord Injury Levels",
         "C1–C4: ventilator-dependent. C5–C6: some arm function. T1–T4: paraplegia. T5 and below: progressively better leg function"),
    (58, "Autonomic Dysreflexia",
         "SCI above T6. Sudden severe hypertension + pounding headache + flushing. CAUSE: bladder/bowel distension. Sit up + empty bladder FIRST"),
    (59, "Meningitis Precautions",
         "Bacterial meningitis: droplet precautions × 24 hrs of antibiotics. Kernig's sign (knee can't extend when hip flexed), Brudzinski's sign"),
    (60, "Myasthenia Gravis vs GBS",
         "MG: weakness worsens with activity, improves with rest. Ptosis. Anticholinesterase meds. GBS: ascending paralysis after infection, areflexia"),
]

S7 = [  # PHARMACOLOGY (61–72)
    (61, "High-Alert Medications",
         "Anticoagulants, insulin, opioids, concentrated electrolytes (KCl, hypertonic NaCl). Require double-check verification before administration"),
    (62, "Opioid Side Effects & Antidote",
         "Respiratory depression, constipation, urinary retention, sedation, miosis. Antidote: Naloxone (Narcan). Assess resp rate before giving"),
    (63, "ACE Inhibitors: -pril Drugs",
         "Lisinopril, enalapril, captopril. Monitor: K⁺ (↑), Cr (↑). Side effect: dry cough (switch to ARB). Teratogenic — avoid in pregnancy"),
    (64, "Beta-Blockers: -olol Drugs",
         "Metoprolol, atenolol, carvedilol. Hold if HR <50 or SBP <90. NEVER abruptly stop — rebound hypertension/MI. Mask hypoglycemia signs"),
    (65, "Corticosteroid Side Effects",
         "Hyperglycemia, ↓K⁺, Na/fluid retention, immunosuppression, osteoporosis, Cushing's features. NEVER stop abruptly — taper required"),
    (66, "Metformin: When to Hold",
         "Hold 48 hrs before and after contrast dye. Hold if eGFR <30. Risk of lactic acidosis (NOT nephrotoxic itself). Supplement B12 long-term"),
    (67, "Lithium Toxicity",
         "Therapeutic: 0.6–1.2 mEq/L. Toxic: >1.5. Signs: tremor, ataxia, confusion. Caused by: low Na diet, dehydration, NSAIDs, diuretics. Antidote: none — supportive"),
    (68, "Phenytoin (Dilantin) Key Facts",
         "Therapeutic: 10–20 µg/mL. Correct for albumin. Toxicity: SAND (Slurred speech, Ataxia, Nystagmus, Diplopia). IV: no faster than 50 mg/min"),
    (69, "Aminoglycosides Toxicity",
         "Nephrotoxic + ototoxic. Monitor peak and trough levels. Check BUN/Cr daily. Avoid loop diuretics concurrently. Baseline audiology"),
    (70, "Statins: When to Hold",
         "Hold if CK >10× ULN (rhabdomyolysis risk). Check LFTs at baseline. Monitor for unexplained muscle pain — myopathy warning sign"),
    (71, "Antipsychotics: EPS Signs",
         "Extrapyramidal: akathisia, dystonia, tardive dyskinesia, pseudo-parkinsonism. Treat with benztropine. Clozapine: weekly CBC for agranulocytosis"),
    (72, "SSRIs + Serotonin Syndrome",
         "Do NOT combine SSRIs with MAOIs (wait 14 days). Serotonin syndrome: hyperthermia, agitation, clonus, diaphoresis, diarrhea. Treatment: cyproheptadine"),
]

S8 = [  # MATERNAL-NEWBORN (73–80)
    (73, "Pre-eclampsia vs Eclampsia",
         "Pre-eclampsia: BP >140/90, proteinuria ≥300 mg/24hr, edema after 20 weeks. Eclampsia = seizures. Treatment: Mg sulfate + antihypertensives"),
    (74, "HELLP Syndrome",
         "Hemolysis, Elevated Liver enzymes, Low Platelets. Emergency — may need immediate delivery. Severe epigastric pain is a red flag"),
    (75, "Placenta Previa vs Abruption",
         "Previa: painless bright red bleeding (low-lying placenta). Abruption: painful dark bleeding + rigid board-like abdomen + fetal distress"),
    (76, "Fetal Heart Rate Decelerations",
         "Early decels: head compression (normal). Variable: cord compression. Late decels: uteroplacental insufficiency (EMERGENCY — turn, O₂, call provider)"),
    (77, "Postpartum Hemorrhage (PPH)",
         "Blood loss >500 mL (vaginal) or >1,000 mL (C-section). Boggy uterus = #1 cause. Fundal massage + oxytocin first. Check for lacerations"),
    (78, "Newborn APGAR Score",
         "Appearance, Pulse, Grimace, Activity, Respiration. 0–3: immediate resuscitation. 4–6: stimulate. 7–10: normal. Assessed at 1 and 5 minutes"),
    (79, "Magnesium Sulfate Monitoring",
         "For pre-eclampsia/eclampsia. Monitor: DTRs (loss = first toxicity sign), resp rate (hold if <12), urine output (hold if <25–30 mL/hr). Antidote: calcium gluconate"),
    (80, "Postpartum BUBBLE-HE",
         "Breasts, Uterus (firm, midline), Bladder (voiding), Bowel, Lochia (color/odor/amount), Episiotomy/incision, Homans sign, Emotions"),
]

S9 = [  # PEDIATRICS & MENTAL HEALTH (81–90)
    (81, "Pediatric Vital Sign Norms",
         "Newborn HR: 100–160. Toddler HR: 80–130. Infant RR: 30–60. Toddler RR: 20–30. BP increases with age. Weight doubles by 6 months, triples by 1 year"),
    (82, "Epiglottitis vs Croup",
         "Epiglottitis: sudden, NO cough, drooling, tripod position, muffled voice. MEDICAL EMERGENCY — do NOT use tongue blade. Croup: barking cough, inspiratory stridor, gradual"),
    (83, "Febrile Seizure Management",
         "Common in children 6 months–5 years. Protect from injury, time seizure, do NOT restrain. Antipyretics after. Reassure parents (usually benign)"),
    (84, "Pyloric Stenosis",
         "Projectile non-bilious vomiting in newborns 2–8 weeks. Olive-shaped mass in RUQ. Metabolic alkalosis (↑pH, ↓K, ↓Cl). Surgery: pyloromyotomy"),
    (85, "Therapeutic Communication Rules",
         "Open-ended questions. Reflect feelings. Avoid: false reassurance ('everything will be OK'), why questions, changing subject, giving advice"),
    (86, "Suicide Risk Assessment",
         "Ask directly about plan, means, intent, timeline. Remove means from environment. 1:1 supervision for high risk. Contract for safety"),
    (87, "Schizophrenia: Positive vs Negative",
         "Positive symptoms: hallucinations, delusions, disorganized speech (added behaviors). Negative: flat affect, alogia, avolition, anhedonia (absent behaviors)"),
    (88, "Alcohol Withdrawal Timeline",
         "6–24 hrs: anxiety, tremors, diaphoresis. 24–48 hrs: seizures. 48–72 hrs: delirium tremens (DTs) — hallucinations, hyperthermia, tachycardia. Treat with benzodiazepines"),
    (89, "Antidepressants: SSRI vs MAOI",
         "SSRIs: first-line (sertraline, fluoxetine). MAOIs: avoid tyramine foods (aged cheese, cured meats, beer). 14-day washout between SSRIs and MAOIs"),
    (90, "Eating Disorders: Priority",
         "Anorexia nervosa: lowest weight, medical instability, refeeding syndrome risk (↓K, ↓P, ↓Mg on refeeding). Monitor electrolytes during refeeding"),
]

S10 = [  # LAB VALUES & CLINICAL CALCULATIONS (91–100)
    (91, "Critical K⁺ Values",
         "Normal 3.5–5.0 mEq/L. Critical: <2.5 or >6.5. ECG changes: low K = U waves; high K = peaked T waves, wide QRS. STAT ECG for both"),
    (92, "Critical Glucose Values",
         "Normal fasting 70–100. Critical: <40 (D50W IV) or >500 (insulin + fluids). HbA1c <5.7% normal; ≥6.5% = DM diagnosis"),
    (93, "Corrected Calcium Formula",
         "Corrected Ca = Measured Ca + 0.8 × (4 − Albumin). Low albumin causes pseudohypocalcemia — always correct before treating"),
    (94, "BUN:Creatinine Ratio",
         ">20:1 = pre-renal (dehydration, GI bleed). 10–20:1 = normal. <10:1 = intrinsic renal disease or low protein intake"),
    (95, "IV Drip Rate Calculation",
         "mL/hr = (Volume to infuse ÷ Time in hours). Drops/min = (Volume × Drop factor) ÷ (Time in minutes). Check pump settings with 2 nurses"),
    (96, "Pediatric Weight-Based Dosing",
         "Safe dose range: ordered dose must fall between minimum and maximum mg/kg. Calculate: child weight in kg × mg/kg = safe dose range"),
    (97, "Urine Specific Gravity",
         "Normal: 1.010–1.030. <1.010 = dilute (DI, overhydration, SIADH early). >1.030 = concentrated (dehydration, SIADH late, fever)"),
    (98, "Therapeutic Drug Levels",
         "Digoxin 0.5–2.0; Lithium 0.6–1.2; Phenytoin 10–20; Vancomycin trough 10–20; Theophylline 10–20; all in respective units"),
    (99, "Blood Transfusion Reactions",
         "Acute hemolytic (STOP immediately, NS only, urine output): fever, chills, back/flank pain, hypotension within minutes. Most dangerous reaction"),
    (100,"Oxygen Delivery Devices",
         "Nasal cannula 1–6 L/min (24–44% FiO₂). Simple mask 6–10 L/min (35–60%). Non-rebreather mask 10–15 L/min (~80–90%). Venturi mask = precise FiO₂"),
]

# ═════════════════════════════════════════════════════════════════════════════
# BUILD STORY
# ═════════════════════════════════════════════════════════════════════════════
story = []
story.append(SP(0.08))

# Intro box
intro = Table([[Paragraph(
    "<b>HOW TO USE:</b>  Work through each section and check off (☐ → ✓) every concept you can explain confidently.  "
    "Anything unchecked = review before exam day.  "
    "<b>Goal: 100% checked before sitting for NCLEX.</b>  "
    "Use this alongside your flashcards, cheat sheet, and study schedule for maximum results.",
    PS("ib", fontSize=8, textColor=NAVY, leading=12, fontName="Helvetica"))
]], colWidths=[FULL])
intro.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), LNAVY),
    ("BOX",(0,0),(-1,-1),1.5,NAVY),
    ("TOPPADDING",(0,0),(-1,-1),7),("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10),
]))
story.append(intro)
story.append(SP(0.10))

SECTIONS = [
    (1, "Safety, Delegation & Management", "Concepts 1–14 | Highest-yield NCLEX area (26–38% of exam)", NAVY, LNAVY, S1),
    (2, "Cardiovascular Nursing",          "Concepts 15–24 | MI, HF, dysrhythmias, anticoagulation",       RED,   LRED,   S2),
    (3, "Respiratory Nursing",             "Concepts 25–32 | ABGs, COPD, PE, TB, chest tubes",              TEAL,  LTEAL,  S3),
    (4, "Fluids, Electrolytes & Renal",    "Concepts 33–44 | Electrolyte imbalances, IV fluids, AKI/CKD",   BLUE,  LBLUE,  S4),
    (5, "Endocrine Nursing",               "Concepts 45–52 | DKA, HHS, thyroid, adrenal, DI vs SIADH",      PURPLE,LPURPLE,S5),
    (6, "Neurological Nursing",            "Concepts 53–60 | Stroke, ICP, seizures, spinal cord",            SLATE, LGREY,  S6),
    (7, "Pharmacology",                    "Concepts 61–72 | High-alert drugs, toxicities, antidotes",       GREEN, LGREEN, S7),
    (8, "Maternal-Newborn Nursing",        "Concepts 73–80 | Pre-eclampsia, fetal monitoring, postpartum",   ROSE,  LRED,   S8),
    (9, "Pediatrics & Mental Health",      "Concepts 81–90 | Pediatric emergencies, therapeutic communication", ORANGE, LORANGE, S9),
    (10,"Lab Values & Calculations",       "Concepts 91–100 | Critical labs, formulas, drug levels",         GOLD,  LGOLD,  S10),
]

for num, title, subtitle, bg, light, data in SECTIONS:
    story.append(sec(num, title, subtitle, bg, light))
    story.append(items(data, light))
    story.append(SP(0.10))

# Final score tracker
story.append(SP(0.05))
score_data = [
    [CC("SECTION", fontName="Helvetica-Bold", textColor=WHITE, fontSize=8),
     CC("TOPIC", fontName="Helvetica-Bold", textColor=WHITE, fontSize=8),
     CC("CONCEPTS", fontName="Helvetica-Bold", textColor=WHITE, fontSize=8),
     CC("# CHECKED", fontName="Helvetica-Bold", textColor=WHITE, fontSize=8),
     CC("% MASTERED", fontName="Helvetica-Bold", textColor=WHITE, fontSize=8),
     CC("READY?", fontName="Helvetica-Bold", textColor=WHITE, fontSize=8)],
    [CC("1"), C("Safety & Management"),        CC("14"), CC("___ / 14"), CC("______"), CC("☐")],
    [CC("2"), C("Cardiovascular"),              CC("10"), CC("___ / 10"), CC("______"), CC("☐")],
    [CC("3"), C("Respiratory"),                 CC("8"),  CC("___ / 8"),  CC("______"), CC("☐")],
    [CC("4"), C("Fluids, Electrolytes & Renal"),CC("12"), CC("___ / 12"), CC("______"), CC("☐")],
    [CC("5"), C("Endocrine"),                   CC("8"),  CC("___ / 8"),  CC("______"), CC("☐")],
    [CC("6"), C("Neurological"),                CC("8"),  CC("___ / 8"),  CC("______"), CC("☐")],
    [CC("7"), C("Pharmacology"),                CC("12"), CC("___ / 12"), CC("______"), CC("☐")],
    [CC("8"), C("Maternal-Newborn"),            CC("8"),  CC("___ / 8"),  CC("______"), CC("☐")],
    [CC("9"), C("Pediatrics & Mental Health"),  CC("10"), CC("___ / 10"), CC("______"), CC("☐")],
    [CC("10"),C("Lab Values & Calculations"),   CC("10"), CC("___ / 10"), CC("______"), CC("☐")],
    [CB("TOTAL", textColor=NAVY, alignment=TA_CENTER), C("All Sections"),
     CB("100", textColor=NAVY, alignment=TA_CENTER),
     CB("___ / 100", textColor=NAVY, alignment=TA_CENTER),
     CB("______%", textColor=NAVY, alignment=TA_CENTER),
     CB("🎓 READY!", textColor=GREEN, alignment=TA_CENTER, fontSize=9)],
]
score_tbl = Table(score_data, colWidths=[0.6*inch, 2.4*inch, 0.75*inch, 0.95*inch, 0.95*inch, 0.8*inch])
score_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), DGREY),
    ("TEXTCOLOR",(0,0),(-1,0), WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("BACKGROUND",(0,-1),(-1,-1), LGREEN),
    ("FONTNAME",(0,-1),(-1,-1),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-2),[LGREY, WHITE]),
    ("GRID",(0,0),(-1,-1),0.4,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("ALIGN",(0,0),(0,-1),"CENTER"),
    ("ALIGN",(2,0),(5,-1),"CENTER"),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))

story.append(sec(0, "MASTERY SELF-ASSESSMENT TRACKER", "Log how many concepts you have checked off per section", GREEN, LGREEN))
story.append(score_tbl)
story.append(SP(0.08))

final = Table([[Paragraph(
    '<b>"Nursing is not for everyone. It takes a very strong, intelligent, and compassionate person to take on '
    'the aches of others without breaking down themselves." — You are that person.</b>  '
    "You have studied hard. You have prepared. Now go pass that NCLEX! 🎓",
    PS("fin", fontSize=9, textColor=NAVY, alignment=TA_CENTER,
       fontName="Helvetica-Bold", leading=13))]], colWidths=[FULL])
final.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), LGOLD),
    ("BOX",(0,0),(-1,-1),1.5,GOLD),
    ("TOPPADDING",(0,0),(-1,-1),8),("BOTTOMPADDING",(0,0),(-1,-1),8),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10),
]))
story.append(final)

# ── Build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.80*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_top100_checklist.py 2>…

Running Command

ls -lh /home/daytona/workspace/nclex-labs/NCLEX_Top100_Concepts_Checklist.pdf

Your checklist PDF is ready!

NCLEX Top100 Concepts Checklist

PDF Document · PDF


What's Inside

100 must-know concepts organized across 10 color-coded sections, each with a one-line explanation and a checkbox:
#SectionConceptsColor
1Safety, Delegation & Management1–14Navy
2Cardiovascular Nursing15–24Red
3Respiratory Nursing25–32Teal
4Fluids, Electrolytes & Renal33–44Blue
5Endocrine Nursing45–52Purple
6Neurological Nursing53–60Slate
7Pharmacology61–72Green
8Maternal-Newborn Nursing73–80Rose
9Pediatrics & Mental Health81–90Orange
10Lab Values & Calculations91–100Gold
Final page: Mastery Self-Assessment Tracker — log how many concepts checked per section, with a % mastered column and "Ready?" checkboxes.

How to Use It

  1. Print and work through each section as you study
  2. Check off (☐ → ✓) each concept you can explain without looking
  3. Any unchecked items on exam week = your targeted review list
  4. Goal: 100/100 checked before sitting down for NCLEX
This pairs perfectly with the flashcard deck, lab values cheat sheet, drug-lab guide, and study schedule from your NCLEX prep collection. You have a complete system now — go use it! 🎓

Design a critical thinking worksheet for analyzing NCLEX test questions

Writing File

~/nclex-labs/generate_critical_thinking_worksheet.py

"""
NCLEX Critical Thinking Worksheet — ReportLab PDF
Page 1: Step-by-step question analysis framework (fillable)
Page 2: Practice question walk-through template × 3 questions
Page 3: Common mistake tracker + personal strategy notes
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Critical_Thinking_Worksheet.pdf"

PAGE_W, PAGE_H = letter

# ── Palette ───────────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#1B2A4A")
TEAL   = colors.HexColor("#0B7A75")
RED    = colors.HexColor("#B03A2E")
ORANGE = colors.HexColor("#D9622B")
GREEN  = colors.HexColor("#1D6A39")
PURPLE = colors.HexColor("#5B2C6F")
GOLD   = colors.HexColor("#9A7D0A")
BLUE   = colors.HexColor("#1A5276")
SLATE  = colors.HexColor("#2E4057")
WHITE  = colors.white
DGREY  = colors.HexColor("#2C3E50")
MGREY  = colors.HexColor("#BDC3C7")
LGREY  = colors.HexColor("#F4F6F9")
XLGREY = colors.HexColor("#FAFAFA")
LNAVY  = colors.HexColor("#EAF0FB")
LTEAL  = colors.HexColor("#E8F8F5")
LGREEN = colors.HexColor("#EAFAF1")
LRED   = colors.HexColor("#FDEDEC")
LPURPLE= colors.HexColor("#F5EEF8")
LBLUE  = colors.HexColor("#EBF5FB")
LORANGE= colors.HexColor("#FEF5E7")
LGOLD  = colors.HexColor("#FDFAE5")

FULL = 7.5 * inch

def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY, leading=11)
    base.update(kw)
    return ParagraphStyle(name, **base)

def C(t, **kw):  return Paragraph(str(t), PS("_", **kw))
def CB(t, **kw): kw.setdefault("fontName","Helvetica-Bold"); return C(t, **kw)
def CW(t, **kw): kw["textColor"]=WHITE; kw.setdefault("fontName","Helvetica-Bold"); return C(t, **kw)
def CC(t, **kw): kw["alignment"]=TA_CENTER; return C(t, **kw)
def CCB(t,**kw): kw["alignment"]=TA_CENTER; kw.setdefault("fontName","Helvetica-Bold"); return C(t,**kw)

SP = lambda n=0.07: Spacer(1, n*inch)
FULL = 7.5*inch

# ── Line / blank field helper ─────────────────────────────────────────────────
def blank_line(label="", lines=1, width=FULL, bg=XLGREY):
    content = label + "\n" * lines if label else "\n" * lines
    t = Table([[C(content, fontSize=7.8, textColor=DGREY, leading=14)]],
              colWidths=[width])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("BOX",(0,0),(-1,-1),0.5,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),6),("RIGHTPADDING",(0,0),(-1,-1),6),
    ]))
    return t

def lined_box(num_lines=3, width=FULL, bg=XLGREY):
    """Box with horizontal ruled lines for writing."""
    line_h = 0.22 * inch
    rows = [[""] for _ in range(num_lines)]
    t = Table(rows, colWidths=[width], rowHeights=[line_h]*num_lines)
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("BOX",(0,0),(-1,-1),0.8,MGREY),
        ("LINEBELOW",(0,0),(-1,-2),0.4,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),0),
        ("BOTTOMPADDING",(0,0),(-1,-1),0),
        ("LEFTPADDING",(0,0),(-1,-1),5),
    ]))
    return t

def section_banner(title, sub, bg, light=None):
    rows = [[CW(title, fontSize=10, alignment=TA_CENTER)]]
    if sub:
        rows.append([C(sub, fontSize=7.5, textColor=colors.HexColor("#D5D8DC"),
                       alignment=TA_CENTER, leading=10)])
    t = Table(rows, colWidths=[FULL])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),bg),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ]))
    return t

def step_header(step_num, title, color, light):
    t = Table([[
        CCB(f"STEP {step_num}", textColor=WHITE, fontSize=9),
        CB(title, textColor=WHITE, fontSize=9)
    ]], colWidths=[0.70*inch, FULL-0.70*inch])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), color),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),8),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]))
    return t

# ── Page callbacks ────────────────────────────────────────────────────────────
current_page_title = [""]

def on_page(canvas, doc):
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-54, PAGE_W, 54, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-58, PAGE_W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 17)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-32,
        "NCLEX CRITICAL THINKING WORKSHEET")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-48,
        "Analyze every practice question systematically  •  Build reasoning skills  •  Eliminate wrong answers with confidence")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"NCLEX Prep Series  •  Critical Thinking Framework  •  Page {doc.page}")

# =============================================================================
story = []
story.append(SP(0.08))

# ─────────────────────────────────────────────────────────────────────────────
# PAGE 1 — THE 7-STEP CRITICAL THINKING FRAMEWORK
# ─────────────────────────────────────────────────────────────────────────────
story.append(section_banner(
    "THE 7-STEP NCLEX CRITICAL THINKING FRAMEWORK",
    "Apply these steps to EVERY practice question. Slow down — the goal is to build a thinking habit, not guess faster.",
    NAVY))
story.append(SP(0.08))

# Step 1
story.append(step_header(1, "READ THE QUESTION STEM — Identify What Is REALLY Being Asked", RED, LRED))
story.append(SP(0.04))
cues_data = [
    [CB("Key Words to Underline:", textColor=RED),
     C("first, priority, most important, best, immediate, initial, except, NOT, contraindicated, next")],
    [CB("Question Type:", textColor=RED),
     C("☐ Priority/First Action   ☐ Assessment   ☐ Intervention   ☐ SATA   ☐ Teaching/Education   ☐ Safety   ☐ Delegation")],
    [CB("Setting/Scenario:", textColor=RED),
     C("☐ Emergency/Acute   ☐ Post-op   ☐ Chronic/Outpatient   ☐ Pediatric   ☐ OB   ☐ Mental Health   ☐ Community")],
]
cues_tbl = Table(cues_data, colWidths=[1.6*inch, FULL-1.6*inch])
cues_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LRED),("GRID",(0,0),(-1,-1),0.3,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),6),("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(cues_tbl)
story.append(SP(0.06))

# Step 2
story.append(step_header(2, "IDENTIFY THE PATIENT — Who Are They and What's the Clinical Context?", ORANGE, LORANGE))
story.append(SP(0.04))
pt_data = [
    [C("Age / developmental stage:"),  lined_box(1, FULL*0.35, LORANGE),
     C("Diagnosis / chief complaint:"), lined_box(1, FULL*0.35, LORANGE)],
]
pt_tbl = Table(pt_data, colWidths=[1.4*inch, FULL*0.35, 1.55*inch, FULL*0.35-0.3*inch])
pt_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LORANGE),("TOPPADDING",(0,0),(-1,-1),4),
    ("BOTTOMPADDING",(0,0),(-1,-1),4),("LEFTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(pt_tbl)
story.append(SP(0.04))
vitals_row = Table([[
    C("Relevant vitals / labs noted:"),
    lined_box(1, FULL*0.55, LORANGE),
    C("Medications noted:"),
    lined_box(1, FULL*0.22, LORANGE),
]], colWidths=[1.6*inch, FULL*0.55, 1.0*inch, FULL*0.22-0.2*inch])
vitals_row.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LORANGE),("TOPPADDING",(0,0),(-1,-1),3),
    ("BOTTOMPADDING",(0,0),(-1,-1),3),("LEFTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(vitals_row)
story.append(SP(0.06))

# Step 3
story.append(step_header(3, "APPLY YOUR PRIORITY FRAMEWORK Before Looking at Options", GOLD, LGOLD))
story.append(SP(0.04))
pf_data = [
    [CCB("FRAMEWORK", textColor=WHITE, fontSize=8),
     CCB("WHEN TO USE", textColor=WHITE, fontSize=8),
     CCB("APPLIES HERE?", textColor=WHITE, fontSize=8),
     CCB("WHAT IT TELLS YOU", textColor=WHITE, fontSize=8)],
    [CB("ABC"), C("Any acute physiological problem"),
     CC("☐ Yes   ☐ No"), C("Airway > Breathing > Circulation. Address in this order first")],
    [CB("Maslow"), C("Comparing physiological vs psychosocial needs"),
     CC("☐ Yes   ☐ No"), C("Physiological need wins unless all are physiological, then use ABC")],
    [CB("Safety"), C("Risk of harm, fall, infection, medication error"),
     CC("☐ Yes   ☐ No"), C("Prevent harm before addressing comfort or education")],
    [CB("Acute vs Chronic"), C("Multiple patients or multiple problems"),
     CC("☐ Yes   ☐ No"), C("Acute/new onset trumps chronic/expected finding")],
    [CB("Unstable vs Stable"), C("Delegation or assignment questions"),
     CC("☐ Yes   ☐ No"), C("Unstable patient always stays with RN; stable can be delegated")],
]
pf_tbl = Table(pf_data, colWidths=[0.95*inch, 1.85*inch, 1.0*inch, 3.7*inch])
pf_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0),DGREY),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LGOLD,WHITE]),
    ("GRID",(0,0),(-1,-1),0.3,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))
story.append(pf_tbl)
story.append(SP(0.06))

# Step 4
story.append(step_header(4, "ELIMINATE — Cross Off Wrong Answers ONE at a Time", PURPLE, LPURPLE))
story.append(SP(0.04))
elim_data = [
    [CCB("OPTION",textColor=WHITE), CCB("ELIMINATE IF…",textColor=WHITE), CCB("KEEP IF…",textColor=WHITE), CCB("MY REASONING",textColor=WHITE)],
    [CB("A"), C("Contains 'always/never', reassures without action, requires MD order first"),
     C("Directly addresses ABC/safety, is assessment-focused if no acute crisis"),
     lined_box(1, 2.40*inch, LPURPLE)],
    [CB("B"), C("Same as above"),  C("Same as above"), lined_box(1, 2.40*inch, LPURPLE)],
    [CB("C"), C("Same as above"),  C("Same as above"), lined_box(1, 2.40*inch, LPURPLE)],
    [CB("D"), C("Same as above"),  C("Same as above"), lined_box(1, 2.40*inch, LPURPLE)],
]
elim_tbl = Table(elim_data, colWidths=[0.40*inch, 1.95*inch, 2.30*inch, 2.85*inch])
elim_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0),PURPLE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LPURPLE,WHITE]),
    ("GRID",(0,0),(-1,-1),0.3,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("ALIGN",(0,0),(0,-1),"CENTER"),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(elim_tbl)
story.append(SP(0.06))

# Step 5
story.append(step_header(5, "SELECT YOUR ANSWER — Apply the Final Decision Rules", TEAL, LTEAL))
story.append(SP(0.04))
rules_data = [
    [CB("My selected answer:", textColor=TEAL),
     lined_box(1, 1.2*inch, LTEAL),
     CB("Confidence level:", textColor=TEAL),
     C("☐ Sure   ☐ 50/50   ☐ Guessing")],
]
rules_tbl = Table(rules_data, colWidths=[1.45*inch, 1.2*inch, 1.2*inch, 3.65*inch])
rules_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LTEAL),("TOPPADDING",(0,0),(-1,-1),4),
    ("BOTTOMPADDING",(0,0),(-1,-1),4),("LEFTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(rules_tbl)

decision_rules = Table([[Paragraph(
    "<b>Final Decision Rules:</b>  "
    "(1) If two answers both seem right, pick the one that is MORE complete or addresses a higher-level need.  "
    "(2) 'Assess/Evaluate' is usually correct EARLY in a clinical sequence — before intervening.  "
    "(3) 'Notify the provider' is rarely the best FIRST action — do something yourself first unless outside nursing scope.  "
    "(4) Eliminate options that delegate assessment, teaching, or IV push to non-RN staff.  "
    "(5) For SATA: each option is independent — select ALL that are true, even if it feels like too many.",
    PS("dr", fontSize=7.8, textColor=BLUE, leading=11))]], colWidths=[FULL])
decision_rules.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LBLUE),("BOX",(0,0),(-1,-1),0.8,BLUE),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),8),("RIGHTPADDING",(0,0),(-1,-1),8),
]))
story.append(SP(0.04))
story.append(decision_rules)
story.append(SP(0.06))

# Steps 6 & 7 side by side
story.append(step_header(6, "REVIEW THE RATIONALE — Right or Wrong, Learn From Every Question", GREEN, LGREEN))
story.append(SP(0.04))
review_data = [
    [CB("Correct answer was:", textColor=GREEN),
     lined_box(1, 0.5*inch, LGREEN),
     CB("☐ I was correct   ☐ I was wrong", textColor=GREEN),
     CB("Why was the correct answer RIGHT?", textColor=GREEN)],
    [C(""), C(""), C(""), lined_box(2, FULL*0.45, LGREEN)],
    [CB("Why was my answer WRONG\n(if incorrect)?", textColor=GREEN),
     lined_box(2, FULL*0.5, LGREEN), C(""),
     CB("What concept do I need to review?", textColor=GREEN)],
    [C(""), C(""), C(""), lined_box(1, FULL*0.45, LGREEN)],
]
rev_tbl = Table(review_data,
    colWidths=[1.5*inch, FULL*0.5, 0.05*inch, FULL*0.45-0.05*inch])
rev_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LGREEN),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(rev_tbl)
story.append(SP(0.06))

story.append(step_header(7, "CLASSIFY YOUR MISTAKE — Track Patterns to Stop Repeating Errors", SLATE, LGREY))
story.append(SP(0.04))
mistake_types = [
    ("Content Gap",        "I didn't know the information. Need to study this topic."),
    ("Misread Question",   "I didn't read carefully. I missed a key word (NOT, EXCEPT, FIRST)."),
    ("Second-Guessed",     "My first instinct was right but I changed my answer."),
    ("Wrong Framework",    "I didn't apply ABC/Maslow correctly. Picked comfort over safety."),
    ("Option Confusion",   "Two options looked similar. I picked the less complete one."),
    ("SATA Error",         "I under-selected or over-selected. Need to treat each option independently."),
    ("Delegation Error",   "I delegated something the RN cannot delegate (assessment, IV push, unstable pt)."),
    ("Normal vs Abnormal", "I didn't recognize the finding as normal or expected — acted when I shouldn't have."),
]
mk_data = [[CCB("MISTAKE TYPE",textColor=WHITE), CCB("DESCRIPTION",textColor=WHITE),
            CCB("☐ THIS WAS MY ERROR",textColor=WHITE)]]
for t, d in mistake_types:
    mk_data.append([CB(t), C(d), CC("☐")])
mk_tbl = Table(mk_data, colWidths=[1.45*inch, 4.80*inch, 1.25*inch])
mk_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0),SLATE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LGREY,WHITE]),
    ("GRID",(0,0),(-1,-1),0.3,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("ALIGN",(2,0),(2,-1),"CENTER"),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(mk_tbl)

# ─────────────────────────────────────────────────────────────────────────────
# PAGE 2 — PRACTICE QUESTION ANALYSIS TEMPLATE (3 questions)
# ─────────────────────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(SP(0.08))
story.append(section_banner(
    "PRACTICE QUESTION ANALYSIS TEMPLATE",
    "Use this page for each practice session. Photocopy as needed. Date your sessions to track progress.",
    BLUE))
story.append(SP(0.08))

def q_block(q_num, accent, light):
    rows = []
    hdr = Table([[
        CCB(f"Q{q_num}", textColor=WHITE, fontSize=11),
        CW(f"Question {q_num} Analysis", fontSize=9),
        CW("Date: ________________", fontSize=8, alignment=TA_LEFT),
    ]], colWidths=[0.40*inch, 5.5*inch, 1.6*inch])
    hdr.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),accent),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),6),("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]))
    rows.append(hdr)

    # Write question area
    q_box = Table([[C("Write the question stem here (or paste question number/first line):", fontSize=7.5, textColor=DGREY)]],
                  colWidths=[FULL])
    q_box.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),light),("LEFTPADDING",(0,0),(-1,-1),6),
                                ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2)]))
    rows.append(q_box)
    rows.append(lined_box(2, FULL, light))

    # Answer options
    opts = Table([
        [CB("A:", textColor=accent), lined_box(1, FULL*0.38, light),
         CB("B:", textColor=accent), lined_box(1, FULL*0.38, light)],
        [CB("C:", textColor=accent), lined_box(1, FULL*0.38, light),
         CB("D:", textColor=accent), lined_box(1, FULL*0.38, light)],
    ], colWidths=[0.22*inch, FULL*0.38, 0.22*inch, FULL*0.38+0.05*inch])
    opts.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),light),("TOPPADDING",(0,0),(-1,-1),2),
                               ("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4),
                               ("VALIGN",(0,0),(-1,-1),"MIDDLE")]))
    rows.append(opts)

    # Analysis row
    analysis = Table([[
        Table([
            [CB("Key word(s) I underlined:", textColor=accent)],
            [lined_box(1, FULL*0.28-0.1*inch, light)],
            [CB("Question type:", textColor=accent)],
            [C("☐ Priority  ☐ Assessment  ☐ Intervention\n☐ Delegation  ☐ SATA  ☐ Teaching",
               fontSize=7.3, leading=11)],
        ], colWidths=[FULL*0.28-0.1*inch]),
        Table([
            [CB("Framework I used:", textColor=accent)],
            [C("☐ ABC  ☐ Maslow  ☐ Safety  ☐ Acute vs Chronic", fontSize=7.3)],
            [CB("My answer:"), lined_box(1, FULL*0.28-0.1*inch, light)],
            [CB("Correct answer:"), lined_box(1, FULL*0.28-0.1*inch, light)],
        ], colWidths=[FULL*0.28-0.1*inch]),
        Table([
            [CB("Result:", textColor=accent)],
            [C("☐ Correct  ☐ Wrong  ☐ 50/50 guess", fontSize=7.3)],
            [CB("Mistake type:", textColor=accent)],
            [C("☐ Content gap\n☐ Misread question\n☐ Second-guessed\n☐ Wrong framework\n☐ SATA error\n☐ Normal vs abnormal",
               fontSize=7.3, leading=10)],
        ], colWidths=[FULL*0.28-0.1*inch]),
    ]], colWidths=[FULL*0.28, FULL*0.28, FULL*0.28])
    analysis.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),light),
        ("GRID",(0,0),(-1,-1),0.3,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
        ("LEFTPADDING",(0,0),(-1,-1),5),("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    rows.append(analysis)

    # Rationale
    rat_hdr = Table([[CB("What the rationale taught me / concept to review:", textColor=accent, fontSize=7.8)]],
                    colWidths=[FULL])
    rat_hdr.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),light),
                                  ("LEFTPADDING",(0,0),(-1,-1),6),
                                  ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2)]))
    rows.append(rat_hdr)
    rows.append(lined_box(2, FULL, light))

    outer = Table([[item] for item in rows], colWidths=[FULL])
    outer.setStyle(TableStyle([
        ("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),1),
        ("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0),
        ("BOX",(0,0),(-1,-1),1.2,accent),
    ]))
    return outer

story.append(q_block(1, NAVY, LNAVY))
story.append(SP(0.10))
story.append(q_block(2, RED, LRED))
story.append(SP(0.10))
story.append(q_block(3, GREEN, LGREEN))

# ─────────────────────────────────────────────────────────────────────────────
# PAGE 3 — ERROR TRACKER + STRATEGY NOTES
# ─────────────────────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(SP(0.08))
story.append(section_banner(
    "SESSION ERROR TRACKER & PERSONAL STRATEGY NOTES",
    "Complete after every practice session. Patterns = your roadmap to improvement.",
    PURPLE))
story.append(SP(0.08))

# Session log table
story.append(CB("📊  SESSION PERFORMANCE LOG", textColor=PURPLE, fontSize=9))
story.append(SP(0.04))
log_data = [
    [CCB("DATE",textColor=WHITE), CCB("# QUESTIONS",textColor=WHITE), CCB("# CORRECT",textColor=WHITE),
     CCB("% SCORE",textColor=WHITE), CCB("TOPIC FOCUS",textColor=WHITE), CCB("TOP MISTAKE TYPE",textColor=WHITE),
     CCB("TOPICS TO REVIEW",textColor=WHITE)],
]
for _ in range(10):
    log_data.append(["","","","","","",""])
log_tbl = Table(log_data,
    colWidths=[0.72*inch,0.82*inch,0.82*inch,0.72*inch,1.10*inch,1.20*inch,2.08*inch])
log_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0),PURPLE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LPURPLE,WHITE]),
    ("GRID",(0,0),(-1,-1),0.4,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("ALIGN",(0,0),(3,-1),"CENTER"),
    ("FONTSIZE",(0,1),(-1,-1),8),
]))
story.append(log_tbl)
story.append(SP(0.10))

# Error pattern tracker
story.append(CB("🔍  CUMULATIVE ERROR PATTERN TRACKER", textColor=RED, fontSize=9))
story.append(SP(0.04))
error_data = [
    [CCB("MISTAKE TYPE",textColor=WHITE), CCB("TALLY (add mark each time)",textColor=WHITE),
     CCB("TOTAL",textColor=WHITE), CCB("MY CORRECTIVE STRATEGY",textColor=WHITE)],
    [CB("Content Gap"),        lined_box(1, 2.80*inch, LRED), CC(""),
     C("Flag topics → add to study schedule weak areas section")],
    [CB("Misread Question"),   lined_box(1, 2.80*inch, LRED), CC(""),
     C("Underline key words BEFORE reading options. Circle NOT/EXCEPT")],
    [CB("Second-Guessed"),     lined_box(1, 2.80*inch, LRED), CC(""),
     C("Write first instinct. Only change with CLEAR reason, not anxiety")],
    [CB("Wrong Framework"),    lined_box(1, 2.80*inch, LRED), CC(""),
     C("Ask: Is there an ABC issue? If yes → that trumps everything")],
    [CB("Option Confusion"),   lined_box(1, 2.80*inch, LRED), CC(""),
     C("Cover all options. Predict answer before reading choices")],
    [CB("SATA Error"),         lined_box(1, 2.80*inch, LRED), CC(""),
     C("Each option = True/False. Select all TRUE ones, even if 4–5")],
    [CB("Delegation Error"),   lined_box(1, 2.80*inch, LRED), CC(""),
     C("RN CANNOT delegate: assessment, teaching, IV push, unstable patients")],
    [CB("Normal vs Abnormal"), lined_box(1, 2.80*inch, LRED), CC(""),
     C("Review expected post-op/post-procedure findings before exam")],
]
err_tbl = Table(error_data, colWidths=[1.30*inch, 2.80*inch, 0.55*inch, 2.85*inch])
err_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0),RED),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LRED,WHITE]),
    ("GRID",(0,0),(-1,-1),0.4,MGREY),
    ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ("LEFTPADDING",(0,0),(-1,-1),5),("ALIGN",(2,0),(2,-1),"CENTER"),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
]))
story.append(err_tbl)
story.append(SP(0.10))

# Personal strategy notes
story.append(CB("✏️  MY PERSONAL NCLEX STRATEGY NOTES", textColor=NAVY, fontSize=9))
story.append(SP(0.04))

strategy_prompts = [
    ("My biggest content weakness right now:", 2),
    ("The priority framework I keep forgetting to apply:", 1),
    ("Words or phrases I need to underline more carefully:", 1),
    ("Topics I've reviewed this week:", 2),
    ("My score trend (circle): Getting better  /  About the same  /  Needs work", 1),
    ("One thing I will do differently on my next practice session:", 2),
    ("My exam date:", 1),
]
for label, lines in strategy_prompts:
    lbl_tbl = Table([[CB(label, textColor=NAVY, fontSize=7.8)]],
                    colWidths=[FULL])
    lbl_tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),LNAVY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2),
        ("LEFTPADDING",(0,0),(-1,-1),6),
    ]))
    story.append(lbl_tbl)
    story.append(lined_box(lines, FULL, WHITE))
    story.append(SP(0.04))

# Motivational close
final_box = Table([[Paragraph(
    "<b>\"The expert in anything was once a beginner.\"</b>  "
    "Every question you analyze carefully — right or wrong — makes you a sharper thinker.  "
    "Work the process. The score will follow.  <b>You are going to pass this exam. 🎓</b>",
    PS("fin", fontSize=9, textColor=NAVY, alignment=TA_CENTER,
       fontName="Helvetica-Bold", leading=14))]], colWidths=[FULL])
final_box.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1),LGOLD),("BOX",(0,0),(-1,-1),1.5,GOLD),
    ("TOPPADDING",(0,0),(-1,-1),8),("BOTTOMPADDING",(0,0),(-1,-1),8),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10),
]))
story.append(final_box)

# ── Build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.80*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_critical_thinking_work…

Editing File

~/nclex-labs/generate_critical_thinking_worksheet.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/nclex-labs/generate_critical_thinking_worksheet.py'
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_critical_thinking_work…

Editing File

~/nclex-labs/generate_critical_thinking_worksheet.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/nclex-labs/generate_critical_thinking_worksheet.py'
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_critical_thinking_work…

Editing File

~/nclex-labs/generate_critical_thinking_worksheet.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/nclex-labs/generate_critical_thinking_worksheet.py'
Editing File

~/nclex-labs/generate_critical_thinking_worksheet.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/nclex-labs/generate_critical_thinking_worksheet.py'
Running Command

cd /home/daytona/workspace/nclex-labs && python3 generate_critical_thinking_work…

!

Failed to execute session command:

We encountered an error while processing your request. Please try again or reach out to us for help.

Design a critical thinking worksheet for analyzing NCLEX test questions

Writing File

~/nclex-labs/gen_worksheet.py

"""
NCLEX Critical Thinking Worksheet — ReportLab PDF
3 pages: Framework | Practice Templates | Error Tracker
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, HRFlowable
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Critical_Thinking_Worksheet.pdf"
PAGE_W, PAGE_H = letter

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#1B2A4A")
TEAL   = colors.HexColor("#0B7A75")
RED    = colors.HexColor("#B03A2E")
ORANGE = colors.HexColor("#D9622B")
GREEN  = colors.HexColor("#1D6A39")
PURPLE = colors.HexColor("#5B2C6F")
GOLD   = colors.HexColor("#9A7D0A")
BLUE   = colors.HexColor("#1A5276")
SLATE  = colors.HexColor("#2E4057")
WHITE  = colors.white
DGREY  = colors.HexColor("#2C3E50")
MGREY  = colors.HexColor("#BDC3C7")
LGREY  = colors.HexColor("#F4F6F9")
LNAVY  = colors.HexColor("#EAF0FB")
LTEAL  = colors.HexColor("#E8F8F5")
LGREEN = colors.HexColor("#EAFAF1")
LRED   = colors.HexColor("#FDEDEC")
LPURPLE= colors.HexColor("#F5EEF8")
LBLUE  = colors.HexColor("#EBF5FB")
LORANGE= colors.HexColor("#FEF5E7")
LGOLD  = colors.HexColor("#FDFAE5")
XLGREY = colors.HexColor("#FAFAFA")

FULL = 7.5 * inch

# ── Style helpers ─────────────────────────────────────────────────────────────
def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY, leading=11)
    base.update(kw)
    return ParagraphStyle(name, **base)

def P(txt, **kw):   return Paragraph(str(txt), PS("x", **kw))
def PB(txt, **kw):  kw.setdefault("fontName","Helvetica-Bold"); return P(txt, **kw)
def PW(txt, **kw):  kw["textColor"]=WHITE; kw.setdefault("fontName","Helvetica-Bold"); return P(txt,**kw)
def PC(txt, **kw):  kw["alignment"]=TA_CENTER; return P(txt, **kw)
def PCB(txt,**kw):  kw["alignment"]=TA_CENTER; kw.setdefault("fontName","Helvetica-Bold"); return P(txt,**kw)

SP = lambda n=0.06: Spacer(1, n*inch)
FULL = 7.5*inch

BASE = [
    ("FONTNAME",      (0,0),(-1,-1),"Helvetica"),
    ("FONTSIZE",      (0,0),(-1,-1),8),
    ("GRID",          (0,0),(-1,-1),0.4,MGREY),
    ("VALIGN",        (0,0),(-1,-1),"MIDDLE"),
    ("TOPPADDING",    (0,0),(-1,-1),3),
    ("BOTTOMPADDING", (0,0),(-1,-1),3),
    ("LEFTPADDING",   (0,0),(-1,-1),5),
    ("RIGHTPADDING",  (0,0),(-1,-1),5),
]

def row_tbl(rows_data, col_widths, extra=None):
    t = Table(rows_data, colWidths=col_widths)
    style = list(BASE)
    if extra: style += extra
    t.setStyle(TableStyle(style))
    return t

def banner(title, sub, bg):
    rows = [[PW(title, fontSize=10, alignment=TA_CENTER)]]
    if sub:
        rows.append([P(sub, fontSize=7.5, textColor=colors.HexColor("#D5D8DC"),
                       alignment=TA_CENTER, leading=10)])
    t = Table(rows, colWidths=[FULL])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("TOPPADDING",(0,0),(-1,-1),5),
                            ("BOTTOMPADDING",(0,0),(-1,-1),5)]))
    return t

def step_hdr(num, title, bg):
    t = Table([[PCB(f"STEP {num}", textColor=WHITE, fontSize=9),
                PW(title, fontSize=9)]],
              colWidths=[0.72*inch, FULL-0.72*inch])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("TOPPADDING",(0,0),(-1,-1),4),
                            ("BOTTOMPADDING",(0,0),(-1,-1),4),
                            ("LEFTPADDING",(0,0),(-1,-1),6),
                            ("VALIGN",(0,0),(-1,-1),"MIDDLE")]))
    return t

def write_lines(n, width=FULL, bg=XLGREY):
    """n ruled lines for handwriting."""
    H = 0.24*inch
    data = [[P("", fontSize=1)] for _ in range(n)]
    t = Table(data, colWidths=[width], rowHeights=[H]*n)
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1),bg),
        ("BOX",(0,0),(-1,-1),0.6,MGREY),
        ("LINEBELOW",(0,0),(-1,-2),0.4,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),0),
        ("BOTTOMPADDING",(0,0),(-1,-1),0),
        ("LEFTPADDING",(0,0),(-1,-1),5),
        ("RIGHTPADDING",(0,0),(-1,-1),5),
    ]))
    return t

def info_box(txt, bg, border):
    t = Table([[P(txt, fontSize=7.8, textColor=DGREY, leading=11)]],
              colWidths=[FULL])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("BOX",(0,0),(-1,-1),1,border),
                            ("TOPPADDING",(0,0),(-1,-1),5),
                            ("BOTTOMPADDING",(0,0),(-1,-1),5),
                            ("LEFTPADDING",(0,0),(-1,-1),8),
                            ("RIGHTPADDING",(0,0),(-1,-1),8)]))
    return t

# ── Page callback ─────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-54, PAGE_W, 54, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-58, PAGE_W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 17)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-32,
        "NCLEX CRITICAL THINKING WORKSHEET")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-48,
        "Analyze every practice question systematically  •  Build reasoning skills  •  Eliminate wrong answers with confidence")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"NCLEX Prep Series  •  Critical Thinking Framework  •  Page {doc.page}")

# =============================================================================
# PAGE 1 — THE 7-STEP FRAMEWORK
# =============================================================================
story = []
story.append(SP(0.08))

story.append(info_box(
    "<b>HOW TO USE:</b>  Work through all 7 steps for EVERY practice question you get wrong — "
    "and even for ones you got right by guessing.  "
    "The goal is to build a <b>thinking habit</b>, not just memorize answers.  "
    "Photocopy pages 2–3 for each practice session.",
    LNAVY, NAVY))
story.append(SP(0.08))

# ── STEP 1 ────────────────────────────────────────────────────────────────────
story.append(step_hdr(1, "READ THE STEM — Identify What Is REALLY Being Asked", RED))
story.append(SP(0.03))
story.append(row_tbl([
    [PB("Underline / Circle:", textColor=RED),
     P("first  •  priority  •  most important  •  best  •  immediate  •  initial  •  EXCEPT  •  NOT  •  contraindicated  •  next")],
    [PB("Question Type:", textColor=RED),
     P("☐ Priority / First Action   ☐ Assessment   ☐ Intervention   ☐ SATA   ☐ Teaching / Education   ☐ Safety   ☐ Delegation")],
    [PB("Clinical Setting:", textColor=RED),
     P("☐ Emergency / Acute   ☐ Post-op   ☐ Chronic / Outpatient   ☐ Pediatric   ☐ OB / Maternity   ☐ Mental Health")],
], [1.5*inch, FULL-1.5*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LRED),
    ("ROWBACKGROUNDS",(0,0),(-1,-1),[LRED,colors.HexColor("#FEF3F2")]),
]))
story.append(SP(0.07))

# ── STEP 2 ────────────────────────────────────────────────────────────────────
story.append(step_hdr(2, "IDENTIFY THE PATIENT — Who Are They and What's the Clinical Context?", ORANGE))
story.append(SP(0.03))
story.append(row_tbl([
    [PB("Age / stage:", textColor=ORANGE), P("________________________________"),
     PB("Diagnosis:", textColor=ORANGE),   P("________________________________"),
     PB("Acuity:", textColor=ORANGE),      P("☐ Stable   ☐ Unstable   ☐ Unknown")],
    [PB("Key vitals / labs:", textColor=ORANGE), P("______________________________"),
     PB("Medications noted:", textColor=ORANGE), P("______________________________"),
     PB("Other clues:", textColor=ORANGE),       P("______________________________")],
], [0.90*inch,1.50*inch, 0.80*inch,1.50*inch, 0.65*inch,2.15*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LORANGE),
]))
story.append(SP(0.07))

# ── STEP 3 ────────────────────────────────────────────────────────────────────
story.append(step_hdr(3, "APPLY YOUR PRIORITY FRAMEWORK Before Reading the Options", GOLD))
story.append(SP(0.03))
story.append(row_tbl([
    [PCB("FRAMEWORK",textColor=WHITE), PCB("WHEN TO USE",textColor=WHITE),
     PCB("APPLIES?",textColor=WHITE),  PCB("WHAT IT TELLS YOU",textColor=WHITE)],
    [PB("ABC"), P("Any acute physiological problem"),
     PC("☐ Yes  ☐ No"),
     P("Airway > Breathing > Circulation — address in this order first")],
    [PB("Maslow"), P("Physiological vs psychosocial needs"),
     PC("☐ Yes  ☐ No"),
     P("Physiological need wins over emotional/psychological needs")],
    [PB("Safety First"), P("Risk of harm, fall, infection, error"),
     PC("☐ Yes  ☐ No"),
     P("Prevent harm before comfort or education")],
    [PB("Acute > Chronic"), P("Multiple problems / patients to prioritize"),
     PC("☐ Yes  ☐ No"),
     P("New / acute symptom trumps known chronic finding")],
    [PB("Unstable > Stable"), P("Delegation or assignment questions"),
     PC("☐ Yes  ☐ No"),
     P("Unstable patient stays with RN; stable may be delegated to LPN/UAP")],
], [0.90*inch,1.95*inch,0.80*inch,3.85*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),DGREY),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LGOLD,WHITE]),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))
story.append(SP(0.07))

# ── STEP 4 ────────────────────────────────────────────────────────────────────
story.append(step_hdr(4, "ELIMINATE — Cross Off Wrong Answers One at a Time", PURPLE))
story.append(SP(0.03))
story.append(row_tbl([
    [PCB("OPTION",textColor=WHITE), PCB("ELIMINATE IF…",textColor=WHITE),
     PCB("KEEP IF…",textColor=WHITE), PCB("MY REASONING (write it)",textColor=WHITE)],
    [PCB("A"), P("Contains 'always/never', reassures without action, requires MD order before nursing action"),
     P("Addresses ABC/safety directly; assessment-focused if no acute crisis"), write_lines(1,2.20*inch,LPURPLE)],
    [PCB("B"), P("Same criteria as above"),  P("Same criteria as above"), write_lines(1,2.20*inch,LPURPLE)],
    [PCB("C"), P("Same criteria as above"),  P("Same criteria as above"), write_lines(1,2.20*inch,LPURPLE)],
    [PCB("D"), P("Same criteria as above"),  P("Same criteria as above"), write_lines(1,2.20*inch,LPURPLE)],
], [0.38*inch,1.85*inch,1.82*inch,2.20*inch+0.25*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),PURPLE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LPURPLE,WHITE]),
    ("ALIGN",(0,0),(0,-1),"CENTER"),
]))
story.append(SP(0.07))

# ── STEP 5 ────────────────────────────────────────────────────────────────────
story.append(step_hdr(5, "SELECT YOUR ANSWER — Apply the Final Decision Rules", TEAL))
story.append(SP(0.03))
story.append(row_tbl([
    [PB("My selected answer:", textColor=TEAL), P("________"),
     PB("Confidence:", textColor=TEAL),
     P("☐ Certain   ☐ 50/50   ☐ Guessing — circle one")],
], [1.50*inch,0.55*inch,0.80*inch,4.65*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LTEAL),
]))
story.append(SP(0.04))
story.append(info_box(
    "<b>Final Decision Rules:</b>  "
    "(1) If two answers both seem right — pick the MORE complete one or the higher-level need.  "
    "(2) <b>Assess first</b>, then intervene — unless patient safety is immediately threatened.  "
    "(3) <b>'Notify the provider'</b> is rarely the BEST first action — do something yourself first unless it's outside nursing scope.  "
    "(4) <b>SATA:</b> each option is True/False independently — select ALL that are true, even if it feels like too many.",
    LBLUE, BLUE))
story.append(SP(0.07))

# ── STEPS 6 & 7 side by side (each as separate section) ──────────────────────
story.append(step_hdr(6, "REVIEW THE RATIONALE — Learn From Every Question, Right or Wrong", GREEN))
story.append(SP(0.03))
story.append(row_tbl([
    [PB("Correct answer:", textColor=GREEN), P("_______"),
     PB("☐ I was correct   ☐ I was wrong", textColor=GREEN),
     PB("Why was the correct answer RIGHT?", textColor=GREEN)],
    [P(""), P(""), P(""), write_lines(2,3.70*inch,LGREEN)],
    [PB("Why was MY answer WRONG?", textColor=GREEN),
     write_lines(2,2.50*inch,LGREEN), P(""),
     PB("Topic / concept I must review:", textColor=GREEN)],
    [P(""), P(""), P(""), write_lines(1,3.70*inch,LGREEN)],
], [1.55*inch,2.50*inch,0.10*inch,3.35*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LGREEN),
    ("TOPPADDING",(0,0),(-1,-1),4),
]))
story.append(SP(0.07))

story.append(step_hdr(7, "CLASSIFY YOUR MISTAKE — Track Patterns to Stop Repeating Errors", SLATE))
story.append(SP(0.03))
mistake_rows = [
    [PCB("TYPE",textColor=WHITE), PCB("DESCRIPTION",textColor=WHITE), PCB("☐ MY ERROR",textColor=WHITE)],
    [PB("Content Gap"),        P("I didn't know the content. Flag this topic for study."),                     PC("☐")],
    [PB("Misread Question"),   P("I missed a key word: NOT, EXCEPT, FIRST, PRIORITY, BEST."),                  PC("☐")],
    [PB("Second-Guessed"),     P("My first instinct was correct but I changed my answer."),                    PC("☐")],
    [PB("Wrong Framework"),    P("I didn't apply ABC/Maslow. I chose comfort over safety or physiology."),     PC("☐")],
    [PB("Option Confusion"),   P("Two options looked similar. I chose the less complete one."),                PC("☐")],
    [PB("SATA Error"),         P("I under-selected or over-selected. I need to treat each option as T/F."),   PC("☐")],
    [PB("Delegation Error"),   P("I delegated something RN cannot: assessment, teaching, IV push, unstable."),PC("☐")],
    [PB("Normal/Abnormal"),    P("I didn't recognize the finding as expected/normal. I acted when I shouldn't."),PC("☐")],
]
story.append(row_tbl(mistake_rows, [1.45*inch,5.30*inch,0.75*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),SLATE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LGREY,WHITE]),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))

# =============================================================================
# PAGE 2 — PRACTICE QUESTION ANALYSIS TEMPLATE × 3
# =============================================================================
story.append(PageBreak())
story.append(SP(0.08))
story.append(banner(
    "PRACTICE QUESTION ANALYSIS TEMPLATE",
    "Use one block per practice question. Photocopy this page for each study session.",
    BLUE))
story.append(SP(0.08))

ACCENTS = [NAVY, RED, GREEN]
LIGHTS  = [LNAVY, LRED, LGREEN]

for qi in range(1, 4):
    ac = ACCENTS[qi-1]
    lc = LIGHTS[qi-1]

    # Header row
    story.append(row_tbl([
        [PCB(f"Q{qi}", textColor=WHITE, fontSize=11),
         PW(f"Question {qi} Analysis", fontSize=9),
         PW("Date: _____________________", fontSize=8)],
    ], [0.42*inch, 5.5*inch, 1.58*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),ac),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ]))

    # Write question
    story.append(row_tbl([
        [PB("Question stem / Q# from bank:", textColor=ac)],
    ],[FULL], extra=[("BACKGROUND",(0,0),(-1,-1),lc),
                     ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2)]))
    story.append(write_lines(2, FULL, lc))

    # Answer options — two per row
    LH = (FULL - 2*0.28*inch) / 2
    story.append(row_tbl([
        [PB("A:", textColor=ac), write_lines(1,LH-0.04*inch,lc),
         PB("B:", textColor=ac), write_lines(1,LH-0.04*inch,lc)],
        [PB("C:", textColor=ac), write_lines(1,LH-0.04*inch,lc),
         PB("D:", textColor=ac), write_lines(1,LH-0.04*inch,lc)],
    ], [0.28*inch,LH,0.28*inch,LH], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),
    ]))

    # Analysis row
    story.append(row_tbl([
        [PB("Key words underlined:", textColor=ac), write_lines(1,1.40*inch,lc),
         PB("Question type:", textColor=ac),
         P("☐ Priority  ☐ Assess\n☐ Intervene  ☐ Delegate\n☐ SATA  ☐ Teaching", fontSize=7.3, leading=10)],
    ], [1.30*inch,1.40*inch,0.92*inch,3.88*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("GRID",(0,0),(-1,-1),0.3,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ]))

    # Result row
    story.append(row_tbl([
        [PB("Framework used:", textColor=ac),
         P("☐ ABC  ☐ Maslow  ☐ Safety  ☐ Acute/Chronic"),
         PB("My answer:", textColor=ac), write_lines(1,0.50*inch,lc),
         PB("Correct:", textColor=ac),  write_lines(1,0.50*inch,lc),
         PB("Result:", textColor=ac),   P("☐ Correct  ☐ Wrong")],
    ], [1.0*inch,1.75*inch,0.72*inch,0.50*inch,0.60*inch,0.50*inch,0.50*inch,1.93*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("GRID",(0,0),(-1,-1),0.3,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ]))

    # Mistake + rationale
    story.append(row_tbl([
        [PB("Mistake type:", textColor=ac),
         P("☐ Content gap  ☐ Misread  ☐ Second-guessed  ☐ Wrong framework  ☐ SATA  ☐ Normal/abnormal  ☐ Delegation")],
    ], [0.95*inch, FULL-0.95*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ]))
    story.append(row_tbl([
        [PB("Rationale / what I learned / concept to review:", textColor=ac)],
    ],[FULL], extra=[("BACKGROUND",(0,0),(-1,-1),lc),
                     ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2)]))
    story.append(write_lines(2, FULL, lc))
    story.append(SP(0.10))

# =============================================================================
# PAGE 3 — ERROR TRACKER + STRATEGY NOTES
# =============================================================================
story.append(PageBreak())
story.append(SP(0.08))
story.append(banner(
    "SESSION ERROR TRACKER & PERSONAL STRATEGY NOTES",
    "Fill in after every practice session. Patterns show you exactly where to focus next.",
    PURPLE))
story.append(SP(0.08))

# Session log
story.append(PB("📊  SESSION PERFORMANCE LOG", textColor=PURPLE, fontSize=9))
story.append(SP(0.04))
log_hdr = [PCB("DATE",textColor=WHITE), PCB("# Qs",textColor=WHITE),
           PCB("# Correct",textColor=WHITE), PCB("% Score",textColor=WHITE),
           PCB("TOPIC FOCUS",textColor=WHITE), PCB("TOP MISTAKE",textColor=WHITE),
           PCB("TOPICS TO REVIEW",textColor=WHITE)]
log_rows = [log_hdr] + [["","","","","","",""] for _ in range(10)]
log_cw = [0.75*inch,0.60*inch,0.78*inch,0.68*inch,1.15*inch,1.20*inch,2.34*inch]
story.append(row_tbl(log_rows, log_cw, extra=[
    ("BACKGROUND",(0,0),(-1,0),PURPLE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LPURPLE,WHITE]),
    ("ALIGN",(0,0),(3,-1),"CENTER"),
    ("ROWHEIGHTS",(0,1),(-1,-1),18),
]))
story.append(SP(0.10))

# Cumulative error tally
story.append(PB("🔍  CUMULATIVE ERROR PATTERN TALLY", textColor=RED, fontSize=9))
story.append(SP(0.04))
err_rows = [
    [PCB("MISTAKE TYPE",textColor=WHITE), PCB("TALLY (add a mark each occurrence)",textColor=WHITE),
     PCB("TOTAL",textColor=WHITE), PCB("MY CORRECTIVE ACTION",textColor=WHITE)],
    [PB("Content Gap"),        write_lines(1,2.40*inch,LRED), PC(""),
     P("Flag topic → add to weak-areas list → schedule extra review")],
    [PB("Misread Question"),   write_lines(1,2.40*inch,LRED), PC(""),
     P("Underline key words BEFORE reading answer options every time")],
    [PB("Second-Guessed"),     write_lines(1,2.40*inch,LRED), PC(""),
     P("Write first instinct. Only change answer with clear logical reason")],
    [PB("Wrong Framework"),    write_lines(1,2.40*inch,LRED), PC(""),
     P("Ask: Is there an ABC issue? If yes → that answer wins every time")],
    [PB("Option Confusion"),   write_lines(1,2.40*inch,LRED), PC(""),
     P("Cover options, predict answer first, then match to choices")],
    [PB("SATA Error"),         write_lines(1,2.40*inch,LRED), PC(""),
     P("Each option = independent True/False. Select ALL that are true")],
    [PB("Delegation Error"),   write_lines(1,2.40*inch,LRED), PC(""),
     P("RN cannot delegate: assessment, teaching, IV push, unstable patients")],
    [PB("Normal/Abnormal"),    write_lines(1,2.40*inch,LRED), PC(""),
     P("Review expected post-op/procedure findings before each exam session")],
]
story.append(row_tbl(err_rows, [1.30*inch,2.40*inch,0.55*inch,3.25*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),RED),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LRED,WHITE]),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))
story.append(SP(0.10))

# Personal notes
story.append(PB("✏️  MY PERSONAL STRATEGY NOTES", textColor=NAVY, fontSize=9))
story.append(SP(0.04))
prompts = [
    ("My biggest content weakness right now:", 2),
    ("The priority framework I keep forgetting to apply:", 1),
    ("Key words / phrases I must circle before reading options:", 1),
    ("My current score trend:  ☐ Improving   ☐ Stable   ☐ Needs work", 1),
    ("One thing I will do differently in my NEXT practice session:", 2),
    ("My target exam date:", 1),
]
for label, n in prompts:
    story.append(row_tbl([[PB(label, textColor=NAVY, fontSize=7.8)]],
        [FULL], extra=[("BACKGROUND",(0,0),(-1,-1),LNAVY),
                       ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2),
                       ("LEFTPADDING",(0,0),(-1,-1),6)]))
    story.append(write_lines(n, FULL, WHITE))
    story.append(SP(0.04))

# Closing
story.append(row_tbl([[P(
    '<b>"The expert in anything was once a beginner."</b>  '
    'Every question you analyze carefully — right or wrong — makes you a sharper thinker.  '
    'Work the process. The score will follow.  <b>You are going to pass this exam. 🎓</b>',
    fontName="Helvetica-Bold", fontSize=9, textColor=NAVY,
    alignment=TA_CENTER, leading=14)]], [FULL], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LGOLD),
    ("BOX",(0,0),(-1,-1),1.5,GOLD),
    ("TOPPADDING",(0,0),(-1,-1),8),("BOTTOMPADDING",(0,0),(-1,-1),8),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10),
]))

# ── Build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.80*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 gen_worksheet.py 2>&1

Running Command

python3 -c " from reportlab.lib.pagesizes import letter from reportlab.lib impor…

Writing File

~/nclex-labs/gen_worksheet_v2.py

"""
NCLEX Critical Thinking Worksheet — final version
Writing areas use custom WritingLines Flowable (never nested in table cells)
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    PageBreak, HRFlowable
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT

OUTPUT = "/home/daytona/workspace/nclex-labs/NCLEX_Critical_Thinking_Worksheet.pdf"
PAGE_W, PAGE_H = letter

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#1B2A4A")
TEAL   = colors.HexColor("#0B7A75")
RED    = colors.HexColor("#B03A2E")
ORANGE = colors.HexColor("#D9622B")
GREEN  = colors.HexColor("#1D6A39")
PURPLE = colors.HexColor("#5B2C6F")
GOLD   = colors.HexColor("#9A7D0A")
BLUE   = colors.HexColor("#1A5276")
SLATE  = colors.HexColor("#2E4057")
WHITE  = colors.white
DGREY  = colors.HexColor("#2C3E50")
MGREY  = colors.HexColor("#BDC3C7")
LGREY  = colors.HexColor("#F4F6F9")
LNAVY  = colors.HexColor("#EAF0FB")
LTEAL  = colors.HexColor("#E8F8F5")
LGREEN = colors.HexColor("#EAFAF1")
LRED   = colors.HexColor("#FDEDEC")
LPURPLE= colors.HexColor("#F5EEF8")
LBLUE  = colors.HexColor("#EBF5FB")
LORANGE= colors.HexColor("#FEF5E7")
LGOLD  = colors.HexColor("#FDFAE5")
XLGREY = colors.HexColor("#FAFAFA")

FULL = 7.5 * inch

# ── Custom writing-lines Flowable ─────────────────────────────────────────────
class WL(Flowable):
    """Horizontal ruled writing lines."""
    def __init__(self, n=2, w=None, h=0.24*inch, bg=XLGREY, border=True):
        super().__init__()
        self.n = n
        self._w = w or FULL
        self._lh = h
        self.bg = bg
        self._border = border

    def wrap(self, aW, aH):
        self._actual_w = min(self._w, aW)
        return (self._actual_w, self.n * self._lh)

    def draw(self):
        c = self.canv
        total_h = self.n * self._lh
        c.setFillColor(self.bg)
        c.rect(0, 0, self._actual_w, total_h, fill=1, stroke=0)
        c.setStrokeColor(MGREY)
        c.setLineWidth(0.4)
        if self._border:
            c.rect(0, 0, self._actual_w, total_h, fill=0, stroke=1)
        for i in range(1, self.n):
            y = i * self._lh
            c.line(2, y, self._actual_w - 2, y)


# ── Helpers ───────────────────────────────────────────────────────────────────
def PS(name, **kw):
    base = dict(fontName="Helvetica", fontSize=8, textColor=DGREY, leading=11)
    base.update(kw)
    return ParagraphStyle(name, **base)

def P(t, **kw):  return Paragraph(str(t), PS("_", **kw))
def PB(t,**kw):  kw.setdefault("fontName","Helvetica-Bold"); return P(t, **kw)
def PW(t,**kw):  kw["textColor"]=WHITE; kw.setdefault("fontName","Helvetica-Bold"); return P(t, **kw)
def PC(t,**kw):  kw["alignment"]=TA_CENTER; return P(t, **kw)
def PCB(t,**kw): kw["alignment"]=TA_CENTER; kw.setdefault("fontName","Helvetica-Bold"); return P(t,**kw)

SP = lambda n=0.06: Spacer(1, n*inch)

BASE = [
    ("FONTNAME",      (0,0),(-1,-1), "Helvetica"),
    ("FONTSIZE",      (0,0),(-1,-1), 8),
    ("GRID",          (0,0),(-1,-1), 0.4, MGREY),
    ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
    ("TOPPADDING",    (0,0),(-1,-1), 3),
    ("BOTTOMPADDING", (0,0),(-1,-1), 3),
    ("LEFTPADDING",   (0,0),(-1,-1), 5),
    ("RIGHTPADDING",  (0,0),(-1,-1), 5),
]

def T(rows, cw, extra=None):
    t = Table(rows, colWidths=cw)
    s = list(BASE) + (extra or [])
    t.setStyle(TableStyle(s))
    return t

def banner(title, sub, bg):
    rows = [[PW(title, fontSize=10, alignment=TA_CENTER)]]
    if sub:
        rows.append([P(sub, fontSize=7.5,
                       textColor=colors.HexColor("#D5D8DC"),
                       alignment=TA_CENTER, leading=10)])
    t = Table(rows, colWidths=[FULL])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("TOPPADDING",(0,0),(-1,-1),5),
                            ("BOTTOMPADDING",(0,0),(-1,-1),5)]))
    return t

def step_hdr(num, title, bg):
    t = Table([[PCB(f"STEP {num}", textColor=WHITE, fontSize=9),
                PW(title, fontSize=9)]],
              colWidths=[0.72*inch, FULL-0.72*inch])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("TOPPADDING",(0,0),(-1,-1),4),
                            ("BOTTOMPADDING",(0,0),(-1,-1),4),
                            ("LEFTPADDING",(0,0),(-1,-1),6),
                            ("VALIGN",(0,0),(-1,-1),"MIDDLE")]))
    return t

def ibox(txt, bg, bc):
    t = Table([[P(txt, fontSize=7.8, textColor=DGREY, leading=11)]],
              colWidths=[FULL])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("BOX",(0,0),(-1,-1),1,bc),
                            ("TOPPADDING",(0,0),(-1,-1),5),
                            ("BOTTOMPADDING",(0,0),(-1,-1),5),
                            ("LEFTPADDING",(0,0),(-1,-1),8),
                            ("RIGHTPADDING",(0,0),(-1,-1),8)]))
    return t

def lbl(txt, ac, bg):
    t = Table([[PB(txt, textColor=ac, fontSize=7.8)]], colWidths=[FULL])
    t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),bg),
                            ("TOPPADDING",(0,0),(-1,-1),3),
                            ("BOTTOMPADDING",(0,0),(-1,-1),2),
                            ("LEFTPADDING",(0,0),(-1,-1),6)]))
    return t

# ── Page callback ─────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.setFillColor(NAVY)
    canvas.rect(0, PAGE_H-54, PAGE_W, 54, fill=1, stroke=0)
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H-58, PAGE_W, 4, fill=1, stroke=0)
    canvas.setFont("Helvetica-Bold", 17)
    canvas.setFillColor(WHITE)
    canvas.drawCentredString(PAGE_W/2, PAGE_H-32,
        "NCLEX CRITICAL THINKING WORKSHEET")
    canvas.setFont("Helvetica", 9)
    canvas.setFillColor(colors.HexColor("#A8D8EA"))
    canvas.drawCentredString(PAGE_W/2, PAGE_H-48,
        "Analyze every question systematically  •  Build reasoning habits  •  Eliminate wrong answers with confidence")
    canvas.setFillColor(TEAL)
    canvas.rect(0, 26, PAGE_W, 2, fill=1, stroke=0)
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(MGREY)
    canvas.drawCentredString(PAGE_W/2, 14,
        f"NCLEX Prep Series  •  Critical Thinking Framework  •  Page {doc.page}")

# =============================================================================
# PAGE 1 — 7-STEP FRAMEWORK
# =============================================================================
story = []
story.append(SP(0.08))

story.append(ibox(
    "<b>HOW TO USE:</b>  Apply all 7 steps to every practice question you miss — "
    "and any you got right by guessing.  The goal is to build a thinking habit, not memorize answers.  "
    "Photocopy pages 2–3 for each study session.",
    LNAVY, NAVY))
story.append(SP(0.08))

# Step 1
story.append(step_hdr(1, "READ THE STEM — Identify What Is REALLY Being Asked", RED))
story.append(SP(0.03))
story.append(T([
    [PB("Circle Key Words:", textColor=RED),
     P("first  •  priority  •  most important  •  best  •  immediate  •  initial  •  EXCEPT  •  NOT  •  contraindicated  •  next")],
    [PB("Question Type:", textColor=RED),
     P("☐ Priority/First Action   ☐ Assessment   ☐ Intervention   ☐ SATA   ☐ Teaching   ☐ Safety   ☐ Delegation")],
    [PB("Setting:", textColor=RED),
     P("☐ Emergency/Acute   ☐ Post-op   ☐ Chronic/Outpatient   ☐ Pediatric   ☐ OB/Maternity   ☐ Mental Health")],
], [1.5*inch, FULL-1.5*inch], extra=[
    ("ROWBACKGROUNDS",(0,0),(-1,-1),[LRED, colors.HexColor("#FEF3F2")]),
]))
story.append(SP(0.07))

# Step 2
story.append(step_hdr(2, "IDENTIFY THE PATIENT — Clinical Context Matters", ORANGE))
story.append(SP(0.03))
story.append(T([
    [PB("Age:", textColor=ORANGE), P("________________"),
     PB("Diagnosis:", textColor=ORANGE), P("___________________________"),
     PB("Acuity:", textColor=ORANGE), P("☐ Stable   ☐ Unstable")],
    [PB("Key vitals/labs:", textColor=ORANGE), P("___________________"),
     PB("Medications:", textColor=ORANGE), P("___________________________"),
     PB("Other clues:", textColor=ORANGE), P("___________________")],
], [0.78*inch,1.20*inch, 0.82*inch,1.88*inch, 0.70*inch,2.12*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LORANGE),
]))
story.append(SP(0.07))

# Step 3
story.append(step_hdr(3, "APPLY YOUR PRIORITY FRAMEWORK Before Reading Answer Options", GOLD))
story.append(SP(0.03))
story.append(T([
    [PCB("FRAMEWORK",textColor=WHITE), PCB("WHEN TO USE",textColor=WHITE),
     PCB("APPLIES?",textColor=WHITE),  PCB("WHAT IT TELLS YOU",textColor=WHITE)],
    [PB("ABC"),
     P("Any acute physiological problem"), PC("☐ Yes  ☐ No"),
     P("Airway > Breathing > Circulation — address in this order first")],
    [PB("Maslow"),
     P("Physiology vs psychosocial needs"), PC("☐ Yes  ☐ No"),
     P("Physiological need always wins over emotional/psychological needs")],
    [PB("Safety"),
     P("Risk of harm, fall, medication error"), PC("☐ Yes  ☐ No"),
     P("Prevent harm before comfort or teaching")],
    [PB("Acute > Chronic"),
     P("Multiple problems to prioritize"), PC("☐ Yes  ☐ No"),
     P("New/acute symptom trumps a known chronic finding")],
    [PB("Unstable > Stable"),
     P("Delegation or assignment questions"), PC("☐ Yes  ☐ No"),
     P("Unstable stays with RN; stable may be delegated to LPN/UAP")],
], [0.90*inch,1.95*inch,0.80*inch,3.85*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),DGREY),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LGOLD,WHITE]),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))
story.append(SP(0.07))

# Step 4
story.append(step_hdr(4, "ELIMINATE — Cross Off Wrong Answers One at a Time", PURPLE))
story.append(SP(0.03))
story.append(T([
    [PCB("OPT",textColor=WHITE), PCB("ELIMINATE IF…",textColor=WHITE),
     PCB("KEEP IF…",textColor=WHITE), PCB("My reasoning (write here)",textColor=WHITE)],
    [PCB("A"), P("Contains 'always/never'; reassures without action; requires MD order before nursing step"),
     P("Addresses ABC/safety; assessment-focused if no acute crisis"), P("_________________________________")],
    [PCB("B"), P("Same criteria"), P("Same criteria"), P("_________________________________")],
    [PCB("C"), P("Same criteria"), P("Same criteria"), P("_________________________________")],
    [PCB("D"), P("Same criteria"), P("Same criteria"), P("_________________________________")],
], [0.38*inch,2.10*inch,2.10*inch,2.92*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),PURPLE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LPURPLE,WHITE]),
    ("ALIGN",(0,0),(0,-1),"CENTER"),
]))
story.append(SP(0.07))

# Step 5
story.append(step_hdr(5, "SELECT YOUR ANSWER — Apply Final Decision Rules", TEAL))
story.append(SP(0.03))
story.append(T([[
    PB("My selected answer:", textColor=TEAL), P("_______"),
    PB("Confidence:", textColor=TEAL),
    P("☐ Certain   ☐ 50/50   ☐ Guessing"),
]], [1.50*inch,0.55*inch,0.80*inch,4.65*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LTEAL),
]))
story.append(SP(0.04))
story.append(ibox(
    "<b>Final Decision Rules:</b>  "
    "(1) Two options both right? Pick the MORE complete one / higher-level need.  "
    "(2) Assess BEFORE intervening — unless safety is immediately threatened.  "
    "(3) 'Notify the provider' is rarely the BEST first nursing action.  "
    "(4) SATA: treat each option as independent True/False — select ALL that are true.",
    LBLUE, BLUE))
story.append(SP(0.07))

# Step 6
story.append(step_hdr(6, "REVIEW THE RATIONALE — Learn From Every Question", GREEN))
story.append(SP(0.03))
story.append(T([
    [PB("Correct answer:", textColor=GREEN), P("_______"),
     PB("☐ I was correct   ☐ I was wrong", textColor=GREEN)],
], [1.5*inch,0.55*inch,5.45*inch], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LGREEN),
    ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
]))
story.append(lbl("Why was the correct answer RIGHT? / What did the rationale teach me?", GREEN, LGREEN))
story.append(WL(2, FULL, bg=LGREEN))
story.append(lbl("Why was MY answer WRONG (if incorrect)? / Concept to review:", GREEN, LGREEN))
story.append(WL(2, FULL, bg=LGREEN))
story.append(SP(0.07))

# Step 7
story.append(step_hdr(7, "CLASSIFY YOUR MISTAKE — Track Patterns to Stop Repeating Errors", SLATE))
story.append(SP(0.03))
story.append(T([
    [PCB("MISTAKE TYPE",textColor=WHITE), PCB("DESCRIPTION",textColor=WHITE), PCB("☐",textColor=WHITE)],
    [PB("Content Gap"),       P("I didn't know the content. Flag this topic for study."), PC("☐")],
    [PB("Misread Question"),  P("I missed a key word: NOT, EXCEPT, FIRST, PRIORITY, BEST."), PC("☐")],
    [PB("Second-Guessed"),    P("My first instinct was correct but I changed my answer."), PC("☐")],
    [PB("Wrong Framework"),   P("I didn't apply ABC/Maslow correctly. Chose comfort over safety."), PC("☐")],
    [PB("Option Confusion"),  P("Two options looked similar. I chose the less complete one."), PC("☐")],
    [PB("SATA Error"),        P("I under-selected or over-selected. Must treat each option as T/F."), PC("☐")],
    [PB("Delegation Error"),  P("I delegated: assessment, teaching, IV push, or care of unstable patient."), PC("☐")],
    [PB("Normal/Abnormal"),   P("I didn't recognize an expected/normal finding. Acted when I shouldn't have."), PC("☐")],
], [1.45*inch,5.30*inch,0.75*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),SLATE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LGREY,WHITE]),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
]))

# =============================================================================
# PAGE 2 — PRACTICE QUESTION ANALYSIS TEMPLATE × 3
# =============================================================================
story.append(PageBreak())
story.append(SP(0.08))
story.append(banner(
    "PRACTICE QUESTION ANALYSIS TEMPLATE",
    "Use one block per question. Photocopy this page for each study session.",
    BLUE))
story.append(SP(0.08))

ACCENTS = [NAVY, RED, GREEN]
LIGHTS  = [LNAVY, LRED, LGREEN]

for qi in range(1, 4):
    ac = ACCENTS[qi-1]
    lc = LIGHTS[qi-1]

    # Header
    story.append(T([[
        PCB(f"Q{qi}", textColor=WHITE, fontSize=11),
        PW(f"Question {qi} Analysis", fontSize=9),
        PW("Date: _____________________", fontSize=8),
    ]], [0.42*inch, 5.5*inch, 1.58*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),ac),
        ("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),
    ]))
    story.append(lbl("Write the question stem here (or paste Q# / first line from question bank):", ac, lc))
    story.append(WL(2, FULL, bg=lc))

    # Options A–D
    story.append(T([
        [PB("A:", textColor=ac), P("_______________________________"),
         PB("B:", textColor=ac), P("_______________________________")],
        [PB("C:", textColor=ac), P("_______________________________"),
         PB("D:", textColor=ac), P("_______________________________")],
    ], [0.28*inch,3.47*inch,0.28*inch,3.47*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
    ]))

    # Analysis
    story.append(T([[
        PB("Key words I circled:", textColor=ac),
        P("__________________________"),
        PB("Type:", textColor=ac),
        P("☐ Priority  ☐ Assess  ☐ Intervene\n☐ Delegate  ☐ SATA  ☐ Teaching", fontSize=7.3, leading=10),
        PB("Framework:", textColor=ac),
        P("☐ ABC  ☐ Maslow\n☐ Safety  ☐ Acute/Chronic", fontSize=7.3, leading=10),
    ]], [1.28*inch,1.60*inch,0.40*inch,1.60*inch,0.72*inch,1.90*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("GRID",(0,0),(-1,-1),0.3,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ]))

    # Result
    story.append(T([[
        PB("My answer:", textColor=ac), P("_______"),
        PB("Correct answer:", textColor=ac), P("_______"),
        PB("Result:", textColor=ac), P("☐ Correct   ☐ Wrong   ☐ Guessed"),
        PB("Mistake:", textColor=ac),
        P("☐ Content  ☐ Misread  ☐ 2nd-guessed  ☐ Framework  ☐ SATA  ☐ Delegation  ☐ Normal/Abnormal",
          fontSize=7.3, leading=10),
    ]], [0.80*inch,0.60*inch,1.05*inch,0.60*inch,0.52*inch,1.55*inch,0.60*inch,2.28*inch], extra=[
        ("BACKGROUND",(0,0),(-1,-1),lc),
        ("GRID",(0,0),(-1,-1),0.3,MGREY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
    ]))

    story.append(lbl("What the rationale taught me / concept to review:", ac, lc))
    story.append(WL(2, FULL, bg=lc))
    story.append(SP(0.10))

# =============================================================================
# PAGE 3 — ERROR TRACKER + STRATEGY NOTES
# =============================================================================
story.append(PageBreak())
story.append(SP(0.08))
story.append(banner(
    "SESSION ERROR TRACKER & PERSONAL STRATEGY NOTES",
    "Fill in after every practice session. Patterns show you exactly where to focus next.",
    PURPLE))
story.append(SP(0.08))

# Session log
story.append(PB("📊  SESSION PERFORMANCE LOG", textColor=PURPLE, fontSize=9))
story.append(SP(0.04))
log_rows = [[PCB("DATE",textColor=WHITE), PCB("# Qs",textColor=WHITE),
             PCB("Correct",textColor=WHITE), PCB("% Score",textColor=WHITE),
             PCB("TOPIC FOCUS",textColor=WHITE), PCB("TOP MISTAKE",textColor=WHITE),
             PCB("REVIEW NEEDED",textColor=WHITE)]]
log_rows += [["","","","","","",""] for _ in range(10)]
story.append(T(log_rows,
    [0.72*inch,0.55*inch,0.65*inch,0.65*inch,1.18*inch,1.22*inch,2.53*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),PURPLE),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LPURPLE,WHITE]),
    ("ALIGN",(0,0),(3,-1),"CENTER"),
    ("ROWHEIGHTS",(0,1),(-1,-1),16),
]))
story.append(SP(0.10))

# Error tally
story.append(PB("🔍  CUMULATIVE MISTAKE TALLY", textColor=RED, fontSize=9))
story.append(SP(0.04))
err_rows = [[PCB("MISTAKE TYPE",textColor=WHITE), PCB("TALLY (mark each occurrence)",textColor=WHITE),
             PCB("TOTAL",textColor=WHITE), PCB("MY CORRECTIVE STRATEGY",textColor=WHITE)]]
mistakes = [
    ("Content Gap",       "Flag topic → review in study schedule weak-areas section"),
    ("Misread Question",  "Circle key words BEFORE reading answer options — every time"),
    ("Second-Guessed",    "Write first instinct first. Change only with a clear logical reason"),
    ("Wrong Framework",   "Ask: Is there an ABC problem? If yes → that answer wins every time"),
    ("Option Confusion",  "Cover options, predict answer first, then match to choices"),
    ("SATA Error",        "Each option = independent T/F. Select ALL that are true"),
    ("Delegation Error",  "RN cannot delegate: assessment, teaching, IV push, unstable patients"),
    ("Normal/Abnormal",   "Review expected post-op/procedure findings before each session"),
]
for m, strategy in mistakes:
    err_rows.append([PB(m), P(""), PC(""), P(strategy)])
story.append(T(err_rows, [1.30*inch,2.55*inch,0.55*inch,3.10*inch], extra=[
    ("BACKGROUND",(0,0),(-1,0),RED),("TEXTCOLOR",(0,0),(-1,0),WHITE),
    ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[LRED,WHITE]),
    ("ALIGN",(2,0),(2,-1),"CENTER"),
    ("ROWHEIGHTS",(0,1),(-1,-1),18),
]))
story.append(SP(0.10))

# Personal notes
story.append(PB("✏️  MY PERSONAL STRATEGY NOTES", textColor=NAVY, fontSize=9))
story.append(SP(0.04))
prompts = [
    ("My biggest content weakness right now:", 2),
    ("The priority framework I keep forgetting to apply:", 1),
    ("Key words / phrases I must circle before reading answer options:", 1),
    ("My current score trend:  ☐ Improving    ☐ About the same    ☐ Needs significant work", 1),
    ("One thing I will do differently in my NEXT practice session:", 2),
    ("My target NCLEX exam date:", 1),
]
for label, n in prompts:
    story.append(T([[PB(label, textColor=NAVY, fontSize=7.8)]], [FULL], extra=[
        ("BACKGROUND",(0,0),(-1,-1),LNAVY),
        ("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),2),
        ("LEFTPADDING",(0,0),(-1,-1),6)]))
    story.append(WL(n, FULL, bg=WHITE))
    story.append(SP(0.04))

# Closing motivational box
story.append(T([[P(
    '<b>"The expert in anything was once a beginner."</b>  '
    'Every question you analyze carefully — right or wrong — makes you a sharper clinical thinker.  '
    'Work the process. The score will follow.  <b>You are going to pass this exam! 🎓</b>',
    fontName="Helvetica-Bold", fontSize=9, textColor=NAVY,
    alignment=TA_CENTER, leading=14)]], [FULL], extra=[
    ("BACKGROUND",(0,0),(-1,-1),LGOLD),
    ("BOX",(0,0),(-1,-1),1.5,GOLD),
    ("TOPPADDING",(0,0),(-1,-1),8),("BOTTOMPADDING",(0,0),(-1,-1),8),
    ("LEFTPADDING",(0,0),(-1,-1),10),("RIGHTPADDING",(0,0),(-1,-1),10),
]))

# ── Build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(OUTPUT, pagesize=letter,
                        topMargin=0.80*inch, bottomMargin=0.45*inch,
                        leftMargin=0.45*inch, rightMargin=0.45*inch)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"SUCCESS: {OUTPUT}")
Running Command

cd /home/daytona/workspace/nclex-labs && python3 gen_worksheet_v2.py 2>&1

Running Command

ls -lh /home/daytona/workspace/nclex-labs/NCLEX_Critical_Thinking_Worksheet.pdf

Your worksheet is ready!

NCLEX Critical Thinking Worksheet

PDF Document · PDF


What's Inside (3 printable pages)

Page 1 — The 7-Step Critical Thinking Framework
StepPurpose
1. Read the StemIdentify key words, question type, clinical setting
2. Identify the PatientAge, diagnosis, acuity, vitals, medications
3. Apply Priority FrameworkABC, Maslow, Safety, Acute vs Chronic, Unstable vs Stable
4. Eliminate OptionsWhy each option stays or goes — with reasoning space
5. Select Your AnswerConfidence level + final decision rules (assess before intervene, etc.)
6. Review the RationaleWhat was right, what was wrong, concept to revisit
7. Classify Your Mistake8 error types with checkboxes to identify your pattern
Page 2 — Practice Question Analysis Template × 3
  • Three identical color-coded blocks (Navy / Red / Green)
  • Each block: question stem space, A–D answer options, key word field, question type, framework used, my answer vs correct answer, result, mistake type checkbox, rationale writing space
Page 3 — Error Tracker & Strategy Notes
  • 10-session performance log (date, Q count, % score, topic, mistake, review needed)
  • Cumulative mistake tally with space to track each error type + corrective strategy
  • Personal strategy notes with 6 guided prompts
How to use it: Print page 1 once (laminate it for repeated use). Photocopy pages 2–3 for each study session. After 2–3 weeks you will see clear patterns in your mistake tally — those patterns are your targeted review list.
This is a shared conversation. Sign in to Orris to start your own chat.