Team Ai
Apppublic

Hanan-Alnakhal/Lab-test-decoder

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
app.py265 linesDownload Raw Back to root
1"""2Lab Report Decoder - Flask Application3Professional web interface for lab report analysis4Fixed for Hugging Face Spaces stateless environment5"""6 7from flask import Flask, render_template, request, jsonify8from werkzeug.utils import secure_filename9import os10import tempfile11import secrets12import json13import uuid14from pdf_extractor import LabReportExtractor, LabResult15from rag_engine import LabReportRAG16from dotenv import load_dotenv17 18load_dotenv()19 20app = Flask(__name__)21app.secret_key = os.getenv('SECRET_KEY', secrets.token_hex(16))22app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB max file size23app.config['UPLOAD_FOLDER'] = "/tmp"24 25# Initialize RAG system (singleton)26rag_system = None27 28# In-memory storage for sessions (better than Flask sessions in HF Spaces)29session_storage = {}30 31def get_rag_system():32    """Lazy load RAG system"""33    global rag_system34    if rag_system is None:35        print("๐Ÿ”„ Initializing RAG system...")36        rag_system = LabReportRAG()37        print("โœ… RAG system ready")38    return rag_system39 40@app.route('/')41def index():42    """Main page"""43    return render_template('index.html')44 45@app.route('/api/upload', methods=['POST'])46def upload_file():47    """Handle PDF upload and extraction"""48    try:49        if 'file' not in request.files:50            return jsonify({'error': 'No file provided'}), 40051        52        file = request.files['file']53        54        if file.filename == '':55            return jsonify({'error': 'No file selected'}), 40056        57        if not file.filename.lower().endswith('.pdf'):58            return jsonify({'error': 'Only PDF files are allowed'}), 40059        60        # Save file temporarily61        filename = secure_filename(file.filename)62        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)63        file.save(filepath)64        65        try:66            # Extract lab results67            print("๐Ÿ“„ Extracting lab results from PDF...")68            extractor = LabReportExtractor()69            results = extractor.extract_from_pdf(filepath)70            71            if not results:72                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.'}), 40073            74            print(f"โœ… Extracted {len(results)} results")75            76            # Convert to JSON-serializable format77            results_data = [78                {79                    'test_name': r.test_name,80                    'value': r.value,81                    'unit': r.unit,82                    'reference_range': r.reference_range,83                    'status': r.status84                }85                for r in results86            ]87            88            # Generate a unique session ID89            session_id = str(uuid.uuid4())90            91            # Store results in memory with session ID92            session_storage[session_id] = {93                'results': results_data,94                'results_objects': results  # Store LabResult objects for later use95            }96            97            print(f"๐Ÿ’พ Stored results in session: {session_id}")98            99            return jsonify({100                'success': True,101                'session_id': session_id,102                'results': results_data,103                'count': len(results_data)104            })105        106        finally:107            # Clean up temp file108            if os.path.exists(filepath):109                os.remove(filepath)110    111    except Exception as e:112        print(f"โŒ Upload error: {str(e)}")113        return jsonify({'error': str(e)}), 500114 115@app.route('/api/explain', methods=['POST'])116def explain_results():117    """Generate explanations for lab results"""118    try:119        data = request.get_json()120        session_id = data.get('session_id')121        122        if not session_id or session_id not in session_storage:123            return jsonify({'error': 'Session expired. Please upload your PDF again.'}), 400124        125        # Get results from storage126        session_data = session_storage[session_id]127        results = session_data['results_objects']128        129        print(f"๐Ÿง  Generating explanations for {len(results)} results...")130        131        # Initialize RAG system132        rag = get_rag_system()133        134        # Generate explanations with timeout protection135        from concurrent.futures import TimeoutError136        import signal137        138        try:139            explanations = rag.explain_all_results(results)140        except Exception as e:141            print(f"โš ๏ธ Explanation generation error: {str(e)}")142            # Provide basic explanations as fallback143            explanations = {}144            for result in results:145                status_msg = {146                    'normal': f"โœ… Your {result.test_name} is within normal range.",147                    'high': f"โš ๏ธ Your {result.test_name} is above normal range. Consult your doctor.",148                    'low': f"โš ๏ธ Your {result.test_name} is below normal range. Consult your doctor.",149                    'unknown': f"Your {result.test_name} result is {result.value} {result.unit}."150                }151                explanations[result.test_name] = status_msg.get(result.status, "Result recorded.")152        153        # Store explanations in session154        session_storage[session_id]['explanations'] = explanations155        156        print(f"โœ… Generated {len(explanations)} explanations")157        158        return jsonify({159            'success': True,160            'explanations': explanations161        })162    163    except Exception as e:164        print(f"โŒ Explanation error: {str(e)}")165        import traceback166        traceback.print_exc()167        return jsonify({'error': 'Error generating explanations. Please try uploading again.'}), 500168 169@app.route('/api/ask', methods=['POST'])170def ask_question():171    """Answer follow-up questions"""172    try:173        data = request.get_json()174        question = data.get('question', '').strip()175        session_id = data.get('session_id')176        177        if not question:178            return jsonify({'error': 'No question provided'}), 400179        180        if not session_id or session_id not in session_storage:181            return jsonify({'error': 'Session expired. Please upload your PDF again.'}), 400182        183        # Get results from storage184        session_data = session_storage[session_id]185        results = session_data['results_objects']186        187        print(f"๐Ÿ’ฌ Answering question: {question}")188        189        # Get answer190        rag = get_rag_system()191        answer = rag.answer_followup_question(question, results)192        193        print("โœ… Answer generated")194        195        return jsonify({196            'success': True,197            'question': question,198            'answer': answer199        })200    201    except Exception as e:202        print(f"โŒ Question error: {str(e)}")203        return jsonify({'error': str(e)}), 500204 205@app.route('/api/summary', methods=['POST'])206def get_summary():207    """Generate overall summary"""208    try:209        data = request.get_json()210        session_id = data.get('session_id')211        212        if not session_id or session_id not in session_storage:213            return jsonify({'error': 'Session expired. Please upload your PDF again.'}), 400214        215        # Get results from storage216        session_data = session_storage[session_id]217        results = session_data['results_objects']218        219        print("๐Ÿ“Š Generating summary...")220        221        # Generate summary222        rag = get_rag_system()223        summary = rag.generate_summary(results)224        225        # Calculate statistics226        stats = {227            'total': len(results),228            'normal': sum(1 for r in results if r.status == 'normal'),229            'high': sum(1 for r in results if r.status == 'high'),230            'low': sum(1 for r in results if r.status == 'low'),231            'unknown': sum(1 for r in results if r.status == 'unknown')232        }233        234        print(f"โœ… Summary generated - Stats: {stats}")235        236        return jsonify({237            'success': True,238            'summary': summary,239            'stats': stats240        })241    242    except Exception as e:243        print(f"โŒ Summary error: {str(e)}")244        return jsonify({'error': str(e)}), 500245 246@app.route('/api/health', methods=['GET'])247def health_check():248    """Health check endpoint"""249    return jsonify({250        'status': 'healthy',251        'active_sessions': len(session_storage)252    })253 254@app.errorhandler(413)255def request_entity_too_large(error):256    return jsonify({'error': 'File too large. Maximum size is 16MB.'}), 413257 258@app.errorhandler(500)259def internal_error(error):260    return jsonify({'error': 'Internal server error'}), 500261 262if __name__ == '__main__':263    print("๐Ÿš€ Starting Lab Report Decoder...")264    print("๐Ÿ“ Server will be available at http://0.0.0.0:7860")265    app.run(debug=True, host='0.0.0.0', port=7860)