empathy-framework 3.7.0__py3-none-any.whl → 3.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coach_wizards/code_reviewer_README.md +60 -0
- coach_wizards/code_reviewer_wizard.py +180 -0
- {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/METADATA +20 -2
- empathy_framework-3.7.1.dist-info/RECORD +327 -0
- {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/top_level.txt +5 -1
- empathy_healthcare_plugin/monitors/__init__.py +9 -0
- empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
- empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
- empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
- empathy_llm_toolkit/agent_factory/__init__.py +53 -0
- empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
- empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
- empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
- empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
- empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
- empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
- empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
- empathy_llm_toolkit/agent_factory/base.py +305 -0
- empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
- empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
- empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
- empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
- empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
- empathy_llm_toolkit/agent_factory/decorators.py +286 -0
- empathy_llm_toolkit/agent_factory/factory.py +558 -0
- empathy_llm_toolkit/agent_factory/framework.py +192 -0
- empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
- empathy_llm_toolkit/agent_factory/resilient.py +320 -0
- empathy_llm_toolkit/cli/__init__.py +8 -0
- empathy_llm_toolkit/cli/sync_claude.py +487 -0
- empathy_llm_toolkit/code_health.py +150 -3
- empathy_llm_toolkit/config/__init__.py +29 -0
- empathy_llm_toolkit/config/unified.py +295 -0
- empathy_llm_toolkit/routing/__init__.py +32 -0
- empathy_llm_toolkit/routing/model_router.py +362 -0
- empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
- empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
- empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
- empathy_llm_toolkit/security/README.md +262 -0
- empathy_llm_toolkit/security/__init__.py +62 -0
- empathy_llm_toolkit/security/audit_logger.py +929 -0
- empathy_llm_toolkit/security/audit_logger_example.py +152 -0
- empathy_llm_toolkit/security/pii_scrubber.py +640 -0
- empathy_llm_toolkit/security/secrets_detector.py +678 -0
- empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
- empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
- empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
- empathy_llm_toolkit/wizards/__init__.py +38 -0
- empathy_llm_toolkit/wizards/base_wizard.py +364 -0
- empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
- empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
- empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
- empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
- empathy_os/__init__.py +52 -52
- empathy_os/adaptive/__init__.py +13 -0
- empathy_os/adaptive/task_complexity.py +127 -0
- empathy_os/cli.py +118 -8
- empathy_os/cli_unified.py +121 -1
- empathy_os/config/__init__.py +63 -0
- empathy_os/config/xml_config.py +239 -0
- empathy_os/dashboard/__init__.py +15 -0
- empathy_os/dashboard/server.py +743 -0
- empathy_os/memory/__init__.py +195 -0
- empathy_os/memory/claude_memory.py +466 -0
- empathy_os/memory/config.py +224 -0
- empathy_os/memory/control_panel.py +1298 -0
- empathy_os/memory/edges.py +179 -0
- empathy_os/memory/graph.py +567 -0
- empathy_os/memory/long_term.py +1193 -0
- empathy_os/memory/nodes.py +179 -0
- empathy_os/memory/redis_bootstrap.py +540 -0
- empathy_os/memory/security/__init__.py +31 -0
- empathy_os/memory/security/audit_logger.py +930 -0
- empathy_os/memory/security/pii_scrubber.py +640 -0
- empathy_os/memory/security/secrets_detector.py +678 -0
- empathy_os/memory/short_term.py +2119 -0
- empathy_os/memory/storage/__init__.py +15 -0
- empathy_os/memory/summary_index.py +583 -0
- empathy_os/memory/unified.py +619 -0
- empathy_os/metrics/__init__.py +12 -0
- empathy_os/metrics/prompt_metrics.py +190 -0
- empathy_os/models/__init__.py +136 -0
- empathy_os/models/__main__.py +13 -0
- empathy_os/models/cli.py +655 -0
- empathy_os/models/empathy_executor.py +354 -0
- empathy_os/models/executor.py +252 -0
- empathy_os/models/fallback.py +671 -0
- empathy_os/models/provider_config.py +563 -0
- empathy_os/models/registry.py +382 -0
- empathy_os/models/tasks.py +302 -0
- empathy_os/models/telemetry.py +548 -0
- empathy_os/models/token_estimator.py +378 -0
- empathy_os/models/validation.py +274 -0
- empathy_os/monitoring/__init__.py +52 -0
- empathy_os/monitoring/alerts.py +23 -0
- empathy_os/monitoring/alerts_cli.py +268 -0
- empathy_os/monitoring/multi_backend.py +271 -0
- empathy_os/monitoring/otel_backend.py +363 -0
- empathy_os/optimization/__init__.py +19 -0
- empathy_os/optimization/context_optimizer.py +272 -0
- empathy_os/plugins/__init__.py +28 -0
- empathy_os/plugins/base.py +361 -0
- empathy_os/plugins/registry.py +268 -0
- empathy_os/project_index/__init__.py +30 -0
- empathy_os/project_index/cli.py +335 -0
- empathy_os/project_index/crew_integration.py +430 -0
- empathy_os/project_index/index.py +425 -0
- empathy_os/project_index/models.py +501 -0
- empathy_os/project_index/reports.py +473 -0
- empathy_os/project_index/scanner.py +538 -0
- empathy_os/prompts/__init__.py +61 -0
- empathy_os/prompts/config.py +77 -0
- empathy_os/prompts/context.py +177 -0
- empathy_os/prompts/parser.py +285 -0
- empathy_os/prompts/registry.py +313 -0
- empathy_os/prompts/templates.py +208 -0
- empathy_os/resilience/__init__.py +56 -0
- empathy_os/resilience/circuit_breaker.py +256 -0
- empathy_os/resilience/fallback.py +179 -0
- empathy_os/resilience/health.py +300 -0
- empathy_os/resilience/retry.py +209 -0
- empathy_os/resilience/timeout.py +135 -0
- empathy_os/routing/__init__.py +43 -0
- empathy_os/routing/chain_executor.py +433 -0
- empathy_os/routing/classifier.py +217 -0
- empathy_os/routing/smart_router.py +234 -0
- empathy_os/routing/wizard_registry.py +307 -0
- empathy_os/trust/__init__.py +28 -0
- empathy_os/trust/circuit_breaker.py +579 -0
- empathy_os/validation/__init__.py +19 -0
- empathy_os/validation/xml_validator.py +281 -0
- empathy_os/wizard_factory_cli.py +170 -0
- empathy_os/workflows/__init__.py +360 -0
- empathy_os/workflows/base.py +1530 -0
- empathy_os/workflows/bug_predict.py +962 -0
- empathy_os/workflows/code_review.py +960 -0
- empathy_os/workflows/code_review_adapters.py +310 -0
- empathy_os/workflows/code_review_pipeline.py +720 -0
- empathy_os/workflows/config.py +600 -0
- empathy_os/workflows/dependency_check.py +648 -0
- empathy_os/workflows/document_gen.py +1069 -0
- empathy_os/workflows/documentation_orchestrator.py +1205 -0
- empathy_os/workflows/health_check.py +679 -0
- empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
- empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
- empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
- empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
- empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
- empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
- empathy_os/workflows/manage_documentation.py +804 -0
- empathy_os/workflows/new_sample_workflow1.py +146 -0
- empathy_os/workflows/new_sample_workflow1_README.md +150 -0
- empathy_os/workflows/perf_audit.py +687 -0
- empathy_os/workflows/pr_review.py +748 -0
- empathy_os/workflows/progress.py +445 -0
- empathy_os/workflows/progress_server.py +322 -0
- empathy_os/workflows/refactor_plan.py +691 -0
- empathy_os/workflows/release_prep.py +808 -0
- empathy_os/workflows/research_synthesis.py +404 -0
- empathy_os/workflows/secure_release.py +585 -0
- empathy_os/workflows/security_adapters.py +297 -0
- empathy_os/workflows/security_audit.py +1050 -0
- empathy_os/workflows/step_config.py +234 -0
- empathy_os/workflows/test5.py +125 -0
- empathy_os/workflows/test5_README.md +158 -0
- empathy_os/workflows/test_gen.py +1855 -0
- empathy_os/workflows/test_lifecycle.py +526 -0
- empathy_os/workflows/test_maintenance.py +626 -0
- empathy_os/workflows/test_maintenance_cli.py +590 -0
- empathy_os/workflows/test_maintenance_crew.py +821 -0
- empathy_os/workflows/xml_enhanced_crew.py +285 -0
- empathy_software_plugin/cli/__init__.py +120 -0
- empathy_software_plugin/cli/inspect.py +362 -0
- empathy_software_plugin/cli.py +3 -1
- empathy_software_plugin/wizards/__init__.py +42 -0
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
- empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
- empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
- empathy_software_plugin/wizards/base_wizard.py +288 -0
- empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
- empathy_software_plugin/wizards/code_review_wizard.py +606 -0
- empathy_software_plugin/wizards/debugging/__init__.py +50 -0
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
- empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
- empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
- empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
- empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
- empathy_software_plugin/wizards/debugging/verification.py +369 -0
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
- empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
- empathy_software_plugin/wizards/performance/__init__.py +9 -0
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
- empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
- empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
- empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
- empathy_software_plugin/wizards/security/__init__.py +32 -0
- empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
- empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
- empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
- empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
- empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
- empathy_software_plugin/wizards/testing/__init__.py +27 -0
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
- empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
- empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
- empathy_software_plugin/wizards/testing_wizard.py +274 -0
- hot_reload/README.md +473 -0
- hot_reload/__init__.py +62 -0
- hot_reload/config.py +84 -0
- hot_reload/integration.py +228 -0
- hot_reload/reloader.py +298 -0
- hot_reload/watcher.py +179 -0
- hot_reload/websocket.py +176 -0
- scaffolding/README.md +589 -0
- scaffolding/__init__.py +35 -0
- scaffolding/__main__.py +14 -0
- scaffolding/cli.py +240 -0
- test_generator/__init__.py +38 -0
- test_generator/__main__.py +14 -0
- test_generator/cli.py +226 -0
- test_generator/generator.py +325 -0
- test_generator/risk_analyzer.py +216 -0
- workflow_patterns/__init__.py +33 -0
- workflow_patterns/behavior.py +249 -0
- workflow_patterns/core.py +76 -0
- workflow_patterns/output.py +99 -0
- workflow_patterns/registry.py +255 -0
- workflow_patterns/structural.py +288 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
- agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
- agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
- agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
- agents/compliance_anticipation_agent.py +0 -1422
- agents/compliance_db.py +0 -339
- agents/epic_integration_wizard.py +0 -530
- agents/notifications.py +0 -291
- agents/trust_building_behaviors.py +0 -872
- empathy_framework-3.7.0.dist-info/RECORD +0 -105
- {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/WHEEL +0 -0
- {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/entry_points.txt +0 -0
- {empathy_framework-3.7.0.dist-info → empathy_framework-3.7.1.dist-info}/licenses/LICENSE +0 -0
- /empathy_os/{monitoring.py → agent_monitoring.py} +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""Clinical Protocol Monitor (Level 4)
|
|
2
|
+
|
|
3
|
+
Main monitoring system that combines protocol checking and trajectory analysis.
|
|
4
|
+
|
|
5
|
+
This is the healthcare equivalent of the Advanced Debugging Wizard.
|
|
6
|
+
|
|
7
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
8
|
+
Licensed under Fair Source 0.9
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .monitoring.protocol_checker import ProtocolChecker, ProtocolCheckResult
|
|
16
|
+
from .monitoring.protocol_loader import ClinicalProtocol, ProtocolLoader
|
|
17
|
+
from .monitoring.sensor_parsers import normalize_vitals, parse_sensor_data
|
|
18
|
+
from .monitoring.trajectory_analyzer import TrajectoryAnalyzer, TrajectoryPrediction
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ClinicalProtocolMonitor:
|
|
24
|
+
"""Clinical Protocol Monitoring System.
|
|
25
|
+
|
|
26
|
+
Monitors patient sensor data against clinical protocols using
|
|
27
|
+
the same systematic approach as linting configuration:
|
|
28
|
+
|
|
29
|
+
1. Load protocol (the "config")
|
|
30
|
+
2. Parse sensor data (the "code state")
|
|
31
|
+
3. Check compliance (run the "linter")
|
|
32
|
+
4. Predict trajectory (Level 4 - anticipatory)
|
|
33
|
+
5. Generate alerts and documentation
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, protocol_directory: str | None = None):
|
|
37
|
+
self.protocol_loader = ProtocolLoader(protocol_directory)
|
|
38
|
+
self.protocol_checker = ProtocolChecker()
|
|
39
|
+
self.trajectory_analyzer = TrajectoryAnalyzer()
|
|
40
|
+
|
|
41
|
+
# Active protocols per patient
|
|
42
|
+
self.active_protocols: dict[str, ClinicalProtocol] = {}
|
|
43
|
+
|
|
44
|
+
# Historical data per patient
|
|
45
|
+
self.patient_history: dict[str, list[dict[str, Any]]] = {}
|
|
46
|
+
|
|
47
|
+
def load_protocol(
|
|
48
|
+
self,
|
|
49
|
+
patient_id: str,
|
|
50
|
+
protocol_name: str,
|
|
51
|
+
patient_context: dict[str, Any] | None = None,
|
|
52
|
+
) -> ClinicalProtocol:
|
|
53
|
+
"""Load and activate protocol for patient.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
patient_id: Patient identifier
|
|
57
|
+
protocol_name: Name of protocol to load
|
|
58
|
+
patient_context: Additional patient context
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Loaded protocol
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
>>> monitor = ClinicalProtocolMonitor()
|
|
65
|
+
>>> protocol = monitor.load_protocol(
|
|
66
|
+
... patient_id="12345",
|
|
67
|
+
... protocol_name="sepsis",
|
|
68
|
+
... patient_context={"age": 65, "post_op_day": 2}
|
|
69
|
+
... )
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
protocol = self.protocol_loader.load_protocol(protocol_name)
|
|
73
|
+
self.active_protocols[patient_id] = protocol
|
|
74
|
+
|
|
75
|
+
logger.info(f"Loaded {protocol.name} v{protocol.version} for patient {patient_id}")
|
|
76
|
+
|
|
77
|
+
return protocol
|
|
78
|
+
|
|
79
|
+
async def analyze(self, context: dict[str, Any]) -> dict[str, Any]:
|
|
80
|
+
"""Main analysis method - unified interface.
|
|
81
|
+
|
|
82
|
+
Context expects:
|
|
83
|
+
- patient_id: Patient identifier
|
|
84
|
+
- sensor_data: Current sensor readings (JSON string or dict)
|
|
85
|
+
- sensor_format: "simple_json" or "fhir" (default: simple_json)
|
|
86
|
+
- protocol_name: Protocol to check (optional if already loaded)
|
|
87
|
+
- intervention_status: Status of interventions (optional)
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Comprehensive analysis with alerts, predictions, recommendations
|
|
91
|
+
|
|
92
|
+
"""
|
|
93
|
+
patient_id = context.get("patient_id")
|
|
94
|
+
sensor_data = context.get("sensor_data")
|
|
95
|
+
sensor_format = context.get("sensor_format", "simple_json")
|
|
96
|
+
protocol_name = context.get("protocol_name")
|
|
97
|
+
intervention_status = context.get("intervention_status", {})
|
|
98
|
+
|
|
99
|
+
if not patient_id:
|
|
100
|
+
return {"error": "patient_id required"}
|
|
101
|
+
|
|
102
|
+
if not sensor_data:
|
|
103
|
+
return {"error": "sensor_data required"}
|
|
104
|
+
|
|
105
|
+
# Load protocol if not already active
|
|
106
|
+
if patient_id not in self.active_protocols and protocol_name:
|
|
107
|
+
self.load_protocol(patient_id, protocol_name)
|
|
108
|
+
|
|
109
|
+
if patient_id not in self.active_protocols:
|
|
110
|
+
return {"error": f"No active protocol for patient {patient_id}"}
|
|
111
|
+
|
|
112
|
+
protocol = self.active_protocols[patient_id]
|
|
113
|
+
|
|
114
|
+
# Parse sensor data
|
|
115
|
+
if isinstance(sensor_data, str):
|
|
116
|
+
readings = parse_sensor_data(sensor_data, sensor_format)
|
|
117
|
+
normalized_data = normalize_vitals(readings)
|
|
118
|
+
else:
|
|
119
|
+
normalized_data = sensor_data
|
|
120
|
+
|
|
121
|
+
# Store in history
|
|
122
|
+
if patient_id not in self.patient_history:
|
|
123
|
+
self.patient_history[patient_id] = []
|
|
124
|
+
|
|
125
|
+
historical_entry = {"timestamp": datetime.now().isoformat(), **normalized_data}
|
|
126
|
+
self.patient_history[patient_id].append(historical_entry)
|
|
127
|
+
|
|
128
|
+
# Keep last 24 hours only
|
|
129
|
+
if len(self.patient_history[patient_id]) > 144: # 24hrs at 10min intervals
|
|
130
|
+
self.patient_history[patient_id] = self.patient_history[patient_id][-144:]
|
|
131
|
+
|
|
132
|
+
# Phase 1: Check protocol compliance
|
|
133
|
+
compliance_result = self.protocol_checker.check_compliance(
|
|
134
|
+
protocol,
|
|
135
|
+
normalized_data,
|
|
136
|
+
intervention_status,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Phase 2: Analyze trajectory (Level 4)
|
|
140
|
+
trajectory_prediction = self.trajectory_analyzer.analyze_trajectory(
|
|
141
|
+
normalized_data,
|
|
142
|
+
self.patient_history[patient_id][:-1], # Exclude current reading
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Phase 3: Generate alerts
|
|
146
|
+
alerts = self._generate_alerts(compliance_result, trajectory_prediction)
|
|
147
|
+
|
|
148
|
+
# Phase 4: Generate recommendations
|
|
149
|
+
recommendations = self._generate_recommendations(
|
|
150
|
+
compliance_result,
|
|
151
|
+
trajectory_prediction,
|
|
152
|
+
protocol,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Phase 5: Generate predictions (Level 4)
|
|
156
|
+
predictions = self._generate_predictions(trajectory_prediction, compliance_result)
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"patient_id": patient_id,
|
|
160
|
+
"protocol": {"name": protocol.name, "version": protocol.version},
|
|
161
|
+
"current_vitals": normalized_data,
|
|
162
|
+
# Protocol compliance (like linter output)
|
|
163
|
+
"protocol_compliance": {
|
|
164
|
+
"activated": compliance_result.protocol_activated,
|
|
165
|
+
"score": compliance_result.activation_score,
|
|
166
|
+
"threshold": compliance_result.threshold,
|
|
167
|
+
"alert_level": compliance_result.alert_level,
|
|
168
|
+
"deviations": [
|
|
169
|
+
{
|
|
170
|
+
"action": d.intervention.action,
|
|
171
|
+
"status": d.status.value,
|
|
172
|
+
"due": d.intervention.timing,
|
|
173
|
+
"reasoning": d.reasoning,
|
|
174
|
+
}
|
|
175
|
+
for d in compliance_result.deviations
|
|
176
|
+
],
|
|
177
|
+
"compliant": compliance_result.compliant_items,
|
|
178
|
+
},
|
|
179
|
+
# Trajectory analysis (Level 4)
|
|
180
|
+
"trajectory": {
|
|
181
|
+
"state": trajectory_prediction.trajectory_state,
|
|
182
|
+
"estimated_time_to_critical": trajectory_prediction.estimated_time_to_critical,
|
|
183
|
+
"trends": [
|
|
184
|
+
{
|
|
185
|
+
"parameter": t.parameter,
|
|
186
|
+
"current": t.current_value,
|
|
187
|
+
"change": t.change,
|
|
188
|
+
"direction": t.direction,
|
|
189
|
+
"concerning": t.concerning,
|
|
190
|
+
"reasoning": t.reasoning,
|
|
191
|
+
}
|
|
192
|
+
for t in trajectory_prediction.vital_trends
|
|
193
|
+
],
|
|
194
|
+
"assessment": trajectory_prediction.overall_assessment,
|
|
195
|
+
"confidence": trajectory_prediction.confidence,
|
|
196
|
+
},
|
|
197
|
+
# Alerts
|
|
198
|
+
"alerts": alerts,
|
|
199
|
+
# Standard wizard outputs
|
|
200
|
+
"predictions": predictions,
|
|
201
|
+
"recommendations": recommendations,
|
|
202
|
+
"confidence": trajectory_prediction.confidence,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
def _generate_alerts(
|
|
206
|
+
self,
|
|
207
|
+
compliance: ProtocolCheckResult,
|
|
208
|
+
trajectory: TrajectoryPrediction,
|
|
209
|
+
) -> list[dict[str, Any]]:
|
|
210
|
+
"""Generate alerts based on compliance and trajectory"""
|
|
211
|
+
alerts = []
|
|
212
|
+
|
|
213
|
+
# Protocol activation alert
|
|
214
|
+
if compliance.protocol_activated:
|
|
215
|
+
alerts.append(
|
|
216
|
+
{
|
|
217
|
+
"type": "protocol_activated",
|
|
218
|
+
"severity": "high",
|
|
219
|
+
"message": compliance.recommendation,
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Overdue intervention alerts
|
|
224
|
+
overdue = [d for d in compliance.deviations if d.status.value == "overdue"]
|
|
225
|
+
if overdue:
|
|
226
|
+
alerts.append(
|
|
227
|
+
{
|
|
228
|
+
"type": "intervention_overdue",
|
|
229
|
+
"severity": "critical",
|
|
230
|
+
"message": f"{len(overdue)} interventions overdue",
|
|
231
|
+
"details": [d.intervention.action for d in overdue],
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Trajectory alerts (Level 4 - early warning)
|
|
236
|
+
if trajectory.trajectory_state == "critical":
|
|
237
|
+
alerts.append(
|
|
238
|
+
{
|
|
239
|
+
"type": "trajectory_critical",
|
|
240
|
+
"severity": "critical",
|
|
241
|
+
"message": trajectory.overall_assessment,
|
|
242
|
+
},
|
|
243
|
+
)
|
|
244
|
+
elif trajectory.trajectory_state == "concerning":
|
|
245
|
+
alerts.append(
|
|
246
|
+
{
|
|
247
|
+
"type": "trajectory_concerning",
|
|
248
|
+
"severity": "warning",
|
|
249
|
+
"message": trajectory.overall_assessment,
|
|
250
|
+
"time_to_critical": trajectory.estimated_time_to_critical,
|
|
251
|
+
},
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return alerts
|
|
255
|
+
|
|
256
|
+
def _generate_recommendations(
|
|
257
|
+
self,
|
|
258
|
+
compliance: ProtocolCheckResult,
|
|
259
|
+
trajectory: TrajectoryPrediction,
|
|
260
|
+
protocol: ClinicalProtocol,
|
|
261
|
+
) -> list[str]:
|
|
262
|
+
"""Generate actionable recommendations"""
|
|
263
|
+
recommendations = []
|
|
264
|
+
|
|
265
|
+
# From protocol compliance
|
|
266
|
+
if compliance.recommendation:
|
|
267
|
+
recommendations.append(compliance.recommendation)
|
|
268
|
+
|
|
269
|
+
# From trajectory analysis
|
|
270
|
+
recommendations.extend(trajectory.recommendations)
|
|
271
|
+
|
|
272
|
+
# From protocol-specific guidance
|
|
273
|
+
if compliance.protocol_activated:
|
|
274
|
+
recommendations.append(
|
|
275
|
+
f"Follow {protocol.name} monitoring frequency: {protocol.monitoring_frequency}",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return list(set(recommendations)) # Deduplicate
|
|
279
|
+
|
|
280
|
+
def _generate_predictions(
|
|
281
|
+
self,
|
|
282
|
+
trajectory: TrajectoryPrediction,
|
|
283
|
+
compliance: ProtocolCheckResult,
|
|
284
|
+
) -> list[dict[str, Any]]:
|
|
285
|
+
"""Generate Level 4 predictions"""
|
|
286
|
+
predictions = []
|
|
287
|
+
|
|
288
|
+
# Trajectory-based predictions
|
|
289
|
+
if trajectory.trajectory_state in ["concerning", "critical"]:
|
|
290
|
+
predictions.append(
|
|
291
|
+
{
|
|
292
|
+
"type": "patient_deterioration",
|
|
293
|
+
"severity": "high" if trajectory.trajectory_state == "critical" else "medium",
|
|
294
|
+
"description": trajectory.overall_assessment,
|
|
295
|
+
"time_horizon": trajectory.estimated_time_to_critical,
|
|
296
|
+
"confidence": trajectory.confidence,
|
|
297
|
+
"prevention_steps": trajectory.recommendations,
|
|
298
|
+
},
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Compliance-based predictions
|
|
302
|
+
if compliance.protocol_activated and compliance.deviations:
|
|
303
|
+
predictions.append(
|
|
304
|
+
{
|
|
305
|
+
"type": "protocol_deviation_risk",
|
|
306
|
+
"severity": "high",
|
|
307
|
+
"description": "In our experience, protocol deviations correlate with adverse outcomes",
|
|
308
|
+
"prevention_steps": [
|
|
309
|
+
"Complete pending interventions",
|
|
310
|
+
"Document deviations and rationale",
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return predictions
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Clinical Monitoring Components
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
4
|
+
Licensed under Fair Source 0.9
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .protocol_checker import (
|
|
8
|
+
ComplianceStatus,
|
|
9
|
+
ProtocolChecker,
|
|
10
|
+
ProtocolCheckResult,
|
|
11
|
+
ProtocolDeviation,
|
|
12
|
+
)
|
|
13
|
+
from .protocol_loader import (
|
|
14
|
+
ClinicalProtocol,
|
|
15
|
+
ProtocolCriterion,
|
|
16
|
+
ProtocolIntervention,
|
|
17
|
+
ProtocolLoader,
|
|
18
|
+
load_protocol,
|
|
19
|
+
)
|
|
20
|
+
from .sensor_parsers import VitalSignReading, VitalSignType, normalize_vitals, parse_sensor_data
|
|
21
|
+
from .trajectory_analyzer import TrajectoryAnalyzer, TrajectoryPrediction, VitalTrend
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
# Protocol Loading
|
|
25
|
+
"ClinicalProtocol",
|
|
26
|
+
"ComplianceStatus",
|
|
27
|
+
"ProtocolCheckResult",
|
|
28
|
+
# Protocol Checking
|
|
29
|
+
"ProtocolChecker",
|
|
30
|
+
"ProtocolCriterion",
|
|
31
|
+
"ProtocolDeviation",
|
|
32
|
+
"ProtocolIntervention",
|
|
33
|
+
"ProtocolLoader",
|
|
34
|
+
# Trajectory Analysis
|
|
35
|
+
"TrajectoryAnalyzer",
|
|
36
|
+
"TrajectoryPrediction",
|
|
37
|
+
# Sensor Parsing
|
|
38
|
+
"VitalSignReading",
|
|
39
|
+
"VitalSignType",
|
|
40
|
+
"VitalTrend",
|
|
41
|
+
"load_protocol",
|
|
42
|
+
"normalize_vitals",
|
|
43
|
+
"parse_sensor_data",
|
|
44
|
+
]
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Protocol Checker
|
|
2
|
+
|
|
3
|
+
Checks patient sensor data against clinical protocol criteria.
|
|
4
|
+
|
|
5
|
+
This is the "linter" for healthcare - runs the protocol rules against current state.
|
|
6
|
+
|
|
7
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
8
|
+
Licensed under Fair Source 0.9
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from .protocol_loader import ClinicalProtocol, ProtocolCriterion, ProtocolIntervention
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ComplianceStatus(Enum):
|
|
20
|
+
"""Status of protocol compliance"""
|
|
21
|
+
|
|
22
|
+
COMPLIANT = "compliant"
|
|
23
|
+
DEVIATION = "deviation"
|
|
24
|
+
OVERDUE = "overdue"
|
|
25
|
+
PENDING = "pending"
|
|
26
|
+
COMPLETED = "completed"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class CriterionResult:
|
|
31
|
+
"""Result of evaluating a single criterion"""
|
|
32
|
+
|
|
33
|
+
criterion: ProtocolCriterion
|
|
34
|
+
met: bool
|
|
35
|
+
actual_value: Any
|
|
36
|
+
points_awarded: int
|
|
37
|
+
reasoning: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class ProtocolDeviation:
|
|
42
|
+
"""A deviation from protocol (like a linting violation)"""
|
|
43
|
+
|
|
44
|
+
intervention: ProtocolIntervention
|
|
45
|
+
status: ComplianceStatus
|
|
46
|
+
time_activated: datetime | None = None
|
|
47
|
+
time_due: datetime | None = None
|
|
48
|
+
time_completed: datetime | None = None
|
|
49
|
+
overdue_by: str | None = None
|
|
50
|
+
reasoning: str = ""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class ProtocolCheckResult:
|
|
55
|
+
"""Result of checking patient against protocol.
|
|
56
|
+
|
|
57
|
+
This is like the output of running a linter.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
protocol_activated: bool
|
|
61
|
+
activation_score: int
|
|
62
|
+
threshold: int
|
|
63
|
+
criteria_results: list[CriterionResult]
|
|
64
|
+
deviations: list[ProtocolDeviation]
|
|
65
|
+
compliant_items: list[str]
|
|
66
|
+
alert_level: str # "NONE", "WARNING", "CRITICAL"
|
|
67
|
+
recommendation: str
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ProtocolChecker:
|
|
71
|
+
"""Checks patient state against clinical protocol.
|
|
72
|
+
|
|
73
|
+
This is the "linter engine" for healthcare.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(self):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
def check_compliance(
|
|
80
|
+
self,
|
|
81
|
+
protocol: ClinicalProtocol,
|
|
82
|
+
patient_data: dict[str, Any],
|
|
83
|
+
intervention_status: dict[str, Any] | None = None,
|
|
84
|
+
) -> ProtocolCheckResult:
|
|
85
|
+
"""Check if patient data meets protocol criteria.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
protocol: Clinical protocol to check against
|
|
89
|
+
patient_data: Current patient sensor data
|
|
90
|
+
intervention_status: Status of interventions (if protocol active)
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ProtocolCheckResult with deviations
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> patient = {"systolic_bp": 95, "respiratory_rate": 24, "hr": 110}
|
|
97
|
+
>>> result = checker.check_compliance(sepsis_protocol, patient)
|
|
98
|
+
>>> if result.protocol_activated:
|
|
99
|
+
... print(f"ALERT: {result.recommendation}")
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
# Step 1: Evaluate screening criteria
|
|
103
|
+
criteria_results = []
|
|
104
|
+
total_points = 0
|
|
105
|
+
|
|
106
|
+
for criterion in protocol.screening_criteria:
|
|
107
|
+
result = self._evaluate_criterion(criterion, patient_data)
|
|
108
|
+
criteria_results.append(result)
|
|
109
|
+
if result.met:
|
|
110
|
+
total_points += result.points_awarded
|
|
111
|
+
|
|
112
|
+
# Step 2: Determine if protocol should activate
|
|
113
|
+
protocol_activated = total_points >= protocol.screening_threshold
|
|
114
|
+
|
|
115
|
+
# Step 3: If activated, check intervention compliance
|
|
116
|
+
deviations = []
|
|
117
|
+
compliant_items = []
|
|
118
|
+
|
|
119
|
+
if protocol_activated and intervention_status:
|
|
120
|
+
for intervention in protocol.interventions:
|
|
121
|
+
status = intervention_status.get(intervention.action, {})
|
|
122
|
+
deviation = self._check_intervention_status(intervention, status)
|
|
123
|
+
|
|
124
|
+
if deviation:
|
|
125
|
+
deviations.append(deviation)
|
|
126
|
+
else:
|
|
127
|
+
compliant_items.append(intervention.action)
|
|
128
|
+
|
|
129
|
+
# Step 4: Determine alert level
|
|
130
|
+
alert_level = self._determine_alert_level(
|
|
131
|
+
protocol_activated,
|
|
132
|
+
deviations,
|
|
133
|
+
total_points,
|
|
134
|
+
protocol.screening_threshold,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Step 5: Generate recommendation
|
|
138
|
+
recommendation = self._generate_recommendation(
|
|
139
|
+
protocol,
|
|
140
|
+
protocol_activated,
|
|
141
|
+
deviations,
|
|
142
|
+
criteria_results,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return ProtocolCheckResult(
|
|
146
|
+
protocol_activated=protocol_activated,
|
|
147
|
+
activation_score=total_points,
|
|
148
|
+
threshold=protocol.screening_threshold,
|
|
149
|
+
criteria_results=criteria_results,
|
|
150
|
+
deviations=deviations,
|
|
151
|
+
compliant_items=compliant_items,
|
|
152
|
+
alert_level=alert_level,
|
|
153
|
+
recommendation=recommendation,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def _evaluate_criterion(
|
|
157
|
+
self,
|
|
158
|
+
criterion: ProtocolCriterion,
|
|
159
|
+
patient_data: dict[str, Any],
|
|
160
|
+
) -> CriterionResult:
|
|
161
|
+
"""Evaluate a single criterion"""
|
|
162
|
+
actual_value = patient_data.get(criterion.parameter)
|
|
163
|
+
|
|
164
|
+
if actual_value is None:
|
|
165
|
+
return CriterionResult(
|
|
166
|
+
criterion=criterion,
|
|
167
|
+
met=False,
|
|
168
|
+
actual_value=None,
|
|
169
|
+
points_awarded=0,
|
|
170
|
+
reasoning=f"{criterion.parameter} not available",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Evaluate condition
|
|
174
|
+
met = self._evaluate_condition(actual_value, criterion.condition, criterion.value)
|
|
175
|
+
|
|
176
|
+
return CriterionResult(
|
|
177
|
+
criterion=criterion,
|
|
178
|
+
met=met,
|
|
179
|
+
actual_value=actual_value,
|
|
180
|
+
points_awarded=criterion.points if met else 0,
|
|
181
|
+
reasoning=f"{criterion.parameter}={actual_value} {criterion.condition} {criterion.value}",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def _evaluate_condition(self, actual: Any, condition: str, expected: Any) -> bool:
|
|
185
|
+
"""Evaluate a condition (like <=, >=, ==, etc.)"""
|
|
186
|
+
if condition == "<=":
|
|
187
|
+
return actual <= expected
|
|
188
|
+
if condition == ">=":
|
|
189
|
+
return actual >= expected
|
|
190
|
+
if condition == "==":
|
|
191
|
+
return actual == expected
|
|
192
|
+
if condition == "!=":
|
|
193
|
+
return actual != expected
|
|
194
|
+
if condition == "<":
|
|
195
|
+
return actual < expected
|
|
196
|
+
if condition == ">":
|
|
197
|
+
return actual > expected
|
|
198
|
+
if condition == "altered":
|
|
199
|
+
# Special case for mental status
|
|
200
|
+
return actual != "normal" if isinstance(actual, str) else actual < 15
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
def _check_intervention_status(
|
|
204
|
+
self,
|
|
205
|
+
intervention: ProtocolIntervention,
|
|
206
|
+
status: dict[str, Any],
|
|
207
|
+
) -> ProtocolDeviation | None:
|
|
208
|
+
"""Check if intervention has been completed"""
|
|
209
|
+
completed = status.get("completed", False)
|
|
210
|
+
_time_completed = status.get("time_completed")
|
|
211
|
+
time_due = status.get("time_due")
|
|
212
|
+
|
|
213
|
+
if completed:
|
|
214
|
+
return None # No deviation - intervention done
|
|
215
|
+
|
|
216
|
+
# Check if overdue
|
|
217
|
+
if time_due:
|
|
218
|
+
if isinstance(time_due, datetime) and datetime.now() > time_due:
|
|
219
|
+
return ProtocolDeviation(
|
|
220
|
+
intervention=intervention,
|
|
221
|
+
status=ComplianceStatus.OVERDUE,
|
|
222
|
+
time_due=time_due,
|
|
223
|
+
overdue_by=str(datetime.now() - time_due),
|
|
224
|
+
reasoning=f"{intervention.action} overdue (due: {intervention.timing})",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Pending but not yet overdue
|
|
228
|
+
return ProtocolDeviation(
|
|
229
|
+
intervention=intervention,
|
|
230
|
+
status=ComplianceStatus.PENDING,
|
|
231
|
+
reasoning=f"{intervention.action} pending (due: {intervention.timing})",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def _determine_alert_level(
|
|
235
|
+
self,
|
|
236
|
+
protocol_activated: bool,
|
|
237
|
+
deviations: list[ProtocolDeviation],
|
|
238
|
+
score: int,
|
|
239
|
+
threshold: int,
|
|
240
|
+
) -> str:
|
|
241
|
+
"""Determine alert level"""
|
|
242
|
+
if not protocol_activated:
|
|
243
|
+
return "NONE"
|
|
244
|
+
|
|
245
|
+
# Check for overdue critical interventions
|
|
246
|
+
overdue_count = sum(1 for d in deviations if d.status == ComplianceStatus.OVERDUE)
|
|
247
|
+
|
|
248
|
+
if overdue_count > 0:
|
|
249
|
+
return "CRITICAL"
|
|
250
|
+
|
|
251
|
+
# Check for pending interventions
|
|
252
|
+
pending_count = sum(1 for d in deviations if d.status == ComplianceStatus.PENDING)
|
|
253
|
+
|
|
254
|
+
if pending_count > 0:
|
|
255
|
+
return "WARNING"
|
|
256
|
+
|
|
257
|
+
return "NONE"
|
|
258
|
+
|
|
259
|
+
def _generate_recommendation(
|
|
260
|
+
self,
|
|
261
|
+
protocol: ClinicalProtocol,
|
|
262
|
+
activated: bool,
|
|
263
|
+
deviations: list[ProtocolDeviation],
|
|
264
|
+
criteria_results: list[CriterionResult],
|
|
265
|
+
) -> str:
|
|
266
|
+
"""Generate actionable recommendation"""
|
|
267
|
+
if not activated:
|
|
268
|
+
met_criteria = [c for c in criteria_results if c.met]
|
|
269
|
+
if met_criteria:
|
|
270
|
+
return (
|
|
271
|
+
f"Patient meets {len(met_criteria)} of {protocol.screening_threshold} "
|
|
272
|
+
f"criteria. Continue monitoring."
|
|
273
|
+
)
|
|
274
|
+
return "Patient stable. Continue routine monitoring."
|
|
275
|
+
|
|
276
|
+
# Protocol activated
|
|
277
|
+
if not deviations:
|
|
278
|
+
return (
|
|
279
|
+
f"{protocol.name} protocol active. All interventions complete. "
|
|
280
|
+
f"Continue monitoring per protocol."
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Has deviations
|
|
284
|
+
overdue = [d for d in deviations if d.status == ComplianceStatus.OVERDUE]
|
|
285
|
+
pending = [d for d in deviations if d.status == ComplianceStatus.PENDING]
|
|
286
|
+
|
|
287
|
+
if overdue:
|
|
288
|
+
return (
|
|
289
|
+
f"CRITICAL: {len(overdue)} interventions OVERDUE. "
|
|
290
|
+
f"Immediate action required: {', '.join(d.intervention.action for d in overdue[:3])}"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if pending:
|
|
294
|
+
return (
|
|
295
|
+
f"WARNING: {protocol.name} activated. "
|
|
296
|
+
f"{len(pending)} interventions pending: "
|
|
297
|
+
f"{', '.join(d.intervention.action for d in pending[:3])}"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return "Protocol monitoring active."
|