// ============================================================ // WELL VISIT ROUTES — SSHADESS assessment + note generation // ============================================================ var express = require('express'); var router = express.Router(); var { callAI } = require('../utils/ai'); var PROMPTS = require('../utils/prompts'); var { authMiddleware } = require('../middleware/auth'); var logger = require('../utils/logger'); // Load growth reference and BMI classification data var scheduleData; try { scheduleData = require('../../public/js/pediatricScheduleData'); } catch(e) { scheduleData = {}; } var GROWTH_REFERENCE = scheduleData.GROWTH_REFERENCE || {}; var BMI_CLASSIFICATION = scheduleData.BMI_CLASSIFICATION || {}; // Map visit age text to GROWTH_REFERENCE keys function getGrowthRefForAge(ageStr) { if (!ageStr) return null; var a = ageStr.toLowerCase().trim(); // Direct key matches var directKeys = ['newborn', '1mo', '2mo', '4mo', '6mo', '9mo', '12mo', '15mo', '18mo', '24mo', '30mo', '3y', '4y', '5y']; for (var i = 0; i < directKeys.length; i++) { if (a.indexOf(directKeys[i]) !== -1) return GROWTH_REFERENCE[directKeys[i]] || null; } // Age-based range matching var ageMatch = a.match(/(\d+)\s*(month|mo|year|y|yr)/i); if (ageMatch) { var num = parseInt(ageMatch[1]); var unit = ageMatch[2].toLowerCase(); if (unit === 'month' || unit === 'mo') { if (num <= 1) return GROWTH_REFERENCE['1mo']; if (num <= 3) return GROWTH_REFERENCE['2mo']; if (num <= 5) return GROWTH_REFERENCE['4mo']; if (num <= 8) return GROWTH_REFERENCE['6mo']; if (num <= 11) return GROWTH_REFERENCE['9mo']; if (num <= 14) return GROWTH_REFERENCE['12mo']; if (num <= 17) return GROWTH_REFERENCE['15mo']; if (num <= 23) return GROWTH_REFERENCE['18mo']; if (num <= 29) return GROWTH_REFERENCE['24mo']; if (num <= 35) return GROWTH_REFERENCE['30mo']; } if (unit === 'year' || unit === 'y' || unit === 'yr') { if (num <= 3) return GROWTH_REFERENCE['3y']; if (num <= 4) return GROWTH_REFERENCE['4y']; if (num <= 5) return GROWTH_REFERENCE['5y']; if (num <= 10) return GROWTH_REFERENCE['6y_to_10y']; if (num <= 14) return GROWTH_REFERENCE['11y_to_14y']; return GROWTH_REFERENCE['15y_to_21y']; } } if (a.indexOf('newborn') !== -1 || a.indexOf('birth') !== -1) return GROWTH_REFERENCE['newborn']; return null; } // ── POST generate SSHADESS assessment summary ──────────────────────────── router.post('/well-visit/shadess', authMiddleware, async function(req, res) { var start = Date.now(); try { var { domains, patientAge, patientGender, dictationText, model } = req.body; if (!domains && !dictationText) { return res.status(400).json({ error: 'Provide domain responses or dictation text' }); } // Build structured input text var inputText = 'PATIENT: ' + (patientAge || 'Adolescent') + ', ' + (patientGender || 'Unknown gender') + '\n\n'; inputText += 'SSHADESS SCREENING DATA:\n\n'; if (dictationText) { inputText += 'PHYSICIAN DICTATION:\n' + dictationText + '\n\n'; } if (domains) { var domainOrder = ['strengths', 'school', 'home', 'activities', 'drugs', 'emotions', 'sexuality', 'safety']; var domainNames = { strengths: 'STRENGTHS', school: 'SCHOOL', home: 'HOME', activities: 'ACTIVITIES', drugs: 'DRUGS/SUBSTANCES', emotions: 'EMOTIONS/EATING', sexuality: 'SEXUALITY', safety: 'SAFETY' }; domainOrder.forEach(function(key) { var d = domains[key]; if (!d || d.skipped) return; inputText += domainNames[key] + ':\n'; if (d.questions) { d.questions.forEach(function(q) { if (q.answer !== null && q.answer !== undefined && q.answer !== '') { inputText += ' - ' + q.text + ': ' + (q.answer === true || q.answer === 'yes' ? 'YES' : (q.answer === false || q.answer === 'no' ? 'NO' : q.answer)) + '\n'; } }); } if (d.comment && d.comment.trim()) { inputText += ' Comments: ' + d.comment + '\n'; } if (d.concern) { inputText += ' *** CONCERN FLAGGED ***\n'; } inputText += '\n'; }); } var result = await callAI([ { role: 'system', content: PROMPTS.shadessAssessment }, { role: 'user', content: inputText } ], { model }); var dur = Date.now() - start; logger.apiCall(req.user.id, '/api/well-visit/shadess', { model: result.model, tokensInput: result.usage?.input_tokens, tokensOutput: result.usage?.output_tokens, duration: dur, statusCode: 200 }); res.json({ success: true, assessment: result.content, model: result.model }); } catch (e) { logger.error('[WellVisit] SHADESS generation failed', e.message); res.status(500).json({ error: e.message }); } }); // ── POST generate full well visit encounter note ───────────────────────── router.post('/well-visit/note', authMiddleware, async function(req, res) { var start = Date.now(); try { var { patientAge, patientGender, visitAge, vitals, measurements, transcript, dictation, screenings, vaccines, shadessAssessment, parentConcerns, ros, physicalExam, diagnoses, physicianMemories, noteStyle, // 'full' | 'short' model } = req.body; if (!patientAge && !visitAge) { return res.status(400).json({ error: 'Patient age or visit age required' }); } // Assemble context var context = 'WELL CHILD VISIT\n'; context += 'Patient: ' + (patientAge || visitAge) + ', ' + (patientGender || 'Unknown') + '\n\n'; if (vitals) { context += 'VITAL SIGNS:\n' + vitals + '\n\n'; } if (measurements) { context += 'MEASUREMENTS/GROWTH:\n' + measurements + '\n\n'; } if (parentConcerns) { context += 'PARENT/PATIENT CONCERNS:\n' + parentConcerns + '\n\n'; } if (transcript) { context += 'ENCOUNTER TRANSCRIPT/DICTATION:\n' + transcript + '\n\n'; } else if (dictation) { context += 'PHYSICIAN DICTATION:\n' + dictation + '\n\n'; } if (screenings) { context += 'SCREENINGS COMPLETED:\n' + screenings + '\n\n'; } if (vaccines) { context += 'IMMUNIZATIONS TODAY:\n' + vaccines + '\n\n'; } if (shadessAssessment) { context += 'SSHADESS PSYCHOSOCIAL ASSESSMENT:\n' + shadessAssessment + '\n\n'; } if (ros) { context += ros + '\n\n'; } if (physicalExam) { context += physicalExam + '\n\n'; } if (diagnoses) { context += diagnoses + '\n\n'; } if (physicianMemories) { context += physicianMemories + '\n\n'; } // Add growth reference and feeding guidance for this age var growthRef = getGrowthRefForAge(patientAge || visitAge); if (growthRef) { context += 'GROWTH/NUTRITION GUIDANCE (AAP reference for this age):\n'; if (growthRef.weight) context += 'Expected weight gain: ' + growthRef.weight + '\n'; if (growthRef.length) context += 'Expected length/height gain: ' + growthRef.length + '\n'; if (growthRef.headCirc) context += 'Expected head circumference gain: ' + growthRef.headCirc + '\n'; if (growthRef.feeding && growthRef.feeding.length) { context += 'Feeding guidance:\n'; growthRef.feeding.forEach(function(f) { context += ' - ' + f + '\n'; }); } if (growthRef.bmiClassification && BMI_CLASSIFICATION.categories) { context += 'BMI Classification (AAP 2023 / CDC Extended BMI):\n'; BMI_CLASSIFICATION.categories.forEach(function(c) { context += ' - ' + c.label + ': ' + c.range + ' → ' + c.action + '\n'; }); } context += '\n'; } var prompt = (noteStyle === 'short') ? PROMPTS.wellVisitShort : PROMPTS.wellVisitNote; var result = await callAI([ { role: 'system', content: prompt }, { role: 'user', content: context } ], { model }); var dur = Date.now() - start; logger.apiCall(req.user.id, '/api/well-visit/note', { model: result.model, tokensInput: result.usage?.input_tokens, tokensOutput: result.usage?.output_tokens, duration: dur, statusCode: 200 }); res.json({ success: true, note: result.content, model: result.model }); } catch (e) { logger.error('[WellVisit] Note generation failed', e.message); res.status(500).json({ error: e.message }); } }); module.exports = router;