SmartHeal commited on
Commit
be42318
·
verified ·
1 Parent(s): 9b394b1

Update src/dashboard_database_manager.py

Browse files
Files changed (1) hide show
  1. src/dashboard_database_manager.py +241 -249
src/dashboard_database_manager.py CHANGED
@@ -6,10 +6,9 @@ import json
6
  import uuid
7
  import os
8
  from PIL import Image
9
- from typing import Dict, Any, Optional, List
10
 
11
- class DashboardDatabaseManager:
12
- """Enhanced database manager for SmartHeal bot with dashboard integration"""
13
 
14
  def __init__(self, mysql_config):
15
  """Initialize database manager with MySQL configuration"""
@@ -87,14 +86,8 @@ class DashboardDatabaseManager:
87
  if connection and connection.is_connected():
88
  connection.close()
89
 
90
- def get_last_insert_id(self, connection, cursor):
91
- """Get the last inserted ID"""
92
- return cursor.lastrowid
93
-
94
- def save_questionnaire_response(self, questionnaire_data: Dict[str, Any], user_id: int) -> Optional[int]:
95
- """
96
- Save questionnaire response to dashboard-compatible tables
97
- """
98
  connection = None
99
  cursor = None
100
  try:
@@ -102,18 +95,18 @@ class DashboardDatabaseManager:
102
  if not connection:
103
  return None
104
  cursor = connection.cursor()
105
-
106
- # (1) Create or get patient
107
  patient_id = self._create_or_get_patient(cursor, questionnaire_data)
108
  if not patient_id:
109
  raise Exception("Failed to get or create patient")
110
-
111
- # (2) Get or create default questionnaire
112
  questionnaire_id = self._get_or_create_default_questionnaire(cursor)
113
  if not questionnaire_id:
114
  raise Exception("Failed to get or create questionnaire")
115
-
116
- # (3) Prepare response_data JSON
117
  response_data = {
118
  'patient_info': {
119
  'name': questionnaire_data.get('patient_name', ''),
@@ -136,8 +129,8 @@ class DashboardDatabaseManager:
136
  'additional_notes': questionnaire_data.get('additional_notes', '')
137
  }
138
  }
139
-
140
- # (4) Insert into questionnaire_responses
141
  insert_resp = """
142
  INSERT INTO questionnaire_responses
143
  (questionnaire_id, patient_id, practitioner_id, response_data, submitted_at)
@@ -151,13 +144,13 @@ class DashboardDatabaseManager:
151
  datetime.now()
152
  ))
153
  response_id = cursor.lastrowid
154
-
155
  connection.commit()
156
  logging.info(f"✅ Saved questionnaire response ID {response_id}")
157
  return response_id
158
-
159
  except Exception as e:
160
- logging.error(f"❌ Error saving questionnaire response: {e}")
161
  if connection:
162
  connection.rollback()
163
  return None
@@ -167,39 +160,18 @@ class DashboardDatabaseManager:
167
  if connection:
168
  connection.close()
169
 
170
- def _get_or_create_default_questionnaire(self, cursor) -> Optional[int]:
171
- """Get or create default questionnaire"""
172
- try:
173
- # Check if default questionnaire exists
174
- cursor.execute("SELECT id FROM questionnaires WHERE name = 'Default Patient Assessment' LIMIT 1")
175
- questionnaire_row = cursor.fetchone()
176
-
177
- if questionnaire_row:
178
- return questionnaire_row[0]
179
-
180
- # Create default questionnaire
181
- cursor.execute("""
182
- INSERT INTO questionnaires (name, description, created_at)
183
- VALUES ('Default Patient Assessment', 'Standard patient wound assessment form', NOW())
184
- """)
185
- return cursor.lastrowid
186
-
187
- except Exception as e:
188
- logging.error(f"Error getting/creating questionnaire: {e}")
189
- return None
190
-
191
- def _create_or_get_patient(self, cursor, questionnaire_data: Dict[str, Any]) -> Optional[int]:
192
  """Create or get existing patient record"""
193
  try:
194
- # Check if patient exists by name and age
195
  select_query = """
196
  SELECT id FROM patients
197
- WHERE name = %s AND age = %s
198
- LIMIT 1
199
  """
200
  cursor.execute(select_query, (
201
  questionnaire_data.get('patient_name', ''),
202
- questionnaire_data.get('patient_age', 0)
 
203
  ))
204
 
205
  existing_patient = cursor.fetchone()
@@ -231,75 +203,112 @@ class DashboardDatabaseManager:
231
  logging.error(f"Error creating/getting patient: {e}")
232
  return None
233
 
234
- def save_wound_image(self, questionnaire_response_id: int, image, original_filename: str = None) -> Optional[int]:
235
- """Save wound image to filesystem and database"""
236
  try:
237
- # Generate unique filename
238
- image_id = str(uuid.uuid4())
239
- filename = f"wound_{image_id}.jpg"
240
- file_path = os.path.join("uploads", filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
 
242
- # Ensure uploads directory exists
243
- os.makedirs("uploads", exist_ok=True)
 
 
 
 
244
 
245
- # Save image to disk
246
  if hasattr(image, 'save'):
247
  image.save(file_path, format='JPEG', quality=95)
248
-
249
- # Get image dimensions and file size
250
  width, height = image.size
251
  file_size = os.path.getsize(file_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
- # Save to wound_images table
254
- query = """
255
- INSERT INTO wound_images (
256
- questionnaire_id, image_url, original_filename, file_size,
257
- image_width, image_height, created_at
258
- ) VALUES (%s, %s, %s, %s, %s, %s, %s)
259
- """
260
 
261
- params = (
262
- questionnaire_response_id,
263
- file_path,
264
- original_filename or filename,
265
- file_size,
266
- width,
267
- height,
268
- datetime.now()
269
- )
270
 
271
- result = self.execute_query(query, params)
272
- if result:
273
- # Get the inserted ID
274
- connection = self.get_connection()
275
- cursor = connection.cursor()
276
- cursor.execute("SELECT LAST_INSERT_ID()")
277
- image_db_id = cursor.fetchone()[0]
278
- cursor.close()
279
- connection.close()
280
-
281
- logging.info(f"✅ Image saved with ID: {image_db_id}")
282
- return image_db_id
283
 
284
  except Exception as e:
285
  logging.error(f"❌ Error saving wound image: {e}")
286
-
287
- return None
288
 
289
- def save_ai_analysis(self, analysis_data: Dict[str, Any]) -> Optional[int]:
290
- """
291
- Save AI analysis results to dashboard-compatible ai_analyses table
292
- """
293
  try:
294
- query = """
295
- INSERT INTO ai_analyses (
296
- questionnaire_id, image_id, analysis_data, summary, recommendations,
297
- risk_score, risk_level, wound_type, wound_dimensions, processing_time,
298
- model_version, created_at
299
- ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
300
- """
301
 
302
- # Calculate risk level based on risk score
303
  risk_score = analysis_data.get('risk_score', 0)
304
  if risk_score >= 70:
305
  risk_level = 'High'
@@ -308,89 +317,99 @@ class DashboardDatabaseManager:
308
  else:
309
  risk_level = 'Low'
310
 
311
- # Format wound dimensions
312
- visual_results = analysis_data.get('visual_results', {})
313
- wound_dimensions = f"{visual_results.get('length_cm', 0)}x{visual_results.get('breadth_cm', 0)} cm"
 
 
 
 
 
 
 
 
 
 
314
 
315
  params = (
316
- analysis_data.get('questionnaire_id'),
317
- analysis_data.get('image_id'),
318
  json.dumps(analysis_data.get('analysis_data', {})),
319
- analysis_data.get('summary', ''),
320
  analysis_data.get('recommendations', ''),
321
  risk_score,
322
  risk_level,
323
  visual_results.get('wound_type', 'Unknown'),
324
  wound_dimensions,
325
- analysis_data.get('processing_time', 0.0),
326
  analysis_data.get('model_version', 'v1.0'),
327
  datetime.now()
328
  )
329
 
330
  result = self.execute_query(query, params)
331
- if result:
332
- # Get the inserted ID
333
  connection = self.get_connection()
334
- cursor = connection.cursor()
335
- cursor.execute("SELECT LAST_INSERT_ID()")
336
- analysis_id = cursor.fetchone()[0]
337
- cursor.close()
338
- connection.close()
339
-
340
- logging.info(f"✅ AI analysis saved with ID: {analysis_id}")
341
- return analysis_id
342
-
 
 
 
343
  except Exception as e:
344
  logging.error(f"❌ Error saving AI analysis: {e}")
345
-
346
- return None
347
 
348
- def save_analysis_session(self, session_data: Dict[str, Any]) -> Optional[int]:
349
- """
350
- Save analysis session data for dashboard analytics
351
- """
352
  try:
353
  query = """
354
  INSERT INTO analysis_sessions (
355
- user_id, questionnaire_id, image_id, analysis_id,
356
  session_duration, created_at
357
  ) VALUES (%s, %s, %s, %s, %s, %s)
358
  """
359
 
360
  params = (
361
  session_data.get('user_id'),
362
- session_data.get('questionnaire_id'),
363
  session_data.get('image_id'),
364
  session_data.get('analysis_id'),
365
- session_data.get('session_duration', 0.0),
366
  datetime.now()
367
  )
368
 
369
  result = self.execute_query(query, params)
370
- if result:
371
  connection = self.get_connection()
372
- cursor = connection.cursor()
373
- cursor.execute("SELECT LAST_INSERT_ID()")
374
- session_id = cursor.fetchone()[0]
375
- cursor.close()
376
- connection.close()
377
-
378
- logging.info(f"✅ Analysis session saved with ID: {session_id}")
379
- return session_id
380
-
 
 
 
381
  except Exception as e:
382
  logging.error(f"❌ Error saving analysis session: {e}")
383
-
384
- return None
385
 
386
- def save_bot_interaction(self, interaction_data: Dict[str, Any]) -> Optional[int]:
387
- """
388
- Save bot interaction data for dashboard analytics
389
- """
390
  try:
391
  query = """
392
  INSERT INTO bot_interactions (
393
- patient_id, practitioner_id, input_text, output_text,
394
  wound_image_url, interaction_type, interacted_at
395
  ) VALUES (%s, %s, %s, %s, %s, %s, %s)
396
  """
@@ -401,127 +420,105 @@ class DashboardDatabaseManager:
401
  interaction_data.get('input_text', ''),
402
  interaction_data.get('output_text', ''),
403
  interaction_data.get('wound_image_url', ''),
404
- interaction_data.get('interaction_type', 'analysis'),
405
  datetime.now()
406
  )
407
 
408
  result = self.execute_query(query, params)
409
- if result:
410
  connection = self.get_connection()
411
- cursor = connection.cursor()
412
- cursor.execute("SELECT LAST_INSERT_ID()")
413
- interaction_id = cursor.fetchone()[0]
414
- cursor.close()
415
- connection.close()
416
-
417
- logging.info(f"✅ Bot interaction saved with ID: {interaction_id}")
418
- return interaction_id
419
-
 
 
 
420
  except Exception as e:
421
  logging.error(f"❌ Error saving bot interaction: {e}")
422
-
423
- return None
424
 
425
- def get_analytics_data(self) -> Dict[str, Any]:
426
- """
427
- Get comprehensive analytics data for dashboard
428
- """
429
  try:
430
  analytics = {}
431
 
432
  # Total analyses
433
- total_analyses = self.execute_query_one("SELECT COUNT(*) as count FROM ai_analyses")
434
- analytics['total_analyses'] = total_analyses['count'] if total_analyses else 0
435
 
436
  # Average processing time
437
- avg_time = self.execute_query_one("SELECT AVG(processing_time) as avg_time FROM ai_analyses WHERE processing_time IS NOT NULL")
438
- analytics['avg_processing_time'] = round(avg_time['avg_time'], 2) if avg_time and avg_time['avg_time'] else 0
 
 
 
439
 
440
  # High risk count
441
- high_risk = self.execute_query_one("SELECT COUNT(*) as count FROM ai_analyses WHERE risk_level = 'High'")
442
- analytics['high_risk_count'] = high_risk['count'] if high_risk else 0
 
 
 
443
 
444
  # Average risk score
445
- avg_risk = self.execute_query_one("SELECT AVG(risk_score) as avg_risk FROM ai_analyses WHERE risk_score IS NOT NULL")
446
- analytics['avg_risk_score'] = round(avg_risk['avg_risk'], 1) if avg_risk and avg_risk['avg_risk'] else 0
447
-
448
- # Risk level distribution
449
- risk_distribution = self.execute_query("""
450
- SELECT risk_level, COUNT(*) as count
451
- FROM ai_analyses
452
- GROUP BY risk_level
453
- """, fetch=True)
454
-
455
- analytics['risk_level_distribution'] = {}
456
- if risk_distribution:
457
- for row in risk_distribution:
458
- analytics['risk_level_distribution'][row['risk_level']] = row['count']
459
-
460
- # Recent analyses
461
- recent_analyses = self.execute_query("""
462
- SELECT * FROM ai_analyses
463
- ORDER BY created_at DESC
464
- LIMIT 10
465
- """, fetch=True)
466
- analytics['recent_analyses'] = recent_analyses or []
467
-
468
- # Model performance
469
- model_performance = self.execute_query("""
470
- SELECT
471
- model_version,
472
- COUNT(*) as count,
473
- AVG(processing_time) as avg_processing_time,
474
- AVG(risk_score) as avg_risk_score
475
- FROM ai_analyses
476
- WHERE model_version IS NOT NULL
477
- GROUP BY model_version
478
- """, fetch=True)
479
- analytics['model_performance'] = model_performance or []
480
-
481
- # Today's analyses
482
- today_analyses = self.execute_query_one("""
483
- SELECT COUNT(*) as count
484
- FROM ai_analyses
485
  WHERE DATE(created_at) = CURDATE()
486
  """)
487
- analytics['analyses_today'] = today_analyses['count'] if today_analyses else 0
488
 
489
- # This week's analyses
490
- week_analyses = self.execute_query_one("""
491
- SELECT COUNT(*) as count
492
- FROM ai_analyses
493
  WHERE YEARWEEK(created_at) = YEARWEEK(NOW())
494
  """)
495
- analytics['analyses_this_week'] = week_analyses['count'] if week_analyses else 0
496
 
497
- # Unique questionnaires
498
- unique_questionnaires = self.execute_query_one("""
499
- SELECT COUNT(DISTINCT questionnaire_id) as count
500
- FROM ai_analyses
501
  """)
502
- analytics['unique_questionnaires'] = unique_questionnaires['count'] if unique_questionnaires else 0
503
 
504
  # Analyses with images
505
- with_images = self.execute_query_one("""
506
- SELECT COUNT(*) as count
507
- FROM ai_analyses
508
  WHERE image_id IS NOT NULL
509
  """)
510
- analytics['analyses_with_images'] = with_images['count'] if with_images else 0
511
 
512
  return analytics
513
 
514
  except Exception as e:
515
- logging.error(f"Error getting analytics data: {e}")
516
  return {}
517
 
518
- def get_interaction_history(self, limit: int = 50) -> List[Dict[str, Any]]:
519
- """
520
- Get bot interaction history for dashboard
521
- """
522
  try:
523
  query = """
524
- SELECT bi.*, p.name as patient_name, u.name as practitioner_name
 
 
 
 
 
 
 
 
525
  FROM bot_interactions bi
526
  LEFT JOIN patients p ON bi.patient_id = p.id
527
  LEFT JOIN users u ON bi.practitioner_id = u.id
@@ -529,43 +526,38 @@ class DashboardDatabaseManager:
529
  LIMIT %s
530
  """
531
 
532
- interactions = self.execute_query(query, (limit,), fetch=True)
533
- return interactions or []
534
 
535
  except Exception as e:
536
- logging.error(f"Error getting interaction history: {e}")
537
  return []
538
 
539
- def get_session_analytics(self) -> Dict[str, Any]:
540
- """
541
- Get session analytics for dashboard
542
- """
543
  try:
544
  analytics = {}
545
 
546
  # Total sessions
547
- total_sessions = self.execute_query_one("SELECT COUNT(*) as count FROM analysis_sessions")
548
- analytics['total_sessions'] = total_sessions['count'] if total_sessions else 0
549
 
550
  # Average session duration
551
- avg_duration = self.execute_query_one("""
552
- SELECT AVG(session_duration) as avg_duration
553
- FROM analysis_sessions
554
  WHERE session_duration IS NOT NULL
555
  """)
556
- analytics['avg_session_duration'] = round(avg_duration['avg_duration'], 2) if avg_duration and avg_duration['avg_duration'] else 0
557
 
558
  # Sessions today
559
- today_sessions = self.execute_query_one("""
560
- SELECT COUNT(*) as count
561
- FROM analysis_sessions
562
  WHERE DATE(created_at) = CURDATE()
563
  """)
564
- analytics['sessions_today'] = today_sessions['count'] if today_sessions else 0
565
 
566
  return analytics
567
 
568
  except Exception as e:
569
- logging.error(f"Error getting session analytics: {e}")
570
- return {}
571
-
 
6
  import uuid
7
  import os
8
  from PIL import Image
 
9
 
10
+ class FixedDatabaseManager:
11
+ """Fixed database operations manager for SmartHeal application with dashboard integration"""
12
 
13
  def __init__(self, mysql_config):
14
  """Initialize database manager with MySQL configuration"""
 
86
  if connection and connection.is_connected():
87
  connection.close()
88
 
89
+ def save_questionnaire_response(self, questionnaire_data, user_id):
90
+ """Save questionnaire response to database"""
 
 
 
 
 
 
91
  connection = None
92
  cursor = None
93
  try:
 
95
  if not connection:
96
  return None
97
  cursor = connection.cursor()
98
+
99
+ # Create or get patient
100
  patient_id = self._create_or_get_patient(cursor, questionnaire_data)
101
  if not patient_id:
102
  raise Exception("Failed to get or create patient")
103
+
104
+ # Get or create default questionnaire
105
  questionnaire_id = self._get_or_create_default_questionnaire(cursor)
106
  if not questionnaire_id:
107
  raise Exception("Failed to get or create questionnaire")
108
+
109
+ # Prepare response data JSON
110
  response_data = {
111
  'patient_info': {
112
  'name': questionnaire_data.get('patient_name', ''),
 
129
  'additional_notes': questionnaire_data.get('additional_notes', '')
130
  }
131
  }
132
+
133
+ # Insert into questionnaire_responses
134
  insert_resp = """
135
  INSERT INTO questionnaire_responses
136
  (questionnaire_id, patient_id, practitioner_id, response_data, submitted_at)
 
144
  datetime.now()
145
  ))
146
  response_id = cursor.lastrowid
147
+
148
  connection.commit()
149
  logging.info(f"✅ Saved questionnaire response ID {response_id}")
150
  return response_id
151
+
152
  except Exception as e:
153
+ logging.error(f"❌ Error saving questionnaire: {e}")
154
  if connection:
155
  connection.rollback()
156
  return None
 
160
  if connection:
161
  connection.close()
162
 
163
+ def _create_or_get_patient(self, cursor, questionnaire_data):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  """Create or get existing patient record"""
165
  try:
166
+ # Check if patient exists
167
  select_query = """
168
  SELECT id FROM patients
169
+ WHERE name = %s AND age = %s AND gender = %s
 
170
  """
171
  cursor.execute(select_query, (
172
  questionnaire_data.get('patient_name', ''),
173
+ questionnaire_data.get('patient_age', 0),
174
+ questionnaire_data.get('patient_gender', '')
175
  ))
176
 
177
  existing_patient = cursor.fetchone()
 
203
  logging.error(f"Error creating/getting patient: {e}")
204
  return None
205
 
206
+ def _get_or_create_default_questionnaire(self, cursor):
207
+ """Get or create default questionnaire"""
208
  try:
209
+ # Check if default questionnaire exists
210
+ cursor.execute("SELECT id FROM questionnaires WHERE name = 'Default Patient Assessment' LIMIT 1")
211
+ questionnaire_row = cursor.fetchone()
212
+
213
+ if questionnaire_row:
214
+ return questionnaire_row[0]
215
+
216
+ # Create default questionnaire
217
+ cursor.execute("""
218
+ INSERT INTO questionnaires (name, description, created_at)
219
+ VALUES ('Default Patient Assessment', 'Standard patient wound assessment form', NOW())
220
+ """)
221
+ return cursor.lastrowid
222
+
223
+ except Exception as e:
224
+ logging.error(f"Error getting/creating questionnaire: {e}")
225
+ return None
226
+
227
+ def save_wound_image(self, response_id, image, filename):
228
+ """Save wound image to dataset and database with proper URL"""
229
+ try:
230
+ # Generate unique filename with timestamp
231
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
232
+ unique_id = str(uuid.uuid4())[:8]
233
+ file_extension = os.path.splitext(filename)[1] or '.jpg'
234
+ unique_filename = f"wound_{timestamp}_{unique_id}{file_extension}"
235
 
236
+ # Create dataset directory structure
237
+ dataset_dir = os.path.join("dataset", "wound_images")
238
+ os.makedirs(dataset_dir, exist_ok=True)
239
+
240
+ # Save image to dataset
241
+ file_path = os.path.join(dataset_dir, unique_filename)
242
 
 
243
  if hasattr(image, 'save'):
244
  image.save(file_path, format='JPEG', quality=95)
 
 
245
  width, height = image.size
246
  file_size = os.path.getsize(file_path)
247
+ else:
248
+ # Handle numpy array or other formats
249
+ from PIL import Image as PILImage
250
+ if hasattr(image, 'shape'):
251
+ pil_image = PILImage.fromarray(image)
252
+ pil_image.save(file_path, format='JPEG', quality=95)
253
+ width, height = pil_image.size
254
+ file_size = os.path.getsize(file_path)
255
+ else:
256
+ raise ValueError("Unsupported image format")
257
+
258
+ # Create URL for dashboard access
259
+ image_url = f"/dataset/wound_images/{unique_filename}"
260
+
261
+ # Save to database
262
+ query = """
263
+ INSERT INTO wound_images (
264
+ questionnaire_response_id, image_url, original_filename,
265
+ file_size, image_width, image_height, created_at
266
+ ) VALUES (%s, %s, %s, %s, %s, %s, %s)
267
+ """
268
+
269
+ params = (
270
+ response_id,
271
+ image_url,
272
+ filename,
273
+ file_size,
274
+ width,
275
+ height,
276
+ datetime.now()
277
+ )
278
+
279
+ connection = self.get_connection()
280
+ if not connection:
281
+ return None
282
+
283
+ try:
284
+ cursor = connection.cursor()
285
+ cursor.execute(query, params)
286
+ connection.commit()
287
+ image_id = cursor.lastrowid
288
 
289
+ logging.info(f"✅ Image saved to dataset: {file_path}")
290
+ logging.info(f"✅ Image URL: {image_url}")
291
+ logging.info(f"✅ Database image ID: {image_id}")
 
 
 
 
292
 
293
+ return image_id
 
 
 
 
 
 
 
 
294
 
295
+ finally:
296
+ cursor.close()
297
+ connection.close()
 
 
 
 
 
 
 
 
 
298
 
299
  except Exception as e:
300
  logging.error(f"❌ Error saving wound image: {e}")
301
+ return None
 
302
 
303
+ def save_ai_analysis(self, analysis_data):
304
+ """Save AI analysis results to database"""
 
 
305
  try:
306
+ # Extract data from analysis_data
307
+ response_id = analysis_data.get('questionnaire_id') # This is actually response_id
308
+ image_id = analysis_data.get('image_id')
309
+ visual_results = analysis_data.get('visual_results', {})
 
 
 
310
 
311
+ # Calculate risk level from risk score
312
  risk_score = analysis_data.get('risk_score', 0)
313
  if risk_score >= 70:
314
  risk_level = 'High'
 
317
  else:
318
  risk_level = 'Low'
319
 
320
+ # Prepare wound dimensions string
321
+ length = visual_results.get('length_cm', 0)
322
+ breadth = visual_results.get('breadth_cm', 0)
323
+ area = visual_results.get('surface_area_cm2', 0)
324
+ wound_dimensions = f"{length} × {breadth} cm (Area: {area} cm²)"
325
+
326
+ query = """
327
+ INSERT INTO ai_analyses (
328
+ questionnaire_response_id, image_id, analysis_data, summary,
329
+ recommendations, risk_score, risk_level, wound_type,
330
+ wound_dimensions, processing_time, model_version, created_at
331
+ ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
332
+ """
333
 
334
  params = (
335
+ response_id,
336
+ image_id,
337
  json.dumps(analysis_data.get('analysis_data', {})),
338
+ analysis_data.get('summary', '')[:1000], # Limit summary length
339
  analysis_data.get('recommendations', ''),
340
  risk_score,
341
  risk_level,
342
  visual_results.get('wound_type', 'Unknown'),
343
  wound_dimensions,
344
+ analysis_data.get('processing_time', 0),
345
  analysis_data.get('model_version', 'v1.0'),
346
  datetime.now()
347
  )
348
 
349
  result = self.execute_query(query, params)
350
+ if result is not None:
351
+ # Get the last inserted ID
352
  connection = self.get_connection()
353
+ if connection:
354
+ cursor = connection.cursor()
355
+ cursor.execute("SELECT LAST_INSERT_ID()")
356
+ analysis_id = cursor.fetchone()[0]
357
+ cursor.close()
358
+ connection.close()
359
+
360
+ logging.info(f"✅ AI analysis saved with ID: {analysis_id}")
361
+ return analysis_id
362
+
363
+ return None
364
+
365
  except Exception as e:
366
  logging.error(f"❌ Error saving AI analysis: {e}")
367
+ return None
 
368
 
369
+ def save_analysis_session(self, session_data):
370
+ """Save analysis session data"""
 
 
371
  try:
372
  query = """
373
  INSERT INTO analysis_sessions (
374
+ user_id, questionnaire_response_id, image_id, analysis_id,
375
  session_duration, created_at
376
  ) VALUES (%s, %s, %s, %s, %s, %s)
377
  """
378
 
379
  params = (
380
  session_data.get('user_id'),
381
+ session_data.get('questionnaire_id'), # This is actually response_id
382
  session_data.get('image_id'),
383
  session_data.get('analysis_id'),
384
+ session_data.get('session_duration', 0),
385
  datetime.now()
386
  )
387
 
388
  result = self.execute_query(query, params)
389
+ if result is not None:
390
  connection = self.get_connection()
391
+ if connection:
392
+ cursor = connection.cursor()
393
+ cursor.execute("SELECT LAST_INSERT_ID()")
394
+ session_id = cursor.fetchone()[0]
395
+ cursor.close()
396
+ connection.close()
397
+
398
+ logging.info(f"✅ Analysis session saved with ID: {session_id}")
399
+ return session_id
400
+
401
+ return None
402
+
403
  except Exception as e:
404
  logging.error(f"❌ Error saving analysis session: {e}")
405
+ return None
 
406
 
407
+ def save_bot_interaction(self, interaction_data):
408
+ """Save bot interaction data"""
 
 
409
  try:
410
  query = """
411
  INSERT INTO bot_interactions (
412
+ patient_id, practitioner_id, input_text, output_text,
413
  wound_image_url, interaction_type, interacted_at
414
  ) VALUES (%s, %s, %s, %s, %s, %s, %s)
415
  """
 
420
  interaction_data.get('input_text', ''),
421
  interaction_data.get('output_text', ''),
422
  interaction_data.get('wound_image_url', ''),
423
+ interaction_data.get('interaction_type', 'wound_analysis'),
424
  datetime.now()
425
  )
426
 
427
  result = self.execute_query(query, params)
428
+ if result is not None:
429
  connection = self.get_connection()
430
+ if connection:
431
+ cursor = connection.cursor()
432
+ cursor.execute("SELECT LAST_INSERT_ID()")
433
+ interaction_id = cursor.fetchone()[0]
434
+ cursor.close()
435
+ connection.close()
436
+
437
+ logging.info(f"✅ Bot interaction saved with ID: {interaction_id}")
438
+ return interaction_id
439
+
440
+ return None
441
+
442
  except Exception as e:
443
  logging.error(f"❌ Error saving bot interaction: {e}")
444
+ return None
 
445
 
446
+ def get_analytics_data(self):
447
+ """Get comprehensive analytics data for dashboard"""
 
 
448
  try:
449
  analytics = {}
450
 
451
  # Total analyses
452
+ result = self.execute_query_one("SELECT COUNT(*) as count FROM ai_analyses")
453
+ analytics['total_analyses'] = result['count'] if result else 0
454
 
455
  # Average processing time
456
+ result = self.execute_query_one("""
457
+ SELECT AVG(processing_time) as avg_time FROM ai_analyses
458
+ WHERE processing_time IS NOT NULL
459
+ """)
460
+ analytics['avg_processing_time'] = round(result['avg_time'], 2) if result and result['avg_time'] else 0
461
 
462
  # High risk count
463
+ result = self.execute_query_one("""
464
+ SELECT COUNT(*) as count FROM ai_analyses
465
+ WHERE risk_level = 'High'
466
+ """)
467
+ analytics['high_risk_count'] = result['count'] if result else 0
468
 
469
  # Average risk score
470
+ result = self.execute_query_one("""
471
+ SELECT AVG(risk_score) as avg_risk FROM ai_analyses
472
+ WHERE risk_score IS NOT NULL
473
+ """)
474
+ analytics['avg_risk_score'] = round(result['avg_risk'], 1) if result and result['avg_risk'] else 0
475
+
476
+ # Analyses today
477
+ result = self.execute_query_one("""
478
+ SELECT COUNT(*) as count FROM ai_analyses
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  WHERE DATE(created_at) = CURDATE()
480
  """)
481
+ analytics['analyses_today'] = result['count'] if result else 0
482
 
483
+ # Analyses this week
484
+ result = self.execute_query_one("""
485
+ SELECT COUNT(*) as count FROM ai_analyses
 
486
  WHERE YEARWEEK(created_at) = YEARWEEK(NOW())
487
  """)
488
+ analytics['analyses_this_week'] = result['count'] if result else 0
489
 
490
+ # Unique questionnaire responses
491
+ result = self.execute_query_one("""
492
+ SELECT COUNT(DISTINCT questionnaire_response_id) as count FROM ai_analyses
 
493
  """)
494
+ analytics['unique_questionnaires'] = result['count'] if result else 0
495
 
496
  # Analyses with images
497
+ result = self.execute_query_one("""
498
+ SELECT COUNT(*) as count FROM ai_analyses
 
499
  WHERE image_id IS NOT NULL
500
  """)
501
+ analytics['analyses_with_images'] = result['count'] if result else 0
502
 
503
  return analytics
504
 
505
  except Exception as e:
506
+ logging.error(f"Error getting analytics data: {e}")
507
  return {}
508
 
509
+ def get_interaction_history(self, limit=50):
510
+ """Get bot interaction history"""
 
 
511
  try:
512
  query = """
513
+ SELECT
514
+ bi.id,
515
+ bi.input_text,
516
+ bi.output_text,
517
+ bi.wound_image_url,
518
+ bi.interaction_type,
519
+ bi.interacted_at,
520
+ p.name as patient_name,
521
+ u.name as practitioner_name
522
  FROM bot_interactions bi
523
  LEFT JOIN patients p ON bi.patient_id = p.id
524
  LEFT JOIN users u ON bi.practitioner_id = u.id
 
526
  LIMIT %s
527
  """
528
 
529
+ results = self.execute_query(query, (limit,), fetch=True)
530
+ return results or []
531
 
532
  except Exception as e:
533
+ logging.error(f"Error getting interaction history: {e}")
534
  return []
535
 
536
+ def get_session_analytics(self):
537
+ """Get session analytics data"""
 
 
538
  try:
539
  analytics = {}
540
 
541
  # Total sessions
542
+ result = self.execute_query_one("SELECT COUNT(*) as count FROM analysis_sessions")
543
+ analytics['total_sessions'] = result['count'] if result else 0
544
 
545
  # Average session duration
546
+ result = self.execute_query_one("""
547
+ SELECT AVG(session_duration) as avg_duration FROM analysis_sessions
 
548
  WHERE session_duration IS NOT NULL
549
  """)
550
+ analytics['avg_session_duration'] = round(result['avg_duration'], 2) if result and result['avg_duration'] else 0
551
 
552
  # Sessions today
553
+ result = self.execute_query_one("""
554
+ SELECT COUNT(*) as count FROM analysis_sessions
 
555
  WHERE DATE(created_at) = CURDATE()
556
  """)
557
+ analytics['sessions_today'] = result['count'] if result else 0
558
 
559
  return analytics
560
 
561
  except Exception as e:
562
+ logging.error(f"Error getting session analytics: {e}")
563
+ return {}