Tantawi65 commited on
Commit
7a00767
Β·
1 Parent(s): 57cb63a

Converted to pure FastAPI with beautiful web interface - Flutter compatible

Browse files
Files changed (4) hide show
  1. Dockerfile +4 -5
  2. README.md +80 -21
  3. app.py +446 -145
  4. requirements.txt +3 -4
Dockerfile CHANGED
@@ -8,7 +8,6 @@ WORKDIR /app
8
  RUN apt-get update && apt-get install -y \
9
  build-essential \
10
  curl \
11
- software-properties-common \
12
  && rm -rf /var/lib/apt/lists/*
13
 
14
  # Copy requirements and install Python dependencies
@@ -18,11 +17,11 @@ RUN pip install --no-cache-dir -r requirements.txt
18
  # Copy application files
19
  COPY . .
20
 
21
- # Expose port
22
  EXPOSE 7860
23
 
24
  # Health check
25
- HEALTHCHECK CMD curl --fail http://localhost:7860/_stcore/health
26
 
27
- # Run the application
28
- CMD ["python", "app.py"]
 
8
  RUN apt-get update && apt-get install -y \
9
  build-essential \
10
  curl \
 
11
  && rm -rf /var/lib/apt/lists/*
12
 
13
  # Copy requirements and install Python dependencies
 
17
  # Copy application files
18
  COPY . .
19
 
20
+ # Expose port 7860 (Hugging Face Spaces default)
21
  EXPOSE 7860
22
 
23
  # Health check
24
+ HEALTHCHECK CMD curl --fail http://localhost:7860/health
25
 
26
+ # Run the FastAPI application
27
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,19 +1,18 @@
1
  ---
2
- title: Lab Report Analysis AI
3
  emoji: πŸ₯
4
  colorFrom: blue
5
  colorTo: green
6
- sdk: gradio
7
- sdk_version: 4.44.0
8
- app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
- short_description: AI-powered lab report analysis using Google AI Studio
12
  ---
13
 
14
- # πŸ₯ Lab Report Analysis AI
15
 
16
- An intelligent lab report analysis system that uses Google AI Studio's Gemini model to analyze medical lab reports and provide structured interpretations.
17
 
18
  ## ✨ Features
19
 
@@ -22,26 +21,65 @@ An intelligent lab report analysis system that uses Google AI Studio's Gemini mo
22
  - πŸ“Š **Structured Output**: Provides organized summary, key findings, and interpretations
23
  - ⚑ **Fast Processing**: Quick analysis with real-time results
24
  - πŸ”’ **Secure**: Images are processed securely and not stored
25
- - 🌐 **Web Interface**: Easy-to-use Gradio interface
 
26
 
27
  ## πŸš€ How to Use
28
 
29
- 1. **Upload Image**: Click on the upload area and select your lab report image
30
- 2. **Analyze**: Click the "Analyze Report" button
31
- 3. **Review Results**: Get structured analysis with:
32
- - Summary of the report
33
- - Key findings and abnormal values
34
- - Medical interpretation
35
- - Important disclaimers
 
 
 
 
 
 
 
36
 
37
  ## πŸ› οΈ Technology Stack
38
 
39
- - **Frontend**: Gradio for interactive web interface
40
- - **Backend**: FastAPI for robust API handling
41
  - **AI Model**: Google AI Studio (Gemini 2.0 Flash)
42
  - **Image Processing**: PIL for image handling
43
  - **Deployment**: Docker containerization for Hugging Face Spaces
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ## ⚠️ Important Disclaimer
46
 
47
  **This tool is for educational and informational purposes only. It should not be used as a substitute for professional medical advice, diagnosis, or treatment. Always consult with qualified healthcare professionals for medical concerns.**
@@ -66,14 +104,23 @@ An intelligent lab report analysis system that uses Google AI Studio's Gemini mo
66
  ```
67
  4. Run the application:
68
  ```bash
69
- python app.py
70
  ```
71
 
 
 
 
 
 
 
 
 
 
 
72
  ## πŸ“ Project Structure
73
 
74
  ```
75
- β”œβ”€β”€ app.py # Main Gradio application
76
- β”œβ”€β”€ main.py # FastAPI server
77
  β”œβ”€β”€ lab_analyzer.py # Core analysis logic
78
  β”œβ”€β”€ models.py # Data models
79
  β”œβ”€β”€ requirements.txt # Python dependencies
@@ -81,8 +128,20 @@ An intelligent lab report analysis system that uses Google AI Studio's Gemini mo
81
  └── README.md # Documentation
82
  ```
83
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  ## πŸ™ Acknowledgments
85
 
86
  - Google AI Studio for providing the Gemini API
87
  - Hugging Face for Spaces hosting
88
- - Gradio for the web interface framework
 
1
  ---
2
+ title: Lab Report Analysis API
3
  emoji: πŸ₯
4
  colorFrom: blue
5
  colorTo: green
6
+ sdk: docker
7
+ app_port: 7860
 
8
  pinned: false
9
  license: apache-2.0
10
+ short_description: AI-powered lab report analysis API using Google AI Studio
11
  ---
12
 
13
+ # πŸ₯ Lab Report Analysis API
14
 
15
+ A powerful FastAPI-based web service for analyzing medical lab reports using Google AI Studio's Gemini model. Provides both a beautiful web interface and REST API endpoints for programmatic access.
16
 
17
  ## ✨ Features
18
 
 
21
  - πŸ“Š **Structured Output**: Provides organized summary, key findings, and interpretations
22
  - ⚑ **Fast Processing**: Quick analysis with real-time results
23
  - πŸ”’ **Secure**: Images are processed securely and not stored
24
+ - 🌐 **Web Interface**: Beautiful HTML interface for manual testing
25
+ - πŸ“± **Flutter Compatible**: Full REST API support for mobile apps
26
 
27
  ## πŸš€ How to Use
28
 
29
+ ### Web Interface
30
+ 1. Visit the main page
31
+ 2. **Upload Image**: Click on the upload area and select your lab report image
32
+ 3. **Analyze**: Click the "Analyze Report" button
33
+ 4. **Review Results**: Get structured analysis with summary, key findings, and interpretations
34
+
35
+ ### API Endpoints
36
+ - **POST** `/analyze` - Upload file analysis
37
+ - **POST** `/analyze-base64` - Base64 image analysis
38
+ - **POST** `/api/analyze-lab` - Flutter-friendly file upload
39
+ - **POST** `/api/analyze-lab-base64` - Flutter-friendly base64 analysis
40
+ - **GET** `/health` - Health check endpoint
41
+ - **GET** `/docs` - Interactive API documentation
42
+ - **GET** `/redoc` - ReDoc documentation
43
 
44
  ## πŸ› οΈ Technology Stack
45
 
46
+ - **Backend**: FastAPI for high-performance API
47
+ - **Frontend**: Custom HTML/CSS/JavaScript interface
48
  - **AI Model**: Google AI Studio (Gemini 2.0 Flash)
49
  - **Image Processing**: PIL for image handling
50
  - **Deployment**: Docker containerization for Hugging Face Spaces
51
 
52
+ ## πŸ“± Flutter Integration
53
+
54
+ Perfect for Flutter apps! All endpoints return consistent JSON responses:
55
+
56
+ ```dart
57
+ // File upload
58
+ POST /api/analyze-lab
59
+ Content-Type: multipart/form-data
60
+
61
+ // Base64 upload
62
+ POST /api/analyze-lab-base64
63
+ Content-Type: application/json
64
+ {
65
+ "image": "base64_encoded_image_string"
66
+ }
67
+
68
+ // Response format
69
+ {
70
+ "success": true,
71
+ "filename": "lab_report.jpg",
72
+ "analysis": {
73
+ "error": false,
74
+ "summary": "Analysis summary",
75
+ "key_findings": ["Finding 1", "Finding 2"],
76
+ "interpretation": "Medical interpretation",
77
+ "note": "Disclaimer about medical advice",
78
+ "raw_response": "Full AI response"
79
+ }
80
+ }
81
+ ```
82
+
83
  ## ⚠️ Important Disclaimer
84
 
85
  **This tool is for educational and informational purposes only. It should not be used as a substitute for professional medical advice, diagnosis, or treatment. Always consult with qualified healthcare professionals for medical concerns.**
 
104
  ```
105
  4. Run the application:
106
  ```bash
107
+ uvicorn app:app --host 0.0.0.0 --port 7860 --reload
108
  ```
109
 
110
+ ### Docker Development
111
+
112
+ ```bash
113
+ # Build the image
114
+ docker build -t lab-analysis-api .
115
+
116
+ # Run the container
117
+ docker run -p 7860:7860 -e GOOGLE_AI_STUDIO_API_KEY=your_key lab-analysis-api
118
+ ```
119
+
120
  ## πŸ“ Project Structure
121
 
122
  ```
123
+ β”œβ”€β”€ app.py # Main FastAPI application with web interface
 
124
  β”œβ”€β”€ lab_analyzer.py # Core analysis logic
125
  β”œβ”€β”€ models.py # Data models
126
  β”œβ”€β”€ requirements.txt # Python dependencies
 
128
  └── README.md # Documentation
129
  ```
130
 
131
+ ## 🎯 Endpoints Overview
132
+
133
+ | Method | Endpoint | Description | Use Case |
134
+ |--------|----------|-------------|----------|
135
+ | GET | `/` | Web interface | Manual testing |
136
+ | POST | `/analyze` | File upload analysis | General API usage |
137
+ | POST | `/analyze-base64` | Base64 analysis | Web applications |
138
+ | POST | `/api/analyze-lab` | Flutter file upload | Mobile apps |
139
+ | POST | `/api/analyze-lab-base64` | Flutter base64 | Mobile apps |
140
+ | GET | `/health` | Health check | Monitoring |
141
+ | GET | `/docs` | OpenAPI docs | API reference |
142
+
143
  ## πŸ™ Acknowledgments
144
 
145
  - Google AI Studio for providing the Gemini API
146
  - Hugging Face for Spaces hosting
147
+ - FastAPI for the excellent web framework
app.py CHANGED
@@ -1,177 +1,478 @@
1
- import gradio as gr
 
 
 
2
  import base64
3
- import asyncio
4
- import os
5
- from lab_analyzer import LabReportAnalyzer
6
- from PIL import Image
7
  import io
 
 
8
  import logging
 
9
 
10
  # Configure logging
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # Initialize the lab analyzer
15
  analyzer = LabReportAnalyzer()
16
 
17
- async def analyze_lab_report(image):
 
 
 
 
 
 
18
  """
19
- Analyze a lab report image using the LabReportAnalyzer
20
 
21
  Args:
22
- image: PIL Image from Gradio interface
23
-
24
  Returns:
25
- Formatted analysis results
26
  """
27
  try:
28
- if image is None:
29
- return "❌ Please upload an image first."
 
 
 
 
30
 
31
- # Convert PIL image to base64
32
- buffer = io.BytesIO()
33
- image.save(buffer, format="JPEG")
34
- image_b64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
 
 
 
 
 
 
 
 
 
 
35
 
36
  # Analyze the lab report
 
37
  analysis_result = await analyzer.analyze_report(image_b64)
38
 
39
- if analysis_result.get("error", False):
40
- return f"❌ Error: {analysis_result.get('message', 'Unknown error occurred')}"
 
 
 
 
 
 
41
 
42
- # Format the results for display
43
- formatted_result = f"""
44
- ## πŸ“Š Lab Report Analysis Results
45
-
46
- ### πŸ“‹ Summary
47
- {analysis_result.get('summary', 'No summary available')}
48
 
49
- ### πŸ” Key Findings
50
- """
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- key_findings = analysis_result.get('key_findings', [])
53
- if key_findings:
54
- for finding in key_findings:
55
- formatted_result += f"β€’ {finding}\n"
56
- else:
57
- formatted_result += "β€’ No specific findings identified\n"
58
 
59
- formatted_result += f"""
60
- ### πŸ’‘ Interpretation
61
- {analysis_result.get('interpretation', 'No interpretation available')}
62
-
63
- ### ⚠️ Important Note
64
- {analysis_result.get('note', 'This analysis is for educational purposes only and should not replace professional medical advice.')}
65
-
66
- ---
67
- *Analysis powered by Google AI Studio (Gemini 2.0 Flash)*
68
- """
 
 
 
 
 
69
 
70
- return formatted_result
 
 
 
 
 
 
71
 
 
 
72
  except Exception as e:
73
- logger.error(f"Error in analyze_lab_report: {str(e)}")
74
- return f"❌ Analysis failed: {str(e)}"
75
-
76
- def analyze_wrapper(image):
77
- """Wrapper function to run async analysis in Gradio"""
78
- return asyncio.run(analyze_lab_report(image))
79
-
80
- # Create Gradio interface
81
- with gr.Blocks(
82
- theme=gr.themes.Soft(),
83
- title="Lab Report Analysis AI",
84
- css="""
85
- .gradio-container {
86
- max-width: 1200px !important;
87
- }
88
- .analysis-output {
89
- font-family: 'Georgia', serif;
90
- line-height: 1.6;
91
- }
92
- """
93
- ) as demo:
94
-
95
- gr.Markdown("""
96
- # πŸ₯ Lab Report Analysis AI
97
-
98
- Upload a lab report image and get an AI-powered analysis with key findings and interpretations.
99
-
100
- **Features:**
101
- - πŸ“Έ Image-to-text analysis using advanced AI
102
- - πŸ” Structured medical report interpretation
103
- - ⚑ Fast and accurate results
104
- - πŸ”’ Secure processing (images are not stored)
105
-
106
- **Supported formats:** JPG, JPEG, PNG, BMP, TIFF, WEBP
107
- """)
108
-
109
- with gr.Row():
110
- with gr.Column(scale=1):
111
- gr.Markdown("### πŸ“€ Upload Lab Report")
112
- image_input = gr.Image(
113
- type="pil",
114
- label="Lab Report Image",
115
- height=400
116
- )
117
-
118
- analyze_btn = gr.Button(
119
- "πŸ”¬ Analyze Report",
120
- variant="primary",
121
- size="lg"
122
- )
123
-
124
- gr.Markdown("""
125
- ### πŸ“ Instructions:
126
- 1. Upload a clear image of your lab report
127
- 2. Click "Analyze Report" button
128
- 3. Wait for AI analysis results
129
- 4. Review the structured findings
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
- **Note:** This tool is for educational purposes only.
132
- Always consult healthcare professionals for medical advice.
133
- """)
134
-
135
- with gr.Column(scale=2):
136
- gr.Markdown("### πŸ“Š Analysis Results")
137
- analysis_output = gr.Markdown(
138
- "Upload an image and click 'Analyze Report' to see results here.",
139
- elem_classes=["analysis-output"]
140
- )
141
-
142
- # Event handlers
143
- analyze_btn.click(
144
- fn=analyze_wrapper,
145
- inputs=[image_input],
146
- outputs=[analysis_output],
147
- show_progress=True
148
- )
149
-
150
- # Example images section
151
- gr.Markdown("""
152
- ---
153
- ### 🎯 Example Lab Reports
154
- Try uploading sample lab reports to see how the analysis works!
155
- """)
156
-
157
- # Footer
158
- gr.Markdown("""
159
- ---
160
- **Powered by:**
161
- - πŸ€– Google AI Studio (Gemini 2.0 Flash)
162
- - ⚑ FastAPI Backend
163
- - 🎨 Gradio Interface
164
- - 🐳 Docker Containerization
165
-
166
- **Privacy:** Your images are processed securely and not stored permanently.
167
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
- # Launch the app
170
  if __name__ == "__main__":
171
- # For Hugging Face Spaces, use the default port 7860
172
- demo.launch(
173
- server_name="0.0.0.0",
174
- server_port=7860,
175
- share=False,
176
- show_error=True
177
- )
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
+ from fastapi.responses import JSONResponse, HTMLResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.staticfiles import StaticFiles
5
  import base64
 
 
 
 
6
  import io
7
+ from PIL import Image
8
+ from lab_analyzer import LabReportAnalyzer
9
  import logging
10
+ import os
11
 
12
  # Configure logging
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
+ # Initialize FastAPI app
17
+ app = FastAPI(
18
+ title="Lab Report Analysis API",
19
+ description="AI-powered lab report analysis service using Google AI Studio",
20
+ version="1.0.0",
21
+ docs_url="/docs",
22
+ redoc_url="/redoc"
23
+ )
24
+
25
+ # Add CORS middleware
26
+ app.add_middleware(
27
+ CORSMiddleware,
28
+ allow_origins=["*"], # Configure this properly for production
29
+ allow_credentials=True,
30
+ allow_methods=["*"],
31
+ allow_headers=["*"],
32
+ )
33
+
34
  # Initialize the lab analyzer
35
  analyzer = LabReportAnalyzer()
36
 
37
+ @app.get("/health")
38
+ async def health_check():
39
+ """Health check endpoint for monitoring"""
40
+ return {"status": "healthy", "service": "lab-report-analyzer"}
41
+
42
+ @app.post("/analyze")
43
+ async def analyze_lab_report(file: UploadFile = File(...)):
44
  """
45
+ Analyze a lab report image and return structured results
46
 
47
  Args:
48
+ file: Uploaded image file (jpg, jpeg, png, bmp, tiff, webp)
49
+
50
  Returns:
51
+ JSON response with analysis results
52
  """
53
  try:
54
+ # Validate file type
55
+ if not file.content_type.startswith('image/'):
56
+ raise HTTPException(
57
+ status_code=400,
58
+ detail="File must be an image (jpg, jpeg, png, bmp, tiff, webp)"
59
+ )
60
 
61
+ # Read and validate image
62
+ contents = await file.read()
63
+ if len(contents) == 0:
64
+ raise HTTPException(status_code=400, detail="Empty file uploaded")
65
+
66
+ # Validate image can be opened
67
+ try:
68
+ image = Image.open(io.BytesIO(contents))
69
+ image.verify() # Verify it's a valid image
70
+ except Exception as e:
71
+ raise HTTPException(status_code=400, detail=f"Invalid image file: {str(e)}")
72
+
73
+ # Convert to base64 for analysis
74
+ image_b64 = base64.b64encode(contents).decode("utf-8")
75
 
76
  # Analyze the lab report
77
+ logger.info(f"Analyzing lab report: {file.filename}")
78
  analysis_result = await analyzer.analyze_report(image_b64)
79
 
80
+ return JSONResponse(
81
+ status_code=200,
82
+ content={
83
+ "success": True,
84
+ "filename": file.filename,
85
+ "analysis": analysis_result
86
+ }
87
+ )
88
 
89
+ except HTTPException:
90
+ raise
91
+ except Exception as e:
92
+ logger.error(f"Error analyzing lab report: {str(e)}")
93
+ raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
 
94
 
95
+ @app.post("/analyze-base64")
96
+ async def analyze_lab_report_base64(data: dict):
97
+ """
98
+ Analyze a lab report from base64 encoded image
99
+
100
+ Args:
101
+ data: JSON with 'image' key containing base64 encoded image
102
+
103
+ Returns:
104
+ JSON response with analysis results
105
+ """
106
+ try:
107
+ if 'image' not in data:
108
+ raise HTTPException(status_code=400, detail="Missing 'image' field in request body")
109
 
110
+ image_b64 = data['image']
 
 
 
 
 
111
 
112
+ # Remove data:image/...;base64, prefix if present
113
+ if image_b64.startswith('data:image'):
114
+ image_b64 = image_b64.split(',')[1]
115
+
116
+ # Validate base64 and image
117
+ try:
118
+ image_bytes = base64.b64decode(image_b64)
119
+ image = Image.open(io.BytesIO(image_bytes))
120
+ image.verify()
121
+ except Exception as e:
122
+ raise HTTPException(status_code=400, detail=f"Invalid base64 image: {str(e)}")
123
+
124
+ # Analyze the lab report
125
+ logger.info("Analyzing lab report from base64 data")
126
+ analysis_result = await analyzer.analyze_report(image_b64)
127
 
128
+ return JSONResponse(
129
+ status_code=200,
130
+ content={
131
+ "success": True,
132
+ "analysis": analysis_result
133
+ }
134
+ )
135
 
136
+ except HTTPException:
137
+ raise
138
  except Exception as e:
139
+ logger.error(f"Error analyzing base64 lab report: {str(e)}")
140
+ raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
141
+
142
+ # Flutter-friendly endpoint aliases
143
+ @app.post("/api/analyze-lab")
144
+ async def analyze_lab_api(file: UploadFile = File(...)):
145
+ """Flutter-friendly endpoint for lab analysis"""
146
+ return await analyze_lab_report(file)
147
+
148
+ @app.post("/api/analyze-lab-base64")
149
+ async def analyze_lab_base64_api(data: dict):
150
+ """Flutter-friendly endpoint for base64 lab analysis"""
151
+ return await analyze_lab_report_base64(data)
152
+
153
+ @app.get("/", response_class=HTMLResponse)
154
+ async def root():
155
+ """Main page with upload interface"""
156
+ return """
157
+ <!DOCTYPE html>
158
+ <html lang="en">
159
+ <head>
160
+ <meta charset="UTF-8">
161
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
162
+ <title>πŸ₯ Lab Report Analysis AI</title>
163
+ <style>
164
+ * { margin: 0; padding: 0; box-sizing: border-box; }
165
+ body {
166
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
167
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
168
+ min-height: 100vh;
169
+ padding: 20px;
170
+ }
171
+ .container {
172
+ max-width: 1200px;
173
+ margin: 0 auto;
174
+ background: white;
175
+ border-radius: 20px;
176
+ box-shadow: 0 20px 60px rgba(0,0,0,0.1);
177
+ overflow: hidden;
178
+ }
179
+ .header {
180
+ background: linear-gradient(45deg, #2196F3, #21CBF3);
181
+ color: white;
182
+ padding: 40px;
183
+ text-align: center;
184
+ }
185
+ .header h1 { font-size: 2.5em; margin-bottom: 10px; }
186
+ .header p { font-size: 1.2em; opacity: 0.9; }
187
+ .content { padding: 40px; }
188
+ .upload-area {
189
+ border: 3px dashed #2196F3;
190
+ border-radius: 15px;
191
+ padding: 40px;
192
+ text-align: center;
193
+ margin: 20px 0;
194
+ transition: all 0.3s ease;
195
+ cursor: pointer;
196
+ }
197
+ .upload-area:hover {
198
+ border-color: #1976D2;
199
+ background-color: #f5f5f5;
200
+ }
201
+ .upload-area.dragover {
202
+ border-color: #4CAF50;
203
+ background-color: #e8f5e8;
204
+ }
205
+ input[type="file"] { display: none; }
206
+ .btn {
207
+ background: linear-gradient(45deg, #2196F3, #21CBF3);
208
+ color: white;
209
+ border: none;
210
+ padding: 15px 30px;
211
+ border-radius: 25px;
212
+ cursor: pointer;
213
+ font-size: 16px;
214
+ margin: 10px;
215
+ transition: transform 0.2s ease;
216
+ }
217
+ .btn:hover { transform: translateY(-2px); }
218
+ .btn:disabled {
219
+ background: #ccc;
220
+ cursor: not-allowed;
221
+ transform: none;
222
+ }
223
+ .result {
224
+ margin-top: 30px;
225
+ padding: 20px;
226
+ border-radius: 10px;
227
+ background: #f8f9fa;
228
+ border-left: 5px solid #2196F3;
229
+ }
230
+ .loading {
231
+ text-align: center;
232
+ color: #2196F3;
233
+ font-size: 18px;
234
+ }
235
+ .error {
236
+ background: #ffebee;
237
+ border-left-color: #f44336;
238
+ color: #c62828;
239
+ }
240
+ .success {
241
+ background: #e8f5e8;
242
+ border-left-color: #4CAF50;
243
+ }
244
+ .api-info {
245
+ background: #f0f4f8;
246
+ padding: 30px;
247
+ margin-top: 40px;
248
+ border-radius: 15px;
249
+ }
250
+ .endpoint {
251
+ background: #fff;
252
+ padding: 15px;
253
+ margin: 10px 0;
254
+ border-radius: 8px;
255
+ border-left: 4px solid #2196F3;
256
+ }
257
+ .method {
258
+ background: #2196F3;
259
+ color: white;
260
+ padding: 4px 8px;
261
+ border-radius: 4px;
262
+ font-weight: bold;
263
+ font-size: 12px;
264
+ }
265
+ pre {
266
+ background: #2d3748;
267
+ color: #e2e8f0;
268
+ padding: 15px;
269
+ border-radius: 8px;
270
+ overflow-x: auto;
271
+ margin: 10px 0;
272
+ }
273
+ .features {
274
+ display: grid;
275
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
276
+ gap: 20px;
277
+ margin: 30px 0;
278
+ }
279
+ .feature {
280
+ text-align: center;
281
+ padding: 20px;
282
+ border-radius: 10px;
283
+ background: #f8f9fa;
284
+ }
285
+ .feature-icon {
286
+ font-size: 2em;
287
+ margin-bottom: 10px;
288
+ }
289
+ </style>
290
+ </head>
291
+ <body>
292
+ <div class="container">
293
+ <div class="header">
294
+ <h1>πŸ₯ Lab Report Analysis AI</h1>
295
+ <p>AI-powered medical lab report analysis using Google AI Studio</p>
296
+ </div>
297
 
298
+ <div class="content">
299
+ <div class="features">
300
+ <div class="feature">
301
+ <div class="feature-icon">πŸ“Έ</div>
302
+ <h3>Image Upload</h3>
303
+ <p>Support for JPG, PNG, TIFF, and more</p>
304
+ </div>
305
+ <div class="feature">
306
+ <div class="feature-icon">πŸ€–</div>
307
+ <h3>AI Analysis</h3>
308
+ <p>Google Gemini 2.0 Flash model</p>
309
+ </div>
310
+ <div class="feature">
311
+ <div class="feature-icon">πŸ“Š</div>
312
+ <h3>Structured Results</h3>
313
+ <p>Summary, findings, and interpretations</p>
314
+ </div>
315
+ <div class="feature">
316
+ <div class="feature-icon">⚑</div>
317
+ <h3>Fast Processing</h3>
318
+ <p>Real-time analysis results</p>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="upload-area" onclick="document.getElementById('fileInput').click()">
323
+ <h3>πŸ“€ Upload Lab Report Image</h3>
324
+ <p>Click here or drag and drop your lab report image</p>
325
+ <input type="file" id="fileInput" accept="image/*">
326
+ </div>
327
+
328
+ <div style="text-align: center;">
329
+ <button class="btn" onclick="analyzeImage()" id="analyzeBtn" disabled>
330
+ πŸ”¬ Analyze Report
331
+ </button>
332
+ <button class="btn" onclick="clearResults()">
333
+ πŸ—‘οΈ Clear
334
+ </button>
335
+ </div>
336
+
337
+ <div id="result"></div>
338
+
339
+ <div class="api-info">
340
+ <h2>πŸ”§ API Documentation</h2>
341
+ <p>This service provides RESTful API endpoints for programmatic access:</p>
342
+
343
+ <div class="endpoint">
344
+ <span class="method">POST</span> <strong>/analyze</strong>
345
+ <p>Upload lab report image file for analysis</p>
346
+ </div>
347
+
348
+ <div class="endpoint">
349
+ <span class="method">POST</span> <strong>/analyze-base64</strong>
350
+ <p>Analyze lab report from base64 encoded image</p>
351
+ </div>
352
+
353
+ <div class="endpoint">
354
+ <span class="method">POST</span> <strong>/api/analyze-lab</strong>
355
+ <p>Flutter-friendly endpoint for file upload</p>
356
+ </div>
357
+
358
+ <div class="endpoint">
359
+ <span class="method">POST</span> <strong>/api/analyze-lab-base64</strong>
360
+ <p>Flutter-friendly endpoint for base64 analysis</p>
361
+ </div>
362
+
363
+ <p><strong>πŸ“š Interactive Documentation:</strong></p>
364
+ <button class="btn" onclick="window.open('/docs', '_blank')">
365
+ πŸ“– OpenAPI Docs
366
+ </button>
367
+ <button class="btn" onclick="window.open('/redoc', '_blank')">
368
+ πŸ“˜ ReDoc
369
+ </button>
370
+ </div>
371
+
372
+ <div style="text-align: center; margin-top: 40px; color: #666;">
373
+ <p>⚠️ This tool is for educational purposes only. Always consult healthcare professionals for medical advice.</p>
374
+ </div>
375
+ </div>
376
+ </div>
377
+
378
+ <script>
379
+ let selectedFile = null;
380
+
381
+ document.getElementById('fileInput').addEventListener('change', function(e) {
382
+ selectedFile = e.target.files[0];
383
+ if (selectedFile) {
384
+ document.querySelector('.upload-area h3').innerHTML = 'βœ… ' + selectedFile.name;
385
+ document.getElementById('analyzeBtn').disabled = false;
386
+ }
387
+ });
388
+
389
+ // Drag and drop functionality
390
+ const uploadArea = document.querySelector('.upload-area');
391
+ uploadArea.addEventListener('dragover', (e) => {
392
+ e.preventDefault();
393
+ uploadArea.classList.add('dragover');
394
+ });
395
+ uploadArea.addEventListener('dragleave', () => {
396
+ uploadArea.classList.remove('dragover');
397
+ });
398
+ uploadArea.addEventListener('drop', (e) => {
399
+ e.preventDefault();
400
+ uploadArea.classList.remove('dragover');
401
+ const files = e.dataTransfer.files;
402
+ if (files.length > 0) {
403
+ selectedFile = files[0];
404
+ document.querySelector('.upload-area h3').innerHTML = 'βœ… ' + selectedFile.name;
405
+ document.getElementById('analyzeBtn').disabled = false;
406
+ }
407
+ });
408
+
409
+ async function analyzeImage() {
410
+ if (!selectedFile) {
411
+ alert('Please select an image first');
412
+ return;
413
+ }
414
+
415
+ const resultDiv = document.getElementById('result');
416
+ resultDiv.innerHTML = '<div class="result loading">πŸ”„ Analyzing lab report...</div>';
417
+
418
+ const analyzeBtn = document.getElementById('analyzeBtn');
419
+ analyzeBtn.disabled = true;
420
+ analyzeBtn.innerHTML = '⏳ Processing...';
421
+
422
+ const formData = new FormData();
423
+ formData.append('file', selectedFile);
424
+
425
+ try {
426
+ const response = await fetch('/analyze', {
427
+ method: 'POST',
428
+ body: formData
429
+ });
430
+
431
+ const data = await response.json();
432
+
433
+ if (data.success) {
434
+ const analysis = data.analysis;
435
+ resultDiv.innerHTML = `
436
+ <div class="result success">
437
+ <h3>πŸ“Š Analysis Results</h3>
438
+ <h4>πŸ“‹ Summary</h4>
439
+ <p>${analysis.summary || 'No summary available'}</p>
440
+
441
+ <h4>πŸ” Key Findings</h4>
442
+ <ul>
443
+ ${analysis.key_findings?.map(finding => `<li>${finding}</li>`).join('') || '<li>No specific findings identified</li>'}
444
+ </ul>
445
+
446
+ <h4>πŸ’‘ Interpretation</h4>
447
+ <p>${analysis.interpretation || 'No interpretation available'}</p>
448
+
449
+ <h4>⚠️ Important Note</h4>
450
+ <p><em>${analysis.note || 'This analysis is for educational purposes only and should not replace professional medical advice.'}</em></p>
451
+ </div>
452
+ `;
453
+ } else {
454
+ resultDiv.innerHTML = `<div class="result error">❌ Error: ${data.error || 'Analysis failed'}</div>`;
455
+ }
456
+ } catch (error) {
457
+ resultDiv.innerHTML = `<div class="result error">❌ Error: ${error.message}</div>`;
458
+ } finally {
459
+ analyzeBtn.disabled = false;
460
+ analyzeBtn.innerHTML = 'πŸ”¬ Analyze Report';
461
+ }
462
+ }
463
+
464
+ function clearResults() {
465
+ document.getElementById('result').innerHTML = '';
466
+ document.getElementById('fileInput').value = '';
467
+ document.querySelector('.upload-area h3').innerHTML = 'πŸ“€ Upload Lab Report Image';
468
+ document.getElementById('analyzeBtn').disabled = true;
469
+ selectedFile = null;
470
+ }
471
+ </script>
472
+ </body>
473
+ </html>
474
+ """
475
 
 
476
  if __name__ == "__main__":
477
+ import uvicorn
478
+ uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True)
 
 
 
 
 
requirements.txt CHANGED
@@ -1,9 +1,8 @@
1
- gradio>=4.0.0
 
 
2
  Pillow>=10.0.0
3
  numpy>=1.26.0
4
  requests>=2.31.0
5
- fastapi>=0.104.0
6
- uvicorn>=0.24.0
7
- python-multipart>=0.0.6
8
  pydantic>=2.4.0
9
  aiofiles>=23.1.0
 
1
+ fastapi>=0.104.0
2
+ uvicorn[standard]>=0.24.0
3
+ python-multipart>=0.0.6
4
  Pillow>=10.0.0
5
  numpy>=1.26.0
6
  requests>=2.31.0
 
 
 
7
  pydantic>=2.4.0
8
  aiofiles>=23.1.0