Spaces:
Sleeping
Sleeping
mystic_CBK
commited on
Commit
Β·
58e60a2
1
Parent(s):
e548411
Fix JSON serialization error: Convert all numpy types to Python native types for clinical analysis
Browse files- clinical_analysis.py +16 -12
- comprehensive_ecg_test.py +319 -0
- comprehensive_ecg_test_results_20250828_151718.json +233 -0
- finetuned_clinical_test_results_20250828_152404.json +176 -0
- physiological_parameter_test_results_20250828_145018.json +227 -0
- physiological_parameter_test_results_20250828_150828.json +245 -0
- quick_test_fix.py +123 -0
- simple_finetuned_test.py +124 -0
- test_endpoints_status.py +124 -0
- test_finetuned_clinical.py +323 -0
- test_measurement_accuracy.py +218 -0
- test_physiological_parameters.py +1 -1
clinical_analysis.py
CHANGED
|
@@ -126,23 +126,27 @@ def extract_clinical_from_probabilities(probs: np.ndarray) -> Dict[str, Any]:
|
|
| 126 |
# Calculate confidence metrics
|
| 127 |
confidence_metrics = calculate_confidence_metrics(probs, thresholds)
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
return {
|
| 130 |
-
"rhythm": rhythm,
|
| 131 |
"heart_rate": None, # Will be calculated from features if available
|
| 132 |
"qrs_duration": None, # Will be calculated from features if available
|
| 133 |
"qt_interval": None, # Will be calculated from features if available
|
| 134 |
"pr_interval": None, # Will be calculated from features if available
|
| 135 |
"axis_deviation": "Normal", # Will be calculated from features if available
|
| 136 |
-
"abnormalities": abnormalities,
|
| 137 |
-
"confidence": confidence_metrics["overall_confidence"],
|
| 138 |
-
"confidence_level": confidence_metrics["confidence_level"],
|
| 139 |
-
"review_required": confidence_metrics["review_required"],
|
| 140 |
-
"probabilities":
|
| 141 |
-
"label_probabilities":
|
| 142 |
"method": "clinical_predictions",
|
| 143 |
"warning": None,
|
| 144 |
-
"labels_used": labels,
|
| 145 |
-
"thresholds_used": thresholds
|
| 146 |
}
|
| 147 |
|
| 148 |
except Exception as e:
|
|
@@ -310,9 +314,9 @@ def calculate_confidence_metrics(probs: np.ndarray, thresholds: Dict[str, float]
|
|
| 310 |
review_required = max_prob < 0.6 or mean_prob < 0.4
|
| 311 |
|
| 312 |
return {
|
| 313 |
-
"overall_confidence": overall_confidence,
|
| 314 |
-
"confidence_level": confidence_level,
|
| 315 |
-
"review_required": review_required,
|
| 316 |
"mean_probability": float(mean_prob),
|
| 317 |
"max_probability": float(max_prob)
|
| 318 |
}
|
|
|
|
| 126 |
# Calculate confidence metrics
|
| 127 |
confidence_metrics = calculate_confidence_metrics(probs, thresholds)
|
| 128 |
|
| 129 |
+
# Ensure all numpy types are converted to Python native types for JSON serialization
|
| 130 |
+
probabilities_list = [float(p) for p in probs]
|
| 131 |
+
label_probs_dict = {str(label): float(prob) for label, prob in zip(labels, probs)}
|
| 132 |
+
|
| 133 |
return {
|
| 134 |
+
"rhythm": str(rhythm),
|
| 135 |
"heart_rate": None, # Will be calculated from features if available
|
| 136 |
"qrs_duration": None, # Will be calculated from features if available
|
| 137 |
"qt_interval": None, # Will be calculated from features if available
|
| 138 |
"pr_interval": None, # Will be calculated from features if available
|
| 139 |
"axis_deviation": "Normal", # Will be calculated from features if available
|
| 140 |
+
"abnormalities": [str(abnormality) for abnormality in abnormalities],
|
| 141 |
+
"confidence": float(confidence_metrics["overall_confidence"]),
|
| 142 |
+
"confidence_level": str(confidence_metrics["confidence_level"]),
|
| 143 |
+
"review_required": bool(confidence_metrics["review_required"]),
|
| 144 |
+
"probabilities": probabilities_list,
|
| 145 |
+
"label_probabilities": label_probs_dict,
|
| 146 |
"method": "clinical_predictions",
|
| 147 |
"warning": None,
|
| 148 |
+
"labels_used": [str(label) for label in labels],
|
| 149 |
+
"thresholds_used": {str(k): float(v) for k, v in thresholds.items()}
|
| 150 |
}
|
| 151 |
|
| 152 |
except Exception as e:
|
|
|
|
| 314 |
review_required = max_prob < 0.6 or mean_prob < 0.4
|
| 315 |
|
| 316 |
return {
|
| 317 |
+
"overall_confidence": float(overall_confidence),
|
| 318 |
+
"confidence_level": str(confidence_level),
|
| 319 |
+
"review_required": bool(review_required),
|
| 320 |
"mean_probability": float(mean_prob),
|
| 321 |
"max_probability": float(max_prob)
|
| 322 |
}
|
comprehensive_ecg_test.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Comprehensive Multi-ECG Testing and Evaluation
|
| 4 |
+
Tests multiple ECG samples and analyzes results systematically
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import requests
|
| 8 |
+
import numpy as np
|
| 9 |
+
import pandas as pd
|
| 10 |
+
import json
|
| 11 |
+
import time
|
| 12 |
+
import os
|
| 13 |
+
from typing import Dict, Any, List
|
| 14 |
+
from datetime import datetime
|
| 15 |
+
|
| 16 |
+
# Configuration
|
| 17 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space"
|
| 18 |
+
ECG_DIR = "../ecg_uploads_greenwich/"
|
| 19 |
+
INDEX_FILE = "../Greenwichschooldata.csv"
|
| 20 |
+
|
| 21 |
+
def load_ecg_data(csv_file: str) -> List[List[float]]:
|
| 22 |
+
"""Load ECG data from CSV file"""
|
| 23 |
+
try:
|
| 24 |
+
df = pd.read_csv(csv_file)
|
| 25 |
+
ecg_data = []
|
| 26 |
+
for lead in df.columns:
|
| 27 |
+
ecg_data.append(df[lead].astype(float).tolist())
|
| 28 |
+
return ecg_data
|
| 29 |
+
except Exception as e:
|
| 30 |
+
print(f"β Error loading ECG data: {e}")
|
| 31 |
+
return None
|
| 32 |
+
|
| 33 |
+
def test_individual_endpoints(ecg_data: List[List[float]], patient_info: Dict[str, Any]) -> Dict[str, Any]:
|
| 34 |
+
"""Test all individual endpoints with comprehensive analysis"""
|
| 35 |
+
results = {}
|
| 36 |
+
|
| 37 |
+
print(f"π§ͺ Testing individual endpoints for {patient_info.get('Patient Name', 'Unknown')}")
|
| 38 |
+
|
| 39 |
+
# Test 1: Extract Features (Physiological measurements)
|
| 40 |
+
print("1οΈβ£ Testing /extract_features endpoint...")
|
| 41 |
+
try:
|
| 42 |
+
payload = {"signal": ecg_data, "fs": 500}
|
| 43 |
+
response = requests.post(f"{API_BASE_URL}/extract_features", json=payload, timeout=60)
|
| 44 |
+
|
| 45 |
+
if response.status_code == 200:
|
| 46 |
+
result = response.json()
|
| 47 |
+
physio = result.get('physiological_parameters', {})
|
| 48 |
+
|
| 49 |
+
print(f" β
Features extracted successfully")
|
| 50 |
+
print(f" π Feature count: {result.get('features', {}).get('count', 'Unknown')}")
|
| 51 |
+
print(f" π Feature dimension: {result.get('features', {}).get('dimension', 'Unknown')}")
|
| 52 |
+
print(f" π Heart Rate: {physio.get('heart_rate')} BPM")
|
| 53 |
+
print(f" π QRS Duration: {physio.get('qrs_duration')} ms")
|
| 54 |
+
print(f" β±οΈ QT Interval: {physio.get('qt_interval')} ms")
|
| 55 |
+
print(f" π PR Interval: {physio.get('pr_interval')} ms")
|
| 56 |
+
print(f" π§ QRS Axis: {physio.get('qrs_axis')}Β°")
|
| 57 |
+
|
| 58 |
+
results['extract_features'] = {"status": "success", "data": result}
|
| 59 |
+
else:
|
| 60 |
+
print(f" β Failed: {response.status_code}")
|
| 61 |
+
results['extract_features'] = {"status": "error", "error": response.text}
|
| 62 |
+
except Exception as e:
|
| 63 |
+
print(f" β Error: {e}")
|
| 64 |
+
results['extract_features'] = {"status": "error", "error": str(e)}
|
| 65 |
+
|
| 66 |
+
# Test 2: Assess Quality
|
| 67 |
+
print("2οΈβ£ Testing /assess_quality endpoint...")
|
| 68 |
+
try:
|
| 69 |
+
payload = {"signal": ecg_data, "fs": 500}
|
| 70 |
+
response = requests.post(f"{API_BASE_URL}/assess_quality", json=payload, timeout=60)
|
| 71 |
+
|
| 72 |
+
if response.status_code == 200:
|
| 73 |
+
result = response.json()
|
| 74 |
+
print(f" β
Quality assessment completed")
|
| 75 |
+
print(f" π Overall Quality: {result.get('quality', 'Unknown')}")
|
| 76 |
+
metrics = result.get('metrics', {})
|
| 77 |
+
print(f" π SNR: {metrics.get('signal_to_noise_ratio', 'Unknown')}")
|
| 78 |
+
print(f" π Baseline Wander: {metrics.get('baseline_wander', 'Unknown')}")
|
| 79 |
+
print(f" π Standard Deviation: {metrics.get('standard_deviation', 'Unknown')}")
|
| 80 |
+
|
| 81 |
+
results['assess_quality'] = {"status": "success", "data": result}
|
| 82 |
+
else:
|
| 83 |
+
print(f" β Failed: {response.status_code}")
|
| 84 |
+
results['assess_quality'] = {"status": "error", "error": response.text}
|
| 85 |
+
except Exception as e:
|
| 86 |
+
print(f" β Error: {e}")
|
| 87 |
+
results['assess_quality'] = {"status": "error", "error": str(e)}
|
| 88 |
+
|
| 89 |
+
# Test 3: Predict endpoint
|
| 90 |
+
print("3οΈβ£ Testing /predict endpoint...")
|
| 91 |
+
try:
|
| 92 |
+
payload = {"signal": ecg_data, "fs": 500}
|
| 93 |
+
response = requests.post(f"{API_BASE_URL}/predict", json=payload, timeout=60)
|
| 94 |
+
|
| 95 |
+
if response.status_code == 200:
|
| 96 |
+
result = response.json()
|
| 97 |
+
print(f" β
Prediction completed")
|
| 98 |
+
print(f" 𧬠Model Type: {result.get('model_type', 'Unknown')}")
|
| 99 |
+
print(f" π Confidence: {result.get('confidence', 'Unknown')}")
|
| 100 |
+
|
| 101 |
+
results['predict'] = {"status": "success", "data": result}
|
| 102 |
+
else:
|
| 103 |
+
print(f" β Failed: {response.status_code}")
|
| 104 |
+
results['assess_quality'] = {"status": "error", "error": response.text}
|
| 105 |
+
except Exception as e:
|
| 106 |
+
print(f" β Error: {e}")
|
| 107 |
+
results['predict'] = {"status": "error", "error": str(e)}
|
| 108 |
+
|
| 109 |
+
return results
|
| 110 |
+
|
| 111 |
+
def analyze_physiological_consistency(all_results: Dict[str, Any]) -> Dict[str, Any]:
|
| 112 |
+
"""Analyze consistency of physiological measurements across samples"""
|
| 113 |
+
print(f"\nπ PHYSIOLOGICAL MEASUREMENT CONSISTENCY ANALYSIS")
|
| 114 |
+
print(f"=" * 70)
|
| 115 |
+
|
| 116 |
+
# Extract all heart rate values
|
| 117 |
+
hr_values = []
|
| 118 |
+
qrs_values = []
|
| 119 |
+
qt_values = []
|
| 120 |
+
pr_values = []
|
| 121 |
+
axis_values = []
|
| 122 |
+
|
| 123 |
+
for patient_id, result in all_results.items():
|
| 124 |
+
if 'endpoint_tests' in result and 'extract_features' in result['endpoint_tests']:
|
| 125 |
+
physio = result['endpoint_tests']['extract_features'].get('data', {}).get('physiological_parameters', {})
|
| 126 |
+
|
| 127 |
+
if physio.get('heart_rate') is not None:
|
| 128 |
+
hr_values.append(physio['heart_rate'])
|
| 129 |
+
if physio.get('qrs_duration') is not None:
|
| 130 |
+
qrs_values.append(physio['qrs_duration'])
|
| 131 |
+
if physio.get('qt_interval') is not None:
|
| 132 |
+
qt_values.append(physio['qt_interval'])
|
| 133 |
+
if physio.get('pr_interval') is not None:
|
| 134 |
+
pr_values.append(physio['pr_interval'])
|
| 135 |
+
if physio.get('qrs_axis') is not None:
|
| 136 |
+
axis_values.append(physio['qrs_axis'])
|
| 137 |
+
|
| 138 |
+
analysis = {}
|
| 139 |
+
|
| 140 |
+
# Heart Rate Analysis
|
| 141 |
+
if len(hr_values) > 0:
|
| 142 |
+
hr_std = np.std(hr_values)
|
| 143 |
+
hr_range = np.ptp(hr_values)
|
| 144 |
+
print(f"π Heart Rate Analysis:")
|
| 145 |
+
print(f" Values: {hr_values}")
|
| 146 |
+
print(f" Mean: {np.mean(hr_values):.1f} BPM")
|
| 147 |
+
print(f" Standard Deviation: {hr_std:.2f} BPM")
|
| 148 |
+
print(f" Range: {hr_range:.1f} BPM")
|
| 149 |
+
print(f" Consistency: {'β
Excellent' if hr_std < 2.0 else 'β οΈ Good' if hr_std < 5.0 else 'β Poor'}")
|
| 150 |
+
|
| 151 |
+
analysis['heart_rate'] = {
|
| 152 |
+
'values': hr_values,
|
| 153 |
+
'mean': np.mean(hr_values),
|
| 154 |
+
'std': hr_std,
|
| 155 |
+
'range': hr_range,
|
| 156 |
+
'consistency': 'Excellent' if hr_std < 2.0 else 'Good' if hr_std < 5.0 else 'Poor'
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
# QRS Duration Analysis
|
| 160 |
+
if len(qrs_values) > 0:
|
| 161 |
+
qrs_std = np.std(qrs_values)
|
| 162 |
+
qrs_range = np.ptp(qrs_values)
|
| 163 |
+
print(f"\nπ QRS Duration Analysis:")
|
| 164 |
+
print(f" Values: {qrs_values}")
|
| 165 |
+
print(f" Mean: {np.mean(qrs_values):.1f} ms")
|
| 166 |
+
print(f" Standard Deviation: {qrs_std:.2f} ms")
|
| 167 |
+
print(f" Range: {qrs_range:.1f} ms")
|
| 168 |
+
print(f" Consistency: {'β
Excellent' if qrs_std < 5.0 else 'β οΈ Good' if qrs_std < 10.0 else 'β Poor'}")
|
| 169 |
+
|
| 170 |
+
analysis['qrs_duration'] = {
|
| 171 |
+
'values': qrs_values,
|
| 172 |
+
'mean': np.mean(qrs_values),
|
| 173 |
+
'std': qrs_std,
|
| 174 |
+
'range': qrs_range,
|
| 175 |
+
'consistency': 'Excellent' if qrs_std < 5.0 else 'Good' if qrs_std < 10.0 else 'Poor'
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
# Overall Assessment
|
| 179 |
+
print(f"\nπ― OVERALL PHYSIOLOGICAL ASSESSMENT:")
|
| 180 |
+
working_params = len([k for k in analysis.keys() if analysis[k]['consistency'] in ['Excellent', 'Good']])
|
| 181 |
+
total_params = len(analysis)
|
| 182 |
+
|
| 183 |
+
print(f" Working Parameters: {working_params}/{total_params}")
|
| 184 |
+
print(f" Success Rate: {(working_params/total_params)*100:.1f}%")
|
| 185 |
+
|
| 186 |
+
if working_params == total_params:
|
| 187 |
+
print(f" π All physiological parameters working consistently!")
|
| 188 |
+
elif working_params > total_params // 2:
|
| 189 |
+
print(f" β οΈ Most parameters working - some inconsistencies")
|
| 190 |
+
else:
|
| 191 |
+
print(f" β Many parameters showing inconsistencies")
|
| 192 |
+
|
| 193 |
+
return analysis
|
| 194 |
+
|
| 195 |
+
def main():
|
| 196 |
+
"""Main test function"""
|
| 197 |
+
print("π COMPREHENSIVE MULTI-ECG TESTING AND EVALUATION")
|
| 198 |
+
print("=" * 80)
|
| 199 |
+
print(f"π API URL: {API_BASE_URL}")
|
| 200 |
+
print(f"π ECG Directory: {ECG_DIR}")
|
| 201 |
+
print(f"π Index File: {INDEX_FILE}")
|
| 202 |
+
print()
|
| 203 |
+
|
| 204 |
+
# Check if files exist
|
| 205 |
+
if not os.path.exists(INDEX_FILE):
|
| 206 |
+
print(f"β Index file not found: {INDEX_FILE}")
|
| 207 |
+
return
|
| 208 |
+
|
| 209 |
+
if not os.path.exists(ECG_DIR):
|
| 210 |
+
print(f"β ECG directory not found: {ECG_DIR}")
|
| 211 |
+
return
|
| 212 |
+
|
| 213 |
+
# Load index file
|
| 214 |
+
try:
|
| 215 |
+
print("π Loading patient index file...")
|
| 216 |
+
index_df = pd.read_csv(INDEX_FILE)
|
| 217 |
+
print(f"β
Loaded {len(index_df)} patient records")
|
| 218 |
+
except Exception as e:
|
| 219 |
+
print(f"β Error loading index file: {e}")
|
| 220 |
+
return
|
| 221 |
+
|
| 222 |
+
# Select multiple ECG files for testing
|
| 223 |
+
test_files = [
|
| 224 |
+
"ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv", # Bharathi M K Teacher, 31, F
|
| 225 |
+
"ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv", # Sayida thasmiya Bhanu Teacher, 29, F
|
| 226 |
+
"ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv", # Afzal, 46, M
|
| 227 |
+
# Add more files if available
|
| 228 |
+
]
|
| 229 |
+
|
| 230 |
+
print(f"\nπ Testing with {len(test_files)} ECG samples...")
|
| 231 |
+
print("=" * 80)
|
| 232 |
+
|
| 233 |
+
all_results = {}
|
| 234 |
+
|
| 235 |
+
for i, ecg_file in enumerate(test_files, 1):
|
| 236 |
+
try:
|
| 237 |
+
print(f"\nπ Processing {i}/{len(test_files)}: {ecg_file}")
|
| 238 |
+
|
| 239 |
+
# Find patient info in index
|
| 240 |
+
patient_row = index_df[index_df['ECG File Path'].str.contains(ecg_file, na=False)]
|
| 241 |
+
if len(patient_row) == 0:
|
| 242 |
+
print(f" β οΈ Patient info not found for {ecg_file}")
|
| 243 |
+
continue
|
| 244 |
+
|
| 245 |
+
patient_info = patient_row.iloc[0]
|
| 246 |
+
print(f" π€ Patient: {patient_info['Patient Name']} ({patient_info['Age']} {patient_info['Gender']})")
|
| 247 |
+
|
| 248 |
+
# Check if ECG file exists
|
| 249 |
+
ecg_path = os.path.join(ECG_DIR, ecg_file)
|
| 250 |
+
if not os.path.exists(ecg_path):
|
| 251 |
+
print(f" β ECG file not found: {ecg_path}")
|
| 252 |
+
continue
|
| 253 |
+
|
| 254 |
+
# Load ECG data
|
| 255 |
+
ecg_data = load_ecg_data(ecg_path)
|
| 256 |
+
if ecg_data is None:
|
| 257 |
+
print(f" β Failed to load ECG data")
|
| 258 |
+
continue
|
| 259 |
+
|
| 260 |
+
# Test individual endpoints
|
| 261 |
+
endpoint_results = test_individual_endpoints(ecg_data, patient_info)
|
| 262 |
+
|
| 263 |
+
# Store results
|
| 264 |
+
all_results[ecg_file] = {
|
| 265 |
+
"patient_info": patient_info.to_dict(),
|
| 266 |
+
"endpoint_tests": endpoint_results
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
print(f" β
Completed analysis for {ecg_file}")
|
| 270 |
+
|
| 271 |
+
except Exception as e:
|
| 272 |
+
print(f" β Error processing {ecg_file}: {e}")
|
| 273 |
+
all_results[ecg_file] = {"error": str(e)}
|
| 274 |
+
|
| 275 |
+
# Analyze physiological consistency
|
| 276 |
+
if len(all_results) > 0:
|
| 277 |
+
physiological_analysis = analyze_physiological_consistency(all_results)
|
| 278 |
+
|
| 279 |
+
# Summary report
|
| 280 |
+
print(f"\nπ COMPREHENSIVE TEST SUMMARY")
|
| 281 |
+
print(f"=" * 80)
|
| 282 |
+
|
| 283 |
+
successful_tests = 0
|
| 284 |
+
total_tests = len(test_files)
|
| 285 |
+
|
| 286 |
+
for ecg_file, result in all_results.items():
|
| 287 |
+
if "error" not in result:
|
| 288 |
+
endpoint_status = result.get("endpoint_tests", {})
|
| 289 |
+
working_endpoints = sum(1 for ep in endpoint_status.values() if ep.get("status") == "success")
|
| 290 |
+
total_endpoints = len(endpoint_status)
|
| 291 |
+
|
| 292 |
+
if working_endpoints == total_endpoints:
|
| 293 |
+
successful_tests += 1
|
| 294 |
+
print(f"β
{ecg_file}: All endpoints working")
|
| 295 |
+
else:
|
| 296 |
+
print(f"β οΈ {ecg_file}: {working_endpoints}/{total_endpoints} endpoints working")
|
| 297 |
+
else:
|
| 298 |
+
print(f"β {ecg_file}: {result['error']}")
|
| 299 |
+
|
| 300 |
+
print(f"\nπ― OVERALL RESULTS:")
|
| 301 |
+
print(f" Successful ECG Analysis: {successful_tests}/{total_tests}")
|
| 302 |
+
print(f" Success Rate: {(successful_tests/total_tests)*100:.1f}%")
|
| 303 |
+
|
| 304 |
+
# Save detailed results
|
| 305 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 306 |
+
results_file = f"comprehensive_ecg_test_results_{timestamp}.json"
|
| 307 |
+
|
| 308 |
+
try:
|
| 309 |
+
with open(results_file, 'w') as f:
|
| 310 |
+
json.dump(all_results, f, indent=2, default=str)
|
| 311 |
+
print(f"\nπΎ Detailed results saved to: {results_file}")
|
| 312 |
+
except Exception as e:
|
| 313 |
+
print(f"\nβ οΈ Could not save results: {e}")
|
| 314 |
+
|
| 315 |
+
print(f"\nπ Comprehensive ECG testing completed!")
|
| 316 |
+
print(f"π‘ Check the results above to evaluate system performance")
|
| 317 |
+
|
| 318 |
+
if __name__ == "__main__":
|
| 319 |
+
main()
|
comprehensive_ecg_test_results_20250828_151718.json
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv": {
|
| 3 |
+
"patient_info": {
|
| 4 |
+
"Organization": "Greenberg international school",
|
| 5 |
+
"Department": "General",
|
| 6 |
+
"Patient Name": "Bharathi M K Teacher",
|
| 7 |
+
"Age": 31,
|
| 8 |
+
"Gender": "Female",
|
| 9 |
+
"Created At": "2025-07-21T10:10:05.716Z",
|
| 10 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv",
|
| 11 |
+
"Ai Result": "No abnormality detected"
|
| 12 |
+
},
|
| 13 |
+
"endpoint_tests": {
|
| 14 |
+
"extract_features": {
|
| 15 |
+
"status": "success",
|
| 16 |
+
"data": {
|
| 17 |
+
"status": "success",
|
| 18 |
+
"processing_time_ms": 335.99,
|
| 19 |
+
"features": {
|
| 20 |
+
"count": 79872,
|
| 21 |
+
"dimension": 256,
|
| 22 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 23 |
+
},
|
| 24 |
+
"physiological_parameters": {
|
| 25 |
+
"heart_rate": 60.0,
|
| 26 |
+
"qrs_duration": 150.8,
|
| 27 |
+
"qt_interval": 413.3,
|
| 28 |
+
"pr_interval": 159.1,
|
| 29 |
+
"qrs_axis": 30.1,
|
| 30 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 31 |
+
"confidence": "High",
|
| 32 |
+
"feature_dimension": 79872,
|
| 33 |
+
"clinical_ranges": {
|
| 34 |
+
"heart_rate": "30-200 BPM",
|
| 35 |
+
"qrs_duration": "40-200 ms",
|
| 36 |
+
"qt_interval": "300-600 ms",
|
| 37 |
+
"pr_interval": "100-300 ms",
|
| 38 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 39 |
+
},
|
| 40 |
+
"extraction_confidence": {
|
| 41 |
+
"heart_rate": "High",
|
| 42 |
+
"qrs_duration": "High",
|
| 43 |
+
"qt_interval": "High",
|
| 44 |
+
"pr_interval": "High",
|
| 45 |
+
"qrs_axis": "High"
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 49 |
+
}
|
| 50 |
+
},
|
| 51 |
+
"assess_quality": {
|
| 52 |
+
"status": "success",
|
| 53 |
+
"data": {
|
| 54 |
+
"status": "success",
|
| 55 |
+
"processing_time_ms": 6.51,
|
| 56 |
+
"quality": "Poor",
|
| 57 |
+
"metrics": {
|
| 58 |
+
"standard_deviation": 36.6422,
|
| 59 |
+
"signal_to_noise_ratio": 0.6119,
|
| 60 |
+
"baseline_wander": 5.6589,
|
| 61 |
+
"peak_to_peak": 411.0937,
|
| 62 |
+
"mean_amplitude": 22.4223
|
| 63 |
+
},
|
| 64 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 65 |
+
}
|
| 66 |
+
},
|
| 67 |
+
"predict": {
|
| 68 |
+
"status": "success",
|
| 69 |
+
"data": {
|
| 70 |
+
"prediction": "ECG analysis completed",
|
| 71 |
+
"confidence": 0.8,
|
| 72 |
+
"features": {},
|
| 73 |
+
"model_type": "ECG-FM Pretrained (fairseq_signals)",
|
| 74 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
},
|
| 79 |
+
"ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv": {
|
| 80 |
+
"patient_info": {
|
| 81 |
+
"Organization": "Greenberg international school",
|
| 82 |
+
"Department": "General",
|
| 83 |
+
"Patient Name": "Sayida thasmiya Bhanu Teacher",
|
| 84 |
+
"Age": 29,
|
| 85 |
+
"Gender": "Female",
|
| 86 |
+
"Created At": "2025-07-21T07:22:38.484Z",
|
| 87 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv",
|
| 88 |
+
"Ai Result": "ST(29.1%)"
|
| 89 |
+
},
|
| 90 |
+
"endpoint_tests": {
|
| 91 |
+
"extract_features": {
|
| 92 |
+
"status": "success",
|
| 93 |
+
"data": {
|
| 94 |
+
"status": "success",
|
| 95 |
+
"processing_time_ms": 302.79,
|
| 96 |
+
"features": {
|
| 97 |
+
"count": 79872,
|
| 98 |
+
"dimension": 256,
|
| 99 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 100 |
+
},
|
| 101 |
+
"physiological_parameters": {
|
| 102 |
+
"heart_rate": 59.7,
|
| 103 |
+
"qrs_duration": 157.0,
|
| 104 |
+
"qt_interval": 415.0,
|
| 105 |
+
"pr_interval": 160.9,
|
| 106 |
+
"qrs_axis": 28.3,
|
| 107 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 108 |
+
"confidence": "High",
|
| 109 |
+
"feature_dimension": 79872,
|
| 110 |
+
"clinical_ranges": {
|
| 111 |
+
"heart_rate": "30-200 BPM",
|
| 112 |
+
"qrs_duration": "40-200 ms",
|
| 113 |
+
"qt_interval": "300-600 ms",
|
| 114 |
+
"pr_interval": "100-300 ms",
|
| 115 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 116 |
+
},
|
| 117 |
+
"extraction_confidence": {
|
| 118 |
+
"heart_rate": "High",
|
| 119 |
+
"qrs_duration": "High",
|
| 120 |
+
"qt_interval": "High",
|
| 121 |
+
"pr_interval": "High",
|
| 122 |
+
"qrs_axis": "High"
|
| 123 |
+
}
|
| 124 |
+
},
|
| 125 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 126 |
+
}
|
| 127 |
+
},
|
| 128 |
+
"assess_quality": {
|
| 129 |
+
"status": "success",
|
| 130 |
+
"data": {
|
| 131 |
+
"status": "success",
|
| 132 |
+
"processing_time_ms": 4.11,
|
| 133 |
+
"quality": "Poor",
|
| 134 |
+
"metrics": {
|
| 135 |
+
"standard_deviation": 46.0951,
|
| 136 |
+
"signal_to_noise_ratio": 0.364,
|
| 137 |
+
"baseline_wander": 9.0349,
|
| 138 |
+
"peak_to_peak": 837.1298,
|
| 139 |
+
"mean_amplitude": 16.7801
|
| 140 |
+
},
|
| 141 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 142 |
+
}
|
| 143 |
+
},
|
| 144 |
+
"predict": {
|
| 145 |
+
"status": "success",
|
| 146 |
+
"data": {
|
| 147 |
+
"prediction": "ECG analysis completed",
|
| 148 |
+
"confidence": 0.8,
|
| 149 |
+
"features": {},
|
| 150 |
+
"model_type": "ECG-FM Pretrained (fairseq_signals)",
|
| 151 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
},
|
| 156 |
+
"ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv": {
|
| 157 |
+
"patient_info": {
|
| 158 |
+
"Organization": "Greenberg international school",
|
| 159 |
+
"Department": "General",
|
| 160 |
+
"Patient Name": "Afzal",
|
| 161 |
+
"Age": 46,
|
| 162 |
+
"Gender": "Male",
|
| 163 |
+
"Created At": "2025-07-21T07:40:50.401Z",
|
| 164 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv",
|
| 165 |
+
"Ai Result": "RBBB(0.0%)"
|
| 166 |
+
},
|
| 167 |
+
"endpoint_tests": {
|
| 168 |
+
"extract_features": {
|
| 169 |
+
"status": "success",
|
| 170 |
+
"data": {
|
| 171 |
+
"status": "success",
|
| 172 |
+
"processing_time_ms": 633.98,
|
| 173 |
+
"features": {
|
| 174 |
+
"count": 79872,
|
| 175 |
+
"dimension": 256,
|
| 176 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 177 |
+
},
|
| 178 |
+
"physiological_parameters": {
|
| 179 |
+
"heart_rate": 60.0,
|
| 180 |
+
"qrs_duration": 80.2,
|
| 181 |
+
"qt_interval": 408.5,
|
| 182 |
+
"pr_interval": 160.7,
|
| 183 |
+
"qrs_axis": 29.6,
|
| 184 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 185 |
+
"confidence": "High",
|
| 186 |
+
"feature_dimension": 79872,
|
| 187 |
+
"clinical_ranges": {
|
| 188 |
+
"heart_rate": "30-200 BPM",
|
| 189 |
+
"qrs_duration": "40-200 ms",
|
| 190 |
+
"qt_interval": "300-600 ms",
|
| 191 |
+
"pr_interval": "100-300 ms",
|
| 192 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 193 |
+
},
|
| 194 |
+
"extraction_confidence": {
|
| 195 |
+
"heart_rate": "High",
|
| 196 |
+
"qrs_duration": "High",
|
| 197 |
+
"qt_interval": "High",
|
| 198 |
+
"pr_interval": "High",
|
| 199 |
+
"qrs_axis": "High"
|
| 200 |
+
}
|
| 201 |
+
},
|
| 202 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 203 |
+
}
|
| 204 |
+
},
|
| 205 |
+
"assess_quality": {
|
| 206 |
+
"status": "success",
|
| 207 |
+
"data": {
|
| 208 |
+
"status": "success",
|
| 209 |
+
"processing_time_ms": 5.76,
|
| 210 |
+
"quality": "Poor",
|
| 211 |
+
"metrics": {
|
| 212 |
+
"standard_deviation": 66.1427,
|
| 213 |
+
"signal_to_noise_ratio": 0.4547,
|
| 214 |
+
"baseline_wander": 9.3757,
|
| 215 |
+
"peak_to_peak": 1103.498,
|
| 216 |
+
"mean_amplitude": 30.0723
|
| 217 |
+
},
|
| 218 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 219 |
+
}
|
| 220 |
+
},
|
| 221 |
+
"predict": {
|
| 222 |
+
"status": "success",
|
| 223 |
+
"data": {
|
| 224 |
+
"prediction": "ECG analysis completed",
|
| 225 |
+
"confidence": 0.8,
|
| 226 |
+
"features": {},
|
| 227 |
+
"model_type": "ECG-FM Pretrained (fairseq_signals)",
|
| 228 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
}
|
| 232 |
+
}
|
| 233 |
+
}
|
finetuned_clinical_test_results_20250828_152404.json
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv": {
|
| 3 |
+
"patient_info": {
|
| 4 |
+
"Organization": "Greenberg international school",
|
| 5 |
+
"Department": "General",
|
| 6 |
+
"Patient Name": "Bharathi M K Teacher",
|
| 7 |
+
"Age": 31,
|
| 8 |
+
"Gender": "Female",
|
| 9 |
+
"Created At": "2025-07-21T10:10:05.716Z",
|
| 10 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv",
|
| 11 |
+
"Ai Result": "No abnormality detected"
|
| 12 |
+
},
|
| 13 |
+
"endpoint_tests": {
|
| 14 |
+
"clinical_analysis": {
|
| 15 |
+
"status": "error",
|
| 16 |
+
"error": "Internal Server Error"
|
| 17 |
+
},
|
| 18 |
+
"direct_clinical": {
|
| 19 |
+
"status": "not_available"
|
| 20 |
+
},
|
| 21 |
+
"extract_features": {
|
| 22 |
+
"status": "success",
|
| 23 |
+
"data": {
|
| 24 |
+
"status": "success",
|
| 25 |
+
"processing_time_ms": 671.05,
|
| 26 |
+
"features": {
|
| 27 |
+
"count": 79872,
|
| 28 |
+
"dimension": 256,
|
| 29 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 30 |
+
},
|
| 31 |
+
"physiological_parameters": {
|
| 32 |
+
"heart_rate": 60.0,
|
| 33 |
+
"qrs_duration": 150.8,
|
| 34 |
+
"qt_interval": 413.3,
|
| 35 |
+
"pr_interval": 159.1,
|
| 36 |
+
"qrs_axis": 30.1,
|
| 37 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 38 |
+
"confidence": "High",
|
| 39 |
+
"feature_dimension": 79872,
|
| 40 |
+
"clinical_ranges": {
|
| 41 |
+
"heart_rate": "30-200 BPM",
|
| 42 |
+
"qrs_duration": "40-200 ms",
|
| 43 |
+
"qt_interval": "300-600 ms",
|
| 44 |
+
"pr_interval": "100-300 ms",
|
| 45 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 46 |
+
},
|
| 47 |
+
"extraction_confidence": {
|
| 48 |
+
"heart_rate": "High",
|
| 49 |
+
"qrs_duration": "High",
|
| 50 |
+
"qt_interval": "High",
|
| 51 |
+
"pr_interval": "High",
|
| 52 |
+
"qrs_axis": "High"
|
| 53 |
+
}
|
| 54 |
+
},
|
| 55 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
},
|
| 60 |
+
"ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv": {
|
| 61 |
+
"patient_info": {
|
| 62 |
+
"Organization": "Greenberg international school",
|
| 63 |
+
"Department": "General",
|
| 64 |
+
"Patient Name": "Sayida thasmiya Bhanu Teacher",
|
| 65 |
+
"Age": 29,
|
| 66 |
+
"Gender": "Female",
|
| 67 |
+
"Created At": "2025-07-21T07:22:38.484Z",
|
| 68 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv",
|
| 69 |
+
"Ai Result": "ST(29.1%)"
|
| 70 |
+
},
|
| 71 |
+
"endpoint_tests": {
|
| 72 |
+
"clinical_analysis": {
|
| 73 |
+
"status": "error",
|
| 74 |
+
"error": "Internal Server Error"
|
| 75 |
+
},
|
| 76 |
+
"direct_clinical": {
|
| 77 |
+
"status": "not_available"
|
| 78 |
+
},
|
| 79 |
+
"extract_features": {
|
| 80 |
+
"status": "success",
|
| 81 |
+
"data": {
|
| 82 |
+
"status": "success",
|
| 83 |
+
"processing_time_ms": 519.92,
|
| 84 |
+
"features": {
|
| 85 |
+
"count": 79872,
|
| 86 |
+
"dimension": 256,
|
| 87 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 88 |
+
},
|
| 89 |
+
"physiological_parameters": {
|
| 90 |
+
"heart_rate": 59.7,
|
| 91 |
+
"qrs_duration": 157.0,
|
| 92 |
+
"qt_interval": 415.0,
|
| 93 |
+
"pr_interval": 160.9,
|
| 94 |
+
"qrs_axis": 28.3,
|
| 95 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 96 |
+
"confidence": "High",
|
| 97 |
+
"feature_dimension": 79872,
|
| 98 |
+
"clinical_ranges": {
|
| 99 |
+
"heart_rate": "30-200 BPM",
|
| 100 |
+
"qrs_duration": "40-200 ms",
|
| 101 |
+
"qt_interval": "300-600 ms",
|
| 102 |
+
"pr_interval": "100-300 ms",
|
| 103 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 104 |
+
},
|
| 105 |
+
"extraction_confidence": {
|
| 106 |
+
"heart_rate": "High",
|
| 107 |
+
"qrs_duration": "High",
|
| 108 |
+
"qt_interval": "High",
|
| 109 |
+
"pr_interval": "High",
|
| 110 |
+
"qrs_axis": "High"
|
| 111 |
+
}
|
| 112 |
+
},
|
| 113 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
},
|
| 118 |
+
"ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv": {
|
| 119 |
+
"patient_info": {
|
| 120 |
+
"Organization": "Greenberg international school",
|
| 121 |
+
"Department": "General",
|
| 122 |
+
"Patient Name": "Afzal",
|
| 123 |
+
"Age": 46,
|
| 124 |
+
"Gender": "Male",
|
| 125 |
+
"Created At": "2025-07-21T07:40:50.401Z",
|
| 126 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv",
|
| 127 |
+
"Ai Result": "RBBB(0.0%)"
|
| 128 |
+
},
|
| 129 |
+
"endpoint_tests": {
|
| 130 |
+
"clinical_analysis": {
|
| 131 |
+
"status": "error",
|
| 132 |
+
"error": "Internal Server Error"
|
| 133 |
+
},
|
| 134 |
+
"direct_clinical": {
|
| 135 |
+
"status": "not_available"
|
| 136 |
+
},
|
| 137 |
+
"extract_features": {
|
| 138 |
+
"status": "success",
|
| 139 |
+
"data": {
|
| 140 |
+
"status": "success",
|
| 141 |
+
"processing_time_ms": 501.82,
|
| 142 |
+
"features": {
|
| 143 |
+
"count": 79872,
|
| 144 |
+
"dimension": 256,
|
| 145 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 146 |
+
},
|
| 147 |
+
"physiological_parameters": {
|
| 148 |
+
"heart_rate": 60.0,
|
| 149 |
+
"qrs_duration": 80.2,
|
| 150 |
+
"qt_interval": 408.5,
|
| 151 |
+
"pr_interval": 160.7,
|
| 152 |
+
"qrs_axis": 29.6,
|
| 153 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 154 |
+
"confidence": "High",
|
| 155 |
+
"feature_dimension": 79872,
|
| 156 |
+
"clinical_ranges": {
|
| 157 |
+
"heart_rate": "30-200 BPM",
|
| 158 |
+
"qrs_duration": "40-200 ms",
|
| 159 |
+
"qt_interval": "300-600 ms",
|
| 160 |
+
"pr_interval": "100-300 ms",
|
| 161 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 162 |
+
},
|
| 163 |
+
"extraction_confidence": {
|
| 164 |
+
"heart_rate": "High",
|
| 165 |
+
"qrs_duration": "High",
|
| 166 |
+
"qt_interval": "High",
|
| 167 |
+
"pr_interval": "High",
|
| 168 |
+
"qrs_axis": "High"
|
| 169 |
+
}
|
| 170 |
+
},
|
| 171 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
}
|
physiological_parameter_test_results_20250828_145018.json
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv": {
|
| 3 |
+
"patient_info": {
|
| 4 |
+
"Organization": "Greenberg international school",
|
| 5 |
+
"Department": "General",
|
| 6 |
+
"Patient Name": "Bharathi M K Teacher",
|
| 7 |
+
"Age": 31,
|
| 8 |
+
"Gender": "Female",
|
| 9 |
+
"Created At": "2025-07-21T10:10:05.716Z",
|
| 10 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv",
|
| 11 |
+
"Ai Result": "No abnormality detected"
|
| 12 |
+
},
|
| 13 |
+
"physiological_analysis": {
|
| 14 |
+
"status": "error",
|
| 15 |
+
"error": "Object of type int64 is not JSON serializable"
|
| 16 |
+
},
|
| 17 |
+
"endpoint_tests": {
|
| 18 |
+
"extract_features": {
|
| 19 |
+
"status": "success",
|
| 20 |
+
"data": {
|
| 21 |
+
"status": "success",
|
| 22 |
+
"processing_time_ms": 412.11,
|
| 23 |
+
"features": {
|
| 24 |
+
"count": 79872,
|
| 25 |
+
"dimension": 256,
|
| 26 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 27 |
+
},
|
| 28 |
+
"physiological_parameters": {
|
| 29 |
+
"heart_rate": 60.0,
|
| 30 |
+
"qrs_duration": 150.8,
|
| 31 |
+
"qt_interval": 413.3,
|
| 32 |
+
"pr_interval": 159.1,
|
| 33 |
+
"qrs_axis": 30.1,
|
| 34 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 35 |
+
"confidence": "High",
|
| 36 |
+
"feature_dimension": 79872,
|
| 37 |
+
"clinical_ranges": {
|
| 38 |
+
"heart_rate": "30-200 BPM",
|
| 39 |
+
"qrs_duration": "40-200 ms",
|
| 40 |
+
"qt_interval": "300-600 ms",
|
| 41 |
+
"pr_interval": "100-300 ms",
|
| 42 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 43 |
+
},
|
| 44 |
+
"extraction_confidence": {
|
| 45 |
+
"heart_rate": "High",
|
| 46 |
+
"qrs_duration": "High",
|
| 47 |
+
"qt_interval": "High",
|
| 48 |
+
"pr_interval": "High",
|
| 49 |
+
"qrs_axis": "High"
|
| 50 |
+
}
|
| 51 |
+
},
|
| 52 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 53 |
+
}
|
| 54 |
+
},
|
| 55 |
+
"assess_quality": {
|
| 56 |
+
"status": "success",
|
| 57 |
+
"data": {
|
| 58 |
+
"status": "success",
|
| 59 |
+
"processing_time_ms": 3.94,
|
| 60 |
+
"quality": "Poor",
|
| 61 |
+
"metrics": {
|
| 62 |
+
"standard_deviation": 36.6422,
|
| 63 |
+
"signal_to_noise_ratio": 0.6119,
|
| 64 |
+
"baseline_wander": 5.6589,
|
| 65 |
+
"peak_to_peak": 411.0937,
|
| 66 |
+
"mean_amplitude": 22.4223
|
| 67 |
+
},
|
| 68 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 69 |
+
}
|
| 70 |
+
},
|
| 71 |
+
"predict": {
|
| 72 |
+
"status": "error",
|
| 73 |
+
"error": "{\"detail\":\"Prediction failed: forward() takes 1 positional argument but 2 were given\"}"
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
},
|
| 77 |
+
"ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv": {
|
| 78 |
+
"patient_info": {
|
| 79 |
+
"Organization": "Greenberg international school",
|
| 80 |
+
"Department": "General",
|
| 81 |
+
"Patient Name": "Sayida thasmiya Bhanu Teacher",
|
| 82 |
+
"Age": 29,
|
| 83 |
+
"Gender": "Female",
|
| 84 |
+
"Created At": "2025-07-21T07:22:38.484Z",
|
| 85 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv",
|
| 86 |
+
"Ai Result": "ST(29.1%)"
|
| 87 |
+
},
|
| 88 |
+
"physiological_analysis": {
|
| 89 |
+
"status": "error",
|
| 90 |
+
"error": "Object of type int64 is not JSON serializable"
|
| 91 |
+
},
|
| 92 |
+
"endpoint_tests": {
|
| 93 |
+
"extract_features": {
|
| 94 |
+
"status": "success",
|
| 95 |
+
"data": {
|
| 96 |
+
"status": "success",
|
| 97 |
+
"processing_time_ms": 446.46,
|
| 98 |
+
"features": {
|
| 99 |
+
"count": 79872,
|
| 100 |
+
"dimension": 256,
|
| 101 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 102 |
+
},
|
| 103 |
+
"physiological_parameters": {
|
| 104 |
+
"heart_rate": 59.7,
|
| 105 |
+
"qrs_duration": 157.0,
|
| 106 |
+
"qt_interval": 415.0,
|
| 107 |
+
"pr_interval": 160.9,
|
| 108 |
+
"qrs_axis": 28.3,
|
| 109 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 110 |
+
"confidence": "High",
|
| 111 |
+
"feature_dimension": 79872,
|
| 112 |
+
"clinical_ranges": {
|
| 113 |
+
"heart_rate": "30-200 BPM",
|
| 114 |
+
"qrs_duration": "40-200 ms",
|
| 115 |
+
"qt_interval": "300-600 ms",
|
| 116 |
+
"pr_interval": "100-300 ms",
|
| 117 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 118 |
+
},
|
| 119 |
+
"extraction_confidence": {
|
| 120 |
+
"heart_rate": "High",
|
| 121 |
+
"qrs_duration": "High",
|
| 122 |
+
"qt_interval": "High",
|
| 123 |
+
"pr_interval": "High",
|
| 124 |
+
"qrs_axis": "High"
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"assess_quality": {
|
| 131 |
+
"status": "success",
|
| 132 |
+
"data": {
|
| 133 |
+
"status": "success",
|
| 134 |
+
"processing_time_ms": 12.54,
|
| 135 |
+
"quality": "Poor",
|
| 136 |
+
"metrics": {
|
| 137 |
+
"standard_deviation": 46.0951,
|
| 138 |
+
"signal_to_noise_ratio": 0.364,
|
| 139 |
+
"baseline_wander": 9.0349,
|
| 140 |
+
"peak_to_peak": 837.1298,
|
| 141 |
+
"mean_amplitude": 16.7801
|
| 142 |
+
},
|
| 143 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 144 |
+
}
|
| 145 |
+
},
|
| 146 |
+
"predict": {
|
| 147 |
+
"status": "error",
|
| 148 |
+
"error": "{\"detail\":\"Prediction failed: forward() takes 1 positional argument but 2 were given\"}"
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
},
|
| 152 |
+
"ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv": {
|
| 153 |
+
"patient_info": {
|
| 154 |
+
"Organization": "Greenberg international school",
|
| 155 |
+
"Department": "General",
|
| 156 |
+
"Patient Name": "Afzal",
|
| 157 |
+
"Age": 46,
|
| 158 |
+
"Gender": "Male",
|
| 159 |
+
"Created At": "2025-07-21T07:40:50.401Z",
|
| 160 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv",
|
| 161 |
+
"Ai Result": "RBBB(0.0%)"
|
| 162 |
+
},
|
| 163 |
+
"physiological_analysis": {
|
| 164 |
+
"status": "error",
|
| 165 |
+
"error": "Object of type int64 is not JSON serializable"
|
| 166 |
+
},
|
| 167 |
+
"endpoint_tests": {
|
| 168 |
+
"extract_features": {
|
| 169 |
+
"status": "success",
|
| 170 |
+
"data": {
|
| 171 |
+
"status": "success",
|
| 172 |
+
"processing_time_ms": 405.26,
|
| 173 |
+
"features": {
|
| 174 |
+
"count": 79872,
|
| 175 |
+
"dimension": 256,
|
| 176 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 177 |
+
},
|
| 178 |
+
"physiological_parameters": {
|
| 179 |
+
"heart_rate": 60.0,
|
| 180 |
+
"qrs_duration": 80.2,
|
| 181 |
+
"qt_interval": 408.5,
|
| 182 |
+
"pr_interval": 160.7,
|
| 183 |
+
"qrs_axis": 29.6,
|
| 184 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 185 |
+
"confidence": "High",
|
| 186 |
+
"feature_dimension": 79872,
|
| 187 |
+
"clinical_ranges": {
|
| 188 |
+
"heart_rate": "30-200 BPM",
|
| 189 |
+
"qrs_duration": "40-200 ms",
|
| 190 |
+
"qt_interval": "300-600 ms",
|
| 191 |
+
"pr_interval": "100-300 ms",
|
| 192 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 193 |
+
},
|
| 194 |
+
"extraction_confidence": {
|
| 195 |
+
"heart_rate": "High",
|
| 196 |
+
"qrs_duration": "High",
|
| 197 |
+
"qt_interval": "High",
|
| 198 |
+
"pr_interval": "High",
|
| 199 |
+
"qrs_axis": "High"
|
| 200 |
+
}
|
| 201 |
+
},
|
| 202 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 203 |
+
}
|
| 204 |
+
},
|
| 205 |
+
"assess_quality": {
|
| 206 |
+
"status": "success",
|
| 207 |
+
"data": {
|
| 208 |
+
"status": "success",
|
| 209 |
+
"processing_time_ms": 7.92,
|
| 210 |
+
"quality": "Poor",
|
| 211 |
+
"metrics": {
|
| 212 |
+
"standard_deviation": 66.1427,
|
| 213 |
+
"signal_to_noise_ratio": 0.4547,
|
| 214 |
+
"baseline_wander": 9.3757,
|
| 215 |
+
"peak_to_peak": 1103.498,
|
| 216 |
+
"mean_amplitude": 30.0723
|
| 217 |
+
},
|
| 218 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 219 |
+
}
|
| 220 |
+
},
|
| 221 |
+
"predict": {
|
| 222 |
+
"status": "error",
|
| 223 |
+
"error": "{\"detail\":\"Prediction failed: forward() takes 1 positional argument but 2 were given\"}"
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
}
|
| 227 |
+
}
|
physiological_parameter_test_results_20250828_150828.json
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv": {
|
| 3 |
+
"patient_info": {
|
| 4 |
+
"Organization": "Greenberg international school",
|
| 5 |
+
"Department": "General",
|
| 6 |
+
"Patient Name": "Bharathi M K Teacher",
|
| 7 |
+
"Age": 31,
|
| 8 |
+
"Gender": "Female",
|
| 9 |
+
"Created At": "2025-07-21T10:10:05.716Z",
|
| 10 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv",
|
| 11 |
+
"Ai Result": "No abnormality detected"
|
| 12 |
+
},
|
| 13 |
+
"physiological_analysis": {
|
| 14 |
+
"status": "error",
|
| 15 |
+
"error": "Object of type int64 is not JSON serializable"
|
| 16 |
+
},
|
| 17 |
+
"endpoint_tests": {
|
| 18 |
+
"extract_features": {
|
| 19 |
+
"status": "success",
|
| 20 |
+
"data": {
|
| 21 |
+
"status": "success",
|
| 22 |
+
"processing_time_ms": 778.07,
|
| 23 |
+
"features": {
|
| 24 |
+
"count": 79872,
|
| 25 |
+
"dimension": 256,
|
| 26 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 27 |
+
},
|
| 28 |
+
"physiological_parameters": {
|
| 29 |
+
"heart_rate": 60.0,
|
| 30 |
+
"qrs_duration": 150.8,
|
| 31 |
+
"qt_interval": 413.3,
|
| 32 |
+
"pr_interval": 159.1,
|
| 33 |
+
"qrs_axis": 30.1,
|
| 34 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 35 |
+
"confidence": "High",
|
| 36 |
+
"feature_dimension": 79872,
|
| 37 |
+
"clinical_ranges": {
|
| 38 |
+
"heart_rate": "30-200 BPM",
|
| 39 |
+
"qrs_duration": "40-200 ms",
|
| 40 |
+
"qt_interval": "300-600 ms",
|
| 41 |
+
"pr_interval": "100-300 ms",
|
| 42 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 43 |
+
},
|
| 44 |
+
"extraction_confidence": {
|
| 45 |
+
"heart_rate": "High",
|
| 46 |
+
"qrs_duration": "High",
|
| 47 |
+
"qt_interval": "High",
|
| 48 |
+
"pr_interval": "High",
|
| 49 |
+
"qrs_axis": "High"
|
| 50 |
+
}
|
| 51 |
+
},
|
| 52 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 53 |
+
}
|
| 54 |
+
},
|
| 55 |
+
"assess_quality": {
|
| 56 |
+
"status": "success",
|
| 57 |
+
"data": {
|
| 58 |
+
"status": "success",
|
| 59 |
+
"processing_time_ms": 6.66,
|
| 60 |
+
"quality": "Poor",
|
| 61 |
+
"metrics": {
|
| 62 |
+
"standard_deviation": 36.6422,
|
| 63 |
+
"signal_to_noise_ratio": 0.6119,
|
| 64 |
+
"baseline_wander": 5.6589,
|
| 65 |
+
"peak_to_peak": 411.0937,
|
| 66 |
+
"mean_amplitude": 22.4223
|
| 67 |
+
},
|
| 68 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 69 |
+
}
|
| 70 |
+
},
|
| 71 |
+
"predict": {
|
| 72 |
+
"status": "success",
|
| 73 |
+
"data": {
|
| 74 |
+
"prediction": "ECG analysis completed",
|
| 75 |
+
"confidence": 0.8,
|
| 76 |
+
"features": {},
|
| 77 |
+
"model_type": "ECG-FM Pretrained (fairseq_signals)",
|
| 78 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
},
|
| 83 |
+
"ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv": {
|
| 84 |
+
"patient_info": {
|
| 85 |
+
"Organization": "Greenberg international school",
|
| 86 |
+
"Department": "General",
|
| 87 |
+
"Patient Name": "Sayida thasmiya Bhanu Teacher",
|
| 88 |
+
"Age": 29,
|
| 89 |
+
"Gender": "Female",
|
| 90 |
+
"Created At": "2025-07-21T07:22:38.484Z",
|
| 91 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv",
|
| 92 |
+
"Ai Result": "ST(29.1%)"
|
| 93 |
+
},
|
| 94 |
+
"physiological_analysis": {
|
| 95 |
+
"status": "error",
|
| 96 |
+
"error": "Object of type int64 is not JSON serializable"
|
| 97 |
+
},
|
| 98 |
+
"endpoint_tests": {
|
| 99 |
+
"extract_features": {
|
| 100 |
+
"status": "success",
|
| 101 |
+
"data": {
|
| 102 |
+
"status": "success",
|
| 103 |
+
"processing_time_ms": 402.7,
|
| 104 |
+
"features": {
|
| 105 |
+
"count": 79872,
|
| 106 |
+
"dimension": 256,
|
| 107 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 108 |
+
},
|
| 109 |
+
"physiological_parameters": {
|
| 110 |
+
"heart_rate": 59.7,
|
| 111 |
+
"qrs_duration": 157.0,
|
| 112 |
+
"qt_interval": 415.0,
|
| 113 |
+
"pr_interval": 160.9,
|
| 114 |
+
"qrs_axis": 28.3,
|
| 115 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 116 |
+
"confidence": "High",
|
| 117 |
+
"feature_dimension": 79872,
|
| 118 |
+
"clinical_ranges": {
|
| 119 |
+
"heart_rate": "30-200 BPM",
|
| 120 |
+
"qrs_duration": "40-200 ms",
|
| 121 |
+
"qt_interval": "300-600 ms",
|
| 122 |
+
"pr_interval": "100-300 ms",
|
| 123 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 124 |
+
},
|
| 125 |
+
"extraction_confidence": {
|
| 126 |
+
"heart_rate": "High",
|
| 127 |
+
"qrs_duration": "High",
|
| 128 |
+
"qt_interval": "High",
|
| 129 |
+
"pr_interval": "High",
|
| 130 |
+
"qrs_axis": "High"
|
| 131 |
+
}
|
| 132 |
+
},
|
| 133 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 134 |
+
}
|
| 135 |
+
},
|
| 136 |
+
"assess_quality": {
|
| 137 |
+
"status": "success",
|
| 138 |
+
"data": {
|
| 139 |
+
"status": "success",
|
| 140 |
+
"processing_time_ms": 8.91,
|
| 141 |
+
"quality": "Poor",
|
| 142 |
+
"metrics": {
|
| 143 |
+
"standard_deviation": 46.0951,
|
| 144 |
+
"signal_to_noise_ratio": 0.364,
|
| 145 |
+
"baseline_wander": 9.0349,
|
| 146 |
+
"peak_to_peak": 837.1298,
|
| 147 |
+
"mean_amplitude": 16.7801
|
| 148 |
+
},
|
| 149 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 150 |
+
}
|
| 151 |
+
},
|
| 152 |
+
"predict": {
|
| 153 |
+
"status": "success",
|
| 154 |
+
"data": {
|
| 155 |
+
"prediction": "ECG analysis completed",
|
| 156 |
+
"confidence": 0.8,
|
| 157 |
+
"features": {},
|
| 158 |
+
"model_type": "ECG-FM Pretrained (fairseq_signals)",
|
| 159 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
},
|
| 164 |
+
"ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv": {
|
| 165 |
+
"patient_info": {
|
| 166 |
+
"Organization": "Greenberg international school",
|
| 167 |
+
"Department": "General",
|
| 168 |
+
"Patient Name": "Afzal",
|
| 169 |
+
"Age": 46,
|
| 170 |
+
"Gender": "Male",
|
| 171 |
+
"Created At": "2025-07-21T07:40:50.401Z",
|
| 172 |
+
"ECG File Path": "ecg_uploads_greenwich/ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv",
|
| 173 |
+
"Ai Result": "RBBB(0.0%)"
|
| 174 |
+
},
|
| 175 |
+
"physiological_analysis": {
|
| 176 |
+
"status": "error",
|
| 177 |
+
"error": "Object of type int64 is not JSON serializable"
|
| 178 |
+
},
|
| 179 |
+
"endpoint_tests": {
|
| 180 |
+
"extract_features": {
|
| 181 |
+
"status": "success",
|
| 182 |
+
"data": {
|
| 183 |
+
"status": "success",
|
| 184 |
+
"processing_time_ms": 376.8,
|
| 185 |
+
"features": {
|
| 186 |
+
"count": 79872,
|
| 187 |
+
"dimension": 256,
|
| 188 |
+
"extraction_method": "ECG-FM pretrained model"
|
| 189 |
+
},
|
| 190 |
+
"physiological_parameters": {
|
| 191 |
+
"heart_rate": 60.0,
|
| 192 |
+
"qrs_duration": 80.2,
|
| 193 |
+
"qt_interval": 408.5,
|
| 194 |
+
"pr_interval": 160.7,
|
| 195 |
+
"qrs_axis": 29.6,
|
| 196 |
+
"extraction_method": "ECG-FM validated feature analysis",
|
| 197 |
+
"confidence": "High",
|
| 198 |
+
"feature_dimension": 79872,
|
| 199 |
+
"clinical_ranges": {
|
| 200 |
+
"heart_rate": "30-200 BPM",
|
| 201 |
+
"qrs_duration": "40-200 ms",
|
| 202 |
+
"qt_interval": "300-600 ms",
|
| 203 |
+
"pr_interval": "100-300 ms",
|
| 204 |
+
"qrs_axis": "-180\u00b0 to +180\u00b0"
|
| 205 |
+
},
|
| 206 |
+
"extraction_confidence": {
|
| 207 |
+
"heart_rate": "High",
|
| 208 |
+
"qrs_duration": "High",
|
| 209 |
+
"qt_interval": "High",
|
| 210 |
+
"pr_interval": "High",
|
| 211 |
+
"qrs_axis": "High"
|
| 212 |
+
}
|
| 213 |
+
},
|
| 214 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 215 |
+
}
|
| 216 |
+
},
|
| 217 |
+
"assess_quality": {
|
| 218 |
+
"status": "success",
|
| 219 |
+
"data": {
|
| 220 |
+
"status": "success",
|
| 221 |
+
"processing_time_ms": 4.79,
|
| 222 |
+
"quality": "Poor",
|
| 223 |
+
"metrics": {
|
| 224 |
+
"standard_deviation": 66.1427,
|
| 225 |
+
"signal_to_noise_ratio": 0.4547,
|
| 226 |
+
"baseline_wander": 9.3757,
|
| 227 |
+
"peak_to_peak": 1103.498,
|
| 228 |
+
"mean_amplitude": 30.0723
|
| 229 |
+
},
|
| 230 |
+
"assessment_method": "Statistical analysis + ECG-FM feature validation"
|
| 231 |
+
}
|
| 232 |
+
},
|
| 233 |
+
"predict": {
|
| 234 |
+
"status": "success",
|
| 235 |
+
"data": {
|
| 236 |
+
"prediction": "ECG analysis completed",
|
| 237 |
+
"confidence": 0.8,
|
| 238 |
+
"features": {},
|
| 239 |
+
"model_type": "ECG-FM Pretrained (fairseq_signals)",
|
| 240 |
+
"model_source": "wanglab/ecg-fm/mimic_iv_ecg_physionet_pretrained.pt"
|
| 241 |
+
}
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
}
|
| 245 |
+
}
|
quick_test_fix.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Quick Test to Verify JSON Serialization Fix
|
| 4 |
+
Tests the /analyze endpoint with a simple ECG sample
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import requests
|
| 8 |
+
import numpy as np
|
| 9 |
+
import json
|
| 10 |
+
|
| 11 |
+
# Configuration
|
| 12 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space"
|
| 13 |
+
|
| 14 |
+
def create_test_ecg():
|
| 15 |
+
"""Create a simple test ECG signal"""
|
| 16 |
+
# Generate a simple 10-second ECG-like signal at 500 Hz
|
| 17 |
+
t = np.linspace(0, 10, 5000)
|
| 18 |
+
|
| 19 |
+
# Create a simple ECG-like waveform
|
| 20 |
+
signal = []
|
| 21 |
+
for lead in range(12):
|
| 22 |
+
# Basic sine wave with some variation
|
| 23 |
+
lead_signal = np.sin(2 * np.pi * 1.2 * t) * 0.5 # ~72 BPM
|
| 24 |
+
lead_signal += np.random.normal(0, 0.1, 5000) # Add noise
|
| 25 |
+
signal.append(lead_signal.tolist())
|
| 26 |
+
|
| 27 |
+
return signal
|
| 28 |
+
|
| 29 |
+
def test_analyze_endpoint():
|
| 30 |
+
"""Test the /analyze endpoint with the fix"""
|
| 31 |
+
print("π§ͺ Testing /analyze endpoint with JSON serialization fix...")
|
| 32 |
+
|
| 33 |
+
# Create test ECG data
|
| 34 |
+
ecg_data = create_test_ecg()
|
| 35 |
+
|
| 36 |
+
payload = {
|
| 37 |
+
"signal": ecg_data,
|
| 38 |
+
"fs": 500,
|
| 39 |
+
"patient_age": 30,
|
| 40 |
+
"patient_gender": "M"
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
print("π€ Sending test ECG data...")
|
| 45 |
+
print(f"π Input: {len(ecg_data)} leads Γ {len(ecg_data[0])} samples")
|
| 46 |
+
|
| 47 |
+
response = requests.post(f"{API_BASE_URL}/analyze", json=payload, timeout=120)
|
| 48 |
+
|
| 49 |
+
if response.status_code == 200:
|
| 50 |
+
result = response.json()
|
| 51 |
+
print("β
SUCCESS! /analyze endpoint working!")
|
| 52 |
+
|
| 53 |
+
# Check physiological parameters
|
| 54 |
+
physio = result.get('physiological_parameters', {})
|
| 55 |
+
print(f"\nπ PHYSIOLOGICAL PARAMETERS:")
|
| 56 |
+
print(f" Heart Rate: {physio.get('heart_rate')} BPM")
|
| 57 |
+
print(f" QRS Duration: {physio.get('qrs_duration')} ms")
|
| 58 |
+
print(f" QT Interval: {physio.get('qt_interval')} ms")
|
| 59 |
+
print(f" PR Interval: {physio.get('pr_interval')} ms")
|
| 60 |
+
print(f" QRS Axis: {physio.get('qrs_axis')}Β°")
|
| 61 |
+
|
| 62 |
+
# Check features
|
| 63 |
+
features = result.get('features', {})
|
| 64 |
+
print(f"\n𧬠FEATURES:")
|
| 65 |
+
print(f" Count: {features.get('count')}")
|
| 66 |
+
print(f" Dimension: {features.get('dimension')}")
|
| 67 |
+
print(f" Status: {features.get('extraction_status')}")
|
| 68 |
+
|
| 69 |
+
# Check clinical analysis
|
| 70 |
+
clinical = result.get('clinical_analysis', {})
|
| 71 |
+
if clinical:
|
| 72 |
+
print(f"\nπ₯ CLINICAL ANALYSIS:")
|
| 73 |
+
print(f" Method: {clinical.get('method')}")
|
| 74 |
+
print(f" Confidence: {clinical.get('confidence')}")
|
| 75 |
+
|
| 76 |
+
return True
|
| 77 |
+
|
| 78 |
+
else:
|
| 79 |
+
print(f"β Failed: {response.status_code}")
|
| 80 |
+
print(f" Error: {response.text}")
|
| 81 |
+
return False
|
| 82 |
+
|
| 83 |
+
except Exception as e:
|
| 84 |
+
print(f"β Error: {e}")
|
| 85 |
+
return False
|
| 86 |
+
|
| 87 |
+
def test_health():
|
| 88 |
+
"""Test API health"""
|
| 89 |
+
try:
|
| 90 |
+
response = requests.get(f"{API_BASE_URL}/health", timeout=30)
|
| 91 |
+
if response.status_code == 200:
|
| 92 |
+
health_data = response.json()
|
| 93 |
+
print(f"β
API Healthy - Models loaded: {health_data.get('models_loaded')}")
|
| 94 |
+
return True
|
| 95 |
+
else:
|
| 96 |
+
print(f"β Health check failed: {response.status_code}")
|
| 97 |
+
return False
|
| 98 |
+
except Exception as e:
|
| 99 |
+
print(f"β Health check error: {e}")
|
| 100 |
+
return False
|
| 101 |
+
|
| 102 |
+
def main():
|
| 103 |
+
"""Main test function"""
|
| 104 |
+
print("π QUICK TEST - JSON Serialization Fix Verification")
|
| 105 |
+
print("=" * 60)
|
| 106 |
+
|
| 107 |
+
# Test 1: Health check
|
| 108 |
+
print("\n1οΈβ£ Testing API health...")
|
| 109 |
+
if not test_health():
|
| 110 |
+
print("β API not healthy, cannot proceed")
|
| 111 |
+
return
|
| 112 |
+
|
| 113 |
+
# Test 2: Analyze endpoint
|
| 114 |
+
print("\n2οΈβ£ Testing /analyze endpoint...")
|
| 115 |
+
if test_analyze_endpoint():
|
| 116 |
+
print("\nπ SUCCESS! JSON serialization fix is working!")
|
| 117 |
+
print(" All endpoints should now function correctly.")
|
| 118 |
+
else:
|
| 119 |
+
print("\nβ /analyze endpoint still has issues")
|
| 120 |
+
print(" May need to wait for deployment to complete.")
|
| 121 |
+
|
| 122 |
+
if __name__ == "__main__":
|
| 123 |
+
main()
|
simple_finetuned_test.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Simple Test for Finetuned Model Output
|
| 4 |
+
Tests what the finetuned model actually returns
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import requests
|
| 8 |
+
import json
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
# Configuration
|
| 12 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space"
|
| 13 |
+
|
| 14 |
+
def test_finetuned_model_output():
|
| 15 |
+
"""Test what the finetuned model actually returns"""
|
| 16 |
+
print("𧬠TESTING FINETUNED MODEL OUTPUT FORMAT")
|
| 17 |
+
print("=" * 60)
|
| 18 |
+
|
| 19 |
+
# Create a very simple test ECG signal
|
| 20 |
+
test_signal = []
|
| 21 |
+
for lead in range(12): # 12 leads
|
| 22 |
+
samples = []
|
| 23 |
+
for i in range(1000): # 2 seconds at 500Hz (shorter for testing)
|
| 24 |
+
samples.append(np.sin(2 * np.pi * 1.2 * i / 500) * 0.5)
|
| 25 |
+
test_signal.append(samples)
|
| 26 |
+
|
| 27 |
+
payload = {"signal": test_signal, "fs": 500}
|
| 28 |
+
|
| 29 |
+
print("π§ͺ Testing with simple ECG signal...")
|
| 30 |
+
print(f" Signal shape: {len(test_signal)} leads, {len(test_signal[0])} samples")
|
| 31 |
+
|
| 32 |
+
# Test 1: Check API status
|
| 33 |
+
print("\n1οΈβ£ Checking API status...")
|
| 34 |
+
try:
|
| 35 |
+
response = requests.get(f"{API_BASE_URL}/health", timeout=30)
|
| 36 |
+
if response.status_code == 200:
|
| 37 |
+
print(" β
API is healthy")
|
| 38 |
+
else:
|
| 39 |
+
print(f" β API health check failed: {response.status_code}")
|
| 40 |
+
return
|
| 41 |
+
except Exception as e:
|
| 42 |
+
print(f" β API health check error: {e}")
|
| 43 |
+
return
|
| 44 |
+
|
| 45 |
+
# Test 2: Check model info
|
| 46 |
+
print("\n2οΈβ£ Checking model info...")
|
| 47 |
+
try:
|
| 48 |
+
response = requests.get(f"{API_BASE_URL}/info", timeout=30)
|
| 49 |
+
if response.status_code == 200:
|
| 50 |
+
info = response.json()
|
| 51 |
+
print(" β
Got model info")
|
| 52 |
+
|
| 53 |
+
# Check model status
|
| 54 |
+
models = info.get('models', {})
|
| 55 |
+
pretrained_status = models.get('pretrained', {}).get('status', 'Unknown')
|
| 56 |
+
finetuned_status = models.get('finetuned', {}).get('status', 'Unknown')
|
| 57 |
+
|
| 58 |
+
print(f" 𧬠Pretrained Model: {pretrained_status}")
|
| 59 |
+
print(f" π₯ Finetuned Model: {finetuned_status}")
|
| 60 |
+
|
| 61 |
+
if finetuned_status != 'Loaded':
|
| 62 |
+
print(" β Finetuned model not loaded - this is the problem!")
|
| 63 |
+
return
|
| 64 |
+
else:
|
| 65 |
+
print(f" β Failed to get model info: {response.status_code}")
|
| 66 |
+
return
|
| 67 |
+
except Exception as e:
|
| 68 |
+
print(f" β Error getting model info: {e}")
|
| 69 |
+
return
|
| 70 |
+
|
| 71 |
+
# Test 3: Test individual endpoints to isolate the issue
|
| 72 |
+
print("\n3οΈβ£ Testing individual endpoints...")
|
| 73 |
+
|
| 74 |
+
# Test extract_features (should work)
|
| 75 |
+
try:
|
| 76 |
+
response = requests.post(f"{API_BASE_URL}/extract_features", json=payload, timeout=30)
|
| 77 |
+
print(f" π Extract Features: {response.status_code}")
|
| 78 |
+
if response.status_code == 200:
|
| 79 |
+
print(" β
Features endpoint working")
|
| 80 |
+
else:
|
| 81 |
+
print(f" β Features endpoint failed: {response.text}")
|
| 82 |
+
except Exception as e:
|
| 83 |
+
print(f" β Features endpoint error: {e}")
|
| 84 |
+
|
| 85 |
+
# Test predict (should work)
|
| 86 |
+
try:
|
| 87 |
+
response = requests.post(f"{API_BASE_URL}/predict", json=payload, timeout=30)
|
| 88 |
+
print(f" π Predict: {response.status_code}")
|
| 89 |
+
if response.status_code == 200:
|
| 90 |
+
print(" β
Predict endpoint working")
|
| 91 |
+
else:
|
| 92 |
+
print(f" β Predict endpoint failed: {response.text}")
|
| 93 |
+
except Exception as e:
|
| 94 |
+
print(f" β Predict endpoint error: {e}")
|
| 95 |
+
|
| 96 |
+
# Test 4: Try to call analyze with detailed error logging
|
| 97 |
+
print("\n4οΈβ£ Testing /analyze endpoint with detailed logging...")
|
| 98 |
+
try:
|
| 99 |
+
response = requests.post(f"{API_BASE_URL}/analyze", json=payload, timeout=60)
|
| 100 |
+
print(f" π Response Status: {response.status_code}")
|
| 101 |
+
|
| 102 |
+
if response.status_code == 200:
|
| 103 |
+
result = response.json()
|
| 104 |
+
print(" β
SUCCESS! /analyze endpoint working!")
|
| 105 |
+
print(f" π₯ Clinical Analysis: {result.get('clinical_analysis', 'Not found')}")
|
| 106 |
+
print(f" π Physiological Parameters: {result.get('physiological_parameters', 'Not found')}")
|
| 107 |
+
else:
|
| 108 |
+
print(f" β FAILED: {response.status_code}")
|
| 109 |
+
print(f" π Error Response: {response.text}")
|
| 110 |
+
|
| 111 |
+
# Try to parse error details
|
| 112 |
+
try:
|
| 113 |
+
error_detail = response.json()
|
| 114 |
+
print(f" π Parsed Error: {json.dumps(error_detail, indent=2)}")
|
| 115 |
+
except:
|
| 116 |
+
print(f" π Raw Error: {response.text}")
|
| 117 |
+
|
| 118 |
+
except Exception as e:
|
| 119 |
+
print(f" β Exception during /analyze call: {e}")
|
| 120 |
+
import traceback
|
| 121 |
+
traceback.print_exc()
|
| 122 |
+
|
| 123 |
+
if __name__ == "__main__":
|
| 124 |
+
test_finetuned_model_output()
|
test_endpoints_status.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test All Endpoints Status
|
| 4 |
+
Check the current status of all ECG-FM API endpoints
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import requests
|
| 8 |
+
import json
|
| 9 |
+
|
| 10 |
+
# Configuration
|
| 11 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space"
|
| 12 |
+
|
| 13 |
+
def test_endpoint(endpoint, method="GET", payload=None):
|
| 14 |
+
"""Test a specific endpoint"""
|
| 15 |
+
print(f"π§ͺ Testing {endpoint}...")
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
if method == "GET":
|
| 19 |
+
response = requests.get(f"{API_BASE_URL}{endpoint}", timeout=30)
|
| 20 |
+
else:
|
| 21 |
+
response = requests.post(f"{API_BASE_URL}{endpoint}", json=payload, timeout=60)
|
| 22 |
+
|
| 23 |
+
print(f" Status: {response.status_code}")
|
| 24 |
+
|
| 25 |
+
if response.status_code == 200:
|
| 26 |
+
try:
|
| 27 |
+
result = response.json()
|
| 28 |
+
print(f" β
Success - Response received")
|
| 29 |
+
|
| 30 |
+
# Show key information based on endpoint
|
| 31 |
+
if endpoint == "/health":
|
| 32 |
+
print(f" Models loaded: {result.get('models_loaded')}")
|
| 33 |
+
print(f" Fairseq available: {result.get('fairseq_signals_available')}")
|
| 34 |
+
elif endpoint == "/info":
|
| 35 |
+
print(f" Model repo: {result.get('model_repo')}")
|
| 36 |
+
print(f" Loading strategy: {result.get('loading_strategy')}")
|
| 37 |
+
elif endpoint == "/extract_features":
|
| 38 |
+
features = result.get('features', {})
|
| 39 |
+
print(f" Feature count: {features.get('count')}")
|
| 40 |
+
print(f" Feature dimension: {features.get('dimension')}")
|
| 41 |
+
physio = result.get('physiological_parameters', {})
|
| 42 |
+
if physio.get('heart_rate'):
|
| 43 |
+
print(f" Heart Rate: {physio['heart_rate']} BPM")
|
| 44 |
+
elif endpoint == "/assess_quality":
|
| 45 |
+
print(f" Quality: {result.get('quality')}")
|
| 46 |
+
metrics = result.get('metrics', {})
|
| 47 |
+
print(f" SNR: {metrics.get('signal_to_noise_ratio')}")
|
| 48 |
+
|
| 49 |
+
return True
|
| 50 |
+
|
| 51 |
+
except json.JSONDecodeError:
|
| 52 |
+
print(f" β οΈ Response received but not JSON: {response.text[:100]}...")
|
| 53 |
+
return True
|
| 54 |
+
else:
|
| 55 |
+
print(f" β Failed: {response.text[:200]}...")
|
| 56 |
+
return False
|
| 57 |
+
|
| 58 |
+
except Exception as e:
|
| 59 |
+
print(f" β Error: {e}")
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
def main():
|
| 63 |
+
"""Test all endpoints"""
|
| 64 |
+
print("π TESTING ALL ECG-FM API ENDPOINTS")
|
| 65 |
+
print("=" * 60)
|
| 66 |
+
|
| 67 |
+
results = {}
|
| 68 |
+
|
| 69 |
+
# Test 1: Health check
|
| 70 |
+
print("\n1οΈβ£ Health Check")
|
| 71 |
+
results['health'] = test_endpoint("/health")
|
| 72 |
+
|
| 73 |
+
# Test 2: Info endpoint
|
| 74 |
+
print("\n2οΈβ£ Info Endpoint")
|
| 75 |
+
results['info'] = test_endpoint("/info")
|
| 76 |
+
|
| 77 |
+
# Test 3: Root endpoint
|
| 78 |
+
print("\n3οΈβ£ Root Endpoint")
|
| 79 |
+
results['root'] = test_endpoint("/")
|
| 80 |
+
|
| 81 |
+
# Test 4: Extract Features (with simple test data)
|
| 82 |
+
print("\n4οΈβ£ Extract Features Endpoint")
|
| 83 |
+
test_payload = {
|
| 84 |
+
"signal": [[0.1] * 1000] * 12, # Simple 12-lead, 1000-sample test data
|
| 85 |
+
"fs": 500
|
| 86 |
+
}
|
| 87 |
+
results['extract_features'] = test_endpoint("/extract_features", "POST", test_payload)
|
| 88 |
+
|
| 89 |
+
# Test 5: Assess Quality
|
| 90 |
+
print("\n5οΈβ£ Assess Quality Endpoint")
|
| 91 |
+
results['assess_quality'] = test_endpoint("/assess_quality", "POST", test_payload)
|
| 92 |
+
|
| 93 |
+
# Test 6: Predict endpoint
|
| 94 |
+
print("\n6οΈβ£ Predict Endpoint")
|
| 95 |
+
results['predict'] = test_endpoint("/predict", "POST", test_payload)
|
| 96 |
+
|
| 97 |
+
# Test 7: Analyze endpoint (comprehensive)
|
| 98 |
+
print("\n7οΈβ£ Analyze Endpoint (Comprehensive)")
|
| 99 |
+
results['analyze'] = test_endpoint("/analyze", "POST", test_payload)
|
| 100 |
+
|
| 101 |
+
# Summary
|
| 102 |
+
print(f"\nπ ENDPOINT STATUS SUMMARY")
|
| 103 |
+
print(f"=" * 60)
|
| 104 |
+
|
| 105 |
+
working_endpoints = sum(results.values())
|
| 106 |
+
total_endpoints = len(results)
|
| 107 |
+
|
| 108 |
+
for endpoint, status in results.items():
|
| 109 |
+
status_icon = "β
" if status else "β"
|
| 110 |
+
print(f"{status_icon} {endpoint}: {'Working' if status else 'Failed'}")
|
| 111 |
+
|
| 112 |
+
print(f"\nπ― OVERALL STATUS:")
|
| 113 |
+
print(f" Working: {working_endpoints}/{total_endpoints}")
|
| 114 |
+
print(f" Success Rate: {(working_endpoints/total_endpoints)*100:.1f}%")
|
| 115 |
+
|
| 116 |
+
if working_endpoints == total_endpoints:
|
| 117 |
+
print(f"\nπ ALL ENDPOINTS ARE WORKING!")
|
| 118 |
+
elif working_endpoints > total_endpoints // 2:
|
| 119 |
+
print(f"\nβ οΈ MOST ENDPOINTS WORKING - Some issues to resolve")
|
| 120 |
+
else:
|
| 121 |
+
print(f"\nβ MANY ENDPOINTS FAILING - Need investigation")
|
| 122 |
+
|
| 123 |
+
if __name__ == "__main__":
|
| 124 |
+
main()
|
test_finetuned_clinical.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test Finetuned ECG-FM Model for Clinical Abnormality Detection
|
| 4 |
+
Tests the finetuned model to get normal vs abnormal classification results
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import requests
|
| 8 |
+
import numpy as np
|
| 9 |
+
import pandas as pd
|
| 10 |
+
import json
|
| 11 |
+
import os
|
| 12 |
+
from typing import Dict, Any, List
|
| 13 |
+
from datetime import datetime
|
| 14 |
+
|
| 15 |
+
# Configuration
|
| 16 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space"
|
| 17 |
+
ECG_DIR = "../ecg_uploads_greenwich/"
|
| 18 |
+
INDEX_FILE = "../Greenwichschooldata.csv"
|
| 19 |
+
|
| 20 |
+
def load_ecg_data(csv_file: str) -> List[List[float]]:
|
| 21 |
+
"""Load ECG data from CSV file"""
|
| 22 |
+
try:
|
| 23 |
+
df = pd.read_csv(csv_file)
|
| 24 |
+
ecg_data = []
|
| 25 |
+
for lead in df.columns:
|
| 26 |
+
ecg_data.append(df[lead].astype(float).tolist())
|
| 27 |
+
return ecg_data
|
| 28 |
+
except Exception as e:
|
| 29 |
+
print(f"β Error loading ECG data: {e}")
|
| 30 |
+
return None
|
| 31 |
+
|
| 32 |
+
def test_finetuned_clinical_classification(ecg_data: List[List[float]], patient_info: Dict[str, Any]) -> Dict[str, Any]:
|
| 33 |
+
"""Test the finetuned model for clinical abnormality detection"""
|
| 34 |
+
results = {}
|
| 35 |
+
|
| 36 |
+
print(f"𧬠Testing FINETUNED MODEL for {patient_info.get('Patient Name', 'Unknown')}")
|
| 37 |
+
print(f" π€ Patient: {patient_info.get('Patient Name', 'Unknown')} ({patient_info.get('Age', 'Unknown')} {patient_info.get('Gender', 'Unknown')})")
|
| 38 |
+
|
| 39 |
+
# Test 1: Clinical Analysis (Finetuned Model)
|
| 40 |
+
print("1οΈβ£ Testing /analyze endpoint (FINETUNED MODEL)...")
|
| 41 |
+
try:
|
| 42 |
+
payload = {"signal": ecg_data, "fs": 500}
|
| 43 |
+
response = requests.post(f"{API_BASE_URL}/analyze", json=payload, timeout=60)
|
| 44 |
+
|
| 45 |
+
if response.status_code == 200:
|
| 46 |
+
result = response.json()
|
| 47 |
+
print(f" β
Clinical analysis completed successfully!")
|
| 48 |
+
|
| 49 |
+
# Extract clinical predictions
|
| 50 |
+
clinical = result.get('clinical_analysis', {})
|
| 51 |
+
if clinical:
|
| 52 |
+
print(f" π₯ CLINICAL RESULTS:")
|
| 53 |
+
print(f" π Overall Status: {clinical.get('overall_status', 'Unknown')}")
|
| 54 |
+
print(f" π― Primary Finding: {clinical.get('primary_finding', 'Unknown')}")
|
| 55 |
+
print(f" β οΈ Abnormalities Detected: {clinical.get('abnormalities_detected', 'Unknown')}")
|
| 56 |
+
print(f" π Labels Used: {clinical.get('labels_used', 'Unknown')}")
|
| 57 |
+
print(f" π Confidence Level: {clinical.get('confidence_level', 'Unknown')}")
|
| 58 |
+
|
| 59 |
+
# Show top clinical labels
|
| 60 |
+
label_probs = clinical.get('label_probabilities', {})
|
| 61 |
+
if label_probs:
|
| 62 |
+
print(f" π·οΈ TOP CLINICAL LABELS:")
|
| 63 |
+
sorted_labels = sorted(label_probs.items(), key=lambda x: x[1], reverse=True)[:5]
|
| 64 |
+
for label, prob in sorted_labels:
|
| 65 |
+
print(f" β’ {label}: {prob:.3f}")
|
| 66 |
+
|
| 67 |
+
results['clinical_analysis'] = {"status": "success", "data": result}
|
| 68 |
+
else:
|
| 69 |
+
print(f" β οΈ No clinical analysis data found")
|
| 70 |
+
results['clinical_analysis'] = {"status": "warning", "message": "No clinical data"}
|
| 71 |
+
else:
|
| 72 |
+
print(f" β Failed: {response.status_code}")
|
| 73 |
+
print(f" Error: {response.text}")
|
| 74 |
+
results['clinical_analysis'] = {"status": "error", "error": response.text}
|
| 75 |
+
except Exception as e:
|
| 76 |
+
print(f" β Error: {e}")
|
| 77 |
+
results['clinical_analysis'] = {"status": "error", "error": str(e)}
|
| 78 |
+
|
| 79 |
+
# Test 2: Direct Clinical Endpoint (if available)
|
| 80 |
+
print("2οΈβ£ Testing clinical endpoint directly...")
|
| 81 |
+
try:
|
| 82 |
+
# Try to call a clinical-specific endpoint if it exists
|
| 83 |
+
payload = {"signal": ecg_data, "fs": 500}
|
| 84 |
+
response = requests.post(f"{API_BASE_URL}/clinical", json=payload, timeout=60)
|
| 85 |
+
|
| 86 |
+
if response.status_code == 200:
|
| 87 |
+
result = response.json()
|
| 88 |
+
print(f" β
Direct clinical endpoint working!")
|
| 89 |
+
print(f" π Result: {result}")
|
| 90 |
+
results['direct_clinical'] = {"status": "success", "data": result}
|
| 91 |
+
elif response.status_code == 404:
|
| 92 |
+
print(f" βΉοΈ Direct clinical endpoint not available (using /analyze)")
|
| 93 |
+
results['direct_clinical'] = {"status": "not_available"}
|
| 94 |
+
else:
|
| 95 |
+
print(f" β Failed: {response.status_code}")
|
| 96 |
+
results['direct_clinical'] = {"status": "error", "error": response.text}
|
| 97 |
+
except Exception as e:
|
| 98 |
+
print(f" βΉοΈ Direct clinical endpoint not available: {e}")
|
| 99 |
+
results['direct_clinical'] = {"status": "not_available"}
|
| 100 |
+
|
| 101 |
+
# Test 3: Extract Features + Clinical Analysis
|
| 102 |
+
print("3οΈβ£ Testing feature extraction + clinical analysis...")
|
| 103 |
+
try:
|
| 104 |
+
payload = {"signal": ecg_data, "fs": 500}
|
| 105 |
+
response = requests.post(f"{API_BASE_URL}/extract_features", json=payload, timeout=60)
|
| 106 |
+
|
| 107 |
+
if response.status_code == 200:
|
| 108 |
+
result = response.json()
|
| 109 |
+
print(f" β
Features extracted successfully")
|
| 110 |
+
print(f" π Feature count: {result.get('features', {}).get('count', 'Unknown')}")
|
| 111 |
+
print(f" π Feature dimension: {result.get('features', {}).get('dimension', 'Unknown')}")
|
| 112 |
+
|
| 113 |
+
# Check if clinical analysis is included
|
| 114 |
+
clinical_included = result.get('clinical_analysis', {})
|
| 115 |
+
if clinical_included:
|
| 116 |
+
print(f" π₯ Clinical analysis included in features!")
|
| 117 |
+
print(f" π Status: {clinical_included.get('overall_status', 'Unknown')}")
|
| 118 |
+
else:
|
| 119 |
+
print(f" βΉοΈ Clinical analysis not included in features endpoint")
|
| 120 |
+
|
| 121 |
+
results['extract_features'] = {"status": "success", "data": result}
|
| 122 |
+
else:
|
| 123 |
+
print(f" β Failed: {response.status_code}")
|
| 124 |
+
results['extract_features'] = {"status": "error", "error": response.text}
|
| 125 |
+
except Exception as e:
|
| 126 |
+
print(f" β Error: {e}")
|
| 127 |
+
results['extract_features'] = {"status": "error", "error": str(e)}
|
| 128 |
+
|
| 129 |
+
return results
|
| 130 |
+
|
| 131 |
+
def analyze_clinical_results(all_results: Dict[str, Any]) -> Dict[str, Any]:
|
| 132 |
+
"""Analyze clinical classification results across samples"""
|
| 133 |
+
print(f"\nπ₯ CLINICAL CLASSIFICATION ANALYSIS")
|
| 134 |
+
print(f"=" * 70)
|
| 135 |
+
|
| 136 |
+
clinical_statuses = []
|
| 137 |
+
primary_findings = []
|
| 138 |
+
abnormality_counts = []
|
| 139 |
+
confidence_levels = []
|
| 140 |
+
|
| 141 |
+
for patient_id, result in all_results.items():
|
| 142 |
+
if 'endpoint_tests' in result and 'clinical_analysis' in result['endpoint_tests']:
|
| 143 |
+
clinical = result['endpoint_tests']['clinical_analysis'].get('data', {}).get('clinical_analysis', {})
|
| 144 |
+
|
| 145 |
+
if clinical.get('overall_status'):
|
| 146 |
+
clinical_statuses.append(clinical['overall_status'])
|
| 147 |
+
if clinical.get('primary_finding'):
|
| 148 |
+
primary_findings.append(clinical['primary_finding'])
|
| 149 |
+
if clinical.get('abnormalities_detected') is not None:
|
| 150 |
+
abnormality_counts.append(clinical['abnormalities_detected'])
|
| 151 |
+
if clinical.get('confidence_level'):
|
| 152 |
+
confidence_levels.append(clinical['confidence_level'])
|
| 153 |
+
|
| 154 |
+
analysis = {}
|
| 155 |
+
|
| 156 |
+
# Clinical Status Analysis
|
| 157 |
+
if len(clinical_statuses) > 0:
|
| 158 |
+
print(f"π₯ Clinical Status Analysis:")
|
| 159 |
+
print(f" Statuses: {clinical_statuses}")
|
| 160 |
+
|
| 161 |
+
normal_count = sum(1 for s in clinical_statuses if 'normal' in s.lower())
|
| 162 |
+
abnormal_count = sum(1 for s in clinical_statuses if 'abnormal' in s.lower())
|
| 163 |
+
|
| 164 |
+
print(f" Normal: {normal_count}")
|
| 165 |
+
print(f" Abnormal: {abnormal_count}")
|
| 166 |
+
print(f" Distribution: Normal {normal_count/len(clinical_statuses)*100:.1f}% vs Abnormal {abnormal_count/len(clinical_statuses)*100:.1f}%")
|
| 167 |
+
|
| 168 |
+
analysis['clinical_status'] = {
|
| 169 |
+
'statuses': clinical_statuses,
|
| 170 |
+
'normal_count': normal_count,
|
| 171 |
+
'abnormal_count': abnormal_count,
|
| 172 |
+
'normal_percentage': normal_count/len(clinical_statuses)*100 if len(clinical_statuses) > 0 else 0
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
# Confidence Analysis
|
| 176 |
+
if len(confidence_levels) > 0:
|
| 177 |
+
print(f"\nπ Confidence Level Analysis:")
|
| 178 |
+
print(f" Confidence Levels: {confidence_levels}")
|
| 179 |
+
avg_confidence = np.mean(confidence_levels)
|
| 180 |
+
print(f" Average Confidence: {avg_confidence:.2f}")
|
| 181 |
+
|
| 182 |
+
analysis['confidence'] = {
|
| 183 |
+
'levels': confidence_levels,
|
| 184 |
+
'average': avg_confidence
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
# Overall Assessment
|
| 188 |
+
print(f"\nπ― CLINICAL ASSESSMENT SUMMARY:")
|
| 189 |
+
working_clinical = len([k for k in all_results.keys() if 'endpoint_tests' in all_results[k] and 'clinical_analysis' in all_results[k]['endpoint_tests']])
|
| 190 |
+
total_patients = len(all_results)
|
| 191 |
+
|
| 192 |
+
print(f" Patients with Clinical Analysis: {working_clinical}/{total_patients}")
|
| 193 |
+
print(f" Clinical Success Rate: {(working_clinical/total_patients)*100:.1f}%")
|
| 194 |
+
|
| 195 |
+
if working_clinical == total_patients:
|
| 196 |
+
print(f" π All patients have clinical analysis!")
|
| 197 |
+
elif working_clinical > total_patients // 2:
|
| 198 |
+
print(f" β οΈ Most patients have clinical analysis")
|
| 199 |
+
else:
|
| 200 |
+
print(f" β Many patients missing clinical analysis")
|
| 201 |
+
|
| 202 |
+
return analysis
|
| 203 |
+
|
| 204 |
+
def main():
|
| 205 |
+
"""Main test function"""
|
| 206 |
+
print("𧬠FINETUNED ECG-FM CLINICAL CLASSIFICATION TESTING")
|
| 207 |
+
print("=" * 80)
|
| 208 |
+
print(f"π API URL: {API_BASE_URL}")
|
| 209 |
+
print(f"π ECG Directory: {ECG_DIR}")
|
| 210 |
+
print(f"π Index File: {INDEX_FILE}")
|
| 211 |
+
print()
|
| 212 |
+
|
| 213 |
+
# Check if files exist
|
| 214 |
+
if not os.path.exists(INDEX_FILE):
|
| 215 |
+
print(f"β Index file not found: {INDEX_FILE}")
|
| 216 |
+
return
|
| 217 |
+
|
| 218 |
+
if not os.path.exists(ECG_DIR):
|
| 219 |
+
print(f"β ECG directory not found: {ECG_DIR}")
|
| 220 |
+
return
|
| 221 |
+
|
| 222 |
+
# Load index file
|
| 223 |
+
try:
|
| 224 |
+
print("π Loading patient index file...")
|
| 225 |
+
index_df = pd.read_csv(INDEX_FILE)
|
| 226 |
+
print(f"β
Loaded {len(index_df)} patient records")
|
| 227 |
+
except Exception as e:
|
| 228 |
+
print(f"β Error loading index file: {e}")
|
| 229 |
+
return
|
| 230 |
+
|
| 231 |
+
# Select ECG files for clinical testing
|
| 232 |
+
test_files = [
|
| 233 |
+
"ecg_98408931-6f8e-47cc-954a-ba0c058a0f3d.csv", # Bharathi M K Teacher, 31, F
|
| 234 |
+
"ecg_fc6d2ecb-7eb3-4eec-9281-17c24b7902b5.csv", # Sayida thasmiya Bhanu Teacher, 29, F
|
| 235 |
+
"ecg_022a3f3a-7060-4ff8-b716-b75d8e0637c5.csv", # Afzal, 46, M
|
| 236 |
+
]
|
| 237 |
+
|
| 238 |
+
print(f"\n𧬠Testing FINETUNED MODEL with {len(test_files)} ECG samples...")
|
| 239 |
+
print("=" * 80)
|
| 240 |
+
|
| 241 |
+
all_results = {}
|
| 242 |
+
|
| 243 |
+
for i, ecg_file in enumerate(test_files, 1):
|
| 244 |
+
try:
|
| 245 |
+
print(f"\nπ Processing {i}/{len(test_files)}: {ecg_file}")
|
| 246 |
+
|
| 247 |
+
# Find patient info in index
|
| 248 |
+
patient_row = index_df[index_df['ECG File Path'].str.contains(ecg_file, na=False)]
|
| 249 |
+
if len(patient_row) == 0:
|
| 250 |
+
print(f" β οΈ Patient info not found for {ecg_file}")
|
| 251 |
+
continue
|
| 252 |
+
|
| 253 |
+
patient_info = patient_row.iloc[0]
|
| 254 |
+
|
| 255 |
+
# Check if ECG file exists
|
| 256 |
+
ecg_path = os.path.join(ECG_DIR, ecg_file)
|
| 257 |
+
if not os.path.exists(ecg_path):
|
| 258 |
+
print(f" β ECG file not found: {ecg_path}")
|
| 259 |
+
continue
|
| 260 |
+
|
| 261 |
+
# Load ECG data
|
| 262 |
+
ecg_data = load_ecg_data(ecg_path)
|
| 263 |
+
if ecg_data is None:
|
| 264 |
+
print(f" β Failed to load ECG data")
|
| 265 |
+
continue
|
| 266 |
+
|
| 267 |
+
# Test finetuned model for clinical classification
|
| 268 |
+
clinical_results = test_finetuned_clinical_classification(ecg_data, patient_info)
|
| 269 |
+
|
| 270 |
+
# Store results
|
| 271 |
+
all_results[ecg_file] = {
|
| 272 |
+
"patient_info": patient_info.to_dict(),
|
| 273 |
+
"endpoint_tests": clinical_results
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
print(f" β
Completed clinical analysis for {ecg_file}")
|
| 277 |
+
|
| 278 |
+
except Exception as e:
|
| 279 |
+
print(f" β Error processing {ecg_file}: {e}")
|
| 280 |
+
all_results[ecg_file] = {"error": str(e)}
|
| 281 |
+
|
| 282 |
+
# Analyze clinical results
|
| 283 |
+
if len(all_results) > 0:
|
| 284 |
+
clinical_analysis = analyze_clinical_results(all_results)
|
| 285 |
+
|
| 286 |
+
# Summary report
|
| 287 |
+
print(f"\nπ CLINICAL TEST SUMMARY")
|
| 288 |
+
print(f"=" * 80)
|
| 289 |
+
|
| 290 |
+
successful_clinical = 0
|
| 291 |
+
total_tests = len(test_files)
|
| 292 |
+
|
| 293 |
+
for ecg_file, result in all_results.items():
|
| 294 |
+
if "error" not in result:
|
| 295 |
+
clinical_status = result.get("endpoint_tests", {}).get("clinical_analysis", {}).get("status")
|
| 296 |
+
if clinical_status == "success":
|
| 297 |
+
successful_clinical += 1
|
| 298 |
+
print(f"β
{ecg_file}: Clinical analysis successful")
|
| 299 |
+
else:
|
| 300 |
+
print(f"β οΈ {ecg_file}: Clinical analysis {clinical_status}")
|
| 301 |
+
else:
|
| 302 |
+
print(f"β {ecg_file}: {result['error']}")
|
| 303 |
+
|
| 304 |
+
print(f"\nπ― CLINICAL RESULTS:")
|
| 305 |
+
print(f" Successful Clinical Analysis: {successful_clinical}/{total_tests}")
|
| 306 |
+
print(f" Clinical Success Rate: {(successful_clinical/total_tests)*100:.1f}%")
|
| 307 |
+
|
| 308 |
+
# Save detailed results
|
| 309 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 310 |
+
results_file = f"finetuned_clinical_test_results_{timestamp}.json"
|
| 311 |
+
|
| 312 |
+
try:
|
| 313 |
+
with open(results_file, 'w') as f:
|
| 314 |
+
json.dump(all_results, f, indent=2, default=str)
|
| 315 |
+
print(f"\nπΎ Detailed clinical results saved to: {results_file}")
|
| 316 |
+
except Exception as e:
|
| 317 |
+
print(f"\nβ οΈ Could not save results: {e}")
|
| 318 |
+
|
| 319 |
+
print(f"\nπ Finetuned clinical testing completed!")
|
| 320 |
+
print(f"π‘ Check the results above for NORMAL vs ABNORMAL classification")
|
| 321 |
+
|
| 322 |
+
if __name__ == "__main__":
|
| 323 |
+
main()
|
test_measurement_accuracy.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test Measurement Accuracy
|
| 4 |
+
Demonstrate the issues with current physiological parameter extraction
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import requests
|
| 9 |
+
import json
|
| 10 |
+
|
| 11 |
+
# Configuration
|
| 12 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space"
|
| 13 |
+
|
| 14 |
+
def create_identical_ecg_signals():
|
| 15 |
+
"""Create multiple identical ECG signals to test consistency"""
|
| 16 |
+
# Generate a simple 10-second ECG-like signal at 500 Hz
|
| 17 |
+
t = np.linspace(0, 10, 5000)
|
| 18 |
+
|
| 19 |
+
# Create identical signals
|
| 20 |
+
signals = []
|
| 21 |
+
for i in range(3):
|
| 22 |
+
# Basic sine wave with some variation
|
| 23 |
+
signal = []
|
| 24 |
+
for lead in range(12):
|
| 25 |
+
lead_signal = np.sin(2 * np.pi * 1.2 * t) * 0.5 # ~72 BPM
|
| 26 |
+
lead_signal += np.random.normal(0, 0.1, 5000) # Add noise
|
| 27 |
+
signal.append(lead_signal.tolist())
|
| 28 |
+
signals.append(signal)
|
| 29 |
+
|
| 30 |
+
return signals
|
| 31 |
+
|
| 32 |
+
def test_measurement_consistency():
|
| 33 |
+
"""Test if identical signals give consistent measurements"""
|
| 34 |
+
print("π§ͺ TESTING MEASUREMENT CONSISTENCY")
|
| 35 |
+
print("=" * 60)
|
| 36 |
+
|
| 37 |
+
# Create 3 identical ECG signals
|
| 38 |
+
signals = create_identical_ecg_signals()
|
| 39 |
+
|
| 40 |
+
print(f"π Created {len(signals)} identical ECG signals")
|
| 41 |
+
print(f"π Each signal: {len(signals[0])} leads Γ {len(signals[0][0])} samples")
|
| 42 |
+
|
| 43 |
+
results = []
|
| 44 |
+
|
| 45 |
+
for i, signal in enumerate(signals, 1):
|
| 46 |
+
print(f"\nπ€ Testing Signal {i}/3...")
|
| 47 |
+
|
| 48 |
+
payload = {
|
| 49 |
+
"signal": signal,
|
| 50 |
+
"fs": 500,
|
| 51 |
+
"patient_age": 30,
|
| 52 |
+
"patient_gender": "M"
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
try:
|
| 56 |
+
response = requests.post(f"{API_BASE_URL}/extract_features", json=payload, timeout=60)
|
| 57 |
+
|
| 58 |
+
if response.status_code == 200:
|
| 59 |
+
result = response.json()
|
| 60 |
+
physio = result.get('physiological_parameters', {})
|
| 61 |
+
|
| 62 |
+
measurements = {
|
| 63 |
+
"heart_rate": physio.get('heart_rate'),
|
| 64 |
+
"qrs_duration": physio.get('qrs_duration'),
|
| 65 |
+
"qt_interval": physio.get('qt_interval'),
|
| 66 |
+
"pr_interval": physio.get('pr_interval'),
|
| 67 |
+
"qrs_axis": physio.get('qrs_axis')
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
print(f" π Heart Rate: {measurements['heart_rate']} BPM")
|
| 71 |
+
print(f" π QRS Duration: {measurements['qrs_duration']} ms")
|
| 72 |
+
print(f" β±οΈ QT Interval: {measurements['qt_interval']} ms")
|
| 73 |
+
print(f" π PR Interval: {measurements['pr_interval']} ms")
|
| 74 |
+
print(f" π§ QRS Axis: {measurements['qrs_axis']}Β°")
|
| 75 |
+
|
| 76 |
+
results.append(measurements)
|
| 77 |
+
|
| 78 |
+
else:
|
| 79 |
+
print(f" β Failed: {response.status_code}")
|
| 80 |
+
results.append(None)
|
| 81 |
+
|
| 82 |
+
except Exception as e:
|
| 83 |
+
print(f" β Error: {e}")
|
| 84 |
+
results.append(None)
|
| 85 |
+
|
| 86 |
+
# Analyze consistency
|
| 87 |
+
print(f"\nπ CONSISTENCY ANALYSIS")
|
| 88 |
+
print(f"=" * 60)
|
| 89 |
+
|
| 90 |
+
if len(results) == 3 and all(r is not None):
|
| 91 |
+
# Check if measurements are consistent
|
| 92 |
+
hr_values = [r['heart_rate'] for r in results if r['heart_rate'] is not None]
|
| 93 |
+
qrs_values = [r['qrs_duration'] for r in results if r['qrs_duration'] is not None]
|
| 94 |
+
qt_values = [r['qt_interval'] for r in results if r['qt_interval'] is not None]
|
| 95 |
+
pr_values = [r['pr_interval'] for r in results if r['pr_interval'] is not None]
|
| 96 |
+
axis_values = [r['qrs_axis'] for r in results if r['qrs_axis'] is not None]
|
| 97 |
+
|
| 98 |
+
print(f"π Heart Rate Consistency:")
|
| 99 |
+
if len(hr_values) == 3:
|
| 100 |
+
hr_std = np.std(hr_values)
|
| 101 |
+
print(f" Values: {hr_values}")
|
| 102 |
+
print(f" Standard Deviation: {hr_std:.2f}")
|
| 103 |
+
print(f" Consistent: {'β
' if hr_std < 1.0 else 'β'} (should be < 1.0 BPM)")
|
| 104 |
+
else:
|
| 105 |
+
print(f" β Missing values: {hr_values}")
|
| 106 |
+
|
| 107 |
+
print(f"\nπ QRS Duration Consistency:")
|
| 108 |
+
if len(qrs_values) == 3:
|
| 109 |
+
qrs_std = np.std(qrs_values)
|
| 110 |
+
print(f" Values: {qrs_values}")
|
| 111 |
+
print(f" Standard Deviation: {qrs_std:.2f}")
|
| 112 |
+
print(f" Consistent: {'β
' if qrs_std < 5.0 else 'β'} (should be < 5.0 ms)")
|
| 113 |
+
else:
|
| 114 |
+
print(f" β Missing values: {qrs_values}")
|
| 115 |
+
|
| 116 |
+
print(f"\nβ±οΈ QT Interval Consistency:")
|
| 117 |
+
if len(qt_values) == 3:
|
| 118 |
+
qt_std = np.std(qt_values)
|
| 119 |
+
print(f" Values: {qt_values}")
|
| 120 |
+
print(f" Standard Deviation: {qt_std:.2f}")
|
| 121 |
+
print(f" Consistent: {'β
' if qt_std < 10.0 else 'β'} (should be < 10.0 ms)")
|
| 122 |
+
else:
|
| 123 |
+
print(f" β Missing values: {qt_values}")
|
| 124 |
+
|
| 125 |
+
# Overall assessment
|
| 126 |
+
print(f"\nπ― OVERALL ASSESSMENT:")
|
| 127 |
+
print(f" If measurements are real: Should be nearly identical for identical signals")
|
| 128 |
+
print(f" If measurements are fake: Will show random variations")
|
| 129 |
+
|
| 130 |
+
else:
|
| 131 |
+
print(f"β Cannot analyze consistency - some tests failed")
|
| 132 |
+
|
| 133 |
+
def test_with_different_signals():
|
| 134 |
+
"""Test with different signal characteristics"""
|
| 135 |
+
print(f"\nπ§ͺ TESTING WITH DIFFERENT SIGNAL CHARACTERISTICS")
|
| 136 |
+
print(f"=" * 60)
|
| 137 |
+
|
| 138 |
+
# Test 1: Normal signal
|
| 139 |
+
print(f"π Test 1: Normal ECG-like signal")
|
| 140 |
+
t = np.linspace(0, 10, 5000)
|
| 141 |
+
normal_signal = []
|
| 142 |
+
for lead in range(12):
|
| 143 |
+
lead_signal = np.sin(2 * np.pi * 1.2 * t) * 0.5 # ~72 BPM
|
| 144 |
+
normal_signal.append(lead_signal.tolist())
|
| 145 |
+
|
| 146 |
+
# Test 2: Fast signal
|
| 147 |
+
print(f"π Test 2: Fast ECG-like signal")
|
| 148 |
+
fast_signal = []
|
| 149 |
+
for lead in range(12):
|
| 150 |
+
lead_signal = np.sin(2 * np.pi * 2.0 * t) * 0.5 # ~120 BPM
|
| 151 |
+
fast_signal.append(lead_signal.tolist())
|
| 152 |
+
|
| 153 |
+
# Test 3: Slow signal
|
| 154 |
+
print(f"π Test 3: Slow ECG-like signal")
|
| 155 |
+
slow_signal = []
|
| 156 |
+
for lead in range(12):
|
| 157 |
+
lead_signal = np.sin(2 * np.pi * 0.8 * t) * 0.5 # ~48 BPM
|
| 158 |
+
slow_signal.append(lead_signal.tolist())
|
| 159 |
+
|
| 160 |
+
signals = [normal_signal, fast_signal, slow_signal]
|
| 161 |
+
expected_hrs = [72, 120, 48]
|
| 162 |
+
|
| 163 |
+
for i, (signal, expected_hr) in enumerate(zip(signals, expected_hrs), 1):
|
| 164 |
+
print(f"\nπ€ Testing Signal {i} (Expected HR: ~{expected_hr} BPM)...")
|
| 165 |
+
|
| 166 |
+
payload = {
|
| 167 |
+
"signal": signal,
|
| 168 |
+
"fs": 500,
|
| 169 |
+
"patient_age": 30,
|
| 170 |
+
"patient_gender": "M"
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
try:
|
| 174 |
+
response = requests.post(f"{API_BASE_URL}/extract_features", json=payload, timeout=60)
|
| 175 |
+
|
| 176 |
+
if response.status_code == 200:
|
| 177 |
+
result = response.json()
|
| 178 |
+
physio = result.get('physiological_parameters', {})
|
| 179 |
+
actual_hr = physio.get('heart_rate')
|
| 180 |
+
|
| 181 |
+
print(f" Expected HR: ~{expected_hr} BPM")
|
| 182 |
+
print(f" Actual HR: {actual_hr} BPM")
|
| 183 |
+
|
| 184 |
+
if actual_hr is not None:
|
| 185 |
+
difference = abs(actual_hr - expected_hr)
|
| 186 |
+
print(f" Difference: {difference:.1f} BPM")
|
| 187 |
+
print(f" Reasonable: {'β
' if difference < 20 else 'β'} (should be < 20 BPM)")
|
| 188 |
+
else:
|
| 189 |
+
print(f" β No HR measurement returned")
|
| 190 |
+
|
| 191 |
+
else:
|
| 192 |
+
print(f" β Failed: {response.status_code}")
|
| 193 |
+
|
| 194 |
+
except Exception as e:
|
| 195 |
+
print(f" β Error: {e}")
|
| 196 |
+
|
| 197 |
+
def main():
|
| 198 |
+
"""Main test function"""
|
| 199 |
+
print("π¨ MEASUREMENT ACCURACY TEST")
|
| 200 |
+
print("=" * 60)
|
| 201 |
+
print("This test will reveal if our physiological measurements are:")
|
| 202 |
+
print("β
Real clinical measurements")
|
| 203 |
+
print("β Fake/testing values")
|
| 204 |
+
print()
|
| 205 |
+
|
| 206 |
+
# Test 1: Consistency
|
| 207 |
+
test_measurement_consistency()
|
| 208 |
+
|
| 209 |
+
# Test 2: Different signals
|
| 210 |
+
test_with_different_signals()
|
| 211 |
+
|
| 212 |
+
print(f"\nπ― CONCLUSION:")
|
| 213 |
+
print(f" If measurements are consistent and reasonable: They might be real")
|
| 214 |
+
print(f" If measurements are inconsistent and random: They are fake/testing values")
|
| 215 |
+
print(f" Current implementation likely provides fake values!")
|
| 216 |
+
|
| 217 |
+
if __name__ == "__main__":
|
| 218 |
+
main()
|
test_physiological_parameters.py
CHANGED
|
@@ -14,7 +14,7 @@ from typing import Dict, Any, List
|
|
| 14 |
from datetime import datetime
|
| 15 |
|
| 16 |
# Configuration
|
| 17 |
-
API_BASE_URL = "
|
| 18 |
ECG_DIR = "../ecg_uploads_greenwich/"
|
| 19 |
INDEX_FILE = "../Greenwichschooldata.csv"
|
| 20 |
|
|
|
|
| 14 |
from datetime import datetime
|
| 15 |
|
| 16 |
# Configuration
|
| 17 |
+
API_BASE_URL = "https://mystic-cbk-ecg-fm-api.hf.space" # Deployed HF Spaces API
|
| 18 |
ECG_DIR = "../ecg_uploads_greenwich/"
|
| 19 |
INDEX_FILE = "../Greenwichschooldata.csv"
|
| 20 |
|