File size: 8,384 Bytes
131a6ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""
Lab Report Decoder - Flask Application
Professional web interface for lab report analysis
Fixed for Hugging Face Spaces stateless environment
"""

from flask import Flask, render_template, request, jsonify
from werkzeug.utils import secure_filename
import os
import tempfile
import secrets
import json
import uuid
from pdf_extractor import LabReportExtractor, LabResult
from rag_engine import LabReportRAG
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv('SECRET_KEY', secrets.token_hex(16))
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB max file size
app.config['UPLOAD_FOLDER'] = tempfile.gettempdir()

# Initialize RAG system (singleton)
rag_system = None

# In-memory storage for sessions (better than Flask sessions in HF Spaces)
session_storage = {}

def get_rag_system():
    """Lazy load RAG system"""
    global rag_system
    if rag_system is None:
        print("πŸ”„ Initializing RAG system...")
        rag_system = LabReportRAG()
        print("βœ… RAG system ready")
    return rag_system

@app.route('/')
def index():
    """Main page"""
    return render_template('index.html')

@app.route('/api/upload', methods=['POST'])
def upload_file():
    """Handle PDF upload and extraction"""
    try:
        if 'file' not in request.files:
            return jsonify({'error': 'No file provided'}), 400
        
        file = request.files['file']
        
        if file.filename == '':
            return jsonify({'error': 'No file selected'}), 400
        
        if not file.filename.lower().endswith('.pdf'):
            return jsonify({'error': 'Only PDF files are allowed'}), 400
        
        # Save file temporarily
        filename = secure_filename(file.filename)
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(filepath)
        
        try:
            # Extract lab results
            print("πŸ“„ Extracting lab results from PDF...")
            extractor = LabReportExtractor()
            results = extractor.extract_from_pdf(filepath)
            
            if not results:
                return jsonify({'error': 'No lab results found in PDF. Please make sure your PDF contains a valid lab report with test names, values, and reference ranges.'}), 400
            
            print(f"βœ… Extracted {len(results)} results")
            
            # Convert to JSON-serializable format
            results_data = [
                {
                    'test_name': r.test_name,
                    'value': r.value,
                    'unit': r.unit,
                    'reference_range': r.reference_range,
                    'status': r.status
                }
                for r in results
            ]
            
            # Generate a unique session ID
            session_id = str(uuid.uuid4())
            
            # Store results in memory with session ID
            session_storage[session_id] = {
                'results': results_data,
                'results_objects': results  # Store LabResult objects for later use
            }
            
            print(f"πŸ’Ύ Stored results in session: {session_id}")
            
            return jsonify({
                'success': True,
                'session_id': session_id,
                'results': results_data,
                'count': len(results_data)
            })
        
        finally:
            # Clean up temp file
            if os.path.exists(filepath):
                os.remove(filepath)
    
    except Exception as e:
        print(f"❌ Upload error: {str(e)}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/explain', methods=['POST'])
def explain_results():
    """Generate explanations for lab results"""
    try:
        data = request.get_json()
        session_id = data.get('session_id')
        
        if not session_id or session_id not in session_storage:
            return jsonify({'error': 'Session expired. Please upload your PDF again.'}), 400
        
        # Get results from storage
        session_data = session_storage[session_id]
        results = session_data['results_objects']
        
        print(f"🧠 Generating explanations for {len(results)} results...")
        
        # Generate explanations
        rag = get_rag_system()
        explanations = {}
        
        for i, result in enumerate(results):
            print(f"  Explaining {i+1}/{len(results)}: {result.test_name}...")
            try:
                explanation = rag.explain_result(result)
                explanations[result.test_name] = explanation
            except Exception as e:
                print(f"    Error: {str(e)}")
                explanations[result.test_name] = f"Unable to generate explanation: {str(e)}"
        
        # Store explanations in session
        session_storage[session_id]['explanations'] = explanations
        
        print("βœ… All explanations generated")
        
        return jsonify({
            'success': True,
            'explanations': explanations
        })
    
    except Exception as e:
        print(f"❌ Explanation error: {str(e)}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/ask', methods=['POST'])
def ask_question():
    """Answer follow-up questions"""
    try:
        data = request.get_json()
        question = data.get('question', '').strip()
        session_id = data.get('session_id')
        
        if not question:
            return jsonify({'error': 'No question provided'}), 400
        
        if not session_id or session_id not in session_storage:
            return jsonify({'error': 'Session expired. Please upload your PDF again.'}), 400
        
        # Get results from storage
        session_data = session_storage[session_id]
        results = session_data['results_objects']
        
        print(f"πŸ’¬ Answering question: {question}")
        
        # Get answer
        rag = get_rag_system()
        answer = rag.answer_followup_question(question, results)
        
        print("βœ… Answer generated")
        
        return jsonify({
            'success': True,
            'question': question,
            'answer': answer
        })
    
    except Exception as e:
        print(f"❌ Question error: {str(e)}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/summary', methods=['POST'])
def get_summary():
    """Generate overall summary"""
    try:
        data = request.get_json()
        session_id = data.get('session_id')
        
        if not session_id or session_id not in session_storage:
            return jsonify({'error': 'Session expired. Please upload your PDF again.'}), 400
        
        # Get results from storage
        session_data = session_storage[session_id]
        results = session_data['results_objects']
        
        print("πŸ“Š Generating summary...")
        
        # Generate summary
        rag = get_rag_system()
        summary = rag.generate_summary(results)
        
        # Calculate statistics
        stats = {
            'total': len(results),
            'normal': sum(1 for r in results if r.status == 'normal'),
            'high': sum(1 for r in results if r.status == 'high'),
            'low': sum(1 for r in results if r.status == 'low'),
            'unknown': sum(1 for r in results if r.status == 'unknown')
        }
        
        print(f"βœ… Summary generated - Stats: {stats}")
        
        return jsonify({
            'success': True,
            'summary': summary,
            'stats': stats
        })
    
    except Exception as e:
        print(f"❌ Summary error: {str(e)}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/health', methods=['GET'])
def health_check():
    """Health check endpoint"""
    return jsonify({
        'status': 'healthy',
        'active_sessions': len(session_storage)
    })

@app.errorhandler(413)
def request_entity_too_large(error):
    return jsonify({'error': 'File too large. Maximum size is 16MB.'}), 413

@app.errorhandler(500)
def internal_error(error):
    return jsonify({'error': 'Internal server error'}), 500

if __name__ == '__main__':
    print("πŸš€ Starting Lab Report Decoder...")
    print("πŸ“ Server will be available at http://0.0.0.0:7860")
    app.run(debug=True, host='0.0.0.0', port=7860)