blackshadow1 commited on
Commit
1a16cbe
·
verified ·
1 Parent(s): 5ed9cf9

Updated the code functionality ✅✅

Browse files
Files changed (1) hide show
  1. mediSync/app.py +346 -346
mediSync/app.py CHANGED
@@ -33,356 +33,356 @@ logger = logging.getLogger(__name__)
33
  os.makedirs(os.path.join(parent_dir, "data", "sample"), exist_ok=True)
34
 
35
 
36
- class MediSyncApp:
37
- """
38
- Main application class for the MediSync multi-modal medical analysis system.
39
- """
40
-
41
- def __init__(self):
42
- """Initialize the application and load models."""
43
- self.logger = logging.getLogger(__name__)
44
- self.logger.info("Initializing MediSync application")
45
-
46
- # Initialize models with None for lazy loading
47
- self.fusion_model = None
48
- self.image_model = None
49
- self.text_model = None
50
-
51
- def load_models(self):
52
- """
53
- Load models if not already loaded.
54
-
55
- Returns:
56
- bool: True if models loaded successfully, False otherwise
57
- """
58
- try:
59
- if self.fusion_model is None:
60
- self.logger.info("Loading models...")
61
- self.fusion_model = MultimodalFusion()
62
- self.image_model = self.fusion_model.image_analyzer
63
- self.text_model = self.fusion_model.text_analyzer
64
- self.logger.info("Models loaded successfully")
65
- return True
66
-
67
- except Exception as e:
68
- self.logger.error(f"Error loading models: {e}")
69
- return False
70
-
71
- def analyze_image(self, image):
72
- """
73
- Analyze a medical image.
74
-
75
- Args:
76
- image: Image file uploaded through Gradio
77
-
78
- Returns:
79
- tuple: (image, image_results_html, plot_as_html)
80
- """
81
- try:
82
- # Ensure models are loaded
83
- if not self.load_models() or self.image_model is None:
84
- return image, "Error: Models not loaded properly.", None
85
-
86
- # Save uploaded image to a temporary file
87
- temp_dir = tempfile.mkdtemp()
88
- temp_path = os.path.join(temp_dir, "upload.png")
89
-
90
- if isinstance(image, str):
91
- # Copy the file if it's a path
92
- from shutil import copyfile
93
-
94
- copyfile(image, temp_path)
95
- else:
96
- # Save if it's a Gradio UploadButton image
97
- image.save(temp_path)
98
-
99
- # Run image analysis
100
- self.logger.info(f"Analyzing image: {temp_path}")
101
- results = self.image_model.analyze(temp_path)
102
-
103
- # Create visualization
104
- fig = plot_image_prediction(
105
- image,
106
- results.get("predictions", []),
107
- f"Primary Finding: {results.get('primary_finding', 'Unknown')}",
108
- )
109
-
110
- # Convert to HTML for display
111
- plot_html = self.fig_to_html(fig)
112
-
113
- # Format results as HTML
114
- html_result = f"""
115
- <h2>X-ray Analysis Results</h2>
116
- <p><strong>Primary Finding:</strong> {results.get("primary_finding", "Unknown")}</p>
117
- <p><strong>Confidence:</strong> {results.get("confidence", 0):.1%}</p>
118
- <p><strong>Abnormality Detected:</strong> {"Yes" if results.get("has_abnormality", False) else "No"}</p>
119
 
120
- <h3>Top Predictions:</h3>
121
- <ul>
122
- """
123
-
124
- # Add top 5 predictions
125
- for label, prob in results.get("predictions", [])[:5]:
126
- html_result += f"<li>{label}: {prob:.1%}</li>"
127
-
128
- html_result += "</ul>"
129
-
130
- # Add explanation
131
- explanation = self.image_model.get_explanation(results)
132
- html_result += f"<h3>Analysis Explanation:</h3><p>{explanation}</p>"
133
-
134
- return image, html_result, plot_html
135
-
136
- except Exception as e:
137
- self.logger.error(f"Error in image analysis: {e}")
138
- return image, f"Error analyzing image: {str(e)}", None
139
-
140
- def analyze_text(self, text):
141
- """
142
- Analyze a medical report text.
143
-
144
- Args:
145
- text: Report text input through Gradio
146
-
147
- Returns:
148
- tuple: (text, text_results_html, entities_plot_html)
149
- """
150
- try:
151
- # Ensure models are loaded
152
- if not self.load_models() or self.text_model is None:
153
- return text, "Error: Models not loaded properly.", None
154
-
155
- # Check for empty text
156
- if not text or len(text.strip()) < 10:
157
- return (
158
- text,
159
- "Error: Please enter a valid medical report text (at least 10 characters).",
160
- None,
161
- )
162
-
163
- # Normalize text
164
- normalized_text = normalize_report_text(text)
165
-
166
- # Run text analysis
167
- self.logger.info("Analyzing medical report text")
168
- results = self.text_model.analyze(normalized_text)
169
-
170
- # Get entities and create visualization
171
- entities = results.get("entities", {})
172
- fig = plot_report_entities(normalized_text, entities)
173
-
174
- # Convert to HTML for display
175
- entities_plot_html = self.fig_to_html(fig)
176
-
177
- # Format results as HTML
178
- html_result = f"""
179
- <h2>Medical Report Analysis Results</h2>
180
- <p><strong>Severity Level:</strong> {results.get("severity", {}).get("level", "Unknown")}</p>
181
- <p><strong>Severity Score:</strong> {results.get("severity", {}).get("score", 0)}/4</p>
182
- <p><strong>Confidence:</strong> {results.get("severity", {}).get("confidence", 0):.1%}</p>
183
 
184
- <h3>Key Findings:</h3>
185
- <ul>
186
- """
187
-
188
- # Add findings
189
- findings = results.get("findings", [])
190
- if findings:
191
- for finding in findings:
192
- html_result += f"<li>{finding}</li>"
193
- else:
194
- html_result += "<li>No specific findings detailed.</li>"
195
-
196
- html_result += "</ul>"
197
-
198
- # Add entities
199
- html_result += "<h3>Extracted Medical Entities:</h3>"
200
-
201
- for category, items in entities.items():
202
- if items:
203
- html_result += f"<p><strong>{category.capitalize()}:</strong> {', '.join(items)}</p>"
204
-
205
- # Add follow-up recommendations
206
- html_result += "<h3>Follow-up Recommendations:</h3><ul>"
207
- followups = results.get("followup_recommendations", [])
208
-
209
- if followups:
210
- for rec in followups:
211
- html_result += f"<li>{rec}</li>"
212
- else:
213
- html_result += "<li>No specific follow-up recommendations.</li>"
214
-
215
- html_result += "</ul>"
216
-
217
- return text, html_result, entities_plot_html
218
-
219
- except Exception as e:
220
- self.logger.error(f"Error in text analysis: {e}")
221
- return text, f"Error analyzing text: {str(e)}", None
222
-
223
- def analyze_multimodal(self, image, text):
224
- """
225
- Perform multimodal analysis of image and text.
226
-
227
- Args:
228
- image: Image file uploaded through Gradio
229
- text: Report text input through Gradio
230
-
231
- Returns:
232
- tuple: (results_html, multimodal_plot_html)
233
- """
234
- try:
235
- # Ensure models are loaded
236
- if not self.load_models() or self.fusion_model is None:
237
- return "Error: Models not loaded properly.", None
238
-
239
- # Check for empty inputs
240
- if image is None:
241
- return "Error: Please upload an X-ray image for analysis.", None
242
-
243
- if not text or len(text.strip()) < 10:
244
- return (
245
- "Error: Please enter a valid medical report text (at least 10 characters).",
246
- None,
247
- )
248
-
249
- # Save uploaded image to a temporary file
250
- temp_dir = tempfile.mkdtemp()
251
- temp_path = os.path.join(temp_dir, "upload.png")
252
-
253
- if isinstance(image, str):
254
- # Copy the file if it's a path
255
- from shutil import copyfile
256
-
257
- copyfile(image, temp_path)
258
- else:
259
- # Save if it's a Gradio UploadButton image
260
- image.save(temp_path)
261
-
262
- # Normalize text
263
- normalized_text = normalize_report_text(text)
264
-
265
- # Run multimodal analysis
266
- self.logger.info("Performing multimodal analysis")
267
- results = self.fusion_model.analyze(temp_path, normalized_text)
268
-
269
- # Create visualization
270
- fig = plot_multimodal_results(results, image, text)
271
-
272
- # Convert to HTML for display
273
- plot_html = self.fig_to_html(fig)
274
-
275
- # Generate explanation
276
- explanation = self.fusion_model.get_explanation(results)
277
-
278
- # Format results as HTML
279
- html_result = f"""
280
- <h2>Multimodal Medical Analysis Results</h2>
281
 
282
- <h3>Overview</h3>
283
- <p><strong>Primary Finding:</strong> {results.get("primary_finding", "Unknown")}</p>
284
- <p><strong>Severity Level:</strong> {results.get("severity", {}).get("level", "Unknown")}</p>
285
- <p><strong>Severity Score:</strong> {results.get("severity", {}).get("score", 0)}/4</p>
286
- <p><strong>Agreement Score:</strong> {results.get("agreement_score", 0):.0%}</p>
287
 
288
- <h3>Detailed Findings</h3>
289
- <ul>
290
- """
291
-
292
- # Add findings
293
- findings = results.get("findings", [])
294
- if findings:
295
- for finding in findings:
296
- html_result += f"<li>{finding}</li>"
297
- else:
298
- html_result += "<li>No specific findings detailed.</li>"
299
-
300
- html_result += "</ul>"
301
-
302
- # Add follow-up recommendations
303
- html_result += "<h3>Recommended Follow-up</h3><ul>"
304
- followups = results.get("followup_recommendations", [])
305
-
306
- if followups:
307
- for rec in followups:
308
- html_result += f"<li>{rec}</li>"
309
- else:
310
- html_result += (
311
- "<li>No specific follow-up recommendations provided.</li>"
312
- )
313
-
314
- html_result += "</ul>"
315
-
316
- # Add confidence note
317
- confidence = results.get("severity", {}).get("confidence", 0)
318
- html_result += f"""
319
- <p><em>Note: This analysis has a confidence level of {confidence:.0%}.
320
- Please consult with healthcare professionals for official diagnosis.</em></p>
321
- """
322
-
323
- return html_result, plot_html
324
-
325
- except Exception as e:
326
- self.logger.error(f"Error in multimodal analysis: {e}")
327
- return f"Error in multimodal analysis: {str(e)}", None
328
-
329
- def enhance_image(self, image):
330
- """
331
- Enhance X-ray image contrast.
332
-
333
- Args:
334
- image: Image file uploaded through Gradio
335
-
336
- Returns:
337
- PIL.Image: Enhanced image
338
- """
339
- try:
340
- if image is None:
341
- return None
342
-
343
- # Save uploaded image to a temporary file
344
- temp_dir = tempfile.mkdtemp()
345
- temp_path = os.path.join(temp_dir, "upload.png")
346
-
347
- if isinstance(image, str):
348
- # Copy the file if it's a path
349
- from shutil import copyfile
350
-
351
- copyfile(image, temp_path)
352
- else:
353
- # Save if it's a Gradio UploadButton image
354
- image.save(temp_path)
355
-
356
- # Enhance image
357
- self.logger.info(f"Enhancing image: {temp_path}")
358
- output_path = os.path.join(temp_dir, "enhanced.png")
359
- enhance_xray_image(temp_path, output_path)
360
-
361
- # Load enhanced image
362
- enhanced = Image.open(output_path)
363
- return enhanced
364
-
365
- except Exception as e:
366
- self.logger.error(f"Error enhancing image: {e}")
367
- return image # Return original image on error
368
-
369
- def fig_to_html(self, fig):
370
- """Convert matplotlib figure to HTML for display in Gradio."""
371
- try:
372
- import base64
373
- import io
374
-
375
- buf = io.BytesIO()
376
- fig.savefig(buf, format="png", bbox_inches="tight")
377
- buf.seek(0)
378
- img_str = base64.b64encode(buf.read()).decode("utf-8")
379
- plt.close(fig)
380
-
381
- return f'<img src="data:image/png;base64,{img_str}" alt="Analysis Plot">'
382
-
383
- except Exception as e:
384
- self.logger.error(f"Error converting figure to HTML: {e}")
385
- return "<p>Error displaying visualization.</p>"
386
 
387
 
388
  import logging
 
33
  os.makedirs(os.path.join(parent_dir, "data", "sample"), exist_ok=True)
34
 
35
 
36
+ # class MediSyncApp:
37
+ # """
38
+ # Main application class for the MediSync multi-modal medical analysis system.
39
+ # """
40
+
41
+ # def __init__(self):
42
+ # """Initialize the application and load models."""
43
+ # self.logger = logging.getLogger(__name__)
44
+ # self.logger.info("Initializing MediSync application")
45
+
46
+ # # Initialize models with None for lazy loading
47
+ # self.fusion_model = None
48
+ # self.image_model = None
49
+ # self.text_model = None
50
+
51
+ # def load_models(self):
52
+ # """
53
+ # Load models if not already loaded.
54
+
55
+ # Returns:
56
+ # bool: True if models loaded successfully, False otherwise
57
+ # """
58
+ # try:
59
+ # if self.fusion_model is None:
60
+ # self.logger.info("Loading models...")
61
+ # self.fusion_model = MultimodalFusion()
62
+ # self.image_model = self.fusion_model.image_analyzer
63
+ # self.text_model = self.fusion_model.text_analyzer
64
+ # self.logger.info("Models loaded successfully")
65
+ # return True
66
+
67
+ # except Exception as e:
68
+ # self.logger.error(f"Error loading models: {e}")
69
+ # return False
70
+
71
+ # def analyze_image(self, image):
72
+ # """
73
+ # Analyze a medical image.
74
+
75
+ # Args:
76
+ # image: Image file uploaded through Gradio
77
+
78
+ # Returns:
79
+ # tuple: (image, image_results_html, plot_as_html)
80
+ # """
81
+ # try:
82
+ # # Ensure models are loaded
83
+ # if not self.load_models() or self.image_model is None:
84
+ # return image, "Error: Models not loaded properly.", None
85
+
86
+ # # Save uploaded image to a temporary file
87
+ # temp_dir = tempfile.mkdtemp()
88
+ # temp_path = os.path.join(temp_dir, "upload.png")
89
+
90
+ # if isinstance(image, str):
91
+ # # Copy the file if it's a path
92
+ # from shutil import copyfile
93
+
94
+ # copyfile(image, temp_path)
95
+ # else:
96
+ # # Save if it's a Gradio UploadButton image
97
+ # image.save(temp_path)
98
+
99
+ # # Run image analysis
100
+ # self.logger.info(f"Analyzing image: {temp_path}")
101
+ # results = self.image_model.analyze(temp_path)
102
+
103
+ # # Create visualization
104
+ # fig = plot_image_prediction(
105
+ # image,
106
+ # results.get("predictions", []),
107
+ # f"Primary Finding: {results.get('primary_finding', 'Unknown')}",
108
+ # )
109
+
110
+ # # Convert to HTML for display
111
+ # plot_html = self.fig_to_html(fig)
112
+
113
+ # # Format results as HTML
114
+ # html_result = f"""
115
+ # <h2>X-ray Analysis Results</h2>
116
+ # <p><strong>Primary Finding:</strong> {results.get("primary_finding", "Unknown")}</p>
117
+ # <p><strong>Confidence:</strong> {results.get("confidence", 0):.1%}</p>
118
+ # <p><strong>Abnormality Detected:</strong> {"Yes" if results.get("has_abnormality", False) else "No"}</p>
119
 
120
+ # <h3>Top Predictions:</h3>
121
+ # <ul>
122
+ # """
123
+
124
+ # # Add top 5 predictions
125
+ # for label, prob in results.get("predictions", [])[:5]:
126
+ # html_result += f"<li>{label}: {prob:.1%}</li>"
127
+
128
+ # html_result += "</ul>"
129
+
130
+ # # Add explanation
131
+ # explanation = self.image_model.get_explanation(results)
132
+ # html_result += f"<h3>Analysis Explanation:</h3><p>{explanation}</p>"
133
+
134
+ # return image, html_result, plot_html
135
+
136
+ # except Exception as e:
137
+ # self.logger.error(f"Error in image analysis: {e}")
138
+ # return image, f"Error analyzing image: {str(e)}", None
139
+
140
+ # def analyze_text(self, text):
141
+ # """
142
+ # Analyze a medical report text.
143
+
144
+ # Args:
145
+ # text: Report text input through Gradio
146
+
147
+ # Returns:
148
+ # tuple: (text, text_results_html, entities_plot_html)
149
+ # """
150
+ # try:
151
+ # # Ensure models are loaded
152
+ # if not self.load_models() or self.text_model is None:
153
+ # return text, "Error: Models not loaded properly.", None
154
+
155
+ # # Check for empty text
156
+ # if not text or len(text.strip()) < 10:
157
+ # return (
158
+ # text,
159
+ # "Error: Please enter a valid medical report text (at least 10 characters).",
160
+ # None,
161
+ # )
162
+
163
+ # # Normalize text
164
+ # normalized_text = normalize_report_text(text)
165
+
166
+ # # Run text analysis
167
+ # self.logger.info("Analyzing medical report text")
168
+ # results = self.text_model.analyze(normalized_text)
169
+
170
+ # # Get entities and create visualization
171
+ # entities = results.get("entities", {})
172
+ # fig = plot_report_entities(normalized_text, entities)
173
+
174
+ # # Convert to HTML for display
175
+ # entities_plot_html = self.fig_to_html(fig)
176
+
177
+ # # Format results as HTML
178
+ # html_result = f"""
179
+ # <h2>Medical Report Analysis Results</h2>
180
+ # <p><strong>Severity Level:</strong> {results.get("severity", {}).get("level", "Unknown")}</p>
181
+ # <p><strong>Severity Score:</strong> {results.get("severity", {}).get("score", 0)}/4</p>
182
+ # <p><strong>Confidence:</strong> {results.get("severity", {}).get("confidence", 0):.1%}</p>
183
 
184
+ # <h3>Key Findings:</h3>
185
+ # <ul>
186
+ # """
187
+
188
+ # # Add findings
189
+ # findings = results.get("findings", [])
190
+ # if findings:
191
+ # for finding in findings:
192
+ # html_result += f"<li>{finding}</li>"
193
+ # else:
194
+ # html_result += "<li>No specific findings detailed.</li>"
195
+
196
+ # html_result += "</ul>"
197
+
198
+ # # Add entities
199
+ # html_result += "<h3>Extracted Medical Entities:</h3>"
200
+
201
+ # for category, items in entities.items():
202
+ # if items:
203
+ # html_result += f"<p><strong>{category.capitalize()}:</strong> {', '.join(items)}</p>"
204
+
205
+ # # Add follow-up recommendations
206
+ # html_result += "<h3>Follow-up Recommendations:</h3><ul>"
207
+ # followups = results.get("followup_recommendations", [])
208
+
209
+ # if followups:
210
+ # for rec in followups:
211
+ # html_result += f"<li>{rec}</li>"
212
+ # else:
213
+ # html_result += "<li>No specific follow-up recommendations.</li>"
214
+
215
+ # html_result += "</ul>"
216
+
217
+ # return text, html_result, entities_plot_html
218
+
219
+ # except Exception as e:
220
+ # self.logger.error(f"Error in text analysis: {e}")
221
+ # return text, f"Error analyzing text: {str(e)}", None
222
+
223
+ # def analyze_multimodal(self, image, text):
224
+ # """
225
+ # Perform multimodal analysis of image and text.
226
+
227
+ # Args:
228
+ # image: Image file uploaded through Gradio
229
+ # text: Report text input through Gradio
230
+
231
+ # Returns:
232
+ # tuple: (results_html, multimodal_plot_html)
233
+ # """
234
+ # try:
235
+ # # Ensure models are loaded
236
+ # if not self.load_models() or self.fusion_model is None:
237
+ # return "Error: Models not loaded properly.", None
238
+
239
+ # # Check for empty inputs
240
+ # if image is None:
241
+ # return "Error: Please upload an X-ray image for analysis.", None
242
+
243
+ # if not text or len(text.strip()) < 10:
244
+ # return (
245
+ # "Error: Please enter a valid medical report text (at least 10 characters).",
246
+ # None,
247
+ # )
248
+
249
+ # # Save uploaded image to a temporary file
250
+ # temp_dir = tempfile.mkdtemp()
251
+ # temp_path = os.path.join(temp_dir, "upload.png")
252
+
253
+ # if isinstance(image, str):
254
+ # # Copy the file if it's a path
255
+ # from shutil import copyfile
256
+
257
+ # copyfile(image, temp_path)
258
+ # else:
259
+ # # Save if it's a Gradio UploadButton image
260
+ # image.save(temp_path)
261
+
262
+ # # Normalize text
263
+ # normalized_text = normalize_report_text(text)
264
+
265
+ # # Run multimodal analysis
266
+ # self.logger.info("Performing multimodal analysis")
267
+ # results = self.fusion_model.analyze(temp_path, normalized_text)
268
+
269
+ # # Create visualization
270
+ # fig = plot_multimodal_results(results, image, text)
271
+
272
+ # # Convert to HTML for display
273
+ # plot_html = self.fig_to_html(fig)
274
+
275
+ # # Generate explanation
276
+ # explanation = self.fusion_model.get_explanation(results)
277
+
278
+ # # Format results as HTML
279
+ # html_result = f"""
280
+ # <h2>Multimodal Medical Analysis Results</h2>
281
 
282
+ # <h3>Overview</h3>
283
+ # <p><strong>Primary Finding:</strong> {results.get("primary_finding", "Unknown")}</p>
284
+ # <p><strong>Severity Level:</strong> {results.get("severity", {}).get("level", "Unknown")}</p>
285
+ # <p><strong>Severity Score:</strong> {results.get("severity", {}).get("score", 0)}/4</p>
286
+ # <p><strong>Agreement Score:</strong> {results.get("agreement_score", 0):.0%}</p>
287
 
288
+ # <h3>Detailed Findings</h3>
289
+ # <ul>
290
+ # """
291
+
292
+ # # Add findings
293
+ # findings = results.get("findings", [])
294
+ # if findings:
295
+ # for finding in findings:
296
+ # html_result += f"<li>{finding}</li>"
297
+ # else:
298
+ # html_result += "<li>No specific findings detailed.</li>"
299
+
300
+ # html_result += "</ul>"
301
+
302
+ # # Add follow-up recommendations
303
+ # html_result += "<h3>Recommended Follow-up</h3><ul>"
304
+ # followups = results.get("followup_recommendations", [])
305
+
306
+ # if followups:
307
+ # for rec in followups:
308
+ # html_result += f"<li>{rec}</li>"
309
+ # else:
310
+ # html_result += (
311
+ # "<li>No specific follow-up recommendations provided.</li>"
312
+ # )
313
+
314
+ # html_result += "</ul>"
315
+
316
+ # # Add confidence note
317
+ # confidence = results.get("severity", {}).get("confidence", 0)
318
+ # html_result += f"""
319
+ # <p><em>Note: This analysis has a confidence level of {confidence:.0%}.
320
+ # Please consult with healthcare professionals for official diagnosis.</em></p>
321
+ # """
322
+
323
+ # return html_result, plot_html
324
+
325
+ # except Exception as e:
326
+ # self.logger.error(f"Error in multimodal analysis: {e}")
327
+ # return f"Error in multimodal analysis: {str(e)}", None
328
+
329
+ # def enhance_image(self, image):
330
+ # """
331
+ # Enhance X-ray image contrast.
332
+
333
+ # Args:
334
+ # image: Image file uploaded through Gradio
335
+
336
+ # Returns:
337
+ # PIL.Image: Enhanced image
338
+ # """
339
+ # try:
340
+ # if image is None:
341
+ # return None
342
+
343
+ # # Save uploaded image to a temporary file
344
+ # temp_dir = tempfile.mkdtemp()
345
+ # temp_path = os.path.join(temp_dir, "upload.png")
346
+
347
+ # if isinstance(image, str):
348
+ # # Copy the file if it's a path
349
+ # from shutil import copyfile
350
+
351
+ # copyfile(image, temp_path)
352
+ # else:
353
+ # # Save if it's a Gradio UploadButton image
354
+ # image.save(temp_path)
355
+
356
+ # # Enhance image
357
+ # self.logger.info(f"Enhancing image: {temp_path}")
358
+ # output_path = os.path.join(temp_dir, "enhanced.png")
359
+ # enhance_xray_image(temp_path, output_path)
360
+
361
+ # # Load enhanced image
362
+ # enhanced = Image.open(output_path)
363
+ # return enhanced
364
+
365
+ # except Exception as e:
366
+ # self.logger.error(f"Error enhancing image: {e}")
367
+ # return image # Return original image on error
368
+
369
+ # def fig_to_html(self, fig):
370
+ # """Convert matplotlib figure to HTML for display in Gradio."""
371
+ # try:
372
+ # import base64
373
+ # import io
374
+
375
+ # buf = io.BytesIO()
376
+ # fig.savefig(buf, format="png", bbox_inches="tight")
377
+ # buf.seek(0)
378
+ # img_str = base64.b64encode(buf.read()).decode("utf-8")
379
+ # plt.close(fig)
380
+
381
+ # return f'<img src="data:image/png;base64,{img_str}" alt="Analysis Plot">'
382
+
383
+ # except Exception as e:
384
+ # self.logger.error(f"Error converting figure to HTML: {e}")
385
+ # return "<p>Error displaying visualization.</p>"
386
 
387
 
388
  import logging