File size: 5,725 Bytes
cd5e33d
5266fc5
 
cd5e33d
 
 
 
5266fc5
 
cd5e33d
 
 
 
 
 
5266fc5
 
cd5e33d
 
 
 
 
 
5266fc5
 
cd5e33d
 
 
 
 
 
5266fc5
 
 
 
 
cd5e33d
5266fc5
 
 
 
cd5e33d
 
 
 
 
 
 
 
5266fc5
 
 
cd5e33d
 
 
 
 
 
5266fc5
cd5e33d
 
 
 
 
 
 
5266fc5
cd5e33d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5266fc5
cd5e33d
5266fc5
cd5e33d
5266fc5
 
 
 
 
 
 
 
 
cd5e33d
5266fc5
 
cd5e33d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5266fc5
cd5e33d
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import { generateResponse } from './llmClient.js';
import Warning, { WarningType, WarningSeverity } from '../models/Warning.js';
import Patient from '../models/Patient.js';
import dotenv from 'dotenv';

dotenv.config();

// Re-export enums for backward compatibility
export { WarningType, WarningSeverity };

class WarningService {
  /**
   * Get all warnings for a patient
   */
  async getPatientWarnings(patientId, includeAcknowledged = false) {
    const warnings = await Warning.findByPatient(patientId, includeAcknowledged);
    return warnings.map(w => w.toResponse());
  }
  
  /**
   * Get all unacknowledged warnings across all patients
   */
  async getAllUnacknowledgedWarnings(limit = 50) {
    const warnings = await Warning.findUnacknowledged(limit);
    return warnings.map(w => w.toResponse());
  }
  
  /**
   * Acknowledge a clinical warning
   */
  async acknowledgeWarning(warningId, userId, notes = null) {
    try {
      const warning = await Warning.findById(warningId);
      
      if (!warning) {
        return null;
      }
      
      await warning.acknowledge(userId, notes);
      return warning.toResponse();
    } catch {
      return null;
    }
  }
  
  /**
   * Create a new clinical warning
   */
  async createWarning(warningData) {
    const warning = new Warning(warningData);
    await warning.save();
    return warning.toResponse();
  }
  
  /**
   * Delete all warnings for a patient
   */
  async deletePatientWarnings(patientId) {
    const result = await Warning.deleteMany({ patient_id: patientId });
    return result.deletedCount;
  }
  
  /**
   * Use AI to analyze patient data and generate clinical warnings
   */
  async analyzePatientForWarnings(patientId, newMedication = null, newCondition = null) {
    const patient = await Patient.findById(patientId);
    
    if (!patient) {
      return [];
    }
    
    // Build the analysis prompt
    const medications = patient.medications || [];
    const medNames = medications
      .filter(m => typeof m === 'object')
      .map(m => m.name || '');
    
    let conditions = patient.medical_history || [];
    
    if (newMedication) {
      medNames.push(`${newMedication} (NEW)`);
    }
    if (newCondition) {
      conditions = [...conditions, `${newCondition} (NEW)`];
    }
    
    const conditionsText = conditions.map(c => {
      if (typeof c === 'string') return c;
      return c.condition || 'Unknown';
    }).join(', ');
    
    const prompt = `You are a clinical decision support AI. Analyze this patient's data for potential clinical warnings.

PATIENT INFORMATION:
- Age: ${patient.age || 'Unknown'}
- Gender: ${patient.gender || 'Unknown'}
- Medical Conditions: ${conditionsText || 'None recorded'}
- Current Medications: ${medNames.join(', ') || 'None recorded'}

Analyze for:
1. DRUG INTERACTIONS - Check if any medications interact negatively with each other
2. CONTRAINDICATIONS - Check if any medications are contraindicated given the patient's conditions
3. ALLERGY RISKS - Common allergy cross-reactions
4. DOSAGE CONCERNS - Age-related dosage considerations
5. DUPLICATE THERAPY - Multiple drugs for the same purpose

Return a JSON array of warnings. Each warning should have:
- warning_type: one of "drug_interaction", "allergy", "contraindication", "abnormal_pattern", "dosage_alert", "duplicate_therapy"
- severity: one of "low", "medium", "high", "critical"
- title: short title (max 50 chars)
- description: detailed explanation
- related_medications: array of medication names involved
- related_conditions: array of conditions involved
- recommendation: suggested action

If there are no warnings, return an empty array [].
Return ONLY valid JSON, no markdown or other text.`;

    try {
      let responseText = await generateResponse(prompt);
      responseText = responseText.trim();
      
      // Remove markdown code blocks if present
      if (responseText.startsWith('```')) {
        responseText = responseText.split('```')[1];
        if (responseText.startsWith('json')) {
          responseText = responseText.substring(4);
        }
      }
      responseText = responseText.trim();
      
      const warningsData = JSON.parse(responseText);
      
      if (!Array.isArray(warningsData)) {
        return [];
      }
      
      // Create warnings in database
      const createdWarnings = [];
      for (const warningData of warningsData) {
        try {
          const warning = new Warning({
            patient_id: patientId,
            warning_type: warningData.warning_type || 'general',
            severity: warningData.severity || 'medium',
            title: (warningData.title || 'Clinical Warning').substring(0, 100),
            description: warningData.description || '',
            related_medications: warningData.related_medications || [],
            related_conditions: warningData.related_conditions || [],
            recommendations: warningData.recommendation ? [warningData.recommendation] : [],
            source: 'ai_analysis'
          });
          
          await warning.save();
          createdWarnings.push(warning.toResponse());
        } catch (error) {
          console.log(`Error creating warning: ${error.message}`);
          continue;
        }
      }
      
      return createdWarnings;
    } catch (error) {
      if (error instanceof SyntaxError) {
        console.log(`JSON parse error: ${error.message}`);
      } else {
        console.log(`Error analyzing patient: ${error.message}`);
      }
      return [];
    }
  }
  
  /**
   * Get warning statistics
   */
  async getWarningStats() {
    return await Warning.getStats();
  }
}

// Singleton instance
export const warningService = new WarningService();