← back to AbramsOS

scripts/ndc-recall-check.js

87 lines

#!/usr/bin/env node
// NDC-PRECISE FDA recall match. The by-name pass is advisory ("this drug has had recalls");
// this pass matches each fill's ACTUAL dispensed NDC against openFDA enforcement records, so a
// flag means "your specific product/manufacturer is on a recall list" — the actionable signal.
//
// Our fills store 11-digit package NDCs ("00032-3016-13"); openFDA stores 10-digit product NDCs
// with leading zeros stripped ("0032-3016"). We query a couple of candidate formats, then CONFIRM
// each hit by comparing leading-zero-stripped labeler|product pairs — exact, conservative.
//
// openFDA = free ($0). Idempotent. Usage: node scripts/ndc-recall-check.js

const db = require('../lib/db');
const audit = require('../lib/audit');
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const USER = 'user_steve', SOURCE = 'shangoo-pharmacy-tax-2023-2026';
const BASE = 'https://api.fda.gov/drug/enforcement.json';
const KEY = process.env.OPENFDA_API_KEY ? `&api_key=${process.env.OPENFDA_API_KEY}` : '';

// leading-zero-stripped "labeler|product" from any NDC (ignores the package segment)
function segPair(ndc) {
  const p = String(ndc || '').split('-');
  if (p.length < 2 || !/^\d+$/.test(p[0]) || !/^\d+$/.test(p[1])) return null;
  return `${parseInt(p[0], 10)}|${parseInt(p[1], 10)}`;
}
// candidate product_ndc query strings for a stored 5-4-2 package NDC
function candidates(ndc) {
  const p = String(ndc).split('-'); if (p.length < 2) return [];
  const c = new Set([`${p[0]}-${p[1]}`]);              // as-stored labeler (e.g. 00032-3016)
  if (p[0][0] === '0') c.add(`${p[0].replace(/^0/, '')}-${p[1]}`); // one leading zero off (0032-3016)
  return [...c];
}
const sev = (c) => (c === 'Class I' ? 3 : c === 'Class II' ? 2 : c === 'Class III' ? 1 : 0);

async function main() {
  await db.query(`ALTER TABLE prescription_fill
    ADD COLUMN IF NOT EXISTS ndc_recalled boolean NOT NULL DEFAULT false,
    ADD COLUMN IF NOT EXISTS ndc_recall_number text,
    ADD COLUMN IF NOT EXISTS ndc_recall_class text,
    ADD COLUMN IF NOT EXISTS ndc_recall_reason text`);
  await db.query(`UPDATE prescription_fill SET ndc_recalled=false, ndc_recall_number=NULL, ndc_recall_class=NULL, ndc_recall_reason=NULL
                   WHERE user_id=$1 AND source=$2`, [USER, SOURCE]);

  const ndcs = (await db.query(
    `SELECT DISTINCT ndc FROM prescription_fill WHERE user_id=$1 AND source=$2
       AND ndc IS NOT NULL AND ndc<>'' AND ndc NOT LIKE '00000%'`, [USER, SOURCE])).rows.map((r) => r.ndc);
  console.log(`checking ${ndcs.length} distinct NDCs against openFDA (NDC-precise)…`);

  let flagged = 0, fillsFlagged = 0;
  for (const ndc of ndcs) {
    const want = segPair(ndc); if (!want) continue;
    let best = null;
    for (const cand of candidates(ndc)) {
      let results = [];
      try {
        const r = await fetch(`${BASE}?search=openfda.product_ndc:%22${encodeURIComponent(cand)}%22&limit=20${KEY}`,
          { headers: { 'User-Agent': 'AbramsOS/1.0 (personal household admin)' } });
        if (r.status === 404) { await sleep(180); continue; }
        if (r.ok) results = (await r.json()).results || [];
      } catch { /* transient */ }
      for (const rec of results) {
        const pn = (rec.openfda && rec.openfda.product_ndc) || [];
        if (!pn.some((x) => segPair(x) === want)) continue;   // CONFIRM exact labeler|product
        const cls = rec.classification || null;
        if (!best || sev(cls) > sev(best.classification)) best = rec;
      }
      await sleep(180);
    }
    if (best) {
      const up = await db.query(
        `UPDATE prescription_fill SET ndc_recalled=true, ndc_recall_number=$3, ndc_recall_class=$4, ndc_recall_reason=$5
          WHERE user_id=$1 AND source=$2 AND ndc=$6`,
        [USER, SOURCE, best.recall_number || null, best.classification || null,
         (best.reason_for_recall || '').slice(0, 300), ndc]);
      flagged++; fillsFlagged += up.rowCount;
      console.log(`  ⚠ NDC ${ndc} → ${best.classification} ${best.recall_number} (${up.rowCount} fills)`);
    }
  }

  await audit.log({ actorType: 'user', actorId: USER, objectType: 'prescription_fill', objectId: SOURCE,
    eventType: 'fda_ndc_recall_crossref', metadata: { medical: true, source: 'openFDA', ndcs: ndcs.length, ndcs_recalled: flagged, fills_flagged: fillsFlagged } });

  console.log(`\nNDC-precise DONE: ${flagged} of ${ndcs.length} NDCs on a recall list · ${fillsFlagged} fills flagged`);
  if (!flagged) console.log('  ✓ None of your actual dispensed products appear in an FDA recall.');
  process.exit(0);
}
main().catch((e) => { console.error('FAILED:', e.message); process.exit(1); });