Hanan-Alnakhal's picture
Update app.py
131a6ac verified
raw
history blame
8.38 kB
"""
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)