snikhilesh commited on
Commit
21e0330
·
verified ·
1 Parent(s): f5c6b5e

Deploy admin_endpoints.py to backend/ directory

Browse files
Files changed (1) hide show
  1. backend/admin_endpoints.py +630 -0
backend/admin_endpoints.py ADDED
@@ -0,0 +1,630 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Admin UI Backend Endpoints
3
+ Administrative controls for system oversight and review management
4
+
5
+ Features:
6
+ - Review queue management
7
+ - System configuration
8
+ - User management (placeholder)
9
+ - Performance monitoring dashboard
10
+ - Compliance reporting interface
11
+ - Model versioning controls
12
+
13
+ Author: MiniMax Agent
14
+ Date: 2025-10-29
15
+ Version: 1.0.0
16
+ """
17
+
18
+ from fastapi import APIRouter, HTTPException, Depends
19
+ from typing import Dict, List, Any, Optional
20
+ from datetime import datetime, timedelta
21
+ from pydantic import BaseModel
22
+
23
+ from monitoring_service import get_monitoring_service
24
+ from model_versioning import get_versioning_system
25
+ from production_logging import get_medical_logger
26
+ from compliance_reporting import get_compliance_system
27
+
28
+
29
+ # Create admin router
30
+ admin_router = APIRouter(prefix="/admin", tags=["admin"])
31
+
32
+
33
+ # ================================
34
+ # REQUEST/RESPONSE MODELS
35
+ # ================================
36
+
37
+ class ReviewQueueItem(BaseModel):
38
+ """Review queue item"""
39
+ item_id: str
40
+ document_id: str
41
+ document_type: str
42
+ confidence_score: float
43
+ risk_level: str
44
+ created_at: str
45
+ assigned_to: Optional[str] = None
46
+ priority: str # "critical", "high", "medium", "low"
47
+
48
+
49
+ class ReviewAction(BaseModel):
50
+ """Review action request"""
51
+ item_id: str
52
+ reviewer_id: str
53
+ action: str # "approve", "reject", "escalate"
54
+ comments: Optional[str] = None
55
+
56
+
57
+ class SystemConfiguration(BaseModel):
58
+ """System configuration"""
59
+ error_threshold: float = 0.05
60
+ cache_size_mb: int = 1000
61
+ cache_ttl_hours: int = 24
62
+ alert_email: Optional[str] = None
63
+
64
+
65
+ class ModelDeployment(BaseModel):
66
+ """Model deployment request"""
67
+ model_id: str
68
+ version: str
69
+ set_active: bool = False
70
+
71
+
72
+ # ================================
73
+ # REVIEW QUEUE ENDPOINTS
74
+ # ================================
75
+
76
+ # In-memory review queue (in production, use database)
77
+ review_queue: List[ReviewQueueItem] = []
78
+
79
+
80
+ @admin_router.get("/review-queue")
81
+ async def get_review_queue(
82
+ priority: Optional[str] = None,
83
+ status: Optional[str] = None
84
+ ) -> Dict[str, Any]:
85
+ """Get current review queue"""
86
+
87
+ filtered_queue = review_queue
88
+
89
+ if priority:
90
+ filtered_queue = [item for item in filtered_queue if item.priority == priority]
91
+
92
+ return {
93
+ "total_items": len(review_queue),
94
+ "filtered_items": len(filtered_queue),
95
+ "queue": [item.dict() for item in filtered_queue],
96
+ "summary": {
97
+ "critical": len([i for i in review_queue if i.priority == "critical"]),
98
+ "high": len([i for i in review_queue if i.priority == "high"]),
99
+ "medium": len([i for i in review_queue if i.priority == "medium"]),
100
+ "low": len([i for i in review_queue if i.priority == "low"])
101
+ }
102
+ }
103
+
104
+
105
+ @admin_router.post("/review-queue/action")
106
+ async def submit_review_action(action: ReviewAction) -> Dict[str, Any]:
107
+ """Submit review action (approve/reject/escalate)"""
108
+
109
+ # Find item in queue
110
+ item = next((i for i in review_queue if i.item_id == action.item_id), None)
111
+
112
+ if not item:
113
+ raise HTTPException(status_code=404, detail="Review item not found")
114
+
115
+ # Log review action
116
+ logger = get_medical_logger()
117
+ logger.info(
118
+ f"Review action: {action.action} on {action.item_id}",
119
+ user_id=action.reviewer_id,
120
+ document_id=item.document_id,
121
+ details={"action": action.action, "comments": action.comments}
122
+ )
123
+
124
+ # Log to compliance system
125
+ compliance = get_compliance_system()
126
+ compliance.log_audit_event(
127
+ user_id=action.reviewer_id,
128
+ event_type="REVIEW",
129
+ resource=f"document:{item.document_id}",
130
+ action=action.action.upper(),
131
+ ip_address="internal",
132
+ details={"item_id": action.item_id, "comments": action.comments}
133
+ )
134
+
135
+ # Remove from queue if approved or rejected
136
+ if action.action in ["approve", "reject"]:
137
+ review_queue.remove(item)
138
+
139
+ return {
140
+ "success": True,
141
+ "action": action.action,
142
+ "item_id": action.item_id,
143
+ "message": f"Review {action.action}d successfully"
144
+ }
145
+
146
+
147
+ @admin_router.post("/review-queue/assign")
148
+ async def assign_review(
149
+ item_id: str,
150
+ reviewer_id: str
151
+ ) -> Dict[str, Any]:
152
+ """Assign review to a reviewer"""
153
+
154
+ item = next((i for i in review_queue if i.item_id == item_id), None)
155
+
156
+ if not item:
157
+ raise HTTPException(status_code=404, detail="Review item not found")
158
+
159
+ item.assigned_to = reviewer_id
160
+
161
+ return {
162
+ "success": True,
163
+ "item_id": item_id,
164
+ "assigned_to": reviewer_id
165
+ }
166
+
167
+
168
+ # ================================
169
+ # MONITORING DASHBOARD ENDPOINTS
170
+ # ================================
171
+
172
+ @admin_router.get("/dashboard")
173
+ async def get_admin_dashboard() -> Dict[str, Any]:
174
+ """Get comprehensive admin dashboard data"""
175
+
176
+ monitoring = get_monitoring_service()
177
+ versioning = get_versioning_system()
178
+ compliance = get_compliance_system()
179
+
180
+ return {
181
+ "timestamp": datetime.utcnow().isoformat(),
182
+ "system_health": monitoring.get_system_health(),
183
+ "performance_dashboard": monitoring.get_performance_dashboard(),
184
+ "model_inventory": versioning.get_system_status(),
185
+ "compliance_dashboard": compliance.get_compliance_dashboard(),
186
+ "review_queue_summary": {
187
+ "total_items": len(review_queue),
188
+ "critical_items": len([i for i in review_queue if i.priority == "critical"]),
189
+ "unassigned_items": len([i for i in review_queue if not i.assigned_to])
190
+ }
191
+ }
192
+
193
+
194
+ @admin_router.get("/metrics/performance")
195
+ async def get_performance_metrics(
196
+ window_minutes: int = 60
197
+ ) -> Dict[str, Any]:
198
+ """Get detailed performance metrics"""
199
+
200
+ monitoring = get_monitoring_service()
201
+
202
+ # Get statistics for key stages
203
+ stages = ["pdf_processing", "classification", "model_routing", "synthesis"]
204
+
205
+ performance_data = {}
206
+ for stage in stages:
207
+ stats = monitoring.latency_tracker.get_stage_statistics(stage, window_minutes)
208
+ performance_data[stage] = stats
209
+
210
+ error_summary = monitoring.error_monitor.get_error_summary()
211
+
212
+ return {
213
+ "window_minutes": window_minutes,
214
+ "latency_by_stage": performance_data,
215
+ "error_summary": error_summary,
216
+ "timestamp": datetime.utcnow().isoformat()
217
+ }
218
+
219
+
220
+ @admin_router.get("/metrics/cache")
221
+ async def get_cache_metrics() -> Dict[str, Any]:
222
+ """Get cache performance metrics"""
223
+
224
+ versioning = get_versioning_system()
225
+ cache_stats = versioning.input_cache.get_statistics()
226
+
227
+ return {
228
+ "cache_statistics": cache_stats,
229
+ "recommendations": _generate_cache_recommendations(cache_stats),
230
+ "timestamp": datetime.utcnow().isoformat()
231
+ }
232
+
233
+
234
+ # ================================
235
+ # MODEL MANAGEMENT ENDPOINTS
236
+ # ================================
237
+
238
+ @admin_router.get("/models/inventory")
239
+ async def get_model_inventory() -> Dict[str, Any]:
240
+ """Get complete model inventory"""
241
+
242
+ versioning = get_versioning_system()
243
+ inventory = versioning.model_registry.get_model_inventory()
244
+
245
+ return {
246
+ "inventory": inventory,
247
+ "summary": {
248
+ "total_models": len(inventory),
249
+ "total_versions": sum(data["total_versions"] for data in inventory.values())
250
+ },
251
+ "timestamp": datetime.utcnow().isoformat()
252
+ }
253
+
254
+
255
+ @admin_router.post("/models/deploy")
256
+ async def deploy_model_version(deployment: ModelDeployment) -> Dict[str, Any]:
257
+ """Deploy a model version"""
258
+
259
+ versioning = get_versioning_system()
260
+
261
+ try:
262
+ if deployment.set_active:
263
+ versioning.model_registry.set_active_version(
264
+ deployment.model_id,
265
+ deployment.version
266
+ )
267
+
268
+ # Invalidate cache for this model
269
+ versioning.input_cache.invalidate_model_version(deployment.version)
270
+
271
+ return {
272
+ "success": True,
273
+ "model_id": deployment.model_id,
274
+ "version": deployment.version,
275
+ "active": deployment.set_active,
276
+ "message": f"Model {deployment.model_id} v{deployment.version} deployed"
277
+ }
278
+
279
+ except Exception as e:
280
+ raise HTTPException(status_code=400, detail=str(e))
281
+
282
+
283
+ @admin_router.post("/models/rollback")
284
+ async def rollback_model(
285
+ model_id: str,
286
+ version: str
287
+ ) -> Dict[str, Any]:
288
+ """Rollback to a previous model version"""
289
+
290
+ versioning = get_versioning_system()
291
+
292
+ success = versioning.model_registry.rollback_to_version(model_id, version)
293
+
294
+ if not success:
295
+ raise HTTPException(status_code=404, detail="Model version not found")
296
+
297
+ # Invalidate cache
298
+ versioning.input_cache.invalidate_model_version(version)
299
+
300
+ return {
301
+ "success": True,
302
+ "model_id": model_id,
303
+ "rolled_back_to": version,
304
+ "message": f"Rolled back {model_id} to v{version}"
305
+ }
306
+
307
+
308
+ @admin_router.get("/models/compare")
309
+ async def compare_model_versions(
310
+ model_id: str,
311
+ version1: str,
312
+ version2: str,
313
+ metric: str = "accuracy"
314
+ ) -> Dict[str, Any]:
315
+ """Compare two model versions"""
316
+
317
+ versioning = get_versioning_system()
318
+ comparison = versioning.model_registry.compare_versions(
319
+ model_id, version1, version2, metric
320
+ )
321
+
322
+ return comparison
323
+
324
+
325
+ # ================================
326
+ # COMPLIANCE ENDPOINTS
327
+ # ================================
328
+
329
+ @admin_router.get("/compliance/hipaa-report")
330
+ async def get_hipaa_report(
331
+ days: int = 30
332
+ ) -> Dict[str, Any]:
333
+ """Generate HIPAA compliance report"""
334
+
335
+ compliance = get_compliance_system()
336
+
337
+ end_date = datetime.utcnow()
338
+ start_date = end_date - timedelta(days=days)
339
+
340
+ report = compliance.generate_hipaa_report(start_date, end_date)
341
+
342
+ return report
343
+
344
+
345
+ @admin_router.get("/compliance/gdpr-report")
346
+ async def get_gdpr_report(
347
+ days: int = 30
348
+ ) -> Dict[str, Any]:
349
+ """Generate GDPR compliance report"""
350
+
351
+ compliance = get_compliance_system()
352
+
353
+ end_date = datetime.utcnow()
354
+ start_date = end_date - timedelta(days=days)
355
+
356
+ report = compliance.generate_gdpr_report(start_date, end_date)
357
+
358
+ return report
359
+
360
+
361
+ @admin_router.get("/compliance/quality-metrics")
362
+ async def get_quality_metrics(
363
+ days: int = 30
364
+ ) -> Dict[str, Any]:
365
+ """Get clinical quality metrics"""
366
+
367
+ compliance = get_compliance_system()
368
+ report = compliance.generate_quality_metrics_report(days)
369
+
370
+ return report
371
+
372
+
373
+ @admin_router.get("/compliance/security-incidents")
374
+ async def get_security_incidents(
375
+ days: int = 30
376
+ ) -> Dict[str, Any]:
377
+ """Get security incidents report"""
378
+
379
+ compliance = get_compliance_system()
380
+ report = compliance.generate_security_incidents_report(days)
381
+
382
+ return report
383
+
384
+
385
+ # ================================
386
+ # SYSTEM CONFIGURATION ENDPOINTS
387
+ # ================================
388
+
389
+ # In-memory configuration (in production, use database)
390
+ system_config = SystemConfiguration()
391
+
392
+
393
+ @admin_router.get("/config")
394
+ async def get_system_configuration() -> SystemConfiguration:
395
+ """Get current system configuration"""
396
+ return system_config
397
+
398
+
399
+ @admin_router.post("/config")
400
+ async def update_system_configuration(
401
+ config: SystemConfiguration
402
+ ) -> Dict[str, Any]:
403
+ """Update system configuration"""
404
+
405
+ global system_config
406
+ system_config = config
407
+
408
+ logger = get_medical_logger()
409
+ logger.info(
410
+ "System configuration updated",
411
+ details=config.dict()
412
+ )
413
+
414
+ return {
415
+ "success": True,
416
+ "config": config.dict(),
417
+ "message": "System configuration updated"
418
+ }
419
+
420
+
421
+ @admin_router.post("/cache/clear")
422
+ async def clear_cache() -> Dict[str, Any]:
423
+ """Clear all cache entries"""
424
+
425
+ versioning = get_versioning_system()
426
+ versioning.input_cache.clear()
427
+
428
+ return {
429
+ "success": True,
430
+ "message": "Cache cleared successfully"
431
+ }
432
+
433
+
434
+ # ================================
435
+ # ALERTS MANAGEMENT
436
+ # ================================
437
+
438
+ @admin_router.get("/alerts")
439
+ async def get_active_alerts(
440
+ level: Optional[str] = None
441
+ ) -> Dict[str, Any]:
442
+ """Get active system alerts"""
443
+
444
+ monitoring = get_monitoring_service()
445
+
446
+ from monitoring_service import AlertLevel
447
+
448
+ alert_level = None
449
+ if level:
450
+ alert_level = AlertLevel(level.upper())
451
+
452
+ alerts = monitoring.alert_manager.get_active_alerts(level=alert_level)
453
+ summary = monitoring.alert_manager.get_alert_summary()
454
+
455
+ return {
456
+ "active_alerts": [a.to_dict() for a in alerts],
457
+ "summary": summary,
458
+ "timestamp": datetime.utcnow().isoformat()
459
+ }
460
+
461
+
462
+ @admin_router.post("/alerts/{alert_id}/resolve")
463
+ async def resolve_alert(alert_id: str) -> Dict[str, Any]:
464
+ """Resolve an active alert"""
465
+
466
+ monitoring = get_monitoring_service()
467
+ monitoring.alert_manager.resolve_alert(alert_id)
468
+
469
+ return {
470
+ "success": True,
471
+ "alert_id": alert_id,
472
+ "message": "Alert resolved"
473
+ }
474
+
475
+
476
+ # ================================
477
+ # CACHE MANAGEMENT ENDPOINTS
478
+ # ================================
479
+
480
+ @admin_router.get("/cache/statistics")
481
+ async def get_cache_statistics() -> Dict[str, Any]:
482
+ """
483
+ Get comprehensive cache statistics
484
+
485
+ Returns cache performance metrics including:
486
+ - Hit/miss rates
487
+ - Memory usage
488
+ - Entry count
489
+ - Eviction statistics
490
+ """
491
+
492
+ monitoring = get_monitoring_service()
493
+ cache_stats = monitoring.get_cache_statistics()
494
+
495
+ return {
496
+ "statistics": cache_stats,
497
+ "recommendations": _generate_cache_recommendations_v2(cache_stats),
498
+ "timestamp": datetime.utcnow().isoformat()
499
+ }
500
+
501
+
502
+ @admin_router.get("/cache/entries")
503
+ async def list_cache_entries(limit: int = 100) -> Dict[str, Any]:
504
+ """
505
+ List cache entries with metadata
506
+
507
+ Args:
508
+ limit: Maximum number of entries to return (default: 100)
509
+ """
510
+
511
+ monitoring = get_monitoring_service()
512
+ entries = monitoring.cache_service.list_entries(limit=limit)
513
+
514
+ return {
515
+ "entries": entries,
516
+ "total_shown": len(entries),
517
+ "timestamp": datetime.utcnow().isoformat()
518
+ }
519
+
520
+
521
+ @admin_router.get("/cache/entry/{key}")
522
+ async def get_cache_entry_info(key: str) -> Dict[str, Any]:
523
+ """
524
+ Get detailed information about a specific cache entry
525
+
526
+ Args:
527
+ key: Cache key (SHA256 fingerprint)
528
+ """
529
+
530
+ monitoring = get_monitoring_service()
531
+ entry_info = monitoring.cache_service.get_entry_info(key)
532
+
533
+ if entry_info is None:
534
+ raise HTTPException(status_code=404, detail="Cache entry not found")
535
+
536
+ return entry_info
537
+
538
+
539
+ @admin_router.post("/cache/invalidate/{key}")
540
+ async def invalidate_cache_entry(key: str) -> Dict[str, Any]:
541
+ """
542
+ Invalidate a specific cache entry
543
+
544
+ Args:
545
+ key: Cache key (SHA256 fingerprint)
546
+ """
547
+
548
+ monitoring = get_monitoring_service()
549
+ success = monitoring.cache_service.invalidate(key)
550
+
551
+ if not success:
552
+ raise HTTPException(status_code=404, detail="Cache entry not found")
553
+
554
+ return {
555
+ "success": True,
556
+ "key": key,
557
+ "message": "Cache entry invalidated"
558
+ }
559
+
560
+
561
+ @admin_router.post("/cache/clear")
562
+ async def clear_cache() -> Dict[str, Any]:
563
+ """
564
+ Clear all cache entries
565
+
566
+ WARNING: This will clear all cached data and may temporarily impact performance
567
+ """
568
+
569
+ monitoring = get_monitoring_service()
570
+ monitoring.cache_service.clear()
571
+
572
+ return {
573
+ "success": True,
574
+ "message": "All cache entries cleared",
575
+ "timestamp": datetime.utcnow().isoformat()
576
+ }
577
+
578
+
579
+ # ================================
580
+ # HELPER FUNCTIONS
581
+ # ================================
582
+
583
+ def _generate_cache_recommendations_v2(stats: Dict[str, Any]) -> List[str]:
584
+ """Generate cache optimization recommendations based on statistics"""
585
+ recommendations = []
586
+
587
+ hit_rate = stats.get("hit_rate", 0.0)
588
+ memory_usage = stats.get("memory_usage_mb", 0.0)
589
+ max_memory = stats.get("max_memory_mb", 512)
590
+ evictions = stats.get("evictions", 0)
591
+ total_entries = stats.get("total_entries", 0)
592
+
593
+ # Hit rate recommendations
594
+ if hit_rate < 0.5:
595
+ recommendations.append(f"Low cache hit rate ({hit_rate*100:.1f}%). Consider increasing cache size or TTL.")
596
+ elif hit_rate > 0.8:
597
+ recommendations.append(f"Excellent cache hit rate ({hit_rate*100:.1f}%). Cache performing optimally.")
598
+
599
+ # Memory recommendations
600
+ utilization = (memory_usage / max_memory) * 100 if max_memory > 0 else 0
601
+ if utilization > 90:
602
+ recommendations.append(f"Cache near capacity ({utilization:.1f}% used). Consider increasing max cache size.")
603
+
604
+ # Eviction recommendations
605
+ if total_entries > 0 and evictions > total_entries * 0.1:
606
+ recommendations.append(f"High eviction rate ({evictions} evictions). Increase cache size to improve performance.")
607
+
608
+ # Default message
609
+ if not recommendations:
610
+ recommendations.append("Cache performing within normal parameters.")
611
+
612
+ return recommendations
613
+
614
+ def _generate_cache_recommendations(stats: Dict[str, Any]) -> List[str]:
615
+ """Generate cache optimization recommendations"""
616
+ recommendations = []
617
+
618
+ if stats["hit_rate_percent"] < 50:
619
+ recommendations.append("Low cache hit rate. Consider increasing cache size or TTL.")
620
+
621
+ if stats["utilization_percent"] > 90:
622
+ recommendations.append("Cache near capacity. Consider increasing max cache size.")
623
+
624
+ if stats["evictions"] > stats["total_requests"] * 0.1:
625
+ recommendations.append("High eviction rate. Increase cache size to improve performance.")
626
+
627
+ if not recommendations:
628
+ recommendations.append("Cache performing optimally.")
629
+
630
+ return recommendations