Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:30:18 +08:00
commit 74bee324ab
335 changed files with 147377 additions and 0 deletions

View File

@@ -0,0 +1,318 @@
#!/usr/bin/env python3
"""
Check Treatment Plan Completeness
Validates that all required sections are present in a treatment plan.
"""
import sys
import re
import argparse
from pathlib import Path
from typing import List, Tuple
# Required sections for all treatment plans
REQUIRED_SECTIONS = [
r'\\section\*\{.*Patient Information',
r'\\section\*\{.*Diagnosis.*Assessment',
r'\\section\*\{.*Goals',
r'\\section\*\{.*Interventions',
r'\\section\*\{.*Timeline.*Schedule',
r'\\section\*\{.*Monitoring',
r'\\section\*\{.*Outcomes',
r'\\section\*\{.*Follow[- ]?up',
r'\\section\*\{.*Education',
r'\\section\*\{.*Risk.*Safety',
]
# Section descriptions for user-friendly output
SECTION_DESCRIPTIONS = {
0: 'Patient Information (de-identified)',
1: 'Diagnosis and Assessment',
2: 'Treatment Goals (SMART format)',
3: 'Interventions (pharmacological, non-pharmacological, procedural)',
4: 'Timeline and Schedule',
5: 'Monitoring Parameters',
6: 'Expected Outcomes',
7: 'Follow-up Plan',
8: 'Patient Education',
9: 'Risk Mitigation and Safety'
}
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 FileNotFoundError:
print(f"Error: File not found: {filepath}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error reading file: {e}", file=sys.stderr)
sys.exit(1)
def check_sections(content: str) -> Tuple[List[bool], List[str]]:
"""
Check which required sections are present.
Returns tuple of (checklist, missing_sections).
"""
checklist = []
missing = []
for i, pattern in enumerate(REQUIRED_SECTIONS):
if re.search(pattern, content, re.IGNORECASE):
checklist.append(True)
else:
checklist.append(False)
missing.append(SECTION_DESCRIPTIONS[i])
return checklist, missing
def check_smart_goals(content: str) -> Tuple[bool, List[str]]:
"""
Check if SMART goal criteria are mentioned.
Returns (has_smart, missing_criteria).
"""
smart_criteria = {
'Specific': r'\bspecific\b',
'Measurable': r'\bmeasurable\b',
'Achievable': r'\bachievable\b',
'Relevant': r'\brelevant\b',
'Time-bound': r'\btime[- ]?bound\b'
}
missing = []
for criterion, pattern in smart_criteria.items():
if not re.search(pattern, content, re.IGNORECASE):
missing.append(criterion)
has_smart = len(missing) == 0
return has_smart, missing
def check_hipaa_notice(content: str) -> bool:
"""Check if HIPAA de-identification notice is present."""
pattern = r'HIPAA|de-identif|protected health information|PHI'
return bool(re.search(pattern, content, re.IGNORECASE))
def check_provider_signature(content: str) -> bool:
"""Check if provider signature section is present."""
pattern = r'\\section\*\{.*Signature|Provider Signature|Signature'
return bool(re.search(pattern, content, re.IGNORECASE))
def check_placeholders_remaining(content: str) -> Tuple[int, List[str]]:
"""
Check for uncustomized placeholders [like this].
Returns (count, sample_placeholders).
"""
placeholders = re.findall(r'\[([^\]]+)\]', content)
# Filter out LaTeX commands and references
filtered = []
for p in placeholders:
# Skip if it's a LaTeX command, number, or citation
if not (p.startswith('\\') or p.isdigit() or 'cite' in p.lower() or 'ref' in p.lower()):
filtered.append(p)
count = len(filtered)
samples = filtered[:5] # Return up to 5 examples
return count, samples
def display_results(filepath: Path, checklist: List[bool], missing: List[str],
smart_complete: bool, smart_missing: List[str],
has_hipaa: bool, has_signature: bool,
placeholder_count: int, placeholder_samples: List[str]):
"""Display completeness check results."""
total_sections = len(REQUIRED_SECTIONS)
present_count = sum(checklist)
completeness_pct = (present_count / total_sections) * 100
print("\n" + "="*70)
print("TREATMENT PLAN COMPLETENESS CHECK")
print("="*70)
print(f"\nFile: {filepath}")
print(f"File size: {filepath.stat().st_size:,} bytes")
# Overall completeness
print("\n" + "-"*70)
print("OVERALL COMPLETENESS")
print("-"*70)
print(f"Required sections present: {present_count}/{total_sections} ({completeness_pct:.0f}%)")
if completeness_pct == 100:
print("✓ All required sections present")
else:
print(f"{len(missing)} section(s) missing")
# Section details
print("\n" + "-"*70)
print("SECTION CHECKLIST")
print("-"*70)
for i, (present, desc) in enumerate(zip(checklist, SECTION_DESCRIPTIONS.values())):
status = "" if present else ""
print(f"{status} {desc}")
# Missing sections
if missing:
print("\n" + "-"*70)
print("MISSING SECTIONS")
print("-"*70)
for section in missing:
print(f"{section}")
# SMART goals
print("\n" + "-"*70)
print("SMART GOALS CHECK")
print("-"*70)
if smart_complete:
print("✓ All SMART criteria mentioned in document")
else:
print(f"{len(smart_missing)} SMART criterion/criteria not found:")
for criterion in smart_missing:
print(f"{criterion}")
print("\nNote: Goals should be Specific, Measurable, Achievable, Relevant, Time-bound")
# HIPAA notice
print("\n" + "-"*70)
print("PRIVACY AND COMPLIANCE")
print("-"*70)
if has_hipaa:
print("✓ HIPAA/de-identification notice present")
else:
print("✗ HIPAA de-identification notice not found")
print(" Recommendation: Include HIPAA Safe Harbor de-identification guidance")
if has_signature:
print("✓ Provider signature section present")
else:
print("✗ Provider signature section not found")
# Placeholders
print("\n" + "-"*70)
print("CUSTOMIZATION STATUS")
print("-"*70)
if placeholder_count == 0:
print("✓ No uncustomized placeholders detected")
else:
print(f"{placeholder_count} placeholder(s) may need customization")
print("\nExamples:")
for sample in placeholder_samples:
print(f" • [{sample}]")
print("\nRecommendation: Replace all [bracketed placeholders] with patient-specific information")
# Summary
print("\n" + "="*70)
print("SUMMARY")
print("="*70)
# Calculate overall score
score_components = [
completeness_pct / 100, # Section completeness (0-1)
1.0 if smart_complete else 0.6, # SMART goals (full or partial credit)
1.0 if has_hipaa else 0.0, # HIPAA notice (binary)
1.0 if has_signature else 0.0, # Signature (binary)
1.0 if placeholder_count == 0 else 0.5 # Customization (full or partial)
]
overall_score = (sum(score_components) / len(score_components)) * 100
print(f"\nOverall completeness score: {overall_score:.0f}%")
if overall_score >= 90:
print("Status: ✓ EXCELLENT - Treatment plan is comprehensive")
elif overall_score >= 75:
print("Status: ✓ GOOD - Minor improvements needed")
elif overall_score >= 60:
print("Status: ⚠ FAIR - Several sections need attention")
else:
print("Status: ✗ INCOMPLETE - Significant work needed")
print("\n" + "="*70)
# Return exit code based on completeness
return 0 if completeness_pct >= 80 else 1
def main():
parser = argparse.ArgumentParser(
description='Check treatment plan completeness',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Check a treatment plan file
python check_completeness.py my_treatment_plan.tex
# Check and exit with error code if incomplete (for CI/CD)
python check_completeness.py plan.tex && echo "Complete"
This script checks for:
- All required sections (10 core sections)
- SMART goal criteria
- HIPAA de-identification notice
- Provider signature section
- Uncustomized placeholders
Exit codes:
0 - All required sections present (≥80% complete)
1 - Missing required sections (<80% complete)
2 - File error or invalid arguments
"""
)
parser.add_argument(
'file',
type=Path,
help='Treatment plan file to check (.tex format)'
)
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='Show detailed output'
)
args = parser.parse_args()
# Check file exists and is .tex
if not args.file.exists():
print(f"Error: File not found: {args.file}", file=sys.stderr)
sys.exit(2)
if args.file.suffix.lower() not in ['.tex', '.txt']:
print(f"Warning: Expected .tex file, got {args.file.suffix}", file=sys.stderr)
# Read file
content = read_file(args.file)
# Perform checks
checklist, missing = check_sections(content)
smart_complete, smart_missing = check_smart_goals(content)
has_hipaa = check_hipaa_notice(content)
has_signature = check_provider_signature(content)
placeholder_count, placeholder_samples = check_placeholders_remaining(content)
# Display results
exit_code = display_results(
args.file, checklist, missing,
smart_complete, smart_missing,
has_hipaa, has_signature,
placeholder_count, placeholder_samples
)
sys.exit(exit_code)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,244 @@
#!/usr/bin/env python3
"""
Generate Treatment Plan Template
Interactive script to select and generate treatment plan templates.
"""
import os
import sys
import shutil
import argparse
from pathlib import Path
from datetime import datetime
# Template types and descriptions
TEMPLATES = {
'general_medical': {
'name': 'General Medical Treatment Plan',
'file': 'general_medical_treatment_plan.tex',
'description': 'For primary care and chronic disease management (diabetes, hypertension, etc.)'
},
'rehabilitation': {
'name': 'Rehabilitation Treatment Plan',
'file': 'rehabilitation_treatment_plan.tex',
'description': 'For physical therapy, occupational therapy, and rehabilitation services'
},
'mental_health': {
'name': 'Mental Health Treatment Plan',
'file': 'mental_health_treatment_plan.tex',
'description': 'For psychiatric and behavioral health treatment'
},
'chronic_disease': {
'name': 'Chronic Disease Management Plan',
'file': 'chronic_disease_management_plan.tex',
'description': 'For complex multimorbidity and long-term care coordination'
},
'perioperative': {
'name': 'Perioperative Care Plan',
'file': 'perioperative_care_plan.tex',
'description': 'For surgical and procedural patient management'
},
'pain_management': {
'name': 'Pain Management Plan',
'file': 'pain_management_plan.tex',
'description': 'For acute and chronic pain treatment (multimodal approach)'
}
}
def get_templates_dir():
"""Get the path to the templates directory."""
# Assume script is in .claude/skills/treatment-plans/scripts/
script_dir = Path(__file__).parent
templates_dir = script_dir.parent / 'assets'
return templates_dir
def list_templates():
"""Display available templates."""
print("\n" + "="*70)
print("AVAILABLE TREATMENT PLAN TEMPLATES")
print("="*70)
for i, (key, info) in enumerate(TEMPLATES.items(), 1):
print(f"\n{i}. {info['name']}")
print(f" Type: {key}")
print(f" File: {info['file']}")
print(f" Description: {info['description']}")
print("\n" + "="*70)
def interactive_selection():
"""Interactive template selection."""
list_templates()
while True:
try:
choice = input("\nSelect template number (1-6) or 'q' to quit: ").strip().lower()
if choice == 'q':
print("Exiting...")
sys.exit(0)
choice_num = int(choice)
if 1 <= choice_num <= len(TEMPLATES):
template_key = list(TEMPLATES.keys())[choice_num - 1]
return template_key
else:
print(f"Please enter a number between 1 and {len(TEMPLATES)}.")
except ValueError:
print("Invalid input. Please enter a number or 'q' to quit.")
def get_output_filename(template_key, custom_name=None):
"""Generate output filename."""
if custom_name:
# Ensure .tex extension
if not custom_name.endswith('.tex'):
custom_name += '.tex'
return custom_name
# Default: template_key_YYYYMMDD.tex
timestamp = datetime.now().strftime('%Y%m%d')
return f"{template_key}_plan_{timestamp}.tex"
def copy_template(template_key, output_path):
"""Copy template to output location."""
templates_dir = get_templates_dir()
template_file = TEMPLATES[template_key]['file']
source_path = templates_dir / template_file
if not source_path.exists():
raise FileNotFoundError(f"Template not found: {source_path}")
# Create output directory if it doesn't exist
output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
# Copy template
shutil.copy2(source_path, output_path)
return output_path
def display_success(output_path, template_key):
"""Display success message with next steps."""
template_info = TEMPLATES[template_key]
print("\n" + "="*70)
print("✓ TEMPLATE GENERATED SUCCESSFULLY")
print("="*70)
print(f"\nTemplate: {template_info['name']}")
print(f"Output file: {output_path}")
print(f"File size: {os.path.getsize(output_path):,} bytes")
print("\n" + "-"*70)
print("NEXT STEPS:")
print("-"*70)
print("\n1. CUSTOMIZE THE TEMPLATE:")
print(" - Open the .tex file in your LaTeX editor")
print(" - Replace all [bracketed placeholders] with patient-specific information")
print(" - Remove or modify sections as appropriate for your patient")
print("\n2. COMPILE TO PDF:")
print(f" $ pdflatex {output_path.name}")
print("\n3. VALIDATE (optional):")
print(f" $ python check_completeness.py {output_path.name}")
print(f" $ python validate_treatment_plan.py {output_path.name}")
print("\n4. DE-IDENTIFY BEFORE SHARING:")
print(" - Remove all HIPAA identifiers (18 identifiers)")
print(" - See regulatory_compliance.md reference for details")
print("\n" + "="*70)
def main():
parser = argparse.ArgumentParser(
description='Generate treatment plan template',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Interactive mode (recommended for first-time users)
python generate_template.py
# Direct generation with type specification
python generate_template.py --type general_medical --output diabetes_plan.tex
# Generate with default filename
python generate_template.py --type mental_health
# List available templates
python generate_template.py --list
Available template types:
general_medical, rehabilitation, mental_health, chronic_disease,
perioperative, pain_management
"""
)
parser.add_argument(
'--type',
choices=list(TEMPLATES.keys()),
help='Template type to generate'
)
parser.add_argument(
'--output',
help='Output filename (default: auto-generated with timestamp)'
)
parser.add_argument(
'--list',
action='store_true',
help='List available templates and exit'
)
args = parser.parse_args()
# List templates and exit
if args.list:
list_templates()
return
# Determine template type
if args.type:
template_key = args.type
print(f"\nGenerating template: {TEMPLATES[template_key]['name']}")
else:
# Interactive mode
template_key = interactive_selection()
# Determine output filename
if args.output:
output_filename = args.output
else:
output_filename = get_output_filename(template_key)
# Default output to current directory
output_path = Path.cwd() / output_filename
# Confirm overwrite if file exists
if output_path.exists():
response = input(f"\nFile {output_filename} already exists. Overwrite? (y/n): ").strip().lower()
if response != 'y':
print("Cancelled.")
return
# Copy template
try:
output_path = copy_template(template_key, output_path)
display_success(output_path, template_key)
except Exception as e:
print(f"\n✗ ERROR: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,369 @@
#!/usr/bin/env python3
"""
Treatment Timeline Generator
Generates visual treatment timelines from treatment plan files.
"""
import sys
import re
import argparse
from pathlib import Path
from datetime import datetime, timedelta
from typing import List, Dict, Tuple
# Try to import matplotlib, but make it optional
try:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
HAS_MATPLOTLIB = True
except ImportError:
HAS_MATPLOTLIB = False
def extract_timeline_info(content: str) -> Dict[str, List[Tuple[str, str]]]:
"""
Extract timeline and schedule information from treatment plan.
Returns dict with phases, appointments, milestones.
"""
timeline_data = {
'phases': [],
'appointments': [],
'milestones': []
}
# Extract treatment phases
# Look for patterns like "Week 1-4: Description" or "Months 1-3: Description"
phase_patterns = [
r'(Week[s]?\s*\d+[-]\d+|Month[s]?\s*\d+[-]\d+)[:\s]+([^\n]+)',
r'(POD\s*\d+[-]\d+)[:\s]+([^\n]+)',
r'(\d+[-]\d+\s*week[s]?)[:\s]+([^\n]+)'
]
for pattern in phase_patterns:
matches = re.findall(pattern, content, re.IGNORECASE)
for timeframe, description in matches:
timeline_data['phases'].append((timeframe.strip(), description.strip()))
# Extract appointments
# Look for patterns like "Week 2: Visit" or "Month 3: Follow-up"
apt_patterns = [
r'(Week\s*\d+|Month\s*\d+|POD\s*\d+)[:\s]+(Visit|Appointment|Follow-up|Check-up|Consultation)([^\n]*)',
r'(Every\s+\d+\s+\w+)[:\s]+(Visit|Appointment|therapy|session)([^\n]*)'
]
for pattern in apt_patterns:
matches = re.findall(pattern, content, re.IGNORECASE)
for timeframe, visit_type, details in matches:
timeline_data['appointments'].append((timeframe.strip(), f"{visit_type}{details}".strip()))
# Extract milestones/assessments
# Look for "reassessment", "goal evaluation", "milestone" mentions
milestone_patterns = [
r'(Week\s*\d+|Month\s*\d+)[:\s]+(reassess|evaluation|assessment|milestone)([^\n]*)',
r'(\w+\s*\d+)[:\s]+(HbA1c|labs?|imaging|test)([^\n]*)'
]
for pattern in milestone_patterns:
matches = re.findall(pattern, content, re.IGNORECASE)
for timeframe, event_type, details in matches:
timeline_data['milestones'].append((timeframe.strip(), f"{event_type}{details}".strip()))
return timeline_data
def parse_timeframe_to_days(timeframe: str) -> Tuple[int, int]:
"""
Parse timeframe string to start and end days.
Examples: "Week 1-4" -> (0, 28), "Month 3" -> (60, 90)
"""
timeframe = timeframe.lower()
# Week patterns
if 'week' in timeframe:
weeks = re.findall(r'\d+', timeframe)
if len(weeks) == 2:
start_week = int(weeks[0])
end_week = int(weeks[1])
return ((start_week - 1) * 7, end_week * 7)
elif len(weeks) == 1:
week = int(weeks[0])
return ((week - 1) * 7, week * 7)
# Month patterns
if 'month' in timeframe:
months = re.findall(r'\d+', timeframe)
if len(months) == 2:
start_month = int(months[0])
end_month = int(months[1])
return ((start_month - 1) * 30, end_month * 30)
elif len(months) == 1:
month = int(months[0])
return ((month - 1) * 30, month * 30)
# POD (post-operative day) patterns
if 'pod' in timeframe:
days = re.findall(r'\d+', timeframe)
if len(days) == 2:
return (int(days[0]), int(days[1]))
elif len(days) == 1:
day = int(days[0])
return (day, day + 1)
# Default fallback
return (0, 7)
def create_text_timeline(timeline_data: Dict, output_file: Path = None):
"""Create a text-based timeline representation."""
lines = []
lines.append("="*70)
lines.append("TREATMENT TIMELINE")
lines.append("="*70)
# Treatment phases
if timeline_data['phases']:
lines.append("\nTREATMENT PHASES:")
lines.append("-"*70)
for timeframe, description in timeline_data['phases']:
lines.append(f"{timeframe:20s} | {description}")
# Appointments
if timeline_data['appointments']:
lines.append("\nSCHEDULED APPOINTMENTS:")
lines.append("-"*70)
for timeframe, details in timeline_data['appointments']:
lines.append(f"{timeframe:20s} | {details}")
# Milestones
if timeline_data['milestones']:
lines.append("\nMILESTONES & ASSESSMENTS:")
lines.append("-"*70)
for timeframe, event in timeline_data['milestones']:
lines.append(f"{timeframe:20s} | {event}")
lines.append("\n" + "="*70)
# Output
output_text = "\n".join(lines)
if output_file:
with open(output_file, 'w') as f:
f.write(output_text)
print(f"\nText timeline saved to: {output_file}")
else:
print(output_text)
return output_text
def create_visual_timeline(timeline_data: Dict, output_file: Path, start_date: str = None):
"""Create a visual Gantt-chart style timeline (requires matplotlib)."""
if not HAS_MATPLOTLIB:
print("Error: matplotlib not installed. Install with: pip install matplotlib", file=sys.stderr)
print("Generating text timeline instead...", file=sys.stderr)
text_output = output_file.with_suffix('.txt')
create_text_timeline(timeline_data, text_output)
return
# Parse start date
if start_date:
try:
start = datetime.strptime(start_date, '%Y-%m-%d')
except ValueError:
print(f"Invalid date format: {start_date}. Using today.", file=sys.stderr)
start = datetime.now()
else:
start = datetime.now()
# Prepare data for plotting
phases = []
for timeframe, description in timeline_data['phases']:
start_day, end_day = parse_timeframe_to_days(timeframe)
phases.append({
'name': f"{timeframe}: {description[:40]}",
'start': start + timedelta(days=start_day),
'end': start + timedelta(days=end_day),
'type': 'phase'
})
# Add appointments as events
events = []
for timeframe, details in timeline_data['appointments']:
start_day, _ = parse_timeframe_to_days(timeframe)
events.append({
'name': f"{timeframe}: {details[:40]}",
'date': start + timedelta(days=start_day),
'type': 'appointment'
})
# Add milestones
for timeframe, event in timeline_data['milestones']:
start_day, _ = parse_timeframe_to_days(timeframe)
events.append({
'name': f"{timeframe}: {event[:40]}",
'date': start + timedelta(days=start_day),
'type': 'milestone'
})
# Create figure
fig, ax = plt.subplots(figsize=(12, 8))
# Plot phases as horizontal bars
y_position = len(phases) + len(events)
for i, phase in enumerate(phases):
duration = (phase['end'] - phase['start']).days
ax.barh(y_position - i, duration, left=mdates.date2num(phase['start']),
height=0.6, color='steelblue', alpha=0.7, edgecolor='black')
ax.text(mdates.date2num(phase['start']) + duration/2, y_position - i,
phase['name'], va='center', ha='center', fontsize=9, color='white', weight='bold')
# Plot events as markers
event_y = y_position - len(phases) - 1
for i, event in enumerate(events):
marker = 'o' if event['type'] == 'appointment' else 's'
color = 'green' if event['type'] == 'appointment' else 'orange'
ax.plot(mdates.date2num(event['date']), event_y - i, marker=marker,
markersize=10, color=color, markeredgecolor='black')
ax.text(mdates.date2num(event['date']) + 2, event_y - i, event['name'],
va='center', ha='left', fontsize=8)
# Format x-axis as dates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.xticks(rotation=45, ha='right')
# Labels and title
ax.set_xlabel('Date', fontsize=12, weight='bold')
ax.set_title('Treatment Plan Timeline', fontsize=14, weight='bold', pad=20)
ax.set_yticks([])
ax.grid(axis='x', alpha=0.3, linestyle='--')
# Legend
from matplotlib.lines import Line2D
legend_elements = [
Rectangle((0, 0), 1, 1, fc='steelblue', alpha=0.7, edgecolor='black', label='Treatment Phase'),
Line2D([0], [0], marker='o', color='w', markerfacecolor='green', markersize=10,
markeredgecolor='black', label='Appointment'),
Line2D([0], [0], marker='s', color='w', markerfacecolor='orange', markersize=10,
markeredgecolor='black', label='Milestone/Assessment')
]
ax.legend(handles=legend_elements, loc='upper right', framealpha=0.9)
plt.tight_layout()
# Save
plt.savefig(output_file, dpi=300, bbox_inches='tight')
print(f"\nVisual timeline saved to: {output_file}")
# Close plot
plt.close()
def main():
parser = argparse.ArgumentParser(
description='Generate treatment timeline visualization',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Generate text timeline
python timeline_generator.py --plan my_plan.tex
# Generate visual timeline (requires matplotlib)
python timeline_generator.py --plan my_plan.tex --output timeline.png --visual
# Specify start date for visual timeline
python timeline_generator.py --plan my_plan.tex --output timeline.pdf --visual --start 2025-02-01
Output formats:
Text: .txt
Visual: .png, .pdf, .svg (requires matplotlib)
Note: Visual timeline generation requires matplotlib.
Install with: pip install matplotlib
"""
)
parser.add_argument(
'--plan',
type=Path,
required=True,
help='Treatment plan file to analyze (.tex format)'
)
parser.add_argument(
'--output',
type=Path,
help='Output file (default: timeline.txt or timeline.png if --visual)'
)
parser.add_argument(
'--visual',
action='store_true',
help='Generate visual timeline (requires matplotlib)'
)
parser.add_argument(
'--start',
help='Start date for timeline (YYYY-MM-DD format, default: today)'
)
args = parser.parse_args()
# Check plan file exists
if not args.plan.exists():
print(f"Error: File not found: {args.plan}", file=sys.stderr)
sys.exit(1)
# Read plan
try:
with open(args.plan, 'r', encoding='utf-8') as f:
content = f.read()
except Exception as e:
print(f"Error reading file: {e}", file=sys.stderr)
sys.exit(1)
# Extract timeline information
print("Extracting timeline information from treatment plan...")
timeline_data = extract_timeline_info(content)
# Check if any timeline info found
total_items = (len(timeline_data['phases']) +
len(timeline_data['appointments']) +
len(timeline_data['milestones']))
if total_items == 0:
print("\nWarning: No timeline information detected in treatment plan.", file=sys.stderr)
print("The plan may not contain structured timeline/schedule sections.", file=sys.stderr)
print("\nTip: Include sections with timeframes like:", file=sys.stderr)
print(" - Week 1-4: Initial phase", file=sys.stderr)
print(" - Month 3: Follow-up visit", file=sys.stderr)
sys.exit(1)
print(f"Found {len(timeline_data['phases'])} phase(s), "
f"{len(timeline_data['appointments'])} appointment(s), "
f"{len(timeline_data['milestones'])} milestone(s)")
# Determine output file
if not args.output:
if args.visual:
args.output = Path('timeline.png')
else:
args.output = Path('timeline.txt')
# Generate timeline
if args.visual:
create_visual_timeline(timeline_data, args.output, args.start)
else:
create_text_timeline(timeline_data, args.output)
print(f"\nTimeline generation complete!")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,367 @@
#!/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()