#!/usr/bin/env python3 """ Validate Treatment Plan Quality Comprehensive validation of treatment plan content quality and compliance. """ import sys import re import argparse from pathlib import Path from typing import Dict, List, Tuple # Validation criteria and patterns VALIDATION_CHECKS = { 'smart_goals': { 'name': 'SMART Goals Criteria', 'patterns': [ (r'\bspecific\b', 'Specific criterion'), (r'\bmeasurable\b', 'Measurable criterion'), (r'\bachievable\b', 'Achievable criterion'), (r'\brelevant\b', 'Relevant criterion'), (r'\btime[- ]?bound\b', 'Time-bound criterion') ] }, 'evidence_based': { 'name': 'Evidence-Based Practice', 'patterns': [ (r'guideline|evidence|study|trial|research', 'Evidence/guideline references'), (r'\\cite\{|\\bibitem\{|\\bibliography\{', 'Citations present') ] }, 'patient_centered': { 'name': 'Patient-Centered Care', 'patterns': [ (r'patient.*preference|shared decision|patient.*value|patient.*priority', 'Patient preferences'), (r'quality of life|functional.*goal|patient.*goal', 'Functional/QoL goals') ] }, 'safety': { 'name': 'Safety and Risk Mitigation', 'patterns': [ (r'adverse.*effect|side effect|risk|complication', 'Adverse effects mentioned'), (r'monitoring|warning sign|emergency|when to call', 'Safety monitoring plan') ] }, 'medication': { 'name': 'Medication Documentation', 'patterns': [ (r'\\d+\s*mg|\\d+\s*mcg|dose|dosage', 'Specific doses'), (r'daily|BID|TID|QID|once|twice', 'Frequency specified'), (r'rationale|indication|because|for', 'Rationale provided') ] } } def read_file(filepath: Path) -> str: """Read and return file contents.""" try: with open(filepath, 'r', encoding='utf-8') as f: return f.read() except Exception as e: print(f"Error reading file: {e}", file=sys.stderr) sys.exit(2) def validate_content(content: str) -> Dict[str, Tuple[int, int, List[str]]]: """ Validate content against criteria. Returns dict with results: {category: (passed, total, missing_items)} """ results = {} for category, checks in VALIDATION_CHECKS.items(): patterns = checks['patterns'] passed = 0 missing = [] for pattern, description in patterns: if re.search(pattern, content, re.IGNORECASE): passed += 1 else: missing.append(description) total = len(patterns) results[category] = (passed, total, missing) return results def check_icd10_codes(content: str) -> Tuple[bool, int]: """Check for ICD-10 code presence.""" # ICD-10 format: Letter followed by 2 digits, optionally more digits/letters pattern = r'\b[A-TV-Z]\d{2}\.?[\dA-TV-Z]*\b' matches = re.findall(pattern, content) has_codes = len(matches) > 0 count = len(matches) return has_codes, count def check_timeframes(content: str) -> Tuple[bool, List[str]]: """Check for specific timeframes in goals.""" timeframe_patterns = [ r'\d+\s*week', r'\d+\s*month', r'\d+\s*day', r'within\s+\d+', r'by\s+\w+\s+\d+' ] found_timeframes = [] for pattern in timeframe_patterns: matches = re.findall(pattern, content, re.IGNORECASE) found_timeframes.extend(matches[:3]) # Limit to avoid too many has_timeframes = len(found_timeframes) > 0 return has_timeframes, found_timeframes[:5] def check_quantitative_goals(content: str) -> Tuple[bool, List[str]]: """Check for quantitative/measurable goals.""" # Look for numbers with units in goal context patterns = [ r'\d+\s*%', # Percentages (HbA1c 7%) r'\d+/\d+', # Ratios (BP 130/80) r'\d+\s*mg/dL', # Lab values r'\d+\s*mmHg', # Blood pressure r'\d+\s*feet|meters', # Distance r'\d+\s*pounds|lbs|kg', # Weight r'\d+/10', # Pain scales r'\d+\s*minutes|hours' # Time ] found_metrics = [] for pattern in patterns: matches = re.findall(pattern, content, re.IGNORECASE) found_metrics.extend(matches[:2]) has_metrics = len(found_metrics) > 0 return has_metrics, found_metrics[:5] def assess_readability(content: str) -> str: """Basic readability assessment (very simplified).""" # Remove LaTeX commands for word count text_content = re.sub(r'\\[a-zA-Z]+(\{[^}]*\})?', '', content) text_content = re.sub(r'[{}%\\]', '', text_content) words = text_content.split() word_count = len(words) # Very rough sentences (periods followed by space/newline) sentences = re.split(r'[.!?]+\s+', text_content) sentence_count = len([s for s in sentences if s.strip()]) if sentence_count > 0: avg_words_per_sentence = word_count / sentence_count if avg_words_per_sentence < 15: return "Simple (good for patient materials)" elif avg_words_per_sentence < 25: return "Moderate (appropriate for professional documentation)" else: return "Complex (may be difficult for some readers)" return "Unable to assess" def display_validation_results(filepath: Path, results: Dict, has_icd10: bool, icd10_count: int, has_timeframes: bool, timeframe_examples: List[str], has_metrics: bool, metric_examples: List[str], readability: str): """Display comprehensive validation results.""" print("\n" + "="*70) print("TREATMENT PLAN QUALITY VALIDATION") print("="*70) print(f"\nFile: {filepath}") print(f"File size: {filepath.stat().st_size:,} bytes") # Overall quality score total_passed = sum(r[0] for r in results.values()) total_checks = sum(r[1] for r in results.values()) quality_pct = (total_passed / total_checks) * 100 if total_checks > 0 else 0 print("\n" + "-"*70) print("OVERALL QUALITY SCORE") print("-"*70) print(f"Validation checks passed: {total_passed}/{total_checks} ({quality_pct:.0f}%)") # Detailed category results print("\n" + "-"*70) print("QUALITY CRITERIA ASSESSMENT") print("-"*70) for category, (passed, total, missing) in results.items(): category_name = VALIDATION_CHECKS[category]['name'] pct = (passed / total) * 100 if total > 0 else 0 status = "✓" if passed == total else "⚠" if passed > 0 else "✗" print(f"\n{status} {category_name}: {passed}/{total} ({pct:.0f}%)") if missing: print(" Missing:") for item in missing: print(f" • {item}") # Specific checks print("\n" + "-"*70) print("SPECIFIC VALIDATION CHECKS") print("-"*70) # ICD-10 codes if has_icd10: print(f"✓ ICD-10 diagnosis codes present ({icd10_count} found)") else: print("✗ No ICD-10 diagnosis codes detected") print(" Recommendation: Include ICD-10 codes for all diagnoses") # Timeframes if has_timeframes: print(f"✓ Time-bound goals present") if timeframe_examples: print(" Examples:", ", ".join(timeframe_examples[:3])) else: print("✗ No specific timeframes found in goals") print(" Recommendation: Add specific timeframes (e.g., 'within 3 months', '8 weeks')") # Measurable metrics if has_metrics: print(f"✓ Quantitative/measurable goals present") if metric_examples: print(" Examples:", ", ".join(metric_examples[:3])) else: print("⚠ Limited quantitative metrics found") print(" Recommendation: Include specific measurable targets (HbA1c <7%, BP <130/80)") # Readability print(f"\nReadability assessment: {readability}") # Summary and recommendations print("\n" + "="*70) print("SUMMARY AND RECOMMENDATIONS") print("="*70) if quality_pct >= 90: print("\n✓ EXCELLENT quality - Treatment plan meets high standards") elif quality_pct >= 75: print("\n✓ GOOD quality - Treatment plan is well-developed with minor areas for improvement") elif quality_pct >= 60: print("\n⚠ FAIR quality - Several important elements need strengthening") else: print("\n✗ NEEDS IMPROVEMENT - Significant quality issues to address") # Specific recommendations print("\nKey Recommendations:") recommendations = [] # SMART goals if results['smart_goals'][0] < results['smart_goals'][1]: recommendations.append("Ensure all goals meet SMART criteria (Specific, Measurable, Achievable, Relevant, Time-bound)") # Evidence-based if results['evidence_based'][0] == 0: recommendations.append("Add evidence-based rationale and cite clinical practice guidelines") # Patient-centered if results['patient_centered'][0] < results['patient_centered'][1]: recommendations.append("Incorporate patient preferences and functional quality-of-life goals") # Safety if results['safety'][0] < results['safety'][1]: recommendations.append("Include comprehensive safety monitoring and risk mitigation strategies") # Medication documentation if results['medication'][0] < results['medication'][1]: recommendations.append("Document medications with specific doses, frequencies, and rationales") if not has_icd10: recommendations.append("Add ICD-10 diagnosis codes for billing and documentation support") if not has_timeframes: recommendations.append("Add specific timeframes to all treatment goals") if recommendations: for i, rec in enumerate(recommendations, 1): print(f"{i}. {rec}") else: print("None - Treatment plan demonstrates excellent quality across all criteria!") print("\n" + "="*70) # Return exit code return 0 if quality_pct >= 70 else 1 def main(): parser = argparse.ArgumentParser( description='Validate treatment plan quality and compliance', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Validate a treatment plan python validate_treatment_plan.py my_plan.tex # Use in automated workflows (exits with error if quality <70%) python validate_treatment_plan.py plan.tex && echo "Quality check passed" Validation Categories: - SMART goals criteria (Specific, Measurable, Achievable, Relevant, Time-bound) - Evidence-based practice (guidelines, citations) - Patient-centered care (preferences, functional goals) - Safety and risk mitigation (adverse effects, monitoring) - Medication documentation (doses, frequencies, rationales) - ICD-10 coding, timeframes, measurable metrics Exit Codes: 0 - Quality ≥70% (acceptable) 1 - Quality <70% (needs improvement) 2 - File error or invalid arguments """ ) parser.add_argument( 'file', type=Path, help='Treatment plan file to validate (.tex format)' ) args = parser.parse_args() # Check file exists if not args.file.exists(): print(f"Error: File not found: {args.file}", file=sys.stderr) sys.exit(2) # Read and validate content = read_file(args.file) # Run validation checks results = validate_content(content) has_icd10, icd10_count = check_icd10_codes(content) has_timeframes, timeframe_examples = check_timeframes(content) has_metrics, metric_examples = check_quantitative_goals(content) readability = assess_readability(content) # Display results exit_code = display_validation_results( args.file, results, has_icd10, icd10_count, has_timeframes, timeframe_examples, has_metrics, metric_examples, readability ) sys.exit(exit_code) if __name__ == '__main__': main()