← back to AbramsOS

lib/bp-meds.js

73 lines

// bp-meds.js — deterministic classifier: does an active medication affect blood pressure?
//
// Drug pharmacology is NEVER guessed by an LLM here — it's a curated map of well-known
// generic stems / drug names to their BP effect. Matching is substring on the lowercased
// name + generic_name. This powers the read-only "meds that affect your BP" context on
// /health so a BP trend can be read alongside what the person actually takes.
//
// effect: 'lowers'  — antihypertensives (taken to bring BP down)
//         'raises'  — can elevate BP as a side effect (NSAIDs, decongestants, stimulants)
// This is context, NOT medical advice.

// Explicit generic/brand names (highest confidence).
const NAMED = [
  // ACE inhibitors / ARBs
  ['lisinopril', 'lowers', 'ACE inhibitor'], ['enalapril', 'lowers', 'ACE inhibitor'],
  ['ramipril', 'lowers', 'ACE inhibitor'], ['benazepril', 'lowers', 'ACE inhibitor'],
  ['losartan', 'lowers', 'ARB'], ['valsartan', 'lowers', 'ARB'], ['olmesartan', 'lowers', 'ARB'],
  ['telmisartan', 'lowers', 'ARB'], ['irbesartan', 'lowers', 'ARB'],
  // Beta-blockers
  ['metoprolol', 'lowers', 'beta-blocker'], ['atenolol', 'lowers', 'beta-blocker'],
  ['propranolol', 'lowers', 'beta-blocker'], ['carvedilol', 'lowers', 'beta-blocker'],
  ['bisoprolol', 'lowers', 'beta-blocker'], ['labetalol', 'lowers', 'beta-blocker'], ['nadolol', 'lowers', 'beta-blocker'],
  // Calcium channel blockers
  ['amlodipine', 'lowers', 'calcium channel blocker'], ['nifedipine', 'lowers', 'calcium channel blocker'],
  ['diltiazem', 'lowers', 'calcium channel blocker'], ['verapamil', 'lowers', 'calcium channel blocker'],
  // Diuretics
  ['hydrochlorothiazide', 'lowers', 'diuretic'], ['hctz', 'lowers', 'diuretic'],
  ['chlorthalidone', 'lowers', 'diuretic'], ['furosemide', 'lowers', 'diuretic'],
  ['spironolactone', 'lowers', 'diuretic'], ['triamterene', 'lowers', 'diuretic'],
  // Other antihypertensives
  ['clonidine', 'lowers', 'antihypertensive'], ['hydralazine', 'lowers', 'antihypertensive'],
  ['doxazosin', 'lowers', 'alpha-blocker'], ['prazosin', 'lowers', 'alpha-blocker'], ['terazosin', 'lowers', 'alpha-blocker'],
  // Can RAISE BP
  ['ibuprofen', 'raises', 'NSAID'], ['naproxen', 'raises', 'NSAID'], ['celecoxib', 'raises', 'NSAID'],
  ['indomethacin', 'raises', 'NSAID'], ['meloxicam', 'raises', 'NSAID'], ['diclofenac', 'raises', 'NSAID'], ['ketorolac', 'raises', 'NSAID'],
  ['pseudoephedrine', 'raises', 'decongestant'], ['phenylephrine', 'raises', 'decongestant'],
  ['prednisone', 'raises', 'corticosteroid'], ['methylprednisolone', 'raises', 'corticosteroid'],
  ['amphetamine', 'raises', 'stimulant'], ['methylphenidate', 'raises', 'stimulant'], ['venlafaxine', 'raises', 'SNRI (dose-dependent)'],
];

// Suffix stems (catch generics not explicitly listed). Anchored to word end to limit false hits.
const STEMS = [
  [/\w{3,}pril\b/, 'lowers', 'ACE inhibitor'],
  [/\w{3,}sartan\b/, 'lowers', 'ARB'],
  [/\w{3,}olol\b/, 'lowers', 'beta-blocker'],
  [/\w{3,}dipine\b/, 'lowers', 'calcium channel blocker'],
];

// Classify one medication → { effect, klass } or null.
function classify(med) {
  const hay = `${(med.name || '')} ${(med.generic_name || '')}`.toLowerCase();
  for (const [name, effect, klass] of NAMED) {
    if (hay.includes(name)) return { effect, klass, matched: name };
  }
  for (const [re, effect, klass] of STEMS) {
    const m = hay.match(re);
    if (m) return { effect, klass, matched: m[0] };
  }
  return null;
}

// Given a list of active medication rows, return only the BP-relevant ones tagged.
function bpRelevant(meds) {
  const out = [];
  for (const m of meds) {
    const c = classify(m);
    if (c) out.push({ name: m.name, generic_name: m.generic_name, person: m.person_name || null, ...c });
  }
  return out;
}

module.exports = { classify, bpRelevant };