188 lines
7.3 KiB
Python
188 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Verify that all apps are properly reflecting the Phase 4 model changes.
|
|
This script checks for common issues after the core consolidation.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
|
|
# Color codes for terminal output
|
|
GREEN = '\033[92m'
|
|
RED = '\033[91m'
|
|
YELLOW = '\033[93m'
|
|
BLUE = '\033[94m'
|
|
RESET = '\033[0m'
|
|
|
|
class ModelChangeVerifier:
|
|
def __init__(self, project_root):
|
|
self.project_root = Path(project_root)
|
|
self.issues = []
|
|
self.warnings = []
|
|
self.successes = []
|
|
|
|
def log_issue(self, file_path, line_num, issue):
|
|
self.issues.append(f"{RED}✗{RESET} {file_path}:{line_num} - {issue}")
|
|
|
|
def log_warning(self, file_path, line_num, warning):
|
|
self.warnings.append(f"{YELLOW}⚠{RESET} {file_path}:{line_num} - {warning}")
|
|
|
|
def log_success(self, message):
|
|
self.successes.append(f"{GREEN}✓{RESET} {message}")
|
|
|
|
def check_file(self, file_path):
|
|
"""Check a single Python file for issues."""
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
lines = content.split('\n')
|
|
|
|
rel_path = file_path.relative_to(self.project_root)
|
|
|
|
# Check for old model references
|
|
old_patterns = [
|
|
(r'from patients\.models import PatientProfile',
|
|
"Should import from core.Patient instead of patients.PatientProfile for patient identity"),
|
|
(r'models\.ForeignKey\(["\']patients\.PatientProfile["\']',
|
|
"ForeignKey should reference 'core.Patient' not 'patients.PatientProfile'"),
|
|
(r'AuditLogEntry',
|
|
"Should use 'AuditEvent' instead of 'AuditLogEntry'"),
|
|
(r'\.first_name',
|
|
"Patient first_name is now 'given_name' in core.Patient"),
|
|
(r'\.last_name',
|
|
"Patient last_name is now 'family_name' in core.Patient"),
|
|
(r'\.date_of_birth',
|
|
"Patient date_of_birth is now 'birth_date' in core.Patient"),
|
|
(r'\.gender(?!_)',
|
|
"Patient gender is now 'administrative_gender' in core.Patient"),
|
|
]
|
|
|
|
for line_num, line in enumerate(lines, 1):
|
|
for pattern, message in old_patterns:
|
|
if re.search(pattern, line) and not line.strip().startswith('#'):
|
|
# Skip if it's in a comment
|
|
if '#' in line and line.index('#') < line.find(pattern):
|
|
continue
|
|
self.log_warning(rel_path, line_num, message)
|
|
|
|
# Check for proper imports in models.py files
|
|
if file_path.name == 'models.py':
|
|
if 'from core.models import' in content:
|
|
self.log_success(f"{rel_path} - Imports from core.models")
|
|
|
|
# Check if TenantScopedModel is used
|
|
if 'TenantScopedModel' in content:
|
|
self.log_success(f"{rel_path} - Uses TenantScopedModel")
|
|
|
|
# Check forms.py files
|
|
if file_path.name == 'forms.py':
|
|
# Check for fields that don't exist
|
|
problematic_fields = [
|
|
'first_name', 'last_name', 'date_of_birth', 'gender',
|
|
'id_number', 'subscriber_id_number'
|
|
]
|
|
|
|
for field in problematic_fields:
|
|
pattern = f"['\"]{ field}['\"]"
|
|
if re.search(pattern, content):
|
|
# Check if it's in a PatientProfile or Patient form
|
|
if 'PatientProfileForm' in content or 'PatientForm' in content:
|
|
for line_num, line in enumerate(lines, 1):
|
|
if re.search(pattern, line) and not line.strip().startswith('#'):
|
|
self.log_warning(rel_path, line_num,
|
|
f"Field '{field}' may not exist in model")
|
|
|
|
except Exception as e:
|
|
self.log_issue(file_path, 0, f"Error reading file: {e}")
|
|
|
|
def check_app(self, app_name):
|
|
"""Check all Python files in an app."""
|
|
app_path = self.project_root / app_name
|
|
|
|
if not app_path.exists():
|
|
return
|
|
|
|
print(f"\n{BLUE}Checking app: {app_name}{RESET}")
|
|
|
|
# Check models.py
|
|
models_file = app_path / 'models.py'
|
|
if models_file.exists():
|
|
self.check_file(models_file)
|
|
|
|
# Check forms.py
|
|
forms_file = app_path / 'forms.py'
|
|
if forms_file.exists():
|
|
self.check_file(forms_file)
|
|
|
|
# Check views.py
|
|
views_file = app_path / 'views.py'
|
|
if views_file.exists():
|
|
self.check_file(views_file)
|
|
|
|
# Check admin.py
|
|
admin_file = app_path / 'admin.py'
|
|
if admin_file.exists():
|
|
self.check_file(admin_file)
|
|
|
|
def run_verification(self):
|
|
"""Run verification on all apps."""
|
|
apps = [
|
|
'core', 'accounts', 'patients', 'emr', 'appointments',
|
|
'inpatients', 'pharmacy', 'laboratory', 'radiology',
|
|
'operating_theatre', 'billing', 'inventory', 'hr',
|
|
'analytics', 'communications', 'integration', 'quality',
|
|
'facility_management', 'blood_bank', 'insurance_approvals'
|
|
]
|
|
|
|
print(f"{BLUE}{'='*80}{RESET}")
|
|
print(f"{BLUE}Phase 4 Model Changes Verification{RESET}")
|
|
print(f"{BLUE}{'='*80}{RESET}")
|
|
|
|
for app in apps:
|
|
self.check_app(app)
|
|
|
|
# Print summary
|
|
print(f"\n{BLUE}{'='*80}{RESET}")
|
|
print(f"{BLUE}Verification Summary{RESET}")
|
|
print(f"{BLUE}{'='*80}{RESET}")
|
|
|
|
if self.successes:
|
|
print(f"\n{GREEN}Successes ({len(self.successes)}):{RESET}")
|
|
for success in self.successes[:10]: # Show first 10
|
|
print(f" {success}")
|
|
if len(self.successes) > 10:
|
|
print(f" ... and {len(self.successes) - 10} more")
|
|
|
|
if self.warnings:
|
|
print(f"\n{YELLOW}Warnings ({len(self.warnings)}):{RESET}")
|
|
for warning in self.warnings:
|
|
print(f" {warning}")
|
|
|
|
if self.issues:
|
|
print(f"\n{RED}Issues ({len(self.issues)}):{RESET}")
|
|
for issue in self.issues:
|
|
print(f" {issue}")
|
|
|
|
if not self.issues and not self.warnings:
|
|
print(f"\n{GREEN}✓ All checks passed! No issues found.{RESET}")
|
|
elif not self.issues:
|
|
print(f"\n{YELLOW}⚠ No critical issues, but {len(self.warnings)} warnings to review.{RESET}")
|
|
else:
|
|
print(f"\n{RED}✗ Found {len(self.issues)} issues that need to be fixed.{RESET}")
|
|
|
|
print(f"\n{BLUE}{'='*80}{RESET}\n")
|
|
|
|
return len(self.issues) == 0
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
|
|
# Get project root (parent of tools directory)
|
|
project_root = Path(__file__).parent.parent
|
|
|
|
verifier = ModelChangeVerifier(project_root)
|
|
success = verifier.run_verification()
|
|
|
|
sys.exit(0 if success else 1)
|