setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $pdo->exec('PRAGMA foreign_keys = ON'); $pdo->exec('PRAGMA busy_timeout = 5000'); app_init_db($pdo); return $pdo; } function app_init_db(PDO $pdo): void { $pdo->exec(<< "TEXT NOT NULL DEFAULT 'member'", 'status' => "TEXT NOT NULL DEFAULT 'active'", 'phone' => "TEXT DEFAULT ''", 'birthday' => "TEXT DEFAULT ''", 'height_in' => "REAL DEFAULT 0", 'training_level' => "TEXT DEFAULT ''", 'emergency_contact' => "TEXT DEFAULT ''", 'updated_at' => "TEXT DEFAULT ''", ]; foreach ($definitions as $column => $definition) { if (!in_array($column, $columns, true)) { $pdo->exec("ALTER TABLE users ADD COLUMN {$column} {$definition}"); } } $pdo->exec("UPDATE users SET role = 'member' WHERE role IS NULL OR role = ''"); $pdo->exec("UPDATE users SET status = 'active' WHERE status IS NULL OR status = ''"); $pdo->exec("UPDATE users SET updated_at = created_at WHERE updated_at IS NULL OR updated_at = ''"); } function migrate_medication_catalog_table(PDO $pdo): void { $columns = db_table_columns($pdo, 'medication_catalog'); $definitions = [ 'active_ingredient' => "TEXT DEFAULT ''", 'chemical_name' => "TEXT DEFAULT ''", 'brand_names' => "TEXT DEFAULT ''", 'generic_brands' => "TEXT DEFAULT ''", 'drug_family' => "TEXT DEFAULT ''", 'pubchem_url' => "TEXT DEFAULT ''", 'dailymed_url' => "TEXT DEFAULT ''", 'search_terms' => "TEXT DEFAULT ''", ]; foreach ($definitions as $column => $definition) { if (!in_array($column, $columns, true)) { $pdo->exec("ALTER TABLE medication_catalog ADD COLUMN {$column} {$definition}"); } } } function db_table_columns(PDO $pdo, string $table): array { $rows = db_all($pdo, "PRAGMA table_info({$table})"); return array_map(static fn(array $row): string => $row['name'], $rows); } function ensure_first_user_is_admin(PDO $pdo): void { $firstUserId = (int) db_value($pdo, 'SELECT id FROM users ORDER BY id LIMIT 1'); if ($firstUserId <= 0) { return; } $adminCount = (int) db_value($pdo, "SELECT COUNT(*) FROM users WHERE role = 'admin'"); if ($adminCount === 0) { $stmt = $pdo->prepare("UPDATE users SET role = 'admin', status = 'active', updated_at = ? WHERE id = ?"); $stmt->execute([app_now(), $firstUserId]); } } function seed_libraries(PDO $pdo): void { $exerciseCount = (int) $pdo->query("SELECT COUNT(*) FROM exercises WHERE is_builtin = 1")->fetchColumn(); if ($exerciseCount === 0) { $exercises = [ ['Barbell Back Squat', 'Weight lifting', 'Legs', 'sets x reps x lb'], ['Deadlift', 'Weight lifting', 'Posterior chain', 'sets x reps x lb'], ['Bench Press', 'Weight lifting', 'Chest', 'sets x reps x lb'], ['Overhead Press', 'Weight lifting', 'Shoulders', 'sets x reps x lb'], ['Pull-Up', 'Weight lifting', 'Back', 'sets x reps'], ['Bent-Over Row', 'Weight lifting', 'Back', 'sets x reps x lb'], ['Romanian Deadlift', 'Weight lifting', 'Hamstrings', 'sets x reps x lb'], ['Walking Lunge', 'Weight lifting', 'Legs', 'sets x reps'], ['Hip Thrust', 'Weight lifting', 'Glutes', 'sets x reps x lb'], ['Biceps Curl', 'Weight lifting', 'Arms', 'sets x reps x lb'], ['Triceps Dip', 'Weight lifting', 'Arms', 'sets x reps'], ['Core Plank', 'Core', 'Core', 'minutes'], ['Kettlebell Swing', 'Conditioning', 'Full body', 'sets x reps'], ['HIIT Circuit', 'Conditioning', 'Full body', 'minutes'], ['Indoor Cycling', 'Cycling', 'Cardio', 'minutes + miles'], ['Road Ride', 'Cycling', 'Cardio', 'minutes + miles'], ['Easy Run', 'Running', 'Cardio', 'minutes + miles'], ['Tempo Run', 'Running', 'Cardio', 'minutes + miles'], ['Sprint Intervals', 'Running', 'Cardio', 'rounds + minutes'], ['Pool Laps', 'Swimming', 'Cardio', 'minutes + distance'], ['Open-Water Swim', 'Swimming', 'Cardio', 'minutes + distance'], ['Rowing Machine', 'Conditioning', 'Cardio', 'minutes + meters'], ['Yoga Mobility', 'Mobility', 'Recovery', 'minutes'], ['Hiking', 'Outdoor', 'Cardio', 'minutes + miles'], ['Jump Rope', 'Conditioning', 'Cardio', 'minutes'], ['Elliptical', 'Conditioning', 'Cardio', 'minutes'], ]; $stmt = $pdo->prepare( "INSERT INTO exercises (name, category, muscle_group, default_unit, notes, is_builtin, created_at) VALUES (?, ?, ?, ?, '', 1, ?)" ); foreach ($exercises as $exercise) { $stmt->execute([...$exercise, app_now()]); } } $supplementCount = (int) $pdo->query("SELECT COUNT(*) FROM supplements WHERE is_builtin = 1")->fetchColumn(); if ($supplementCount === 0) { $supplements = [ ['Pre-Workout Blend', 'Pre-workout', '1 scoop', 'Energy and focus'], ['Caffeine', 'Pre-workout', '100-200 mg', 'Alertness'], ['Creatine Monohydrate', 'Daily', '5 g', 'Strength and power output'], ['Beta-Alanine', 'Pre-workout', '3.2 g', 'High-intensity endurance'], ['Citrulline Malate', 'Pre-workout', '6-8 g', 'Pump and blood flow'], ['Electrolytes', 'Intra-workout', '1 serving', 'Hydration support'], ['BCAAs / EAAs', 'Intra-workout', '1 serving', 'Amino acid support'], ['Carb Powder', 'Intra-workout', '25-50 g', 'Training fuel'], ['Whey Protein', 'Post-workout', '25-35 g', 'Protein target support'], ['Casein Protein', 'Evening', '25-35 g', 'Slow-digesting protein'], ['Collagen Peptides', 'Daily', '10-20 g', 'Tendon and joint support'], ['Fish Oil / Omega-3', 'Daily', '1-2 g EPA/DHA', 'General health'], ['Magnesium Glycinate', 'Evening', '200-400 mg', 'Relaxation and sleep'], ['Vitamin D3', 'Daily', '1000-2000 IU', 'General health'], ['Multivitamin', 'Daily', '1 serving', 'Micronutrient coverage'], ['Tart Cherry', 'Post-workout', '1 serving', 'Recovery support'], ['Greens Powder', 'Daily', '1 serving', 'Micronutrient support'], ]; $stmt = $pdo->prepare( "INSERT INTO supplements (name, timing, default_dose, purpose, notes, is_builtin, created_at) VALUES (?, ?, ?, ?, '', 1, ?)" ); foreach ($supplements as $supplement) { $stmt->execute([...$supplement, app_now()]); } } } function seed_medical_library(PDO $pdo): void { $created = app_now(); $termCount = (int) $pdo->query('SELECT COUNT(*) FROM medical_terms')->fetchColumn(); if ($termCount === 0) { $terms = [ ['Blood pressure', 'Vitals', 'Pressure of circulating blood against artery walls, commonly recorded as systolic over diastolic.', 'Often tracked for cardiovascular risk and medication follow-up.', 'MedlinePlus', 'https://medlineplus.gov/highbloodpressure.html'], ['Hypertension', 'Condition', 'A clinical term for high blood pressure.', 'Can be tracked with home readings, clinician visits, and medication adherence.', 'MedlinePlus', 'https://medlineplus.gov/highbloodpressure.html'], ['A1C', 'Laboratory', 'A blood test that reflects average blood glucose over roughly the previous few months.', 'Commonly followed in diabetes care.', 'MedlinePlus', 'https://medlineplus.gov/a1c.html'], ['Hyperglycemia', 'Symptom/Lab', 'Higher-than-desired blood glucose.', 'May be logged with glucose readings, symptoms, meals, and medications.', 'MedlinePlus', 'https://medlineplus.gov/highbloodsugar.html'], ['LDL cholesterol', 'Laboratory', 'A cholesterol fraction often discussed as a cardiovascular risk marker.', 'Frequently reviewed with lipid panels and statin therapy.', 'MedlinePlus', 'https://medlineplus.gov/cholesterollevelswhatyouneedtoknow.html'], ['HDL cholesterol', 'Laboratory', 'A cholesterol fraction commonly included in lipid panels.', 'Used with LDL, triglycerides, and total cholesterol to understand lipid status.', 'MedlinePlus', 'https://medlineplus.gov/cholesterollevelswhatyouneedtoknow.html'], ['Oxygen saturation', 'Vitals', 'An estimate of how much oxygen hemoglobin is carrying.', 'Often recorded as SpO2 from a pulse oximeter.', 'MedlinePlus', 'https://medlineplus.gov/ency/article/003855.htm'], ['eGFR', 'Laboratory', 'Estimated glomerular filtration rate, a calculated kidney-function marker.', 'Often reviewed with kidney disease, diabetes, hypertension, and medication dosing.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/glomerular-filtration-rate-gfr-test/'], ['Adverse drug reaction', 'Medication Safety', 'An unwanted or harmful response associated with taking a medicine.', 'Useful to log with medicine, dose, timing, and symptoms.', 'MedlinePlus', 'https://medlineplus.gov/drugsafety.html'], ['Contraindication', 'Medication Safety', 'A reason a treatment may be unsuitable for a person or situation.', 'Important to discuss with a clinician or pharmacist before medication changes.', 'RxNorm/NLM', 'https://www.nlm.nih.gov/research/umls/rxnorm/index.html'], ['Randomized controlled trial', 'Research', 'A study design that assigns participants to interventions by chance.', 'Common study type in PubMed and ClinicalTrials.gov research records.', 'ClinicalTrials.gov', 'https://clinicaltrials.gov/'], ['Placebo', 'Research', 'An inactive comparison used in some clinical studies.', 'Helps separate treatment effects from expectation or background changes.', 'ClinicalTrials.gov', 'https://clinicaltrials.gov/'], ]; $stmt = $pdo->prepare( "INSERT INTO medical_terms (term, category, plain_language_definition, clinical_context, source_name, source_url, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)" ); foreach ($terms as $term) { $stmt->execute([...$term, $created]); } } $additionalTerms = [ ['Complete blood count (CBC)', 'Laboratory', 'A group of blood tests that count and describe red blood cells, white blood cells, platelets, hemoglobin, hematocrit, and related measures.', 'Commonly logged when tracking anemia, infection, bleeding concerns, or medication effects.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/complete-blood-count-cbc/'], ['Hemoglobin', 'Laboratory', 'An iron-rich protein in red blood cells that carries oxygen.', 'Often reviewed as part of a CBC and anemia evaluation.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/complete-blood-count-cbc/'], ['Hematocrit', 'Laboratory', 'The share of whole blood made up of red blood cells.', 'Often tracked with hemoglobin and red blood cell count in CBC results.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/complete-blood-count-cbc/'], ['White blood cell count', 'Laboratory', 'A measure of white blood cells, which are involved in infection and immune response.', 'May be followed during infections, immune conditions, and medication monitoring.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/complete-blood-count-cbc/'], ['Platelet count', 'Laboratory', 'A measure of platelets, blood components involved in clotting.', 'Useful context for bleeding risk, clotting concerns, and some medication follow-up.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/complete-blood-count-cbc/'], ['Creatinine', 'Laboratory', 'A waste product measured in blood or urine and commonly used to evaluate kidney function.', 'Often reviewed with eGFR, diabetes, hypertension, and medication dosing.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/creatinine-test/'], ['Triglycerides', 'Laboratory', 'A type of fat measured in the blood as part of cardiovascular and metabolic risk review.', 'Frequently tracked with cholesterol labs and lipid-management medicines.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/triglycerides-test/'], ['Body mass index (BMI)', 'Body Measurements', 'A calculated measure based on height and weight.', 'Can be tracked as one rough context marker, though it does not replace clinical assessment.', 'MedlinePlus', 'https://medlineplus.gov/ency/article/007196.htm'], ['INR', 'Laboratory', 'International normalized ratio, a standardized blood-clotting measure often reported with prothrombin time.', 'Important for certain anticoagulant monitoring when ordered by a clinician.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/prothrombin-time-test-and-inr-ptinr/'], ['C-reactive protein (CRP)', 'Laboratory', 'A blood marker that may rise with inflammation.', 'Can be logged with symptoms, diagnosis workups, and clinician instructions.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/c-reactive-protein-crp-test/'], ['Urinalysis', 'Laboratory', 'A urine test that can screen for substances, cells, or signs of several health conditions.', 'Commonly tracked with kidney, urinary, diabetes, or pregnancy-related care.', 'MedlinePlus', 'https://medlineplus.gov/lab-tests/urinalysis/'], ]; $termInsert = $pdo->prepare( "INSERT INTO medical_terms (term, category, plain_language_definition, clinical_context, source_name, source_url, created_at) SELECT ?, ?, ?, ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM medical_terms WHERE term = ?)" ); foreach ($additionalTerms as $term) { $termInsert->execute([...$term, $created, $term[0]]); } $conditionCount = (int) $pdo->query('SELECT COUNT(*) FROM medical_conditions')->fetchColumn(); if ($conditionCount === 0) { $conditions = [ ['Hypertension', 'Cardiovascular', 'High blood pressure is a common condition tracked through repeated readings and risk-factor review.', 'Blood pressure, pulse, medications, symptoms, lifestyle notes', 'MedlinePlus', 'https://medlineplus.gov/highbloodpressure.html'], ['Type 2 Diabetes', 'Endocrine/Metabolic', 'Type 2 diabetes is commonly tracked with glucose, A1C, medications, nutrition, and activity patterns.', 'Glucose, A1C, medication adherence, symptoms, nutrition notes', 'MedlinePlus', 'https://medlineplus.gov/diabetestype2.html'], ['Asthma', 'Respiratory', 'Asthma tracking often focuses on symptoms, rescue inhaler use, triggers, and breathing measures.', 'Symptoms, medication use, peak flow if prescribed, oxygen saturation', 'MedlinePlus', 'https://medlineplus.gov/asthma.html'], ['Depression', 'Mental Health', 'Depression tracking may include mood, sleep, medication use, symptoms, and care-team notes.', 'Mood, sleep, symptoms, medication adherence, side effects', 'MedlinePlus', 'https://medlineplus.gov/depression.html'], ['High Cholesterol', 'Cardiovascular/Metabolic', 'Cholesterol management is commonly followed through lipid labs and medication response.', 'LDL, HDL, triglycerides, statin adherence, side effects', 'MedlinePlus', 'https://medlineplus.gov/cholesterol.html'], ['Pain and Inflammation', 'General Medicine', 'Pain tracking can connect severity, location, medication use, and functional impact.', 'Pain score, medication timing, symptoms, notes', 'MedlinePlus', 'https://medlineplus.gov/pain.html'], ]; $stmt = $pdo->prepare( "INSERT INTO medical_conditions (name, category, overview, common_tracking, source_name, source_url, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)" ); foreach ($conditions as $condition) { $stmt->execute([...$condition, $created]); } } seed_expanded_condition_catalog($pdo, $created); seed_medical_symptom_catalog($pdo, $created); $medicineCount = (int) $pdo->query('SELECT COUNT(*) FROM medication_catalog')->fetchColumn(); if ($medicineCount === 0) { $medicines = [ ['metformin', 'Biguanide antidiabetic', 'Often used in type 2 diabetes care.', 'Track dose, meals if relevant, glucose readings, and GI symptoms.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=metformin', 'https://medlineplus.gov/druginfo/meds/a696005.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=metformin+diabetes+prevention+program', 'https://clinicaltrials.gov/search?term=metformin', 'RxNorm / MedlinePlus / PubMed'], ['lisinopril', 'ACE inhibitor', 'Often used in blood pressure and cardiovascular care.', 'Track blood pressure, pulse, cough, dizziness, and lab follow-up if ordered.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=lisinopril', 'https://medlineplus.gov/druginfo/meds/a692051.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=lisinopril+hypertension+trial', 'https://clinicaltrials.gov/search?term=lisinopril', 'RxNorm / MedlinePlus / PubMed'], ['atorvastatin', 'HMG-CoA reductase inhibitor statin', 'Often used for cholesterol management and cardiovascular risk reduction.', 'Track lipid labs, muscle symptoms, dose changes, and care-team notes.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=atorvastatin', 'https://medlineplus.gov/druginfo/meds/a600045.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=atorvastatin+cardiovascular+trial', 'https://clinicaltrials.gov/search?term=atorvastatin', 'RxNorm / MedlinePlus / PubMed'], ['albuterol', 'Short-acting beta agonist bronchodilator', 'Often used as a rescue medicine for asthma or bronchospasm.', 'Track inhaler use, symptoms, triggers, pulse, and breathing status.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=albuterol', 'https://medlineplus.gov/druginfo/meds/a682145.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=albuterol+asthma+clinical+trial', 'https://clinicaltrials.gov/search?term=albuterol', 'RxNorm / MedlinePlus / PubMed'], ['sertraline', 'Selective serotonin reuptake inhibitor', 'Often used in depression, anxiety, and related mental health care.', 'Track mood, sleep, side effects, dose changes, and follow-up notes.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=sertraline', 'https://medlineplus.gov/druginfo/meds/a697048.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=sertraline+depression+trial', 'https://clinicaltrials.gov/search?term=sertraline', 'RxNorm / MedlinePlus / PubMed'], ['ibuprofen', 'Nonsteroidal anti-inflammatory drug', 'Often used for pain, fever, or inflammation.', 'Track dose, pain score, stomach symptoms, and frequency of use.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=ibuprofen', 'https://medlineplus.gov/druginfo/meds/a682159.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=ibuprofen+pain+clinical+trial', 'https://clinicaltrials.gov/search?term=ibuprofen', 'RxNorm / MedlinePlus / PubMed'], ['acetaminophen', 'Analgesic and antipyretic', 'Often used for pain or fever.', 'Track total daily dose, pain score, fever, and other combination products.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=acetaminophen', 'https://medlineplus.gov/druginfo/meds/a681004.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=acetaminophen+pain+clinical+trial', 'https://clinicaltrials.gov/search?term=acetaminophen', 'RxNorm / MedlinePlus / PubMed'], ['amoxicillin', 'Penicillin antibiotic', 'Often used for susceptible bacterial infections.', 'Track start/stop dates, dose schedule, allergy symptoms, and care-team instructions.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=amoxicillin', 'https://medlineplus.gov/druginfo/meds/a685001.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=amoxicillin+clinical+trial', 'https://clinicaltrials.gov/search?term=amoxicillin', 'RxNorm / MedlinePlus / PubMed'], ['omeprazole', 'Proton pump inhibitor', 'Often used for acid-related gastrointestinal conditions.', 'Track symptom response, dose timing, duration, and clinician instructions.', 'https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=omeprazole', 'https://medlineplus.gov/druginfo/meds/a693050.html', 'https://pubmed.ncbi.nlm.nih.gov/?term=omeprazole+clinical+trial', 'https://clinicaltrials.gov/search?term=omeprazole', 'RxNorm / MedlinePlus / PubMed'], ]; $stmt = $pdo->prepare( "INSERT INTO medication_catalog (generic_name, class_name, common_use, tracking_note, rxnorm_url, medlineplus_url, pubmed_url, clinicaltrials_url, source_name, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); foreach ($medicines as $medicine) { $stmt->execute([...$medicine, $created]); } } seed_expanded_medication_catalog($pdo); $studyCount = (int) $pdo->query('SELECT COUNT(*) FROM research_studies')->fetchColumn(); if ($studyCount === 0) { $studies = [ ['Diabetes', 'metformin', 'Type 2 Diabetes', 'Diabetes Prevention Program and metformin research', 'NIH clinical research program', 'NIH diabetes prevention research is a major category for metformin-related prevention and metabolic-risk evidence.', 'NIDDK / PubMed / ClinicalTrials.gov', 'https://www.niddk.nih.gov/about-niddk/research-areas/diabetes/diabetes-prevention-program-dpp', 'https://pubmed.ncbi.nlm.nih.gov/?term=Diabetes+Prevention+Program+metformin', 'https://clinicaltrials.gov/search?term=metformin%20diabetes%20prevention'], ['Cardiovascular', 'lisinopril', 'Hypertension', 'Blood pressure intervention and antihypertensive treatment studies', 'NIH clinical trial category', 'NIH cardiovascular studies include blood-pressure targets, antihypertensive treatment, and outcomes research relevant to hypertension medicines.', 'NHLBI / PubMed / ClinicalTrials.gov', 'https://www.nhlbi.nih.gov/science/systolic-blood-pressure-intervention-trial-sprint-study', 'https://pubmed.ncbi.nlm.nih.gov/?term=lisinopril+hypertension+clinical+trial', 'https://clinicaltrials.gov/search?term=lisinopril%20hypertension'], ['Cardiovascular', 'atorvastatin', 'High Cholesterol', 'Statin and lipid-management outcomes research', 'Medication outcomes research', 'Statin studies evaluate lipid changes and cardiovascular outcomes; this app links the medication catalog to PubMed and ClinicalTrials.gov searches.', 'PubMed / ClinicalTrials.gov', 'https://pubmed.ncbi.nlm.nih.gov/?term=atorvastatin+cardiovascular+outcomes+trial', 'https://pubmed.ncbi.nlm.nih.gov/?term=atorvastatin+cardiovascular+outcomes+trial', 'https://clinicaltrials.gov/search?term=atorvastatin'], ['Respiratory', 'albuterol', 'Asthma', 'Asthma medication and rescue inhaler studies', 'Clinical research category', 'Asthma studies often evaluate symptom control, rescue medication use, and exacerbation outcomes.', 'NHLBI / PubMed / ClinicalTrials.gov', 'https://www.nhlbi.nih.gov/health/asthma', 'https://pubmed.ncbi.nlm.nih.gov/?term=albuterol+asthma+clinical+trial', 'https://clinicaltrials.gov/search?term=albuterol%20asthma'], ['Mental Health', 'sertraline', 'Depression', 'Depression treatment studies and medication-response research', 'NIH mental health research category', 'Depression studies evaluate symptom scores, remission, side effects, and care pathways for medicines including SSRIs.', 'NIMH / PubMed / ClinicalTrials.gov', 'https://www.nimh.nih.gov/health/topics/depression', 'https://pubmed.ncbi.nlm.nih.gov/?term=sertraline+depression+randomized+trial', 'https://clinicaltrials.gov/search?term=sertraline%20depression'], ['Pain', 'ibuprofen', 'Pain and Inflammation', 'NSAID pain and inflammation studies', 'Medication safety and outcomes category', 'Pain studies can compare pain scores, function, adverse effects, and dose schedules for NSAIDs.', 'PubMed / ClinicalTrials.gov', 'https://pubmed.ncbi.nlm.nih.gov/?term=ibuprofen+pain+randomized+trial', 'https://pubmed.ncbi.nlm.nih.gov/?term=ibuprofen+pain+randomized+trial', 'https://clinicaltrials.gov/search?term=ibuprofen%20pain'], ['Pain', 'acetaminophen', 'Pain and Fever', 'Acetaminophen pain and fever studies', 'Medication safety and outcomes category', 'Acetaminophen studies often evaluate pain relief, fever reduction, dosing, and safety monitoring.', 'PubMed / ClinicalTrials.gov', 'https://pubmed.ncbi.nlm.nih.gov/?term=acetaminophen+pain+randomized+trial', 'https://pubmed.ncbi.nlm.nih.gov/?term=acetaminophen+pain+randomized+trial', 'https://clinicaltrials.gov/search?term=acetaminophen'], ['Infectious Disease', 'amoxicillin', 'Bacterial Infection', 'Antibiotic effectiveness and safety studies', 'Medication outcomes category', 'Antibiotic studies evaluate infection outcomes, adverse reactions, resistance patterns, and dosing strategies.', 'PubMed / ClinicalTrials.gov', 'https://pubmed.ncbi.nlm.nih.gov/?term=amoxicillin+clinical+trial', 'https://pubmed.ncbi.nlm.nih.gov/?term=amoxicillin+clinical+trial', 'https://clinicaltrials.gov/search?term=amoxicillin'], ['Gastroenterology', 'omeprazole', 'Acid-related GI Conditions', 'Proton pump inhibitor studies', 'Medication outcomes category', 'PPI studies evaluate symptom control, healing outcomes, duration, and safety topics.', 'PubMed / ClinicalTrials.gov', 'https://pubmed.ncbi.nlm.nih.gov/?term=omeprazole+clinical+trial', 'https://pubmed.ncbi.nlm.nih.gov/?term=omeprazole+clinical+trial', 'https://clinicaltrials.gov/search?term=omeprazole'], ]; $stmt = $pdo->prepare( "INSERT INTO research_studies (category, related_medicine, related_condition, title, study_type, summary, nih_source, source_url, pubmed_url, clinicaltrials_url, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); foreach ($studies as $study) { $stmt->execute([...$study, $created]); } } } function seed_expanded_condition_catalog(PDO $pdo, string $created): void { $rows = condition_catalog_rows(); $existing = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_conditions'); $uniqueTarget = count(array_unique(array_map(static fn(array $row): string => strtolower($row[0]), $rows))); if ($existing >= $uniqueTarget) { return; } $stmt = $pdo->prepare( "INSERT INTO medical_conditions (name, category, overview, common_tracking, source_name, source_url, created_at) SELECT ?, ?, ?, ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM medical_conditions WHERE lower(name) = lower(?))" ); $startedTransaction = !$pdo->inTransaction(); if ($startedTransaction) { $pdo->beginTransaction(); } try { foreach ($rows as $row) { $stmt->execute([...$row, $created, $row[0]]); } if ($startedTransaction) { $pdo->commit(); } } catch (Throwable $exception) { if ($startedTransaction && $pdo->inTransaction()) { $pdo->rollBack(); } throw $exception; } } function condition_catalog_rows(): array { $groups = [ [ 'Cardiovascular', '{condition} is a heart, blood vessel, blood-pressure, clotting, or circulation-related condition.', 'Track blood pressure, pulse, weight, swelling, chest symptoms, shortness of breath, medicines, labs, procedures, and care-team instructions.', ['Hypertension', 'High Cholesterol', 'Coronary Artery Disease', 'Angina', 'Heart Attack', 'Heart Failure', 'Atrial Fibrillation', 'Arrhythmia', 'Stroke', 'Transient Ischemic Attack', 'Peripheral Artery Disease', 'Deep Vein Thrombosis', 'Pulmonary Embolism', 'Aortic Aneurysm', 'Raynaud Disease'], ], [ 'Respiratory', '{condition} is a lung, airway, breathing, or oxygen-related condition.', 'Track cough, breathing effort, oxygen saturation, wheezing, triggers, fever, inhaler use, smoking or exposure history, and activity tolerance.', ['Asthma', 'Chronic Obstructive Pulmonary Disease', 'Pneumonia', 'Bronchitis', 'Sleep Apnea', 'Pulmonary Fibrosis', 'Tuberculosis', 'Cystic Fibrosis', 'Pleural Effusion', 'Respiratory Syncytial Virus Infection', 'COVID-19', 'Influenza', 'Allergic Rhinitis', 'Sinusitis'], ], [ 'Endocrine / metabolic', '{condition} is a hormone, blood-sugar, thyroid, weight, or metabolism-related condition.', 'Track glucose if relevant, A1C, weight, blood pressure, medicines, energy, thirst, urination, labs, nutrition context, and clinician goals.', ['Type 1 Diabetes', 'Type 2 Diabetes', 'Prediabetes', 'Gestational Diabetes', 'Hypoglycemia', 'Hyperthyroidism', 'Hypothyroidism', 'Hashimoto Thyroiditis', 'Graves Disease', 'Cushing Syndrome', 'Addison Disease', 'Polycystic Ovary Syndrome', 'Obesity', 'Metabolic Syndrome', 'Vitamin D Deficiency', 'Osteoporosis'], ], [ 'Digestive / liver', '{condition} is a digestive tract, liver, gallbladder, pancreas, or bowel-related condition.', 'Track abdominal pain location, bowel pattern, nausea, vomiting, bleeding, diet triggers, hydration, weight changes, medicines, procedures, and lab or imaging follow-up.', ['Gastroesophageal Reflux Disease', 'Peptic Ulcer Disease', 'Gastritis', 'Irritable Bowel Syndrome', 'Crohn Disease', 'Ulcerative Colitis', 'Celiac Disease', 'Diverticulitis', 'Gallstones', 'Pancreatitis', 'Hepatitis A', 'Hepatitis B', 'Hepatitis C', 'Cirrhosis', 'Fatty Liver Disease', 'Appendicitis', 'Hemorrhoids', 'Constipation', 'Diarrhea'], ], [ 'Kidney / urinary', '{condition} is a kidney, bladder, urinary tract, or fluid-balance-related condition.', 'Track urination pattern, pain, fever, fluid intake, urine test results, kidney labs, blood pressure, swelling, medicines, and procedure notes.', ['Chronic Kidney Disease', 'Kidney Stones', 'Urinary Tract Infection', 'Bladder Infection', 'Kidney Infection', 'Urinary Incontinence', 'Overactive Bladder', 'Benign Prostatic Hyperplasia', 'Interstitial Cystitis', 'Glomerulonephritis', 'Nephrotic Syndrome'], ], [ 'Neurologic', '{condition} is a brain, spinal cord, nerve, movement, headache, seizure, or cognition-related condition.', 'Track onset, symptom episodes, weakness, speech or vision changes, headaches, seizures, falls, medicines, triggers, sleep, and safety events.', ['Migraine', 'Epilepsy', 'Multiple Sclerosis', 'Parkinson Disease', 'Alzheimer Disease', 'Dementia', 'Neuropathy', 'Bell Palsy', 'Meningitis', 'Concussion', 'Traumatic Brain Injury', 'Vertigo', 'Amyotrophic Lateral Sclerosis', 'Restless Legs Syndrome', 'Carpal Tunnel Syndrome'], ], [ 'Mental health', '{condition} is a mood, anxiety, attention, behavior, substance-use, sleep, or psychiatric condition.', 'Track mood, sleep, appetite, concentration, safety concerns, triggers, medicines, therapy notes, substance use, and follow-up plans.', ['Depression', 'Anxiety Disorders', 'Generalized Anxiety Disorder', 'Panic Disorder', 'Bipolar Disorder', 'Post-Traumatic Stress Disorder', 'Obsessive-Compulsive Disorder', 'Attention Deficit Hyperactivity Disorder', 'Autism Spectrum Disorder', 'Schizophrenia', 'Eating Disorders', 'Alcohol Use Disorder', 'Substance Use Disorder', 'Insomnia'], ], [ 'Musculoskeletal / rheumatology', '{condition} is a bone, joint, muscle, spine, autoimmune, or inflammation-related condition.', 'Track pain score, stiffness, swelling, range of motion, flares, activity limits, injuries, medicines, injections, imaging, and function.', ['Osteoarthritis', 'Rheumatoid Arthritis', 'Gout', 'Fibromyalgia', 'Lupus', 'Ankylosing Spondylitis', 'Psoriatic Arthritis', 'Bursitis', 'Tendinitis', 'Back Pain', 'Sciatica', 'Scoliosis', 'Fracture', 'Sprain', 'Carpal Tunnel Syndrome', 'Osteomyelitis'], ], [ 'Skin / dermatology', '{condition} is a skin, hair, nail, wound, allergy, or visible-tissue condition.', 'Track location, appearance, spread, itch, pain, fever, drainage, new medicines, exposures, photos, treatment response, and recurrence.', ['Acne', 'Eczema', 'Psoriasis', 'Rosacea', 'Hives', 'Cellulitis', 'Shingles', 'Cold Sores', 'Fungal Skin Infection', 'Warts', 'Vitiligo', 'Alopecia Areata', 'Pressure Ulcers', 'Melanoma', 'Skin Cancer', 'Seborrheic Dermatitis'], ], [ 'Infectious disease', '{condition} is an infection or infectious-disease-related condition.', 'Track fever, symptom onset, exposures, test results, isolation guidance, medicines, completion dates, hydration, and worsening symptoms.', ['Common Cold', 'Strep Throat', 'Mononucleosis', 'Lyme Disease', 'Sepsis', 'HIV', 'Herpes', 'Chickenpox', 'Measles', 'Mumps', 'Whooping Cough', 'Food Poisoning', 'MRSA Infection', 'C. difficile Infection'], ], [ 'Blood / immune', '{condition} is a blood, clotting, anemia, immune, or allergy-related condition.', 'Track CBC results, bleeding or bruising, fatigue, infections, triggers, exposures, medicines, transfusions, and specialist follow-up.', ['Anemia', 'Iron Deficiency Anemia', 'Sickle Cell Disease', 'Hemophilia', 'Thrombocytopenia', 'Leukemia', 'Lymphoma', 'Multiple Myeloma', 'Allergies', 'Anaphylaxis', 'Autoimmune Diseases', 'Immunodeficiency Disorders'], ], [ 'Eye / ear / nose / throat', '{condition} is an eye, ear, sinus, mouth, dental, hearing, or throat condition.', 'Track pain, side affected, vision or hearing changes, discharge, fever, allergies, dental symptoms, medicines, and procedure notes.', ['Conjunctivitis', 'Cataracts', 'Glaucoma', 'Macular Degeneration', 'Diabetic Eye Disease', 'Dry Eye', 'Ear Infection', 'Tinnitus', 'Hearing Loss', 'Tonsillitis', 'Dental Cavities', 'Gum Disease'], ], [ 'Reproductive / pregnancy', '{condition} is a reproductive, menstrual, pregnancy, breast, prostate, or sexual-health-related condition.', 'Track cycle timing, bleeding, pelvic pain, discharge, pregnancy context, urinary symptoms, test results, medicines, and clinician instructions.', ['Endometriosis', 'Uterine Fibroids', 'Ovarian Cysts', 'Menopause', 'Pregnancy', 'Preeclampsia', 'Breast Cancer', 'Prostate Cancer', 'Prostatitis', 'Erectile Dysfunction', 'Sexually Transmitted Infections', 'Infertility'], ], [ 'Cancer / oncology', '{condition} is a cancer, tumor, screening, treatment, or survivorship-related condition.', 'Track diagnosis details, stage if known, treatments, side effects, labs, imaging, symptoms, appointments, medicines, and care-team plans.', ['Lung Cancer', 'Colorectal Cancer', 'Breast Cancer', 'Prostate Cancer', 'Skin Cancer', 'Melanoma', 'Pancreatic Cancer', 'Kidney Cancer', 'Bladder Cancer', 'Thyroid Cancer', 'Ovarian Cancer', 'Cervical Cancer'], ], [ 'General medicine', '{condition} is a general medical condition or care topic that may involve multiple body systems.', 'Track symptoms, onset, severity, medicines, vitals, labs, appointments, self-care steps, and clinician guidance.', ['Pain and Inflammation', 'Chronic Pain', 'Fever', 'Dehydration', 'Malnutrition', 'Falls', 'Frailty', 'Medication Side Effects', 'Adverse Drug Reaction', 'Postoperative Recovery'], ], ]; $rows = []; foreach ($groups as [$category, $overviewTemplate, $tracking, $names]) { foreach ($names as $name) { $rows[] = [ $name, $category, str_replace('{condition}', $name, $overviewTemplate), $tracking, 'MedlinePlus', condition_source_url($name), ]; } } return $rows; } function condition_source_url(string $name): string { $map = [ 'Alzheimer Disease' => 'https://medlineplus.gov/alzheimersdisease.html', 'Anemia' => 'https://medlineplus.gov/anemia.html', 'Anxiety Disorders' => 'https://medlineplus.gov/anxiety.html', 'Asthma' => 'https://medlineplus.gov/asthma.html', 'Back Pain' => 'https://medlineplus.gov/backpain.html', 'Breast Cancer' => 'https://medlineplus.gov/breastcancer.html', 'Chronic Kidney Disease' => 'https://medlineplus.gov/chronickidneydisease.html', 'Chronic Obstructive Pulmonary Disease' => 'https://medlineplus.gov/copd.html', 'COVID-19' => 'https://medlineplus.gov/covid19coronavirusdisease2019.html', 'Depression' => 'https://medlineplus.gov/depression.html', 'Diarrhea' => 'https://medlineplus.gov/diarrhea.html', 'Eczema' => 'https://medlineplus.gov/eczema.html', 'Epilepsy' => 'https://medlineplus.gov/epilepsy.html', 'Fibromyalgia' => 'https://medlineplus.gov/fibromyalgia.html', 'Gastroesophageal Reflux Disease' => 'https://medlineplus.gov/gerd.html', 'Gout' => 'https://medlineplus.gov/gout.html', 'Heart Attack' => 'https://medlineplus.gov/heartattack.html', 'Heart Failure' => 'https://medlineplus.gov/heartfailure.html', 'Hepatitis C' => 'https://medlineplus.gov/hepatitisc.html', 'High Cholesterol' => 'https://medlineplus.gov/cholesterol.html', 'HIV' => 'https://medlineplus.gov/hivaids.html', 'Hypertension' => 'https://medlineplus.gov/highbloodpressure.html', 'Influenza' => 'https://medlineplus.gov/flu.html', 'Insomnia' => 'https://medlineplus.gov/insomnia.html', 'Irritable Bowel Syndrome' => 'https://medlineplus.gov/irritablebowelsyndrome.html', 'Kidney Stones' => 'https://medlineplus.gov/kidneystones.html', 'Lupus' => 'https://medlineplus.gov/lupus.html', 'Migraine' => 'https://medlineplus.gov/migraine.html', 'Multiple Sclerosis' => 'https://medlineplus.gov/multiplesclerosis.html', 'Obesity' => 'https://medlineplus.gov/obesity.html', 'Osteoarthritis' => 'https://medlineplus.gov/osteoarthritis.html', 'Osteoporosis' => 'https://medlineplus.gov/osteoporosis.html', 'Parkinson Disease' => 'https://medlineplus.gov/parkinsonsdisease.html', 'Pneumonia' => 'https://medlineplus.gov/pneumonia.html', 'Pregnancy' => 'https://medlineplus.gov/pregnancy.html', 'Psoriasis' => 'https://medlineplus.gov/psoriasis.html', 'Rheumatoid Arthritis' => 'https://medlineplus.gov/rheumatoidarthritis.html', 'Shingles' => 'https://medlineplus.gov/shingles.html', 'Sickle Cell Disease' => 'https://medlineplus.gov/sicklecelldisease.html', 'Sleep Apnea' => 'https://medlineplus.gov/sleepapnea.html', 'Stroke' => 'https://medlineplus.gov/stroke.html', 'Type 1 Diabetes' => 'https://medlineplus.gov/diabetestype1.html', 'Type 2 Diabetes' => 'https://medlineplus.gov/diabetestype2.html', 'Urinary Tract Infection' => 'https://medlineplus.gov/urinarytractinfections.html', ]; return $map[$name] ?? 'https://medlineplus.gov/search/?query=' . rawurlencode($name); } function seed_medical_symptom_catalog(PDO $pdo, string $created): void { $rows = symptom_catalog_rows(); $existing = (int) db_value($pdo, 'SELECT COUNT(*) FROM medical_symptoms'); if ($existing >= count($rows)) { return; } $stmt = $pdo->prepare( "INSERT INTO medical_symptoms (name, category, body_system, plain_language_description, common_tracking, urgency_note, source_name, source_url, search_terms, created_at) SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM medical_symptoms WHERE lower(name) = lower(?))" ); $startedTransaction = !$pdo->inTransaction(); if ($startedTransaction) { $pdo->beginTransaction(); } try { foreach ($rows as $row) { $stmt->execute([...$row, $created, $row[0]]); } if ($startedTransaction) { $pdo->commit(); } } catch (Throwable $exception) { if ($startedTransaction && $pdo->inTransaction()) { $pdo->rollBack(); } throw $exception; } } function symptom_catalog_rows(): array { $groups = [ [ 'General / systemic', 'Whole body', '{symptom} is a body-wide symptom or change that can be useful to track alongside temperature, hydration, sleep, medicines, and recent exposures.', 'Record onset, duration, severity, temperature if relevant, hydration, recent illness exposure, medicines taken, and what improves or worsens it.', ['Fever', 'Chills', 'Fatigue', 'Malaise', 'General weakness', 'Unintentional weight loss', 'Unintentional weight gain', 'Night sweats', 'Excessive sweating', 'Dehydration', 'Excessive thirst', 'Loss of appetite', 'Increased appetite', 'Heat intolerance', 'Cold intolerance', 'Swelling', 'Edema', 'Swollen lymph nodes', 'Body aches'], ], [ 'Pain', 'Pain / sensory', '{symptom} is a pain or sensory complaint that is often tracked by location, intensity, timing, triggers, and response to treatment.', 'Record pain score, location, character, onset, duration, triggers, injuries, activity limits, medicines used, and associated symptoms.', ['Pain', 'Chronic pain', 'Severe pain', 'Abdominal pain', 'Chest pain', 'Chest pressure', 'Back pain', 'Neck pain', 'Joint pain', 'Muscle pain', 'Pelvic pain', 'Flank pain', 'Ear pain', 'Eye pain', 'Sore throat', 'Headache', 'Severe headache', 'Toothache', 'Facial pain', 'Sciatica', 'Cramps', 'Tenderness', 'Burning pain', 'Numbness', 'Tingling'], ], [ 'Respiratory', 'Lungs / airways', '{symptom} is a breathing, airway, nose, throat, or lung-related symptom that may be tracked with oxygen saturation, triggers, and medication use when relevant.', 'Record breathing effort, oxygen saturation if available, cough character, sputum, fever, triggers, inhaler or medicine use, activity tolerance, and exposure history.', ['Cough', 'Dry cough', 'Productive cough', 'Shortness of breath', 'Trouble breathing', 'Wheezing', 'Chest tightness', 'Rapid breathing', 'Shallow breathing', 'Coughing up blood', 'Noisy breathing', 'Snoring', 'Sleep apnea symptoms', 'Nasal congestion', 'Runny nose', 'Sneezing', 'Postnasal drip', 'Sinus pressure', 'Hoarseness', 'Sputum changes'], ], [ 'Cardiovascular', 'Heart / circulation', '{symptom} is a heart, blood vessel, or circulation-related symptom that may be useful to track with pulse, blood pressure, swelling, exertion, and medication timing.', 'Record pulse, blood pressure if available, activity at onset, duration, swelling location, shortness of breath, chest symptoms, medicines, and whether symptoms occur at rest or with exertion.', ['Palpitations', 'Fainting', 'Near-fainting', 'Dizziness', 'Lightheadedness', 'Leg swelling', 'Blue lips', 'Rapid heartbeat', 'Slow heartbeat', 'Orthopnea', 'Exercise intolerance', 'Cold hands and feet', 'Clammy skin'], ], [ 'Digestive', 'Gastrointestinal', '{symptom} is a digestive or abdominal symptom that may be tracked with meals, hydration, bowel pattern, medicines, and associated pain.', 'Record meals, hydration, bowel frequency, stool appearance, vomiting episodes, abdominal location, fever, weight changes, medicines, and possible food or infection exposures.', ['Nausea', 'Vomiting', 'Vomiting blood', 'Diarrhea', 'Constipation', 'Heartburn', 'Indigestion', 'Gas', 'Bloating', 'Belching', 'Abdominal cramps', 'Blood in stool', 'Black stools', 'Rectal bleeding', 'Difficulty swallowing', 'Loss of bowel control', 'Bowel urgency', 'Mucus in stool', 'Jaundice', 'Hiccups', 'Early fullness', 'Loss of taste for food'], ], [ 'Neurologic', 'Brain / nerves', '{symptom} is a nervous-system symptom that may be important to track with timing, location, mental status, movement changes, and related medicines or exposures.', 'Record onset time, duration, affected body areas, speech or vision changes, balance, weakness, headache, injury, seizure-like activity, medicines, and glucose if relevant.', ['Confusion', 'Memory problems', 'Seizure', 'Tremor', 'Migraine aura', 'Vision loss', 'Sudden vision loss', 'Double vision', 'Slurred speech', 'Difficulty speaking', 'Difficulty walking', 'Balance problems', 'Facial droop', 'Weakness on one side', 'Drowsiness', 'Vertigo', 'Loss of consciousness', 'Restless legs', 'Involuntary movements', 'Poor coordination', 'Light sensitivity', 'Sound sensitivity'], ], [ 'Skin / hair / nails', 'Skin', '{symptom} is a skin, hair, nail, or visible tissue change that can be tracked with appearance, spread, triggers, itching, pain, and exposures.', 'Record appearance, size, color, location, spread, itch or pain, fever, new medicines, foods, soaps, insect exposures, wounds, and photos if useful.', ['Rash', 'Itching', 'Hives', 'Bruises', 'Easy bruising', 'Bleeding', 'Wound', 'Blister', 'Skin redness', 'Skin warmth', 'Dry skin', 'Peeling skin', 'Skin color change', 'Cyanosis', 'Hair loss', 'Nail changes', 'Skin ulcer', 'Mole changes', 'Insect bite reaction', 'Pus or drainage', 'Cracked skin', 'Excessive hair growth'], ], [ 'Eye / ear / nose / throat', 'Head and neck', '{symptom} is an eye, ear, nose, mouth, or throat symptom that may be tracked with pain, discharge, hearing or vision changes, and infection or allergy context.', 'Record side affected, pain, discharge, fever, vision or hearing changes, allergy triggers, recent infections, injuries, dental symptoms, and medicines used.', ['Eye redness', 'Eye discharge', 'Watery eyes', 'Blurred vision', 'Floaters', 'Ear discharge', 'Hearing loss', 'Tinnitus', 'Ear fullness', 'Nosebleed', 'Mouth sores', 'Dry mouth', 'Bad breath', 'Swollen gums', 'Gum bleeding', 'Taste changes', 'Smell changes', 'Jaw pain', 'Tongue swelling', 'Difficulty opening mouth'], ], [ 'Urinary / reproductive', 'Genitourinary', '{symptom} is a urinary, pelvic, breast, menstrual, or reproductive symptom that may be tracked with timing, pain, bleeding, discharge, and fluid intake.', 'Record urination pattern, pain, color, bleeding, discharge, pelvic or flank pain, cycle timing, pregnancy possibility if relevant, sexual exposure concerns, fever, and medicines.', ['Painful urination', 'Frequent urination', 'Urgent urination', 'Blood in urine', 'Cloudy urine', 'Urinary incontinence', 'Urinary retention', 'Nighttime urination', 'Decreased urination', 'Vaginal bleeding', 'Vaginal discharge', 'Missed period', 'Heavy menstrual bleeding', 'Painful periods', 'Hot flashes', 'Breast pain', 'Breast lump', 'Nipple discharge', 'Testicular pain', 'Scrotal swelling', 'Erectile dysfunction', 'Pain with sex'], ], [ 'Musculoskeletal', 'Bones / joints / muscles', '{symptom} is a muscle, joint, bone, or movement-related symptom that may be tracked with location, activity, injury, swelling, stiffness, and function.', 'Record affected area, injury or overuse, swelling, redness, stiffness, range of motion, weakness, pain score, walking changes, falls, medicines, and functional limits.', ['Joint swelling', 'Joint stiffness', 'Limited range of motion', 'Muscle weakness', 'Muscle cramps', 'Muscle twitching', 'Back stiffness', 'Gait change', 'Falls', 'Bone pain', 'Shoulder pain', 'Hip pain', 'Knee pain', 'Ankle swelling', 'Hand pain', 'Foot pain', 'Neck stiffness', 'Limping'], ], [ 'Mental health / sleep', 'Mood / sleep / behavior', '{symptom} is a mood, behavior, thinking, or sleep-related symptom that can be tracked with timing, stressors, sleep, medicines, substance use, and safety concerns.', 'Record mood, sleep hours, triggers, stressors, medicines, substance use, appetite, energy, concentration, safety concerns, and support or care contacts.', ['Anxiety', 'Panic attack', 'Depressed mood', 'Mood swings', 'Irritability', 'Insomnia', 'Excessive sleepiness', 'Nightmares', 'Hallucinations', 'Delusions', 'Suicidal thoughts', 'Self-harm thoughts', 'Trouble concentrating', 'Behavior changes', 'Agitation', 'Social withdrawal', 'Low motivation', 'Racing thoughts'], ], [ 'Metabolic / endocrine', 'Hormones / metabolism', '{symptom} is a metabolism, hormone, or blood-sugar-related symptom that may be tracked with food intake, glucose readings, medicines, hydration, and energy changes.', 'Record meals, hydration, glucose readings if available, medicines, sweating, shaking, urination, thirst, weight changes, energy, and timing around exercise or illness.', ['High blood sugar symptoms', 'Low blood sugar symptoms', 'Increased thirst', 'Frequent hunger', 'Excessive urination', 'Shakiness', 'Sweating episodes', 'Unintentional weight changes', 'Feeling overheated', 'Feeling cold', 'Salt craving'], ], [ 'Medication / allergy', 'Medication safety', '{symptom} is a possible medicine, injection, allergy, or exposure-related symptom that is useful to record with timing, dose, route, and product details.', 'Record medicine or exposure name, dose, timing, route, new products, rash or swelling, breathing symptoms, dizziness, stomach symptoms, bleeding, and whether the medicine was stopped or changed.', ['Allergic reaction', 'Severe allergic reaction', 'Medication side effects', 'Injection-site reaction', 'Dizziness after medication', 'Nausea after medication', 'Rash after medication', 'Swelling after medication', 'Bleeding after medication', 'Sleepiness after medication'], ], ]; $urgentNames = array_fill_keys(array_map('strtolower', [ 'Chest pain', 'Chest pressure', 'Shortness of breath', 'Trouble breathing', 'Coughing up blood', 'Blue lips', 'Fainting', 'Loss of consciousness', 'Seizure', 'Confusion', 'Facial droop', 'Weakness on one side', 'Slurred speech', 'Difficulty speaking', 'Sudden vision loss', 'Severe headache', 'Vomiting blood', 'Blood in stool', 'Black stools', 'Rectal bleeding', 'Severe allergic reaction', 'Suicidal thoughts', 'Self-harm thoughts', ]), true); $rows = []; foreach ($groups as [$category, $bodySystem, $descriptionTemplate, $tracking, $names]) { foreach ($names as $name) { $urgency = isset($urgentNames[strtolower($name)]) ? 'Potential urgent symptom: seek emergency care for sudden, severe, new, or worsening symptoms, or when this appears with breathing trouble, chest symptoms, fainting, neurologic changes, severe bleeding, or safety concerns.' : 'Track patterns and contact a clinician for persistent, worsening, unexplained, recurrent, or concerning symptoms.'; $searchTerms = strtolower($name . ' ' . $category . ' ' . $bodySystem . ' symptom sign medical tracking medlineplus'); $rows[] = [ $name, $category, $bodySystem, str_replace('{symptom}', $name, $descriptionTemplate), $tracking, $urgency, 'MedlinePlus', symptom_source_url($name), $searchTerms, ]; } } return $rows; } function symptom_source_url(string $name): string { $map = [ 'Abdominal pain' => 'https://medlineplus.gov/abdominalpain.html', 'Anxiety' => 'https://medlineplus.gov/anxiety.html', 'Back pain' => 'https://medlineplus.gov/backpain.html', 'Chest pain' => 'https://medlineplus.gov/chestpain.html', 'Constipation' => 'https://medlineplus.gov/constipation.html', 'Cough' => 'https://medlineplus.gov/cough.html', 'Dehydration' => 'https://medlineplus.gov/dehydration.html', 'Depressed mood' => 'https://medlineplus.gov/depression.html', 'Diarrhea' => 'https://medlineplus.gov/diarrhea.html', 'Dizziness' => 'https://medlineplus.gov/dizzinessandvertigo.html', 'Ear pain' => 'https://medlineplus.gov/earinfections.html', 'Fatigue' => 'https://medlineplus.gov/fatigue.html', 'Fever' => 'https://medlineplus.gov/fever.html', 'Hair loss' => 'https://medlineplus.gov/hairloss.html', 'Headache' => 'https://medlineplus.gov/headache.html', 'Heartburn' => 'https://medlineplus.gov/heartburn.html', 'Hives' => 'https://medlineplus.gov/hives.html', 'Insomnia' => 'https://medlineplus.gov/insomnia.html', 'Itching' => 'https://medlineplus.gov/itching.html', 'Jaundice' => 'https://medlineplus.gov/jaundice.html', 'Joint pain' => 'https://medlineplus.gov/jointdisorders.html', 'Nausea' => 'https://medlineplus.gov/nauseaandvomiting.html', 'Neck pain' => 'https://medlineplus.gov/neckinjuriesanddisorders.html', 'Pain' => 'https://medlineplus.gov/pain.html', 'Palpitations' => 'https://medlineplus.gov/heartdiseases.html', 'Rash' => 'https://medlineplus.gov/rashes.html', 'Shortness of breath' => 'https://medlineplus.gov/breathingproblems.html', 'Sore throat' => 'https://medlineplus.gov/sorethroat.html', 'Tinnitus' => 'https://medlineplus.gov/tinnitus.html', 'Tremor' => 'https://medlineplus.gov/tremor.html', 'Urinary incontinence' => 'https://medlineplus.gov/urinaryincontinence.html', 'Vertigo' => 'https://medlineplus.gov/dizzinessandvertigo.html', 'Vomiting' => 'https://medlineplus.gov/nauseaandvomiting.html', 'Wheezing' => 'https://medlineplus.gov/breathingproblems.html', ]; return $map[$name] ?? 'https://medlineplus.gov/search/?query=' . rawurlencode($name); } function medication_source_url(string $base, string $query): string { return $base . rawurlencode($query); } function seed_expanded_medication_catalog(PDO $pdo): void { $created = app_now(); $rows = expanded_medication_catalog_rows(); $existingCount = (int) $pdo->query('SELECT COUNT(*) FROM medication_catalog')->fetchColumn(); $needsSearchText = (int) $pdo->query("SELECT COUNT(*) FROM medication_catalog WHERE search_terms IS NULL OR search_terms = ''")->fetchColumn(); $needsChemicalNames = (int) $pdo->query("SELECT COUNT(*) FROM medication_catalog WHERE chemical_name IS NULL OR chemical_name = ''")->fetchColumn(); if ($existingCount >= count($rows) && $needsSearchText === 0 && $needsChemicalNames === 0) { return; } $select = $pdo->prepare('SELECT id FROM medication_catalog WHERE lower(generic_name) = lower(?) LIMIT 1'); $insert = $pdo->prepare( "INSERT INTO medication_catalog (generic_name, active_ingredient, chemical_name, brand_names, generic_brands, class_name, drug_family, common_use, tracking_note, rxnorm_url, medlineplus_url, pubchem_url, dailymed_url, pubmed_url, clinicaltrials_url, source_name, search_terms, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $update = $pdo->prepare( "UPDATE medication_catalog SET active_ingredient = ?, chemical_name = ?, brand_names = ?, generic_brands = ?, class_name = ?, drug_family = ?, common_use = ?, tracking_note = ?, rxnorm_url = ?, medlineplus_url = ?, pubchem_url = ?, dailymed_url = ?, pubmed_url = ?, clinicaltrials_url = ?, source_name = ?, search_terms = ? WHERE id = ?" ); foreach ($rows as $row) { [$genericName, $activeIngredient, $chemicalName, $brandNames, $genericBrands, $className, $drugFamily, $commonUse, $trackingNote, $medlinePlusUrl] = $row; $rxnormUrl = medication_source_url('https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=', $genericName); $pubchemUrl = medication_source_url('https://pubchem.ncbi.nlm.nih.gov/#query=', $activeIngredient ?: $genericName); $dailymedUrl = medication_source_url('https://dailymed.nlm.nih.gov/dailymed/search.cfm?query=', $genericName); $pubmedUrl = medication_source_url('https://pubmed.ncbi.nlm.nih.gov/?term=', $genericName . ' medication'); $clinicalTrialsUrl = medication_source_url('https://clinicaltrials.gov/search?term=', $genericName); $sourceName = 'RxNorm / DailyMed / PubChem / MedlinePlus'; $searchTerms = implode(' ', [ $genericName, $activeIngredient, $chemicalName, $brandNames, $genericBrands, $className, $drugFamily, $commonUse, ]); $select->execute([$genericName]); $existingId = (int) $select->fetchColumn(); if ($existingId > 0) { $update->execute([ $activeIngredient, $chemicalName, $brandNames, $genericBrands, $className, $drugFamily, $commonUse, $trackingNote, $rxnormUrl, $medlinePlusUrl, $pubchemUrl, $dailymedUrl, $pubmedUrl, $clinicalTrialsUrl, $sourceName, $searchTerms, $existingId, ]); continue; } $insert->execute([ $genericName, $activeIngredient, $chemicalName, $brandNames, $genericBrands, $className, $drugFamily, $commonUse, $trackingNote, $rxnormUrl, $medlinePlusUrl, $pubchemUrl, $dailymedUrl, $pubmedUrl, $clinicalTrialsUrl, $sourceName, $searchTerms, $created, ]); } } function expanded_medication_catalog_rows(): array { $defaultMedline = 'https://medlineplus.gov/druginformation.html'; return [ ['acetaminophen', 'acetaminophen', 'N-(4-hydroxyphenyl)acetamide', 'Tylenol, Mapap, Panadol', 'paracetamol, APAP', 'Analgesic and antipyretic', 'Pain and fever', 'Often used for pain or fever.', 'Track total daily dose and other combination products that may also contain acetaminophen.', 'https://medlineplus.gov/druginfo/meds/a681004.html'], ['ibuprofen', 'ibuprofen', '2-(4-isobutylphenyl)propanoic acid', 'Advil, Motrin, Midol', 'ibuprofen tablets, ibuprofen suspension', 'Nonsteroidal anti-inflammatory drug', 'Pain and inflammation', 'Often used for pain, fever, or inflammation.', 'Track dose, pain score, stomach symptoms, and frequency of use.', 'https://medlineplus.gov/druginfo/meds/a682159.html'], ['naproxen', 'naproxen', '(S)-2-(6-methoxynaphthalen-2-yl)propanoic acid', 'Aleve, Naprosyn, Anaprox', 'naproxen sodium, naproxen tablets', 'Nonsteroidal anti-inflammatory drug', 'Pain and inflammation', 'Often used for pain, fever, or inflammation.', 'Track dose timing, stomach symptoms, kidney-risk context, and pain response.', $defaultMedline], ['aspirin', 'acetylsalicylic acid', '2-acetyloxybenzoic acid', 'Bayer, Bufferin, Ecotrin', 'acetylsalicylic acid, ASA', 'Salicylate antiplatelet and analgesic', 'Pain, fever, antiplatelet therapy', 'Used for pain, fever, inflammation, or antiplatelet therapy when directed.', 'Track indication, dose, bleeding symptoms, and clinician instructions.', $defaultMedline], ['diclofenac', 'diclofenac', '2-[2-(2,6-dichloroanilino)phenyl]acetic acid', 'Voltaren, Cataflam, Zipsor', 'diclofenac sodium, diclofenac potassium', 'Nonsteroidal anti-inflammatory drug', 'Pain and inflammation', 'Often used for arthritis or musculoskeletal pain.', 'Track pain score, application site for topical use, stomach symptoms, and dose schedule.', $defaultMedline], ['celecoxib', 'celecoxib', '4-[5-(4-methylphenyl)-3-(trifluoromethyl)pyrazol-1-yl]benzenesulfonamide', 'Celebrex, Elyxyb', 'celecoxib capsules', 'COX-2 selective nonsteroidal anti-inflammatory drug', 'Pain and inflammation', 'Often used for arthritis pain or inflammatory conditions.', 'Track pain relief, stomach symptoms, swelling, blood pressure, and clinician instructions.', $defaultMedline], ['meloxicam', 'meloxicam', 'meloxicam', 'Mobic, Vivlodex, Anjeso', 'meloxicam tablets, meloxicam capsules', 'Nonsteroidal anti-inflammatory drug', 'Pain and inflammation', 'Often used for arthritis and inflammatory pain.', 'Track pain response, stomach symptoms, and blood pressure or kidney follow-up if ordered.', $defaultMedline], ['tramadol', 'tramadol', 'tramadol hydrochloride', 'Ultram, ConZip, Qdolo', 'tramadol tablets, tramadol ER', 'Opioid analgesic', 'Pain', 'Used for selected pain conditions when prescribed.', 'Track dose, sedation, constipation, dizziness, pain score, and stop/start dates.', $defaultMedline], ['hydrocodone acetaminophen', 'hydrocodone bitartrate and acetaminophen', 'hydrocodone plus acetaminophen', 'Norco, Vicodin, Lortab', 'hydrocodone/APAP', 'Opioid analgesic combination', 'Pain', 'Combination pain medicine containing an opioid and acetaminophen.', 'Track total acetaminophen exposure, sedation, constipation, and prescriber instructions.', $defaultMedline], ['oxycodone', 'oxycodone', 'oxycodone hydrochloride', 'OxyContin, Roxicodone, Oxaydo', 'oxycodone IR, oxycodone ER', 'Opioid analgesic', 'Pain', 'Used for selected pain conditions when prescribed.', 'Track dose, sedation, constipation, pain score, and safety instructions.', $defaultMedline], ['morphine', 'morphine', 'morphine sulfate', 'MS Contin, Kadian, Roxanol', 'morphine IR, morphine ER', 'Opioid analgesic', 'Pain', 'Used for selected pain conditions when prescribed.', 'Track dose, sedation, constipation, respiratory symptoms, and pain score.', $defaultMedline], ['gabapentin', 'gabapentin', '1-(aminomethyl)cyclohexaneacetic acid', 'Neurontin, Gralise, Horizant', 'gabapentin capsules, gabapentin tablets', 'Anticonvulsant nerve-pain medicine', 'Neurology and pain', 'Often used for seizures or nerve-related pain.', 'Track sleepiness, dizziness, dose changes, and symptom response.', $defaultMedline], ['pregabalin', 'pregabalin', '(S)-3-(aminomethyl)-5-methylhexanoic acid', 'Lyrica', 'pregabalin capsules', 'Anticonvulsant nerve-pain medicine', 'Neurology and pain', 'Often used for nerve pain, fibromyalgia, or seizures.', 'Track dizziness, swelling, sleepiness, and symptom response.', $defaultMedline], ['cyclobenzaprine', 'cyclobenzaprine', 'cyclobenzaprine hydrochloride', 'Flexeril, Amrix, Fexmid', 'cyclobenzaprine tablets', 'Skeletal muscle relaxant', 'Musculoskeletal', 'Often used short term for muscle spasm.', 'Track sedation, dry mouth, dose timing, and symptom response.', $defaultMedline], ['amoxicillin', 'amoxicillin', '(2S,5R,6R)-6-[[(2R)-2-amino-2-(4-hydroxyphenyl)acetyl]amino]-3,3-dimethyl-7-oxo-4-thia-1-azabicyclo[3.2.0]heptane-2-carboxylic acid', 'Amoxil, Moxatag, Trimox', 'amoxicillin capsules, amoxicillin suspension', 'Penicillin antibiotic', 'Infectious disease', 'Often used for susceptible bacterial infections.', 'Track start/stop dates, dose schedule, allergy symptoms, and care-team instructions.', 'https://medlineplus.gov/druginfo/meds/a685001.html'], ['amoxicillin clavulanate', 'amoxicillin and clavulanate potassium', 'amoxicillin plus clavulanic acid', 'Augmentin', 'amoxicillin/clavulanate, co-amoxiclav', 'Penicillin antibiotic with beta-lactamase inhibitor', 'Infectious disease', 'Used for selected bacterial infections when prescribed.', 'Track dose schedule, GI symptoms, allergy symptoms, and completion date.', $defaultMedline], ['azithromycin', 'azithromycin', 'azithromycin', 'Zithromax, Z-Pak, Zmax', 'azithromycin tablets, azithromycin suspension', 'Macrolide antibiotic', 'Infectious disease', 'Used for selected bacterial infections when prescribed.', 'Track start/stop dates, dose schedule, diarrhea, rash, and symptom response.', $defaultMedline], ['doxycycline', 'doxycycline', 'doxycycline hyclate or monohydrate', 'Vibramycin, Doryx, Oracea', 'doxycycline tablets, doxycycline capsules', 'Tetracycline antibiotic', 'Infectious disease', 'Used for selected bacterial infections and some inflammatory skin conditions.', 'Track sun sensitivity, stomach symptoms, dose timing, and completion date.', $defaultMedline], ['cephalexin', 'cephalexin', 'cefalexin', 'Keflex', 'cephalexin capsules, cephalexin suspension', 'Cephalosporin antibiotic', 'Infectious disease', 'Used for selected bacterial infections when prescribed.', 'Track start/stop dates, allergy symptoms, GI symptoms, and symptom response.', $defaultMedline], ['ciprofloxacin', 'ciprofloxacin', 'ciprofloxacin hydrochloride', 'Cipro, Cipro XR', 'ciprofloxacin tablets, ciprofloxacin drops', 'Fluoroquinolone antibiotic', 'Infectious disease', 'Used for selected bacterial infections when prescribed.', 'Track tendon pain, nerve symptoms, GI symptoms, and clinician instructions.', $defaultMedline], ['levofloxacin', 'levofloxacin', 'levofloxacin', 'Levaquin', 'levofloxacin tablets', 'Fluoroquinolone antibiotic', 'Infectious disease', 'Used for selected bacterial infections when prescribed.', 'Track tendon pain, nerve symptoms, mood changes, and completion date.', $defaultMedline], ['clindamycin', 'clindamycin', 'clindamycin hydrochloride', 'Cleocin, Clindesse', 'clindamycin capsules, clindamycin topical', 'Lincosamide antibiotic', 'Infectious disease', 'Used for selected bacterial infections or topical acne treatment.', 'Track diarrhea, rash, dose timing, and symptom response.', $defaultMedline], ['metronidazole', 'metronidazole', '2-(2-methyl-5-nitroimidazol-1-yl)ethanol', 'Flagyl, Metrogel, Vandazole', 'metronidazole tablets, metronidazole gel', 'Nitroimidazole antimicrobial', 'Infectious disease', 'Used for selected bacterial or protozoal infections when prescribed.', 'Track dose schedule, GI symptoms, metallic taste, and alcohol-related instructions.', $defaultMedline], ['trimethoprim sulfamethoxazole', 'sulfamethoxazole and trimethoprim', 'trimethoprim plus sulfamethoxazole', 'Bactrim, Septra', 'TMP-SMX, co-trimoxazole', 'Sulfonamide antibiotic combination', 'Infectious disease', 'Used for selected bacterial infections when prescribed.', 'Track rash, sun sensitivity, GI symptoms, dose schedule, and completion date.', $defaultMedline], ['nitrofurantoin', 'nitrofurantoin', 'nitrofurantoin', 'Macrobid, Macrodantin, Furadantin', 'nitrofurantoin capsules, nitrofurantoin suspension', 'Nitrofuran antibiotic', 'Infectious disease', 'Often used for selected urinary tract infections.', 'Track urinary symptoms, dose schedule, GI symptoms, and completion date.', $defaultMedline], ['fluconazole', 'fluconazole', 'fluconazole', 'Diflucan', 'fluconazole tablets', 'Azole antifungal', 'Infectious disease', 'Used for selected fungal infections when prescribed.', 'Track symptom response, rash, stomach symptoms, and liver-lab follow-up if ordered.', $defaultMedline], ['acyclovir', 'acyclovir', 'acyclovir', 'Zovirax', 'acyclovir tablets, acyclovir cream', 'Antiviral nucleoside analog', 'Infectious disease', 'Used for herpes-virus infections when prescribed.', 'Track outbreak timing, dose schedule, kidney-risk context, and symptom response.', $defaultMedline], ['valacyclovir', 'valacyclovir', 'valacyclovir hydrochloride', 'Valtrex', 'valacyclovir tablets', 'Antiviral prodrug', 'Infectious disease', 'Used for herpes-virus infections when prescribed.', 'Track outbreak timing, dose schedule, symptom response, and kidney-risk context.', $defaultMedline], ['lisinopril', 'lisinopril', '(2S)-1-[(2S)-6-amino-2-[[(1S)-1-carboxy-3-phenylpropyl]amino]hexanoyl]pyrrolidine-2-carboxylic acid', 'Prinivil, Zestril, Qbrelis', 'lisinopril tablets, lisinopril solution', 'ACE inhibitor', 'Cardiovascular', 'Often used in blood pressure and cardiovascular care.', 'Track blood pressure, pulse, cough, dizziness, and lab follow-up if ordered.', 'https://medlineplus.gov/druginfo/meds/a692051.html'], ['losartan', 'losartan', 'losartan potassium', 'Cozaar', 'losartan tablets', 'Angiotensin II receptor blocker', 'Cardiovascular', 'Often used for blood pressure, kidney protection, or cardiovascular care.', 'Track blood pressure, dizziness, potassium/kidney labs if ordered, and dose changes.', $defaultMedline], ['valsartan', 'valsartan', 'valsartan', 'Diovan', 'valsartan tablets', 'Angiotensin II receptor blocker', 'Cardiovascular', 'Often used for blood pressure or heart failure care.', 'Track blood pressure, dizziness, kidney/potassium labs if ordered, and dose changes.', $defaultMedline], ['amlodipine', 'amlodipine', 'amlodipine besylate', 'Norvasc, Katerzia', 'amlodipine tablets, amlodipine solution', 'Calcium channel blocker', 'Cardiovascular', 'Often used for blood pressure or angina care.', 'Track blood pressure, swelling, dizziness, and dose changes.', $defaultMedline], ['metoprolol', 'metoprolol', 'metoprolol tartrate or succinate', 'Lopressor, Toprol XL, Kapspargo Sprinkle', 'metoprolol tartrate, metoprolol succinate ER', 'Beta blocker', 'Cardiovascular', 'Often used for blood pressure, heart rate, angina, or heart failure care.', 'Track pulse, blood pressure, dizziness, fatigue, and formulation.', $defaultMedline], ['carvedilol', 'carvedilol', 'carvedilol', 'Coreg, Coreg CR', 'carvedilol tablets, carvedilol ER', 'Alpha/beta blocker', 'Cardiovascular', 'Often used for heart failure or blood pressure care.', 'Track pulse, blood pressure, dizziness, weight changes, and dose titration.', $defaultMedline], ['atenolol', 'atenolol', 'atenolol', 'Tenormin', 'atenolol tablets', 'Beta blocker', 'Cardiovascular', 'Often used for blood pressure, heart rate, or angina care.', 'Track pulse, blood pressure, dizziness, fatigue, and dose changes.', $defaultMedline], ['hydrochlorothiazide', 'hydrochlorothiazide', '6-chloro-3,4-dihydro-2H-1,2,4-benzothiadiazine-7-sulfonamide 1,1-dioxide', 'Microzide, HydroDIURIL', 'HCTZ, hydrochlorothiazide tablets', 'Thiazide diuretic', 'Cardiovascular and renal', 'Often used for blood pressure or fluid management.', 'Track blood pressure, urination, dizziness, electrolytes if ordered, and weight trends.', $defaultMedline], ['chlorthalidone', 'chlorthalidone', 'chlorthalidone', 'Thalitone, Hygroton', 'chlorthalidone tablets', 'Thiazide-like diuretic', 'Cardiovascular and renal', 'Often used for blood pressure or fluid management.', 'Track blood pressure, dizziness, electrolytes if ordered, and urination changes.', $defaultMedline], ['furosemide', 'furosemide', '4-chloro-N-furfuryl-5-sulfamoylanthranilic acid', 'Lasix', 'furosemide tablets, furosemide solution', 'Loop diuretic', 'Cardiovascular and renal', 'Often used for fluid management when prescribed.', 'Track weight, swelling, urine output, dizziness, and electrolytes if ordered.', $defaultMedline], ['spironolactone', 'spironolactone', 'spironolactone', 'Aldactone, CaroSpir', 'spironolactone tablets, spironolactone suspension', 'Potassium-sparing diuretic and aldosterone antagonist', 'Cardiovascular and endocrine', 'Often used for heart failure, blood pressure, fluid, or hormonal indications.', 'Track blood pressure, potassium labs if ordered, dizziness, and dose changes.', $defaultMedline], ['clopidogrel', 'clopidogrel', 'clopidogrel bisulfate', 'Plavix', 'clopidogrel tablets', 'P2Y12 antiplatelet', 'Cardiovascular', 'Used to reduce clotting risk when prescribed.', 'Track bruising, bleeding symptoms, procedures, and prescriber instructions.', $defaultMedline], ['warfarin', 'warfarin', 'warfarin sodium', 'Coumadin, Jantoven', 'warfarin tablets', 'Vitamin K antagonist anticoagulant', 'Cardiovascular and hematology', 'Used for anticoagulation when prescribed.', 'Track INR, dose schedule, bleeding symptoms, diet changes, and interactions.', $defaultMedline], ['apixaban', 'apixaban', 'apixaban', 'Eliquis', 'apixaban tablets', 'Direct factor Xa inhibitor anticoagulant', 'Cardiovascular and hematology', 'Used for anticoagulation when prescribed.', 'Track dose adherence, bleeding symptoms, procedures, kidney follow-up, and refills.', $defaultMedline], ['rivaroxaban', 'rivaroxaban', 'rivaroxaban', 'Xarelto', 'rivaroxaban tablets', 'Direct factor Xa inhibitor anticoagulant', 'Cardiovascular and hematology', 'Used for anticoagulation when prescribed.', 'Track dose timing with food if directed, bleeding symptoms, procedures, and refills.', $defaultMedline], ['nitroglycerin', 'nitroglycerin', 'glyceryl trinitrate', 'Nitrostat, Nitro-Dur, NitroMist', 'nitroglycerin sublingual, nitroglycerin patch', 'Nitrate vasodilator', 'Cardiovascular', 'Used for angina or cardiovascular indications when prescribed.', 'Track symptom timing, dose use, blood pressure symptoms, and emergency instructions.', $defaultMedline], ['atorvastatin', 'atorvastatin', 'atorvastatin calcium', 'Lipitor', 'atorvastatin tablets', 'HMG-CoA reductase inhibitor statin', 'Cardiovascular and metabolic', 'Often used for cholesterol management and cardiovascular risk reduction.', 'Track lipid labs, muscle symptoms, dose changes, and care-team notes.', 'https://medlineplus.gov/druginfo/meds/a600045.html'], ['rosuvastatin', 'rosuvastatin', 'rosuvastatin calcium', 'Crestor, Ezallor Sprinkle', 'rosuvastatin tablets', 'HMG-CoA reductase inhibitor statin', 'Cardiovascular and metabolic', 'Often used for cholesterol management and cardiovascular risk reduction.', 'Track lipid labs, muscle symptoms, dose changes, and care-team notes.', $defaultMedline], ['simvastatin', 'simvastatin', 'simvastatin', 'Zocor, FloLipid', 'simvastatin tablets', 'HMG-CoA reductase inhibitor statin', 'Cardiovascular and metabolic', 'Often used for cholesterol management.', 'Track lipid labs, muscle symptoms, dose changes, and medication interactions.', $defaultMedline], ['ezetimibe', 'ezetimibe', 'ezetimibe', 'Zetia', 'ezetimibe tablets', 'Cholesterol absorption inhibitor', 'Cardiovascular and metabolic', 'Often used for cholesterol management.', 'Track lipid labs, GI symptoms, muscle symptoms, and combination therapy.', $defaultMedline], ['metformin', 'metformin', 'N,N-dimethylbiguanide', 'Glucophage, Glumetza, Fortamet, Riomet', 'metformin tablets, metformin ER', 'Biguanide antidiabetic', 'Endocrine and metabolic', 'Often used in type 2 diabetes care.', 'Track dose, meals if relevant, glucose readings, and GI symptoms.', 'https://medlineplus.gov/druginfo/meds/a696005.html'], ['insulin glargine', 'insulin glargine', 'recombinant human insulin analog insulin glargine', 'Lantus, Basaglar, Toujeo, Semglee', 'insulin glargine-yfgn, insulin glargine pens', 'Long-acting insulin', 'Endocrine and metabolic', 'Used for diabetes care when prescribed.', 'Track dose, glucose readings, hypoglycemia symptoms, injection site, and timing.', $defaultMedline], ['insulin lispro', 'insulin lispro', 'recombinant human insulin analog insulin lispro', 'Humalog, Admelog, Lyumjev', 'insulin lispro-aabc, insulin lispro pens', 'Rapid-acting insulin', 'Endocrine and metabolic', 'Used for diabetes meal or correction dosing when prescribed.', 'Track glucose readings, meals, dose timing, hypoglycemia symptoms, and injection site.', $defaultMedline], ['semaglutide', 'semaglutide', 'glucagon-like peptide-1 receptor agonist peptide analog semaglutide', 'Ozempic, Wegovy, Rybelsus', 'semaglutide injection, semaglutide tablets', 'GLP-1 receptor agonist', 'Endocrine and metabolic', 'Used for diabetes or weight-management indications when prescribed.', 'Track dose titration, GI symptoms, weight, glucose, and prescriber instructions.', $defaultMedline], ['dulaglutide', 'dulaglutide', 'glucagon-like peptide-1 receptor agonist dulaglutide', 'Trulicity', 'dulaglutide injection', 'GLP-1 receptor agonist', 'Endocrine and metabolic', 'Used for type 2 diabetes care when prescribed.', 'Track weekly dose, GI symptoms, glucose readings, and injection site.', $defaultMedline], ['liraglutide', 'liraglutide', 'glucagon-like peptide-1 receptor agonist liraglutide', 'Victoza, Saxenda', 'liraglutide injection', 'GLP-1 receptor agonist', 'Endocrine and metabolic', 'Used for diabetes or weight-management indications when prescribed.', 'Track dose titration, GI symptoms, weight, glucose, and injection site.', $defaultMedline], ['empagliflozin', 'empagliflozin', 'empagliflozin', 'Jardiance', 'empagliflozin tablets', 'SGLT2 inhibitor', 'Endocrine and metabolic', 'Used for diabetes and selected heart/kidney indications when prescribed.', 'Track glucose, urinary symptoms, hydration, weight, and kidney labs if ordered.', $defaultMedline], ['dapagliflozin', 'dapagliflozin', 'dapagliflozin propanediol', 'Farxiga', 'dapagliflozin tablets', 'SGLT2 inhibitor', 'Endocrine and metabolic', 'Used for diabetes and selected heart/kidney indications when prescribed.', 'Track glucose, urinary symptoms, hydration, weight, and kidney labs if ordered.', $defaultMedline], ['sitagliptin', 'sitagliptin', 'sitagliptin phosphate', 'Januvia', 'sitagliptin tablets', 'DPP-4 inhibitor', 'Endocrine and metabolic', 'Used for type 2 diabetes care when prescribed.', 'Track glucose readings, GI symptoms, dose changes, and kidney follow-up if ordered.', $defaultMedline], ['glipizide', 'glipizide', 'glipizide', 'Glucotrol, Glucotrol XL', 'glipizide tablets, glipizide ER', 'Sulfonylurea antidiabetic', 'Endocrine and metabolic', 'Used for type 2 diabetes care when prescribed.', 'Track glucose readings, hypoglycemia symptoms, meals, and dose timing.', $defaultMedline], ['glyburide', 'glyburide', 'glyburide', 'Diabeta, Glynase', 'glyburide tablets', 'Sulfonylurea antidiabetic', 'Endocrine and metabolic', 'Used for type 2 diabetes care when prescribed.', 'Track glucose readings, hypoglycemia symptoms, meals, and dose timing.', $defaultMedline], ['pioglitazone', 'pioglitazone', 'pioglitazone hydrochloride', 'Actos', 'pioglitazone tablets', 'Thiazolidinedione antidiabetic', 'Endocrine and metabolic', 'Used for type 2 diabetes care when prescribed.', 'Track glucose, swelling, weight, shortness of breath, and liver follow-up if ordered.', $defaultMedline], ['albuterol', 'albuterol', '4-[2-(tert-butylamino)-1-hydroxyethyl]-2-(hydroxymethyl)phenol', 'Ventolin, ProAir, Proventil', 'salbutamol, albuterol inhaler', 'Short-acting beta agonist bronchodilator', 'Respiratory', 'Often used as a rescue medicine for asthma or bronchospasm.', 'Track inhaler use, symptoms, triggers, pulse, and breathing status.', 'https://medlineplus.gov/druginfo/meds/a682145.html'], ['fluticasone', 'fluticasone', 'fluticasone propionate or furoate', 'Flonase, Flovent, Arnuity, Flonase Sensimist', 'fluticasone nasal spray, fluticasone inhaler', 'Corticosteroid', 'Respiratory and allergy', 'Used for allergic rhinitis, asthma, or inflammation depending on formulation.', 'Track formulation, symptoms, throat irritation, nosebleeds, and controller adherence.', $defaultMedline], ['budesonide formoterol', 'budesonide and formoterol fumarate', 'budesonide plus formoterol', 'Symbicort, Breyna', 'budesonide/formoterol inhaler', 'Inhaled corticosteroid and long-acting beta agonist', 'Respiratory', 'Used for asthma or COPD controller therapy when prescribed.', 'Track doses, rescue inhaler use, symptoms, oral rinsing, and exacerbations.', $defaultMedline], ['fluticasone salmeterol', 'fluticasone propionate and salmeterol', 'fluticasone plus salmeterol', 'Advair, Wixela Inhub, AirDuo', 'fluticasone/salmeterol inhaler', 'Inhaled corticosteroid and long-acting beta agonist', 'Respiratory', 'Used for asthma or COPD controller therapy when prescribed.', 'Track doses, rescue inhaler use, symptoms, oral rinsing, and exacerbations.', $defaultMedline], ['montelukast', 'montelukast', 'montelukast sodium', 'Singulair', 'montelukast tablets, montelukast chewables', 'Leukotriene receptor antagonist', 'Respiratory and allergy', 'Used for asthma or allergy indications when prescribed.', 'Track symptoms, sleep/mood changes, rescue inhaler use, and triggers.', $defaultMedline], ['cetirizine', 'cetirizine', 'cetirizine hydrochloride', 'Zyrtec, Aller-Tec', 'cetirizine tablets, cetirizine solution', 'Second-generation antihistamine', 'Allergy', 'Used for allergy symptoms or hives.', 'Track symptom response, drowsiness, and dose timing.', $defaultMedline], ['loratadine', 'loratadine', 'loratadine', 'Claritin, Alavert', 'loratadine tablets, loratadine syrup', 'Second-generation antihistamine', 'Allergy', 'Used for allergy symptoms.', 'Track symptom response and dose timing.', $defaultMedline], ['fexofenadine', 'fexofenadine', 'fexofenadine hydrochloride', 'Allegra', 'fexofenadine tablets, fexofenadine suspension', 'Second-generation antihistamine', 'Allergy', 'Used for allergy symptoms or hives.', 'Track symptom response and dose timing.', $defaultMedline], ['diphenhydramine', 'diphenhydramine', 'diphenhydramine hydrochloride', 'Benadryl, ZzzQuil', 'diphenhydramine tablets, diphenhydramine liquid', 'First-generation antihistamine', 'Allergy and sleep', 'Used for allergy symptoms, itching, or sleep aid products.', 'Track drowsiness, confusion, dry mouth, dose timing, and reason for use.', $defaultMedline], ['pseudoephedrine', 'pseudoephedrine', 'pseudoephedrine hydrochloride', 'Sudafed', 'pseudoephedrine tablets', 'Nasal decongestant', 'Respiratory and allergy', 'Used for nasal congestion.', 'Track blood pressure, heart rate symptoms, sleep effects, and dose timing.', $defaultMedline], ['epinephrine', 'epinephrine', 'adrenaline', 'EpiPen, Auvi-Q, Adrenaclick', 'epinephrine auto-injector', 'Adrenergic agonist', 'Emergency allergy and cardiovascular', 'Used for severe allergic reactions or emergency indications when prescribed.', 'Track expiration dates, use events, emergency follow-up, and refill status.', $defaultMedline], ['omeprazole', 'omeprazole', '5-methoxy-2-[(4-methoxy-3,5-dimethylpyridin-2-yl)methylsulfinyl]-1H-benzimidazole', 'Prilosec, Zegerid', 'omeprazole capsules, omeprazole tablets', 'Proton pump inhibitor', 'Gastroenterology', 'Often used for acid-related gastrointestinal conditions.', 'Track symptom response, dose timing, duration, and clinician instructions.', 'https://medlineplus.gov/druginfo/meds/a693050.html'], ['pantoprazole', 'pantoprazole', 'pantoprazole sodium', 'Protonix', 'pantoprazole tablets', 'Proton pump inhibitor', 'Gastroenterology', 'Often used for acid-related gastrointestinal conditions.', 'Track symptom response, dose timing, duration, and clinician instructions.', $defaultMedline], ['famotidine', 'famotidine', 'famotidine', 'Pepcid', 'famotidine tablets, famotidine suspension', 'Histamine H2 receptor antagonist', 'Gastroenterology', 'Often used for acid-related symptoms.', 'Track symptom response, dose timing, kidney-dose context if relevant, and duration.', $defaultMedline], ['ondansetron', 'ondansetron', 'ondansetron hydrochloride', 'Zofran, Zuplenz', 'ondansetron tablets, ondansetron ODT', '5-HT3 receptor antagonist antiemetic', 'Gastroenterology', 'Used for nausea or vomiting when prescribed.', 'Track nausea score, constipation, headache, dose timing, and hydration.', $defaultMedline], ['metoclopramide', 'metoclopramide', 'metoclopramide hydrochloride', 'Reglan, Gimoti', 'metoclopramide tablets, metoclopramide nasal spray', 'Dopamine antagonist prokinetic antiemetic', 'Gastroenterology', 'Used for selected nausea or motility conditions when prescribed.', 'Track movement symptoms, drowsiness, mood changes, and duration.', $defaultMedline], ['polyethylene glycol 3350', 'polyethylene glycol 3350', 'polyethylene glycol 3350', 'MiraLAX, GlycoLax', 'PEG 3350 powder', 'Osmotic laxative', 'Gastroenterology', 'Used for constipation.', 'Track bowel movements, hydration, bloating, and dose frequency.', $defaultMedline], ['loperamide', 'loperamide', 'loperamide hydrochloride', 'Imodium, Diamode', 'loperamide capsules, loperamide liquid', 'Antidiarrheal opioid receptor agonist', 'Gastroenterology', 'Used for diarrhea symptoms when appropriate.', 'Track stool frequency, fever/blood symptoms, dose count, and hydration.', $defaultMedline], ['dicyclomine', 'dicyclomine', 'dicyclomine hydrochloride', 'Bentyl', 'dicyclomine tablets, dicyclomine capsules', 'Anticholinergic antispasmodic', 'Gastroenterology', 'Used for intestinal spasm symptoms when prescribed.', 'Track abdominal pain, dry mouth, dizziness, constipation, and dose timing.', $defaultMedline], ['sertraline', 'sertraline', '(1S,4S)-4-(3,4-dichlorophenyl)-N-methyl-1,2,3,4-tetrahydronaphthalen-1-amine', 'Zoloft', 'sertraline tablets, sertraline solution', 'Selective serotonin reuptake inhibitor', 'Mental health', 'Often used in depression, anxiety, and related mental health care.', 'Track mood, sleep, side effects, dose changes, and follow-up notes.', 'https://medlineplus.gov/druginfo/meds/a697048.html'], ['fluoxetine', 'fluoxetine', 'fluoxetine hydrochloride', 'Prozac, Sarafem, Rapiflux', 'fluoxetine capsules, fluoxetine solution', 'Selective serotonin reuptake inhibitor', 'Mental health', 'Often used in depression, anxiety, or related mental health care.', 'Track mood, sleep, GI symptoms, activation, and dose changes.', $defaultMedline], ['escitalopram', 'escitalopram', 'escitalopram oxalate', 'Lexapro', 'escitalopram tablets, escitalopram solution', 'Selective serotonin reuptake inhibitor', 'Mental health', 'Often used in depression or anxiety care.', 'Track mood, sleep, side effects, dose changes, and follow-up notes.', $defaultMedline], ['citalopram', 'citalopram', 'citalopram hydrobromide', 'Celexa', 'citalopram tablets, citalopram solution', 'Selective serotonin reuptake inhibitor', 'Mental health', 'Often used in depression care.', 'Track mood, sleep, side effects, dose changes, and follow-up notes.', $defaultMedline], ['paroxetine', 'paroxetine', 'paroxetine hydrochloride', 'Paxil, Pexeva, Brisdelle', 'paroxetine tablets, paroxetine suspension', 'Selective serotonin reuptake inhibitor', 'Mental health', 'Used for depression, anxiety, or related indications when prescribed.', 'Track mood, sleep, side effects, dose changes, and discontinuation instructions.', $defaultMedline], ['venlafaxine', 'venlafaxine', 'venlafaxine hydrochloride', 'Effexor XR', 'venlafaxine tablets, venlafaxine ER', 'Serotonin-norepinephrine reuptake inhibitor', 'Mental health', 'Used for depression, anxiety, or related indications when prescribed.', 'Track mood, blood pressure, sleep, side effects, and dose changes.', $defaultMedline], ['duloxetine', 'duloxetine', 'duloxetine hydrochloride', 'Cymbalta, Drizalma Sprinkle', 'duloxetine capsules', 'Serotonin-norepinephrine reuptake inhibitor', 'Mental health and pain', 'Used for depression, anxiety, nerve pain, or related indications when prescribed.', 'Track mood, pain, sleep, blood pressure, and side effects.', $defaultMedline], ['bupropion', 'bupropion', 'bupropion hydrochloride', 'Wellbutrin, Wellbutrin XL, Zyban, Aplenzin', 'bupropion SR, bupropion XL', 'Norepinephrine-dopamine reuptake inhibitor', 'Mental health and smoking cessation', 'Used for depression or smoking cessation indications when prescribed.', 'Track mood, sleep, appetite, anxiety, dose formulation, and seizure-risk context.', $defaultMedline], ['mirtazapine', 'mirtazapine', 'mirtazapine', 'Remeron, Remeron SolTab', 'mirtazapine tablets, mirtazapine ODT', 'Noradrenergic and specific serotonergic antidepressant', 'Mental health', 'Used for depression care when prescribed.', 'Track mood, sleep, appetite, weight, sedation, and dose changes.', $defaultMedline], ['trazodone', 'trazodone', 'trazodone hydrochloride', 'Desyrel, Oleptro', 'trazodone tablets', 'Serotonin antagonist and reuptake inhibitor', 'Mental health and sleep', 'Used for depression or sleep-related indications when prescribed.', 'Track sleep, dizziness, next-day sedation, mood, and dose timing.', $defaultMedline], ['buspirone', 'buspirone', 'buspirone hydrochloride', 'Buspar', 'buspirone tablets', 'Anxiolytic', 'Mental health', 'Used for anxiety care when prescribed.', 'Track anxiety symptoms, dizziness, dose schedule, and time-to-effect.', $defaultMedline], ['hydroxyzine', 'hydroxyzine', 'hydroxyzine hydrochloride or pamoate', 'Atarax, Vistaril', 'hydroxyzine tablets, hydroxyzine capsules', 'First-generation antihistamine anxiolytic', 'Mental health and allergy', 'Used for anxiety, itching, or allergy indications when prescribed.', 'Track drowsiness, dry mouth, anxiety symptoms, itching, and dose timing.', $defaultMedline], ['alprazolam', 'alprazolam', 'alprazolam', 'Xanax, Xanax XR, Niravam', 'alprazolam tablets, alprazolam ER', 'Benzodiazepine', 'Mental health', 'Used for anxiety or panic indications when prescribed.', 'Track sedation, dose timing, driving cautions, refills, and taper instructions.', $defaultMedline], ['lorazepam', 'lorazepam', 'lorazepam', 'Ativan', 'lorazepam tablets, lorazepam solution', 'Benzodiazepine', 'Mental health and neurology', 'Used for anxiety, sedation, or seizure-related indications when prescribed.', 'Track sedation, dose timing, falls risk, and taper instructions.', $defaultMedline], ['clonazepam', 'clonazepam', 'clonazepam', 'Klonopin', 'clonazepam tablets, clonazepam ODT', 'Benzodiazepine', 'Mental health and neurology', 'Used for seizure, panic, or related indications when prescribed.', 'Track sedation, symptom response, dose timing, and taper instructions.', $defaultMedline], ['zolpidem', 'zolpidem', 'zolpidem tartrate', 'Ambien, Ambien CR, Edluar, Intermezzo', 'zolpidem tablets, zolpidem ER', 'Sedative-hypnotic', 'Sleep', 'Used for insomnia when prescribed.', 'Track sleep duration, next-day sedation, unusual sleep behaviors, and dose timing.', $defaultMedline], ['methylphenidate', 'methylphenidate', 'methylphenidate hydrochloride', 'Ritalin, Concerta, Metadate, Jornay PM, Quillivant XR', 'methylphenidate IR, methylphenidate ER', 'Central nervous system stimulant', 'Mental health and neurology', 'Used for ADHD or related indications when prescribed.', 'Track focus, appetite, sleep, pulse, blood pressure, and formulation.', $defaultMedline], ['amphetamine dextroamphetamine', 'mixed amphetamine salts', 'amphetamine and dextroamphetamine salts', 'Adderall, Adderall XR, Mydayis', 'mixed amphetamine salts IR, mixed amphetamine salts ER', 'Central nervous system stimulant', 'Mental health and neurology', 'Used for ADHD or narcolepsy indications when prescribed.', 'Track focus, appetite, sleep, pulse, blood pressure, and formulation.', $defaultMedline], ['atomoxetine', 'atomoxetine', 'atomoxetine hydrochloride', 'Strattera', 'atomoxetine capsules', 'Selective norepinephrine reuptake inhibitor', 'Mental health and neurology', 'Used for ADHD care when prescribed.', 'Track focus, appetite, sleep, mood, pulse, blood pressure, and dose changes.', $defaultMedline], ['lamotrigine', 'lamotrigine', 'lamotrigine', 'Lamictal, Lamictal XR', 'lamotrigine tablets, lamotrigine ODT', 'Anticonvulsant mood stabilizer', 'Neurology and mental health', 'Used for seizures or bipolar disorder indications when prescribed.', 'Track rash, mood, seizure activity, dose titration, and missed doses.', $defaultMedline], ['levetiracetam', 'levetiracetam', 'levetiracetam', 'Keppra, Keppra XR, Spritam', 'levetiracetam tablets, levetiracetam solution', 'Anticonvulsant', 'Neurology', 'Used for seizure care when prescribed.', 'Track seizure activity, mood changes, sleepiness, and dose schedule.', $defaultMedline], ['topiramate', 'topiramate', 'topiramate', 'Topamax, Trokendi XR, Qudexy XR', 'topiramate tablets, topiramate ER', 'Anticonvulsant', 'Neurology', 'Used for seizures or migraine prevention indications when prescribed.', 'Track headache frequency, seizure activity, cognitive symptoms, tingling, and hydration.', $defaultMedline], ['levothyroxine', 'levothyroxine', 'levothyroxine sodium', 'Synthroid, Levoxyl, Unithroid, Tirosint, Euthyrox', 'levothyroxine tablets, levothyroxine capsules', 'Synthetic thyroid hormone', 'Endocrine', 'Used for hypothyroidism or thyroid hormone replacement when prescribed.', 'Track dose timing, TSH/T4 labs if ordered, symptoms, and brand/formulation changes.', $defaultMedline], ['methimazole', 'methimazole', 'methimazole', 'Tapazole', 'methimazole tablets', 'Antithyroid medicine', 'Endocrine', 'Used for hyperthyroidism care when prescribed.', 'Track thyroid labs if ordered, rash, fever/sore throat, and dose changes.', $defaultMedline], ['prednisone', 'prednisone', '17,21-dihydroxypregna-1,4-diene-3,11,20-trione', 'Deltasone, Rayos', 'prednisone tablets, prednisone solution', 'Systemic corticosteroid', 'Inflammation and immune conditions', 'Used for inflammatory, allergic, or immune-related conditions when prescribed.', 'Track dose taper, mood, glucose, sleep, swelling, and infection symptoms.', $defaultMedline], ['methylprednisolone', 'methylprednisolone', 'methylprednisolone', 'Medrol, Solu-Medrol, Depo-Medrol', 'methylprednisolone dose pack, methylprednisolone injection', 'Systemic corticosteroid', 'Inflammation and immune conditions', 'Used for inflammatory, allergic, or immune-related conditions when prescribed.', 'Track dose taper, mood, glucose, sleep, swelling, and infection symptoms.', $defaultMedline], ['hydrocortisone', 'hydrocortisone', 'cortisol', 'Cortef, Solu-Cortef, Anusol-HC', 'hydrocortisone tablets, hydrocortisone cream', 'Corticosteroid', 'Endocrine, inflammation, and dermatology', 'Used for steroid replacement or inflammation depending on formulation.', 'Track formulation, dose timing, skin response for topical use, and sick-day instructions if applicable.', $defaultMedline], ['ethinyl estradiol levonorgestrel', 'ethinyl estradiol and levonorgestrel', 'ethinyl estradiol plus levonorgestrel', 'Alesse, Aviane, Lessina, Seasonale', 'levonorgestrel/ethinyl estradiol tablets', 'Combined hormonal contraceptive', 'Reproductive health', 'Used for contraception or hormonal indications when prescribed.', 'Track cycle changes, missed doses, side effects, blood pressure, and clot-warning symptoms.', $defaultMedline], ['norethindrone', 'norethindrone', 'norethisterone', 'Aygestin, Camila, Errin, Heather', 'norethindrone tablets', 'Progestin', 'Reproductive health', 'Used for contraception or hormonal indications when prescribed.', 'Track cycle changes, missed doses, headaches, mood, and bleeding pattern.', $defaultMedline], ['medroxyprogesterone', 'medroxyprogesterone', 'medroxyprogesterone acetate', 'Provera, Depo-Provera', 'medroxyprogesterone tablets, medroxyprogesterone injection', 'Progestin', 'Reproductive health', 'Used for contraception or hormonal indications when prescribed.', 'Track injection dates, bleeding pattern, mood, weight, and follow-up schedule.', $defaultMedline], ['estradiol', 'estradiol', '17 beta-estradiol', 'Estrace, Vivelle-Dot, Climara, Divigel', 'estradiol tablets, estradiol patch, estradiol cream', 'Estrogen hormone', 'Reproductive health and endocrine', 'Used for hormone therapy or related indications when prescribed.', 'Track formulation, dose changes, symptoms, bleeding, and clinician follow-up.', $defaultMedline], ['clotrimazole', 'clotrimazole', 'clotrimazole', 'Lotrimin, Mycelex, Gyne-Lotrimin', 'clotrimazole cream, clotrimazole lozenges', 'Azole antifungal', 'Dermatology and infectious disease', 'Used for selected fungal infections.', 'Track symptom response, application site, irritation, and completion date.', $defaultMedline], ['terbinafine', 'terbinafine', 'terbinafine hydrochloride', 'Lamisil', 'terbinafine tablets, terbinafine cream', 'Allylamine antifungal', 'Dermatology and infectious disease', 'Used for selected fungal infections.', 'Track skin/nail response, rash, GI symptoms, and liver-lab follow-up if ordered.', $defaultMedline], ['mupirocin', 'mupirocin', 'mupirocin', 'Bactroban, Centany', 'mupirocin ointment, mupirocin cream', 'Topical antibiotic', 'Dermatology and infectious disease', 'Used for selected skin infections when prescribed.', 'Track application schedule, skin response, irritation, and completion date.', $defaultMedline], ['triamcinolone', 'triamcinolone', 'triamcinolone acetonide', 'Kenalog, Nasacort, Triderm', 'triamcinolone cream, triamcinolone nasal spray', 'Corticosteroid', 'Dermatology, allergy, and inflammation', 'Used for inflammation depending on formulation.', 'Track formulation, application site, skin thinning/irritation, and symptom response.', $defaultMedline], ['isotretinoin', 'isotretinoin', '13-cis-retinoic acid', 'Accutane, Absorica, Claravis, Myorisan, Zenatane', 'isotretinoin capsules', 'Retinoid', 'Dermatology', 'Used for severe acne when prescribed under strict monitoring.', 'Track pregnancy-prevention program requirements, mood, dryness, labs, and dose schedule.', $defaultMedline], ['tamsulosin', 'tamsulosin', 'tamsulosin hydrochloride', 'Flomax', 'tamsulosin capsules', 'Alpha-1 blocker', 'Urology', 'Often used for urinary symptoms related to enlarged prostate.', 'Track urinary symptoms, dizziness, falls, and dose timing.', $defaultMedline], ['finasteride', 'finasteride', 'finasteride', 'Proscar, Propecia', 'finasteride tablets', '5-alpha-reductase inhibitor', 'Urology and dermatology', 'Used for enlarged prostate or hair-loss indications when prescribed.', 'Track urinary symptoms or hair goals, sexual side effects, and dose indication.', $defaultMedline], ['sildenafil', 'sildenafil', 'sildenafil citrate', 'Viagra, Revatio', 'sildenafil tablets', 'PDE-5 inhibitor', 'Urology and pulmonary vascular', 'Used for erectile dysfunction or pulmonary hypertension indications when prescribed.', 'Track dose timing, blood pressure symptoms, headache, vision symptoms, and nitrate warnings.', $defaultMedline], ['tadalafil', 'tadalafil', 'tadalafil', 'Cialis, Adcirca', 'tadalafil tablets', 'PDE-5 inhibitor', 'Urology and pulmonary vascular', 'Used for erectile dysfunction, urinary symptoms, or pulmonary hypertension indications when prescribed.', 'Track dose timing, blood pressure symptoms, headache, back pain, and nitrate warnings.', $defaultMedline], ['oxybutynin', 'oxybutynin', 'oxybutynin chloride', 'Ditropan XL, Gelnique, Oxytrol', 'oxybutynin tablets, oxybutynin patch', 'Anticholinergic bladder antispasmodic', 'Urology', 'Used for overactive bladder symptoms when prescribed.', 'Track dry mouth, constipation, confusion, urinary symptoms, and formulation.', $defaultMedline], ['allopurinol', 'allopurinol', 'allopurinol', 'Zyloprim, Aloprim', 'allopurinol tablets', 'Xanthine oxidase inhibitor', 'Rheumatology and metabolic', 'Often used for gout prevention or uric-acid management.', 'Track uric acid labs if ordered, rash, flares, kidney follow-up, and dose changes.', $defaultMedline], ['colchicine', 'colchicine', 'colchicine', 'Colcrys, Mitigare, Gloperba', 'colchicine tablets, colchicine capsules', 'Antigout anti-inflammatory', 'Rheumatology', 'Used for gout flares or prevention when prescribed.', 'Track GI symptoms, flare timing, dose schedule, and interaction cautions.', $defaultMedline], ['methotrexate', 'methotrexate', 'amethopterin', 'Trexall, Rasuvo, Otrexup, RediTrex', 'methotrexate tablets, methotrexate injection', 'Antimetabolite immunomodulator', 'Rheumatology, oncology, and immune conditions', 'Used for autoimmune or cancer indications under clinician monitoring.', 'Track weekly schedule, folic acid plan if prescribed, labs, mouth sores, infection symptoms, and pregnancy precautions.', $defaultMedline], ['adalimumab', 'adalimumab', 'recombinant human monoclonal antibody adalimumab', 'Humira, Amjevita, Hadlima, Hyrimoz, Yuflyma', 'adalimumab biosimilars', 'TNF blocker biologic', 'Immune-mediated conditions', 'Used for selected autoimmune inflammatory conditions when prescribed.', 'Track injection dates, infection symptoms, flares, site reactions, and biologic brand.', $defaultMedline], ['etanercept', 'etanercept', 'recombinant TNF receptor fusion protein etanercept', 'Enbrel, Erelzi, Eticovo', 'etanercept biosimilars', 'TNF blocker biologic', 'Immune-mediated conditions', 'Used for selected autoimmune inflammatory conditions when prescribed.', 'Track injection dates, infection symptoms, flares, site reactions, and biologic brand.', $defaultMedline], ]; } function seed_demo_data(PDO $pdo): void { $userCount = (int) $pdo->query('SELECT COUNT(*) FROM users')->fetchColumn(); if ($userCount > 0) { return; } $created = app_now(); $today = new DateTimeImmutable('today'); $weekStart = $today->modify('monday this week'); $monthStart = $today->modify('first day of this month'); $stmt = $pdo->prepare( "INSERT INTO users (name, email, fitness_focus, role, status, phone, birthday, height_in, training_level, emergency_contact, theme_mode, theme_accent, updated_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ 'Alex Rivera', 'alex@example.test', 'Strength, cycling, and lean muscle', 'admin', 'active', '', '', 70, 'Intermediate', '', 'dark', 'green', $created, $created, ]); $alexId = (int) $pdo->lastInsertId(); $stmt->execute([ 'Maya Chen', 'maya@example.test', 'Running base, swimming, and recovery', 'member', 'active', '', '', 64, 'Advanced', '', 'dark', 'pink', $created, $created, ]); $mayaId = (int) $pdo->lastInsertId(); $goalStmt = $pdo->prepare( "INSERT INTO nutrition_goals (user_id, label, scope, calories, protein_g, carbs_g, fat_g, water_ml, start_date, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $goals = [ [$alexId, 'Lean bulk day', 'daily', 2900, 190, 330, 85, 3600, app_date($weekStart)], [$alexId, 'Lean bulk week', 'weekly', 20300, 1330, 2310, 595, 25200, app_date($weekStart)], [$alexId, 'Monthly build', 'monthly', 87000, 5700, 9900, 2550, 108000, app_date($monthStart)], [$mayaId, 'Endurance day', 'daily', 2350, 135, 310, 65, 3200, app_date($weekStart)], [$mayaId, 'Endurance week', 'weekly', 16450, 945, 2170, 455, 22400, app_date($weekStart)], ]; foreach ($goals as $goal) { $goalStmt->execute([...$goal, $created]); } $alexGoalId = (int) db_value($pdo, "SELECT id FROM nutrition_goals WHERE user_id = ? AND scope = 'weekly' LIMIT 1", [$alexId]); $mayaGoalId = (int) db_value($pdo, "SELECT id FROM nutrition_goals WHERE user_id = ? AND scope = 'weekly' LIMIT 1", [$mayaId]); $routineStmt = $pdo->prepare( "INSERT INTO routines (user_id, nutrition_goal_id, name, workout_type, planner_scope, start_date, end_date, intensity, notes, active, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)" ); $routineStmt->execute([$alexId, $alexGoalId, 'Push Pull Legs + Ride', 'Hybrid', 'weekly', app_date($weekStart), '', 'Moderate', 'Three lifting days with one cycling push.', $created]); $alexRoutineId = (int) $pdo->lastInsertId(); $routineStmt->execute([$alexId, $alexGoalId, 'Monthly Strength Block', 'Weight lifting', 'monthly', app_date($monthStart), '', 'High', 'Progressive overload with deload flexibility.', $created]); $routineStmt->execute([$mayaId, $mayaGoalId, '5K Base + Swim', 'Running', 'weekly', app_date($weekStart), '', 'Moderate', 'Aerobic run focus with low-impact swim.', $created]); $mayaRoutineId = (int) $pdo->lastInsertId(); $exerciseIds = db_key_values($pdo, "SELECT name, id FROM exercises WHERE is_builtin = 1"); $workoutStmt = $pdo->prepare( "INSERT INTO workouts (user_id, routine_id, exercise_id, scheduled_on, title, workout_type, sets, reps, weight_lbs, duration_min, distance_mi, intensity, status, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $workouts = [ [$alexId, $alexRoutineId, $exerciseIds['Bench Press'], app_date($weekStart), 'Push strength', 'Weight lifting', 4, 6, 185, 55, 0, 'RPE 8', 'complete', 'Add 5 lb if bar speed holds.'], [$alexId, $alexRoutineId, $exerciseIds['Indoor Cycling'], app_date($weekStart->modify('+1 day')), 'Zone 2 ride', 'Cycling', 0, 0, 0, 45, 14, 'Easy', 'planned', 'Keep nasal breathing.'], [$alexId, $alexRoutineId, $exerciseIds['Deadlift'], app_date($weekStart->modify('+2 days')), 'Pull strength', 'Weight lifting', 5, 3, 315, 60, 0, 'Heavy', 'planned', 'Long rest between sets.'], [$alexId, $alexRoutineId, $exerciseIds['Barbell Back Squat'], app_date($weekStart->modify('+4 days')), 'Legs volume', 'Weight lifting', 4, 8, 225, 65, 0, 'Moderate', 'planned', 'Superset core plank.'], [$mayaId, $mayaRoutineId, $exerciseIds['Easy Run'], app_date($weekStart), 'Easy miles', 'Running', 0, 0, 0, 35, 3.5, 'Easy', 'complete', 'Relaxed pace.'], [$mayaId, $mayaRoutineId, $exerciseIds['Tempo Run'], app_date($weekStart->modify('+2 days')), 'Tempo intervals', 'Running', 0, 0, 0, 42, 4.2, 'Threshold', 'planned', '3 x 8 min tempo.'], [$mayaId, $mayaRoutineId, $exerciseIds['Pool Laps'], app_date($weekStart->modify('+4 days')), 'Recovery swim', 'Swimming', 0, 0, 0, 30, 0, 'Easy', 'planned', 'Smooth form.'], ]; foreach ($workouts as $workout) { $workoutStmt->execute([...$workout, $created]); } $nutritionStmt = $pdo->prepare( "INSERT INTO nutrition_logs (user_id, logged_on, calories, protein_g, carbs_g, fat_g, water_ml, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $nutritionLogs = [ [$alexId, app_date($today), 1540, 110, 165, 42, 1850, 'Breakfast, shake, and lunch logged.'], [$alexId, app_date($today->modify('-1 day')), 2865, 186, 325, 82, 3700, 'Hit the target closely.'], [$mayaId, app_date($today), 1210, 74, 164, 31, 1700, 'Morning and lunch logged.'], ]; foreach ($nutritionLogs as $log) { $nutritionStmt->execute([...$log, $created]); } $supplementIds = db_key_values($pdo, "SELECT name, id FROM supplements WHERE is_builtin = 1"); $supplementLogStmt = $pdo->prepare( "INSERT INTO supplement_logs (user_id, supplement_id, taken_on, timing, dose, status, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ); $supplementLogs = [ [$alexId, $supplementIds['Creatine Monohydrate'], app_date($today), 'Daily', '5 g', 'taken', 'With breakfast.'], [$alexId, $supplementIds['Pre-Workout Blend'], app_date($today), 'Pre-workout', '1 scoop', 'planned', 'Before pull session.'], [$alexId, $supplementIds['Whey Protein'], app_date($today), 'Post-workout', '30 g', 'planned', 'After training.'], [$mayaId, $supplementIds['Electrolytes'], app_date($today), 'Intra-workout', '1 serving', 'planned', 'For tempo run.'], ]; foreach ($supplementLogs as $log) { $supplementLogStmt->execute([...$log, $created]); } $metricStmt = $pdo->prepare( "INSERT INTO body_metrics (user_id, measured_on, weight_lbs, body_fat_pct, sleep_hours, resting_hr, mood, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $metrics = [ [$alexId, app_date($today), 184.2, 14.8, 7.1, 58, 'Focused', 'Lower back feels good.'], [$alexId, app_date($today->modify('-7 days')), 185.1, 15.0, 6.8, 60, 'Steady', 'Minor shoulder tightness.'], [$mayaId, app_date($today), 132.4, 19.6, 7.8, 53, 'Fresh', 'Good recovery.'], ]; foreach ($metrics as $metric) { $metricStmt->execute([...$metric, $created]); } $medicineIds = db_key_values($pdo, "SELECT generic_name AS name, id FROM medication_catalog"); $medicationStmt = $pdo->prepare( "INSERT INTO medication_logs (user_id, medication_id, taken_on, medication_name, dose, route, frequency, status, prescriber, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $medicationLogs = [ [$alexId, $medicineIds['metformin'] ?? null, app_date($today), 'metformin', '500 mg', 'oral', 'twice daily', 'taken', 'Primary care', 'Demo log for glucose tracking.'], [$alexId, $medicineIds['atorvastatin'] ?? null, app_date($today), 'atorvastatin', '20 mg', 'oral', 'nightly', 'planned', 'Cardiology', 'Review lipid panel at follow-up.'], [$mayaId, $medicineIds['albuterol'] ?? null, app_date($today), 'albuterol', '2 puffs', 'inhaled', 'as needed', 'planned', 'Pulmonology', 'Track use with symptoms and triggers.'], ]; foreach ($medicationLogs as $log) { $medicationStmt->execute([...$log, $created]); } $vitalStmt = $pdo->prepare( "INSERT INTO vital_logs (user_id, measured_on, systolic, diastolic, pulse, temperature_f, oxygen_pct, glucose_mg_dl, pain_score, symptoms, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $vitals = [ [$alexId, app_date($today), 126, 78, 62, 98.4, 98, 112, 1, 'Mild soreness', 'Morning reading before breakfast.'], [$mayaId, app_date($today), 112, 70, 56, 98.2, 99, 0, 0, 'None', 'Rest day baseline.'], ]; foreach ($vitals as $vital) { $vitalStmt->execute([...$vital, $created]); } $labStmt = $pdo->prepare( "INSERT INTO lab_results (user_id, collected_on, test_name, result_value, unit, reference_range, ordering_clinician, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $labs = [ [$alexId, app_date($today->modify('-14 days')), 'A1C', '6.2', '%', 'Clinician supplied', 'Primary care', 'Demo metabolic marker.'], [$alexId, app_date($today->modify('-14 days')), 'LDL cholesterol', '96', 'mg/dL', 'Clinician supplied', 'Cardiology', 'Review with statin plan.'], [$mayaId, app_date($today->modify('-10 days')), 'Ferritin', '42', 'ng/mL', 'Clinician supplied', 'Primary care', 'Endurance training context.'], ]; foreach ($labs as $lab) { $labStmt->execute([...$lab, $created]); } } function seed_medical_demo_data(PDO $pdo): void { $users = db_all($pdo, 'SELECT id, name FROM users ORDER BY id LIMIT 2'); if (!$users) { return; } $created = app_now(); $today = new DateTimeImmutable('today'); $alexId = (int) $users[0]['id']; $mayaId = isset($users[1]) ? (int) $users[1]['id'] : $alexId; $medicineIds = db_key_values($pdo, "SELECT generic_name AS name, id FROM medication_catalog"); if ((int) $pdo->query('SELECT COUNT(*) FROM medication_logs')->fetchColumn() === 0) { $medicationStmt = $pdo->prepare( "INSERT INTO medication_logs (user_id, medication_id, taken_on, medication_name, dose, route, frequency, status, prescriber, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $medicationLogs = [ [$alexId, $medicineIds['metformin'] ?? null, app_date($today), 'metformin', '500 mg', 'oral', 'twice daily', 'taken', 'Primary care', 'Demo log for glucose tracking.'], [$alexId, $medicineIds['atorvastatin'] ?? null, app_date($today), 'atorvastatin', '20 mg', 'oral', 'nightly', 'planned', 'Cardiology', 'Review lipid panel at follow-up.'], [$mayaId, $medicineIds['albuterol'] ?? null, app_date($today), 'albuterol', '2 puffs', 'inhaled', 'as needed', 'planned', 'Pulmonology', 'Track use with symptoms and triggers.'], ]; foreach ($medicationLogs as $log) { $medicationStmt->execute([...$log, $created]); } } if ((int) $pdo->query('SELECT COUNT(*) FROM vital_logs')->fetchColumn() === 0) { $vitalStmt = $pdo->prepare( "INSERT INTO vital_logs (user_id, measured_on, systolic, diastolic, pulse, temperature_f, oxygen_pct, glucose_mg_dl, pain_score, symptoms, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $vitals = [ [$alexId, app_date($today), 126, 78, 62, 98.4, 98, 112, 1, 'Mild soreness', 'Morning reading before breakfast.'], [$mayaId, app_date($today), 112, 70, 56, 98.2, 99, 0, 0, 'None', 'Rest day baseline.'], ]; foreach ($vitals as $vital) { $vitalStmt->execute([...$vital, $created]); } } if ((int) $pdo->query('SELECT COUNT(*) FROM lab_results')->fetchColumn() === 0) { $labStmt = $pdo->prepare( "INSERT INTO lab_results (user_id, collected_on, test_name, result_value, unit, reference_range, ordering_clinician, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $labs = [ [$alexId, app_date($today->modify('-14 days')), 'A1C', '6.2', '%', 'Clinician supplied', 'Primary care', 'Demo metabolic marker.'], [$alexId, app_date($today->modify('-14 days')), 'LDL cholesterol', '96', 'mg/dL', 'Clinician supplied', 'Cardiology', 'Review with statin plan.'], [$mayaId, app_date($today->modify('-10 days')), 'Ferritin', '42', 'ng/mL', 'Clinician supplied', 'Primary care', 'Endurance training context.'], ]; foreach ($labs as $lab) { $labStmt->execute([...$lab, $created]); } } if ((int) $pdo->query('SELECT COUNT(*) FROM symptom_logs')->fetchColumn() === 0) { $symptomStmt = $pdo->prepare( "INSERT INTO symptom_logs (user_id, observed_on, symptom, body_area, severity, duration, triggers, action_taken, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $symptoms = [ [$alexId, app_date($today), 'Headache', 'Head', 3, '2 hours', 'Long screen session', 'Hydration and rest', 'Monitor if it returns.'], [$alexId, app_date($today->modify('-1 day')), 'Dizziness', 'General', 2, '15 minutes', 'Standing quickly', 'Sat down and checked BP', 'No recurrence after breakfast.'], [$mayaId, app_date($today), 'Wheezing', 'Chest', 4, '20 minutes', 'Cold air run', 'Used rescue inhaler as directed', 'Track with activity and weather.'], ]; foreach ($symptoms as $symptom) { $symptomStmt->execute([...$symptom, $created]); } } if ((int) $pdo->query('SELECT COUNT(*) FROM allergy_records')->fetchColumn() === 0) { $allergyStmt = $pdo->prepare( "INSERT INTO allergy_records (user_id, allergen, reaction, severity, first_noted_on, status, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ); $allergies = [ [$alexId, 'Penicillin', 'Rash', 'moderate', app_date($today->modify('-5 years')), 'active', 'Demo allergy record. Confirm with clinician.'], [$mayaId, 'Peanuts', 'Hives and throat tightness', 'severe', app_date($today->modify('-12 years')), 'active', 'Carries emergency plan per clinician.'], ]; foreach ($allergies as $allergy) { $allergyStmt->execute([...$allergy, $created]); } } if ((int) $pdo->query('SELECT COUNT(*) FROM appointments')->fetchColumn() === 0) { $appointmentStmt = $pdo->prepare( "INSERT INTO appointments (user_id, appointment_on, clinician, specialty, location, reason, status, follow_up_notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $appointments = [ [$alexId, app_date($today->modify('+3 days')), 'Dr. Morgan', 'Primary care', 'Downtown Clinic', 'Medication and blood pressure follow-up', 'scheduled', 'Bring home BP readings.'], [$alexId, app_date($today->modify('+21 days')), 'Cardiology team', 'Cardiology', 'Heart Center', 'Lipid panel review', 'scheduled', 'Ask about statin tolerance.'], [$mayaId, app_date($today->modify('+8 days')), 'Pulmonary clinic', 'Pulmonology', 'Respiratory Care', 'Asthma action plan review', 'scheduled', 'Discuss cold-weather symptoms.'], ]; foreach ($appointments as $appointment) { $appointmentStmt->execute([...$appointment, $created]); } } if ((int) $pdo->query('SELECT COUNT(*) FROM immunization_records')->fetchColumn() === 0) { $immunizationStmt = $pdo->prepare( "INSERT INTO immunization_records (user_id, vaccine_name, administered_on, dose, lot_number, clinician_or_site, next_due_on, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $immunizations = [ [$alexId, 'Influenza', app_date($today->modify('-4 months')), '0.5 mL', '', 'Local pharmacy', app_date($today->modify('+8 months')), 'Annual reminder demo.'], [$alexId, 'COVID-19 booster', app_date($today->modify('-7 months')), 'Booster', '', 'Community clinic', '', 'Record source: demo profile.'], [$mayaId, 'Tdap', app_date($today->modify('-2 years')), 'Adult dose', '', 'Primary care', app_date($today->modify('+8 years')), 'Next due date depends on clinician guidance.'], ]; foreach ($immunizations as $immunization) { $immunizationStmt->execute([...$immunization, $created]); } } } function app_now(): string { return (new DateTimeImmutable())->format('Y-m-d H:i:s'); } function app_date(DateTimeImmutable $date): string { return $date->format('Y-m-d'); } function parse_app_date(?string $value = null): DateTimeImmutable { if (!$value) { return new DateTimeImmutable('today'); } $parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $value); return $parsed ?: new DateTimeImmutable('today'); } function range_for_view(string $view, DateTimeImmutable $anchor): array { if ($view === 'weekly') { $start = $anchor->modify('monday this week'); return [$start, $start->modify('+6 days')]; } if ($view === 'monthly') { $start = $anchor->modify('first day of this month'); return [$start, $anchor->modify('last day of this month')]; } return [$anchor, $anchor]; } function db_all(PDO $pdo, string $sql, array $params = []): array { $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(); } function db_one(PDO $pdo, string $sql, array $params = []): ?array { $stmt = $pdo->prepare($sql); $stmt->execute($params); $row = $stmt->fetch(); return $row ?: null; } function db_value(PDO $pdo, string $sql, array $params = []): mixed { $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchColumn(); } function medicine_detail_path(array $medicine): string { return '/medicine.php?id=' . rawurlencode((string) ($medicine['id'] ?? '')); } function term_detail_path(array $term): string { return '/term.php?id=' . rawurlencode((string) ($term['id'] ?? '')); } function symptom_detail_path(array $symptom): string { return '/symptom.php?id=' . rawurlencode((string) ($symptom['id'] ?? '')); } function condition_detail_path(array $condition): string { return '/condition.php?id=' . rawurlencode((string) ($condition['id'] ?? '')); } function find_medication_catalog_item(PDO $pdo, array $query): ?array { $id = filter_var($query['id'] ?? null, FILTER_VALIDATE_INT); if ($id !== false && $id > 0) { return db_one($pdo, 'SELECT * FROM medication_catalog WHERE id = ?', [$id]); } $name = trim((string) ($query['name'] ?? $query['q'] ?? '')); if ($name === '') { return null; } return db_one( $pdo, "SELECT * FROM medication_catalog WHERE lower(generic_name) = lower(?) OR lower(active_ingredient) = lower(?) OR lower(chemical_name) = lower(?) ORDER BY CASE WHEN lower(generic_name) = lower(?) THEN 0 WHEN lower(active_ingredient) = lower(?) THEN 1 ELSE 2 END, generic_name LIMIT 1", [$name, $name, $name, $name, $name] ); } function find_medical_term_item(PDO $pdo, array $query): ?array { $id = filter_var($query['id'] ?? null, FILTER_VALIDATE_INT); if ($id !== false && $id > 0) { return db_one($pdo, 'SELECT * FROM medical_terms WHERE id = ?', [$id]); } $term = trim((string) ($query['term'] ?? $query['q'] ?? '')); if ($term === '') { return null; } return db_one( $pdo, "SELECT * FROM medical_terms WHERE lower(term) = lower(?) ORDER BY term LIMIT 1", [$term] ); } function find_medical_condition_item(PDO $pdo, array $query): ?array { $id = filter_var($query['id'] ?? null, FILTER_VALIDATE_INT); if ($id !== false && $id > 0) { return db_one($pdo, 'SELECT * FROM medical_conditions WHERE id = ?', [$id]); } $name = trim((string) ($query['name'] ?? $query['condition'] ?? $query['q'] ?? '')); if ($name === '') { return null; } return db_one( $pdo, "SELECT * FROM medical_conditions WHERE lower(name) = lower(?) ORDER BY name LIMIT 1", [$name] ); } function find_medical_symptom_item(PDO $pdo, array $query): ?array { $id = filter_var($query['id'] ?? null, FILTER_VALIDATE_INT); if ($id !== false && $id > 0) { return db_one($pdo, 'SELECT * FROM medical_symptoms WHERE id = ?', [$id]); } $name = trim((string) ($query['name'] ?? $query['symptom'] ?? $query['q'] ?? '')); if ($name === '') { return null; } return db_one( $pdo, "SELECT * FROM medical_symptoms WHERE lower(name) = lower(?) ORDER BY name LIMIT 1", [$name] ); } function medicine_reference_links(array $medicine): array { $links = [ 'RxNorm' => $medicine['rxnorm_url'] ?? '', 'PubChem' => $medicine['pubchem_url'] ?? '', 'DailyMed' => $medicine['dailymed_url'] ?? '', 'MedlinePlus' => $medicine['medlineplus_url'] ?? '', 'PubMed' => $medicine['pubmed_url'] ?? '', 'Studies' => $medicine['clinicaltrials_url'] ?? '', ]; return array_filter($links, static fn ($url) => trim((string) $url) !== ''); } function db_key_values(PDO $pdo, string $sql, array $params = []): array { $rows = db_all($pdo, $sql, $params); $values = []; foreach ($rows as $row) { $values[$row['name']] = (int) $row['id']; } return $values; } function text_value(array $data, string $key, string $default = ''): string { if (!array_key_exists($key, $data) || $data[$key] === null) { return $default; } $value = trim((string) $data[$key]); return $value === '' ? $default : $value; } function number_value(array $data, string $key, float $default = 0): float { if (!array_key_exists($key, $data) || $data[$key] === '' || $data[$key] === null) { return $default; } return is_numeric($data[$key]) ? (float) $data[$key] : $default; } function int_value(array $data, string $key, int $default = 0): int { return (int) number_value($data, $key, $default); } function user_id_value(array $data): int { $userId = int_value($data, 'user_id'); if ($userId <= 0) { throw new InvalidArgumentException('A valid profile is required.'); } return $userId; } function clamp_choice(string $value, array $choices, string $fallback): string { return in_array($value, $choices, true) ? $value : $fallback; } function nutrition_goal_for(PDO $pdo, int $userId, string $view, DateTimeImmutable $start, DateTimeImmutable $end): array { $goal = db_one( $pdo, "SELECT * FROM nutrition_goals WHERE user_id = ? AND scope = ? AND start_date <= ? ORDER BY start_date DESC, id DESC LIMIT 1", [$userId, $view, app_date($end)] ); if ($goal) { $goal['scaled_from'] = $goal['scope']; return $goal; } $daily = db_one( $pdo, "SELECT * FROM nutrition_goals WHERE user_id = ? AND scope = 'daily' AND start_date <= ? ORDER BY start_date DESC, id DESC LIMIT 1", [$userId, app_date($end)] ); if ($daily) { $days = ((int) $start->diff($end)->format('%a')) + 1; foreach (['calories', 'protein_g', 'carbs_g', 'fat_g', 'water_ml'] as $field) { $daily[$field] = (float) $daily[$field] * $days; } $daily['label'] = $daily['label'] . ' x ' . $days . ' days'; $daily['scope'] = $view; $daily['scaled_from'] = 'daily'; return $daily; } return [ 'id' => null, 'label' => 'No target', 'scope' => $view, 'calories' => 0, 'protein_g' => 0, 'carbs_g' => 0, 'fat_g' => 0, 'water_ml' => 0, 'start_date' => app_date($start), 'scaled_from' => 'none', ]; } function nutrition_totals(PDO $pdo, int $userId, DateTimeImmutable $start, DateTimeImmutable $end): array { $row = db_one( $pdo, "SELECT COALESCE(SUM(calories), 0) AS calories, COALESCE(SUM(protein_g), 0) AS protein_g, COALESCE(SUM(carbs_g), 0) AS carbs_g, COALESCE(SUM(fat_g), 0) AS fat_g, COALESCE(SUM(water_ml), 0) AS water_ml FROM nutrition_logs WHERE user_id = ? AND logged_on BETWEEN ? AND ?", [$userId, app_date($start), app_date($end)] ) ?? []; foreach (['calories', 'protein_g', 'carbs_g', 'fat_g', 'water_ml'] as $field) { $row[$field] = (float) ($row[$field] ?? 0); } return $row; } function workout_summary(array $workouts): array { $planned = count($workouts); $completed = 0; $skipped = 0; $minutes = 0.0; $distance = 0.0; $volume = 0.0; foreach ($workouts as $workout) { if ($workout['status'] === 'complete') { $completed++; } if ($workout['status'] === 'skipped') { $skipped++; } $minutes += (float) $workout['duration_min']; $distance += (float) $workout['distance_mi']; $volume += (float) $workout['sets'] * (float) $workout['reps'] * (float) $workout['weight_lbs']; } return [ 'planned' => $planned, 'completed' => $completed, 'skipped' => $skipped, 'minutes' => round($minutes, 1), 'distance_mi' => round($distance, 2), 'weight_volume_lbs' => round($volume, 1), 'completion_rate' => $planned > 0 ? round(($completed / $planned) * 100, 1) : 0, ]; } function is_admin(PDO $pdo, int $userId): bool { if ($userId <= 0) { return false; } return (bool) db_value( $pdo, "SELECT COUNT(*) FROM users WHERE id = ? AND role = 'admin' AND status = 'active'", [$userId] ); } function admin_user_rows(PDO $pdo): array { return db_all( $pdo, "SELECT users.*, (SELECT COUNT(*) FROM routines WHERE routines.user_id = users.id) AS routine_count, (SELECT COUNT(*) FROM workouts WHERE workouts.user_id = users.id) AS workout_count, (SELECT COUNT(*) FROM nutrition_logs WHERE nutrition_logs.user_id = users.id) AS nutrition_log_count, (SELECT COUNT(*) FROM supplement_logs WHERE supplement_logs.user_id = users.id) AS supplement_log_count, (SELECT COUNT(*) FROM body_metrics WHERE body_metrics.user_id = users.id) AS metric_count, (SELECT COUNT(*) FROM medication_logs WHERE medication_logs.user_id = users.id) AS medication_log_count, (SELECT COUNT(*) FROM vital_logs WHERE vital_logs.user_id = users.id) AS vital_log_count, (SELECT COUNT(*) FROM lab_results WHERE lab_results.user_id = users.id) AS lab_result_count, (SELECT COUNT(*) FROM symptom_logs WHERE symptom_logs.user_id = users.id) AS symptom_log_count, (SELECT COUNT(*) FROM allergy_records WHERE allergy_records.user_id = users.id) AS allergy_count, (SELECT COUNT(*) FROM appointments WHERE appointments.user_id = users.id) AS appointment_count, (SELECT COUNT(*) FROM immunization_records WHERE immunization_records.user_id = users.id) AS immunization_count, (SELECT MAX(measured_on) FROM body_metrics WHERE body_metrics.user_id = users.id) AS last_metric_on FROM users ORDER BY CASE users.status WHEN 'active' THEN 0 ELSE 1 END, CASE users.role WHEN 'admin' THEN 0 ELSE 1 END, users.name" ); } function admin_summary(PDO $pdo): array { return [ 'total_users' => (int) db_value($pdo, 'SELECT COUNT(*) FROM users'), 'active_users' => (int) db_value($pdo, "SELECT COUNT(*) FROM users WHERE status = 'active'"), 'inactive_users' => (int) db_value($pdo, "SELECT COUNT(*) FROM users WHERE status <> 'active'"), 'admins' => (int) db_value($pdo, "SELECT COUNT(*) FROM users WHERE role = 'admin'"), 'members' => (int) db_value($pdo, "SELECT COUNT(*) FROM users WHERE role = 'member'"), 'medication_logs' => (int) db_value($pdo, 'SELECT COUNT(*) FROM medication_logs'), 'vital_logs' => (int) db_value($pdo, 'SELECT COUNT(*) FROM vital_logs'), 'lab_results' => (int) db_value($pdo, 'SELECT COUNT(*) FROM lab_results'), 'appointments' => (int) db_value($pdo, 'SELECT COUNT(*) FROM appointments'), ]; } function require_admin(PDO $pdo, array $data): int { $actingUserId = int_value($data, 'acting_user_id') ?: int_value($data, 'user_id'); if (!is_admin($pdo, $actingUserId)) { throw new InvalidArgumentException('Admin access is required.'); } return $actingUserId; } function last_admin_guard(PDO $pdo, int $targetUserId, string $nextRole, string $nextStatus): void { $target = db_one($pdo, 'SELECT id, role, status FROM users WHERE id = ?', [$targetUserId]); if (!$target || $target['role'] !== 'admin' || $target['status'] !== 'active') { return; } $wouldRemainActiveAdmin = $nextRole === 'admin' && $nextStatus === 'active'; if ($wouldRemainActiveAdmin) { return; } $activeAdmins = (int) db_value($pdo, "SELECT COUNT(*) FROM users WHERE role = 'admin' AND status = 'active'"); if ($activeAdmins <= 1) { throw new InvalidArgumentException('At least one active admin profile is required.'); } } function state_payload(array $query): array { $pdo = app_db(); $view = clamp_choice((string) ($query['view'] ?? 'daily'), ['daily', 'weekly', 'monthly'], 'daily'); $anchor = parse_app_date(isset($query['date']) ? (string) $query['date'] : null); [$start, $end] = range_for_view($view, $anchor); $users = db_all($pdo, "SELECT * FROM users WHERE status = 'active' ORDER BY id"); if (!$users) { seed_demo_data($pdo); ensure_first_user_is_admin($pdo); $users = db_all($pdo, "SELECT * FROM users WHERE status = 'active' ORDER BY id"); } if (!$users) { $users = db_all($pdo, 'SELECT * FROM users ORDER BY id'); } $requestedUserId = isset($query['user_id']) ? (int) $query['user_id'] : (int) $users[0]['id']; $currentUser = $users[0]; foreach ($users as $user) { if ((int) $user['id'] === $requestedUserId) { $currentUser = $user; break; } } $userId = (int) $currentUser['id']; $currentUserIsAdmin = is_admin($pdo, $userId); $exercises = db_all( $pdo, "SELECT * FROM exercises WHERE is_builtin = 1 OR user_id = ? ORDER BY is_builtin DESC, category, name", [$userId] ); $supplements = db_all( $pdo, "SELECT * FROM supplements WHERE is_builtin = 1 OR user_id = ? ORDER BY timing, name", [$userId] ); $nutritionGoals = db_all( $pdo, "SELECT * FROM nutrition_goals WHERE user_id = ? ORDER BY start_date DESC, scope, label", [$userId] ); $routines = db_all( $pdo, "SELECT routines.*, nutrition_goals.label AS nutrition_goal_label FROM routines LEFT JOIN nutrition_goals ON nutrition_goals.id = routines.nutrition_goal_id WHERE routines.user_id = ? ORDER BY routines.active DESC, routines.start_date DESC, routines.name", [$userId] ); $workouts = db_all( $pdo, "SELECT workouts.*, exercises.name AS exercise_name, routines.name AS routine_name FROM workouts LEFT JOIN exercises ON exercises.id = workouts.exercise_id LEFT JOIN routines ON routines.id = workouts.routine_id WHERE workouts.user_id = ? AND workouts.scheduled_on BETWEEN ? AND ? ORDER BY workouts.scheduled_on, workouts.id", [$userId, app_date($start), app_date($end)] ); $nutritionLogs = db_all( $pdo, "SELECT * FROM nutrition_logs WHERE user_id = ? AND logged_on BETWEEN ? AND ? ORDER BY logged_on DESC, id DESC", [$userId, app_date($start), app_date($end)] ); $supplementLogs = db_all( $pdo, "SELECT supplement_logs.*, supplements.name AS supplement_name, supplements.purpose AS supplement_purpose FROM supplement_logs LEFT JOIN supplements ON supplements.id = supplement_logs.supplement_id WHERE supplement_logs.user_id = ? AND supplement_logs.taken_on BETWEEN ? AND ? ORDER BY supplement_logs.taken_on DESC, supplement_logs.id DESC", [$userId, app_date($start), app_date($end)] ); $bodyMetrics = db_all( $pdo, "SELECT * FROM body_metrics WHERE user_id = ? AND measured_on BETWEEN ? AND ? ORDER BY measured_on DESC, id DESC", [$userId, app_date($start), app_date($end)] ); $latestMetric = db_one( $pdo, "SELECT * FROM body_metrics WHERE user_id = ? ORDER BY measured_on DESC, id DESC LIMIT 1", [$userId] ); $medicalTerms = db_all($pdo, 'SELECT * FROM medical_terms ORDER BY category, term'); $medicalConditions = db_all($pdo, 'SELECT * FROM medical_conditions ORDER BY category, name'); $medicalSymptoms = db_all($pdo, 'SELECT * FROM medical_symptoms ORDER BY category, name'); $medicationCatalog = db_all($pdo, 'SELECT * FROM medication_catalog ORDER BY generic_name'); $researchStudies = db_all($pdo, 'SELECT * FROM research_studies ORDER BY category, related_medicine, title'); $medicationLogs = db_all( $pdo, "SELECT medication_logs.*, medication_catalog.class_name, medication_catalog.rxnorm_url, medication_catalog.pubmed_url FROM medication_logs LEFT JOIN medication_catalog ON medication_catalog.id = medication_logs.medication_id WHERE medication_logs.user_id = ? AND medication_logs.taken_on BETWEEN ? AND ? ORDER BY medication_logs.taken_on DESC, medication_logs.id DESC", [$userId, app_date($start), app_date($end)] ); $vitalLogs = db_all( $pdo, "SELECT * FROM vital_logs WHERE user_id = ? AND measured_on BETWEEN ? AND ? ORDER BY measured_on DESC, id DESC", [$userId, app_date($start), app_date($end)] ); $labResults = db_all( $pdo, "SELECT * FROM lab_results WHERE user_id = ? AND collected_on BETWEEN ? AND ? ORDER BY collected_on DESC, id DESC", [$userId, app_date($start), app_date($end)] ); $symptomLogs = db_all( $pdo, "SELECT * FROM symptom_logs WHERE user_id = ? AND observed_on BETWEEN ? AND ? ORDER BY observed_on DESC, severity DESC, id DESC", [$userId, app_date($start), app_date($end)] ); $allergyRecords = db_all( $pdo, "SELECT * FROM allergy_records WHERE user_id = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, allergen", [$userId] ); $appointments = db_all( $pdo, "SELECT * FROM appointments WHERE user_id = ? AND appointment_on BETWEEN ? AND ? ORDER BY appointment_on, id", [$userId, app_date($start), app_date($end)] ); $immunizationRecords = db_all( $pdo, "SELECT * FROM immunization_records WHERE user_id = ? ORDER BY administered_on DESC, id DESC", [$userId] ); $latestVitals = db_one( $pdo, "SELECT * FROM vital_logs WHERE user_id = ? ORDER BY measured_on DESC, id DESC LIMIT 1", [$userId] ); return [ 'users' => $users, 'adminUsers' => $currentUserIsAdmin ? admin_user_rows($pdo) : [], 'adminSummary' => $currentUserIsAdmin ? admin_summary($pdo) : null, 'currentUser' => $currentUser, 'currentUserIsAdmin' => $currentUserIsAdmin, 'view' => $view, 'range' => [ 'anchor' => app_date($anchor), 'start' => app_date($start), 'end' => app_date($end), 'days' => ((int) $start->diff($end)->format('%a')) + 1, ], 'exercises' => $exercises, 'supplements' => $supplements, 'nutritionGoals' => $nutritionGoals, 'routines' => $routines, 'workouts' => $workouts, 'nutritionLogs' => $nutritionLogs, 'supplementLogs' => $supplementLogs, 'bodyMetrics' => $bodyMetrics, 'latestMetric' => $latestMetric, 'medicalTerms' => $medicalTerms, 'medicalConditions' => $medicalConditions, 'medicalSymptoms' => $medicalSymptoms, 'medicationCatalog' => $medicationCatalog, 'researchStudies' => $researchStudies, 'medicationLogs' => $medicationLogs, 'vitalLogs' => $vitalLogs, 'labResults' => $labResults, 'symptomLogs' => $symptomLogs, 'allergyRecords' => $allergyRecords, 'appointments' => $appointments, 'immunizationRecords' => $immunizationRecords, 'latestVitals' => $latestVitals, 'nutritionSummary' => [ 'target' => nutrition_goal_for($pdo, $userId, $view, $start, $end), 'actual' => nutrition_totals($pdo, $userId, $start, $end), ], 'workoutSummary' => workout_summary($workouts), ]; } function library_search_payload(array $query): array { $pdo = app_db(); $search = trim((string) ($query['q'] ?? '')); $limit = max(10, min(500, (int) ($query['limit'] ?? 250))); if ($search === '') { return [ 'query' => '', 'medicalTerms' => db_all($pdo, 'SELECT * FROM medical_terms ORDER BY category, term LIMIT ?', [$limit]), 'medicalConditions' => db_all($pdo, 'SELECT * FROM medical_conditions ORDER BY category, name LIMIT ?', [$limit]), 'medicalSymptoms' => db_all($pdo, 'SELECT * FROM medical_symptoms ORDER BY category, name LIMIT ?', [$limit]), 'medicationCatalog' => db_all($pdo, 'SELECT * FROM medication_catalog ORDER BY generic_name LIMIT ?', [$limit]), 'researchStudies' => db_all($pdo, 'SELECT * FROM research_studies ORDER BY category, related_medicine, title LIMIT ?', [$limit]), ]; } $like = '%' . $search . '%'; $termParams = array_fill(0, 5, $like); $conditionParams = [...array_fill(0, 4, $like), $search, $search . '%', $like]; $symptomParams = [...array_fill(0, 8, $like), $search, $search . '%', $like]; $medicineParams = array_fill(0, 10, $like); $studyParams = array_fill(0, 6, $like); return [ 'query' => $search, 'medicalTerms' => db_all( $pdo, "SELECT * FROM medical_terms WHERE term LIKE ? OR category LIKE ? OR plain_language_definition LIKE ? OR clinical_context LIKE ? OR source_name LIKE ? ORDER BY category, term LIMIT {$limit}", $termParams ), 'medicalConditions' => db_all( $pdo, "SELECT * FROM medical_conditions WHERE name LIKE ? OR category LIKE ? OR overview LIKE ? OR common_tracking LIKE ? ORDER BY CASE WHEN lower(name) = lower(?) THEN 0 WHEN name LIKE ? THEN 1 WHEN overview LIKE ? THEN 2 ELSE 3 END, category, name LIMIT {$limit}", $conditionParams ), 'medicalSymptoms' => db_all( $pdo, "SELECT * FROM medical_symptoms WHERE name LIKE ? OR category LIKE ? OR body_system LIKE ? OR plain_language_description LIKE ? OR common_tracking LIKE ? OR urgency_note LIKE ? OR source_name LIKE ? OR search_terms LIKE ? ORDER BY CASE WHEN lower(name) = lower(?) THEN 0 WHEN name LIKE ? THEN 1 WHEN search_terms LIKE ? THEN 2 ELSE 3 END, category, name LIMIT {$limit}", $symptomParams ), 'medicationCatalog' => db_all( $pdo, "SELECT * FROM medication_catalog WHERE generic_name LIKE ? OR active_ingredient LIKE ? OR chemical_name LIKE ? OR brand_names LIKE ? OR generic_brands LIKE ? OR class_name LIKE ? OR drug_family LIKE ? OR common_use LIKE ? OR tracking_note LIKE ? OR search_terms LIKE ? ORDER BY generic_name LIMIT {$limit}", $medicineParams ), 'researchStudies' => db_all( $pdo, "SELECT * FROM research_studies WHERE category LIKE ? OR related_medicine LIKE ? OR related_condition LIKE ? OR title LIKE ? OR study_type LIKE ? OR summary LIKE ? ORDER BY category, related_medicine, title LIMIT {$limit}", $studyParams ), ]; } function http_json(string $url, int $timeout = 20): ?array { $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'timeout' => $timeout, 'header' => "Accept: application/json\r\nUser-Agent: NeonMedicalTracker/1.0\r\n", ], ]); $body = @file_get_contents($url, false, $context); if (($body === false || $body === '') && function_exists('shell_exec')) { $curl = trim((string) shell_exec('command -v curl 2>/dev/null')); if ($curl === '' && is_executable('/usr/bin/curl')) { $curl = '/usr/bin/curl'; } if ($curl !== '') { $command = escapeshellarg($curl) . ' -fsSL --max-time ' . (int) $timeout . ' -H ' . escapeshellarg('Accept: application/json') . ' -A ' . escapeshellarg('NeonMedicalTracker/1.0') . ' ' . escapeshellarg($url) . ' 2>/dev/null'; $body = shell_exec($command); } } if (!is_string($body) || $body === '') { return null; } $json = json_decode($body, true); return is_array($json) ? $json : null; } function medication_import_row(string $name, string $source, string $brandNames = '', string $className = 'Imported medicine concept'): array { $cleanName = preg_replace('/\s+/', ' ', trim($name)); if ($cleanName === '') { return []; } $sourceLabel = match ($source) { 'RxNorm' => 'RxNorm / NLM', 'DailyMed' => 'DailyMed / NLM', 'openFDA' => 'openFDA / FDA', default => $source, }; return [ 'generic_name' => strtolower($cleanName), 'active_ingredient' => $cleanName, 'chemical_name' => $cleanName, 'brand_names' => $brandNames, 'generic_brands' => $cleanName, 'class_name' => $className, 'drug_family' => 'Imported reference', 'common_use' => "Imported from {$sourceLabel}. Review official labels and clinician guidance before use.", 'tracking_note' => 'Use this entry as a reference/search starting point; verify dose, indication, and safety details with official labeling or a clinician.', 'rxnorm_url' => medication_source_url('https://mor.nlm.nih.gov/RxNav/search?searchBy=String&searchTerm=', $cleanName), 'medlineplus_url' => 'https://medlineplus.gov/druginformation.html', 'pubchem_url' => medication_source_url('https://pubchem.ncbi.nlm.nih.gov/#query=', $cleanName), 'dailymed_url' => medication_source_url('https://dailymed.nlm.nih.gov/dailymed/search.cfm?query=', $cleanName), 'pubmed_url' => medication_source_url('https://pubmed.ncbi.nlm.nih.gov/?term=', $cleanName . ' medication'), 'clinicaltrials_url' => medication_source_url('https://clinicaltrials.gov/search?term=', $cleanName), 'source_name' => $sourceLabel, 'search_terms' => implode(' ', [$cleanName, $brandNames, $className, $sourceLabel]), ]; } function fetch_rxnorm_medication_rows(int $limit): array { $rows = []; foreach (['IN', 'PIN'] as $tty) { $json = http_json('https://rxnav.nlm.nih.gov/REST/allconcepts.json?tty=' . $tty); $concepts = $json['minConceptGroup']['minConcept'] ?? []; if (!is_array($concepts)) { continue; } foreach ($concepts as $concept) { if (count($rows) >= $limit) { break 2; } $name = (string) ($concept['name'] ?? ''); $row = medication_import_row($name, 'RxNorm'); if ($row) { $rows[] = $row; } } } return $rows; } function fetch_openfda_medication_rows(int $limit): array { $url = 'https://api.fda.gov/drug/label.json?count=openfda.generic_name.exact&limit=' . $limit; $json = http_json($url); $results = $json['results'] ?? []; if (!is_array($results)) { return []; } $rows = []; foreach ($results as $result) { if (count($rows) >= $limit) { break; } $term = (string) ($result['term'] ?? ''); $row = medication_import_row($term, 'openFDA'); if ($row) { $rows[] = $row; } } return $rows; } function fetch_dailymed_medication_rows(int $limit): array { $url = 'https://dailymed.nlm.nih.gov/dailymed/services/v2/drugnames.json'; $json = http_json($url); $data = $json['data'] ?? $json['drugNames'] ?? $json['results'] ?? []; if (!is_array($data)) { return []; } $rows = []; foreach ($data as $item) { if (count($rows) >= $limit) { break; } $name = is_array($item) ? (string) ($item['drug_name'] ?? $item['name'] ?? $item['term'] ?? '') : (string) $item; $row = medication_import_row($name, 'DailyMed'); if ($row) { $rows[] = $row; } } return $rows; } function merge_text_values(string $current, string $incoming): string { $parts = []; foreach (explode(',', $current . ',' . $incoming) as $part) { $value = trim($part); if ($value === '') { continue; } $parts[strtolower($value)] = $value; } return implode(', ', array_values($parts)); } function upsert_imported_medication_rows(PDO $pdo, array $rows): array { $created = app_now(); $select = $pdo->prepare('SELECT * FROM medication_catalog WHERE lower(generic_name) = lower(?) LIMIT 1'); $insert = $pdo->prepare( "INSERT INTO medication_catalog (generic_name, active_ingredient, chemical_name, brand_names, generic_brands, class_name, drug_family, common_use, tracking_note, rxnorm_url, medlineplus_url, pubchem_url, dailymed_url, pubmed_url, clinicaltrials_url, source_name, search_terms, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $update = $pdo->prepare( "UPDATE medication_catalog SET active_ingredient = ?, chemical_name = ?, brand_names = ?, generic_brands = ?, class_name = ?, drug_family = ?, common_use = ?, tracking_note = ?, rxnorm_url = ?, medlineplus_url = ?, pubchem_url = ?, dailymed_url = ?, pubmed_url = ?, clinicaltrials_url = ?, source_name = ?, search_terms = ? WHERE id = ?" ); $seen = []; $inserted = 0; $updated = 0; foreach ($rows as $row) { if (!$row || empty($row['generic_name'])) { continue; } $key = strtolower((string) $row['generic_name']); if (isset($seen[$key])) { continue; } $seen[$key] = true; $select->execute([$row['generic_name']]); $existing = $select->fetch(); if (!$existing) { $insert->execute([ $row['generic_name'], $row['active_ingredient'], $row['chemical_name'], $row['brand_names'], $row['generic_brands'], $row['class_name'], $row['drug_family'], $row['common_use'], $row['tracking_note'], $row['rxnorm_url'], $row['medlineplus_url'], $row['pubchem_url'], $row['dailymed_url'], $row['pubmed_url'], $row['clinicaltrials_url'], $row['source_name'], $row['search_terms'], $created, ]); $inserted++; continue; } $next = []; foreach (['active_ingredient', 'chemical_name', 'class_name', 'drug_family', 'common_use', 'tracking_note', 'rxnorm_url', 'medlineplus_url', 'pubchem_url', 'dailymed_url', 'pubmed_url', 'clinicaltrials_url'] as $field) { $next[$field] = trim((string) ($existing[$field] ?? '')) !== '' ? $existing[$field] : $row[$field]; } $next['brand_names'] = merge_text_values((string) ($existing['brand_names'] ?? ''), (string) $row['brand_names']); $next['generic_brands'] = merge_text_values((string) ($existing['generic_brands'] ?? ''), (string) $row['generic_brands']); $next['source_name'] = merge_text_values((string) ($existing['source_name'] ?? ''), (string) $row['source_name']); $next['search_terms'] = trim((string) ($existing['search_terms'] ?? '') . ' ' . (string) $row['search_terms']); $update->execute([ $next['active_ingredient'], $next['chemical_name'], $next['brand_names'], $next['generic_brands'], $next['class_name'], $next['drug_family'], $next['common_use'], $next['tracking_note'], $next['rxnorm_url'], $next['medlineplus_url'], $next['pubchem_url'], $next['dailymed_url'], $next['pubmed_url'], $next['clinicaltrials_url'], $next['source_name'], $next['search_terms'], (int) $existing['id'], ]); $updated++; } return ['inserted' => $inserted, 'updated' => $updated, 'seen' => count($seen)]; } function record_medication_source_update(PDO $pdo, string $source, string $status, int $inserted, int $updated, string $message): void { $stmt = $pdo->prepare( "INSERT INTO medication_source_updates (source, status, imported_count, updated_count, message, created_at) VALUES (?, ?, ?, ?, ?, ?)" ); $stmt->execute([$source, $status, $inserted, $updated, $message, app_now()]); } function refresh_medication_sources(array $data): array { $pdo = app_db(); if (array_key_exists('acting_user_id', $data) || array_key_exists('user_id', $data)) { require_admin($pdo, $data); } $limit = max(25, min(5000, int_value($data, 'limit', 1000))); $sources = [ 'RxNorm' => 'fetch_rxnorm_medication_rows', 'DailyMed' => 'fetch_dailymed_medication_rows', 'openFDA' => 'fetch_openfda_medication_rows', ]; $requestedSource = text_value($data, 'source', 'all'); if ($requestedSource !== 'all') { $sources = array_intersect_key($sources, [$requestedSource => true]); } $summary = []; $totalInserted = 0; $totalUpdated = 0; foreach ($sources as $source => $fetcher) { try { $rows = $fetcher($limit); if (!$rows) { record_medication_source_update($pdo, $source, 'empty', 0, 0, 'No rows returned. The source may be unavailable or rate-limited.'); $summary[] = ['source' => $source, 'status' => 'empty', 'inserted' => 0, 'updated' => 0, 'message' => 'No rows returned.']; continue; } $result = upsert_imported_medication_rows($pdo, $rows); $totalInserted += $result['inserted']; $totalUpdated += $result['updated']; $message = "Fetched {$result['seen']} unique rows."; record_medication_source_update($pdo, $source, 'ok', $result['inserted'], $result['updated'], $message); $summary[] = ['source' => $source, 'status' => 'ok', 'inserted' => $result['inserted'], 'updated' => $result['updated'], 'message' => $message]; } catch (Throwable $exception) { record_medication_source_update($pdo, $source, 'error', 0, 0, $exception->getMessage()); $summary[] = ['source' => $source, 'status' => 'error', 'inserted' => 0, 'updated' => 0, 'message' => $exception->getMessage()]; } } return [ 'inserted' => $totalInserted, 'updated' => $totalUpdated, 'catalog_count' => (int) db_value($pdo, 'SELECT COUNT(*) FROM medication_catalog'), 'sources' => $summary, 'recent_updates' => db_all($pdo, 'SELECT * FROM medication_source_updates ORDER BY created_at DESC, id DESC LIMIT 10'), ]; } function create_user(array $data): array { $pdo = app_db(); $name = text_value($data, 'name'); if ($name === '') { throw new InvalidArgumentException('Profile name is required.'); } $userCount = (int) db_value($pdo, 'SELECT COUNT(*) FROM users'); if ($userCount > 0) { require_admin($pdo, $data); } $role = $userCount === 0 ? 'admin' : clamp_choice(text_value($data, 'role', 'member'), ['admin', 'member'], 'member'); $status = clamp_choice(text_value($data, 'status', 'active'), ['active', 'inactive'], 'active'); $mode = clamp_choice(text_value($data, 'theme_mode', 'dark'), ['dark', 'light'], 'dark'); $accent = clamp_choice(text_value($data, 'theme_accent', 'green'), ['green', 'blue', 'red', 'pink', 'orange'], 'green'); $stmt = $pdo->prepare( "INSERT INTO users (name, email, fitness_focus, role, status, phone, birthday, height_in, training_level, emergency_contact, theme_mode, theme_accent, updated_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $name, text_value($data, 'email'), text_value($data, 'fitness_focus'), $role, $status, text_value($data, 'phone'), text_value($data, 'birthday'), number_value($data, 'height_in'), text_value($data, 'training_level'), text_value($data, 'emergency_contact'), $mode, $accent, app_now(), app_now(), ]); $userId = (int) $pdo->lastInsertId(); $goalStmt = $pdo->prepare( "INSERT INTO nutrition_goals (user_id, label, scope, calories, protein_g, carbs_g, fat_g, water_ml, start_date, created_at) VALUES (?, 'Starter day', 'daily', 2400, 150, 260, 75, 3000, ?, ?)" ); $goalStmt->execute([$userId, app_date(new DateTimeImmutable('today')), app_now()]); return ['user' => db_one($pdo, 'SELECT * FROM users WHERE id = ?', [$userId])]; } function update_user(array $data): array { $pdo = app_db(); require_admin($pdo, $data); $targetUserId = int_value($data, 'target_user_id'); if ($targetUserId <= 0) { throw new InvalidArgumentException('Target profile is required.'); } $current = db_one($pdo, 'SELECT * FROM users WHERE id = ?', [$targetUserId]); if (!$current) { throw new InvalidArgumentException('Profile not found.'); } $nextRole = array_key_exists('role', $data) ? clamp_choice(text_value($data, 'role', $current['role']), ['admin', 'member'], $current['role']) : $current['role']; $nextStatus = array_key_exists('status', $data) ? clamp_choice(text_value($data, 'status', $current['status']), ['active', 'inactive'], $current['status']) : $current['status']; last_admin_guard($pdo, $targetUserId, $nextRole, $nextStatus); $fields = [ 'name' => array_key_exists('name', $data) ? text_value($data, 'name', $current['name']) : $current['name'], 'email' => array_key_exists('email', $data) ? text_value($data, 'email') : $current['email'], 'fitness_focus' => array_key_exists('fitness_focus', $data) ? text_value($data, 'fitness_focus') : $current['fitness_focus'], 'role' => $nextRole, 'status' => $nextStatus, 'phone' => array_key_exists('phone', $data) ? text_value($data, 'phone') : $current['phone'], 'birthday' => array_key_exists('birthday', $data) ? text_value($data, 'birthday') : $current['birthday'], 'height_in' => array_key_exists('height_in', $data) ? number_value($data, 'height_in') : (float) $current['height_in'], 'training_level' => array_key_exists('training_level', $data) ? text_value($data, 'training_level') : $current['training_level'], 'emergency_contact' => array_key_exists('emergency_contact', $data) ? text_value($data, 'emergency_contact') : $current['emergency_contact'], 'theme_mode' => array_key_exists('theme_mode', $data) ? clamp_choice(text_value($data, 'theme_mode', $current['theme_mode']), ['dark', 'light'], $current['theme_mode']) : $current['theme_mode'], 'theme_accent' => array_key_exists('theme_accent', $data) ? clamp_choice(text_value($data, 'theme_accent', $current['theme_accent']), ['green', 'blue', 'red', 'pink', 'orange'], $current['theme_accent']) : $current['theme_accent'], ]; if ($fields['name'] === '') { throw new InvalidArgumentException('Profile name is required.'); } $stmt = $pdo->prepare( "UPDATE users SET name = ?, email = ?, fitness_focus = ?, role = ?, status = ?, phone = ?, birthday = ?, height_in = ?, training_level = ?, emergency_contact = ?, theme_mode = ?, theme_accent = ?, updated_at = ? WHERE id = ?" ); $stmt->execute([ $fields['name'], $fields['email'], $fields['fitness_focus'], $fields['role'], $fields['status'], $fields['phone'], $fields['birthday'], $fields['height_in'], $fields['training_level'], $fields['emergency_contact'], $fields['theme_mode'], $fields['theme_accent'], app_now(), $targetUserId, ]); return ['user' => db_one($pdo, 'SELECT * FROM users WHERE id = ?', [$targetUserId])]; } function delete_user(array $data): array { $pdo = app_db(); $actingUserId = require_admin($pdo, $data); $targetUserId = int_value($data, 'target_user_id'); if ($targetUserId <= 0) { throw new InvalidArgumentException('Target profile is required.'); } if ($targetUserId === $actingUserId) { throw new InvalidArgumentException('You cannot delete the profile you are currently using.'); } $target = db_one($pdo, 'SELECT * FROM users WHERE id = ?', [$targetUserId]); if (!$target) { throw new InvalidArgumentException('Profile not found.'); } last_admin_guard($pdo, $targetUserId, 'member', 'inactive'); $stmt = $pdo->prepare('DELETE FROM users WHERE id = ?'); $stmt->execute([$targetUserId]); return ['deleted' => true, 'target_user_id' => $targetUserId]; } function update_theme(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $mode = clamp_choice(text_value($data, 'theme_mode', 'dark'), ['dark', 'light'], 'dark'); $accent = clamp_choice(text_value($data, 'theme_accent', 'green'), ['green', 'blue', 'red', 'pink', 'orange'], 'green'); $stmt = $pdo->prepare('UPDATE users SET theme_mode = ?, theme_accent = ? WHERE id = ?'); $stmt->execute([$mode, $accent, $userId]); return ['user' => db_one($pdo, 'SELECT * FROM users WHERE id = ?', [$userId])]; } function create_exercise(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $name = text_value($data, 'name'); if ($name === '') { throw new InvalidArgumentException('Exercise name is required.'); } $stmt = $pdo->prepare( "INSERT INTO exercises (user_id, name, category, muscle_group, default_unit, notes, is_builtin, created_at) VALUES (?, ?, ?, ?, ?, ?, 0, ?)" ); $stmt->execute([ $userId, $name, text_value($data, 'category', 'Custom'), text_value($data, 'muscle_group'), text_value($data, 'default_unit'), text_value($data, 'notes'), app_now(), ]); return ['exercise' => db_one($pdo, 'SELECT * FROM exercises WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_supplement(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $name = text_value($data, 'name'); if ($name === '') { throw new InvalidArgumentException('Supplement name is required.'); } $stmt = $pdo->prepare( "INSERT INTO supplements (user_id, name, timing, default_dose, purpose, notes, is_builtin, created_at) VALUES (?, ?, ?, ?, ?, ?, 0, ?)" ); $stmt->execute([ $userId, $name, text_value($data, 'timing', 'Daily'), text_value($data, 'default_dose'), text_value($data, 'purpose'), text_value($data, 'notes'), app_now(), ]); return ['supplement' => db_one($pdo, 'SELECT * FROM supplements WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_nutrition_goal(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $scope = clamp_choice(text_value($data, 'scope', 'daily'), ['daily', 'weekly', 'monthly'], 'daily'); $label = text_value($data, 'label', ucfirst($scope) . ' target'); $stmt = $pdo->prepare( "INSERT INTO nutrition_goals (user_id, label, scope, calories, protein_g, carbs_g, fat_g, water_ml, start_date, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, $label, $scope, number_value($data, 'calories'), number_value($data, 'protein_g'), number_value($data, 'carbs_g'), number_value($data, 'fat_g'), number_value($data, 'water_ml'), app_date(parse_app_date(text_value($data, 'start_date'))), app_now(), ]); return ['nutritionGoal' => db_one($pdo, 'SELECT * FROM nutrition_goals WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_routine(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $name = text_value($data, 'name'); if ($name === '') { throw new InvalidArgumentException('Routine name is required.'); } $scope = clamp_choice(text_value($data, 'planner_scope', 'weekly'), ['daily', 'weekly', 'monthly'], 'weekly'); $goalId = int_value($data, 'nutrition_goal_id') ?: null; $stmt = $pdo->prepare( "INSERT INTO routines (user_id, nutrition_goal_id, name, workout_type, planner_scope, start_date, end_date, intensity, notes, active, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)" ); $stmt->execute([ $userId, $goalId, $name, text_value($data, 'workout_type', 'Hybrid'), $scope, app_date(parse_app_date(text_value($data, 'start_date'))), text_value($data, 'end_date'), text_value($data, 'intensity'), text_value($data, 'notes'), app_now(), ]); return ['routine' => db_one($pdo, 'SELECT * FROM routines WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_workout(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $title = text_value($data, 'title'); if ($title === '') { throw new InvalidArgumentException('Workout title is required.'); } $status = clamp_choice(text_value($data, 'status', 'planned'), ['planned', 'complete', 'skipped'], 'planned'); $stmt = $pdo->prepare( "INSERT INTO workouts (user_id, routine_id, exercise_id, scheduled_on, title, workout_type, sets, reps, weight_lbs, duration_min, distance_mi, intensity, status, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, int_value($data, 'routine_id') ?: null, int_value($data, 'exercise_id') ?: null, app_date(parse_app_date(text_value($data, 'scheduled_on'))), $title, text_value($data, 'workout_type', 'Weight lifting'), int_value($data, 'sets'), int_value($data, 'reps'), number_value($data, 'weight_lbs'), number_value($data, 'duration_min'), number_value($data, 'distance_mi'), text_value($data, 'intensity'), $status, text_value($data, 'notes'), app_now(), ]); return ['workout' => db_one($pdo, 'SELECT * FROM workouts WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function update_workout(array $data): array { $pdo = app_db(); $workoutId = int_value($data, 'id'); if ($workoutId <= 0) { throw new InvalidArgumentException('Workout id is required.'); } $allowed = [ 'title' => 'text', 'workout_type' => 'text', 'intensity' => 'text', 'notes' => 'text', 'scheduled_on' => 'date', 'status' => 'status', 'sets' => 'int', 'reps' => 'int', 'weight_lbs' => 'float', 'duration_min' => 'float', 'distance_mi' => 'float', ]; $fields = []; $values = []; foreach ($allowed as $field => $type) { if (!array_key_exists($field, $data)) { continue; } $fields[] = $field . ' = ?'; if ($type === 'date') { $values[] = app_date(parse_app_date(text_value($data, $field))); } elseif ($type === 'status') { $values[] = clamp_choice(text_value($data, $field, 'planned'), ['planned', 'complete', 'skipped'], 'planned'); } elseif ($type === 'int') { $values[] = int_value($data, $field); } elseif ($type === 'float') { $values[] = number_value($data, $field); } else { $values[] = text_value($data, $field); } } if (!$fields) { throw new InvalidArgumentException('No workout updates supplied.'); } $values[] = $workoutId; $stmt = $pdo->prepare('UPDATE workouts SET ' . implode(', ', $fields) . ' WHERE id = ?'); $stmt->execute($values); return ['workout' => db_one($pdo, 'SELECT * FROM workouts WHERE id = ?', [$workoutId])]; } function create_nutrition_log(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $stmt = $pdo->prepare( "INSERT INTO nutrition_logs (user_id, logged_on, calories, protein_g, carbs_g, fat_g, water_ml, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, app_date(parse_app_date(text_value($data, 'logged_on'))), number_value($data, 'calories'), number_value($data, 'protein_g'), number_value($data, 'carbs_g'), number_value($data, 'fat_g'), number_value($data, 'water_ml'), text_value($data, 'notes'), app_now(), ]); return ['nutritionLog' => db_one($pdo, 'SELECT * FROM nutrition_logs WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_supplement_log(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $status = clamp_choice(text_value($data, 'status', 'planned'), ['planned', 'taken', 'skipped'], 'planned'); $stmt = $pdo->prepare( "INSERT INTO supplement_logs (user_id, supplement_id, taken_on, timing, dose, status, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, int_value($data, 'supplement_id') ?: null, app_date(parse_app_date(text_value($data, 'taken_on'))), text_value($data, 'timing', 'Daily'), text_value($data, 'dose'), $status, text_value($data, 'notes'), app_now(), ]); return ['supplementLog' => db_one($pdo, 'SELECT * FROM supplement_logs WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_body_metric(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $stmt = $pdo->prepare( "INSERT INTO body_metrics (user_id, measured_on, weight_lbs, body_fat_pct, sleep_hours, resting_hr, mood, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, app_date(parse_app_date(text_value($data, 'measured_on'))), number_value($data, 'weight_lbs'), number_value($data, 'body_fat_pct'), number_value($data, 'sleep_hours'), int_value($data, 'resting_hr'), text_value($data, 'mood'), text_value($data, 'notes'), app_now(), ]); return ['bodyMetric' => db_one($pdo, 'SELECT * FROM body_metrics WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_medication_log(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $medicationId = int_value($data, 'medication_id') ?: null; $catalog = $medicationId ? db_one($pdo, 'SELECT * FROM medication_catalog WHERE id = ?', [$medicationId]) : null; $medicationName = text_value($data, 'medication_name', $catalog['generic_name'] ?? ''); if ($medicationName === '') { throw new InvalidArgumentException('Medication name is required.'); } $status = clamp_choice(text_value($data, 'status', 'planned'), ['planned', 'taken', 'skipped', 'stopped'], 'planned'); $stmt = $pdo->prepare( "INSERT INTO medication_logs (user_id, medication_id, taken_on, medication_name, dose, route, frequency, status, prescriber, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, $medicationId, app_date(parse_app_date(text_value($data, 'taken_on'))), $medicationName, text_value($data, 'dose'), text_value($data, 'route'), text_value($data, 'frequency'), $status, text_value($data, 'prescriber'), text_value($data, 'notes'), app_now(), ]); return ['medicationLog' => db_one($pdo, 'SELECT * FROM medication_logs WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_vital_log(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $painScore = max(0, min(10, int_value($data, 'pain_score'))); $stmt = $pdo->prepare( "INSERT INTO vital_logs (user_id, measured_on, systolic, diastolic, pulse, temperature_f, oxygen_pct, glucose_mg_dl, pain_score, symptoms, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, app_date(parse_app_date(text_value($data, 'measured_on'))), int_value($data, 'systolic'), int_value($data, 'diastolic'), int_value($data, 'pulse'), number_value($data, 'temperature_f'), number_value($data, 'oxygen_pct'), number_value($data, 'glucose_mg_dl'), $painScore, text_value($data, 'symptoms'), text_value($data, 'notes'), app_now(), ]); return ['vitalLog' => db_one($pdo, 'SELECT * FROM vital_logs WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_lab_result(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $testName = text_value($data, 'test_name'); $resultValue = text_value($data, 'result_value'); if ($testName === '' || $resultValue === '') { throw new InvalidArgumentException('Lab test name and result are required.'); } $stmt = $pdo->prepare( "INSERT INTO lab_results (user_id, collected_on, test_name, result_value, unit, reference_range, ordering_clinician, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, app_date(parse_app_date(text_value($data, 'collected_on'))), $testName, $resultValue, text_value($data, 'unit'), text_value($data, 'reference_range'), text_value($data, 'ordering_clinician'), text_value($data, 'notes'), app_now(), ]); return ['labResult' => db_one($pdo, 'SELECT * FROM lab_results WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_symptom_log(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $symptom = text_value($data, 'symptom'); if ($symptom === '') { throw new InvalidArgumentException('Symptom is required.'); } $severity = max(0, min(10, int_value($data, 'severity'))); $stmt = $pdo->prepare( "INSERT INTO symptom_logs (user_id, observed_on, symptom, body_area, severity, duration, triggers, action_taken, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, app_date(parse_app_date(text_value($data, 'observed_on'))), $symptom, text_value($data, 'body_area'), $severity, text_value($data, 'duration'), text_value($data, 'triggers'), text_value($data, 'action_taken'), text_value($data, 'notes'), app_now(), ]); return ['symptomLog' => db_one($pdo, 'SELECT * FROM symptom_logs WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_allergy_record(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $allergen = text_value($data, 'allergen'); if ($allergen === '') { throw new InvalidArgumentException('Allergen is required.'); } $severity = clamp_choice(text_value($data, 'severity', 'unknown'), ['unknown', 'mild', 'moderate', 'severe', 'life-threatening'], 'unknown'); $status = clamp_choice(text_value($data, 'status', 'active'), ['active', 'resolved'], 'active'); $firstNoted = text_value($data, 'first_noted_on'); $stmt = $pdo->prepare( "INSERT INTO allergy_records (user_id, allergen, reaction, severity, first_noted_on, status, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, $allergen, text_value($data, 'reaction'), $severity, $firstNoted === '' ? '' : app_date(parse_app_date($firstNoted)), $status, text_value($data, 'notes'), app_now(), ]); return ['allergyRecord' => db_one($pdo, 'SELECT * FROM allergy_records WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_appointment(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $reason = text_value($data, 'reason'); if ($reason === '') { throw new InvalidArgumentException('Appointment reason is required.'); } $status = clamp_choice(text_value($data, 'status', 'scheduled'), ['scheduled', 'completed', 'canceled'], 'scheduled'); $stmt = $pdo->prepare( "INSERT INTO appointments (user_id, appointment_on, clinician, specialty, location, reason, status, follow_up_notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, app_date(parse_app_date(text_value($data, 'appointment_on'))), text_value($data, 'clinician'), text_value($data, 'specialty'), text_value($data, 'location'), $reason, $status, text_value($data, 'follow_up_notes'), app_now(), ]); return ['appointment' => db_one($pdo, 'SELECT * FROM appointments WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function create_immunization_record(array $data): array { $pdo = app_db(); $userId = user_id_value($data); $vaccineName = text_value($data, 'vaccine_name'); if ($vaccineName === '') { throw new InvalidArgumentException('Vaccine name is required.'); } $nextDue = text_value($data, 'next_due_on'); $stmt = $pdo->prepare( "INSERT INTO immunization_records (user_id, vaccine_name, administered_on, dose, lot_number, clinician_or_site, next_due_on, notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $userId, $vaccineName, app_date(parse_app_date(text_value($data, 'administered_on'))), text_value($data, 'dose'), text_value($data, 'lot_number'), text_value($data, 'clinician_or_site'), $nextDue === '' ? '' : app_date(parse_app_date($nextDue)), text_value($data, 'notes'), app_now(), ]); return ['immunizationRecord' => db_one($pdo, 'SELECT * FROM immunization_records WHERE id = ?', [(int) $pdo->lastInsertId()])]; } function json_input(): array { $raw = file_get_contents('php://input'); if (!$raw) { return []; } $data = json_decode($raw, true); if (!is_array($data)) { throw new InvalidArgumentException('Invalid JSON payload.'); } return $data; } function json_response(array $payload, int $status = 200): void { http_response_code($status); header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store'); echo json_encode($payload, JSON_THROW_ON_ERROR); }