Spaces:
Sleeping
Sleeping
Rajan Sharma
commited on
Update response_formatter.py
Browse files- response_formatter.py +311 -327
response_formatter.py
CHANGED
|
@@ -1,10 +1,14 @@
|
|
| 1 |
# response_formatter.py
|
| 2 |
-
from typing import Dict, List, Any
|
|
|
|
| 3 |
|
| 4 |
class ResponseFormatter:
|
| 5 |
@staticmethod
|
| 6 |
def format_healthcare_response(scenario_text: str, analysis_results: Dict[str, Any]) -> str:
|
| 7 |
-
"""Format healthcare analysis response
|
|
|
|
|
|
|
|
|
|
| 8 |
response = "# Healthcare Scenario Analysis\n\n"
|
| 9 |
|
| 10 |
# Executive Summary
|
|
@@ -12,62 +16,20 @@ class ResponseFormatter:
|
|
| 12 |
response += ResponseFormatter._generate_executive_summary(analysis_results)
|
| 13 |
response += "\n\n"
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
# Facility Distribution Analysis
|
| 22 |
-
if "facility_distribution" in analysis_results:
|
| 23 |
-
response += "## 2. Facility Distribution Analysis\n\n"
|
| 24 |
-
response += ResponseFormatter._format_facility_distribution(analysis_results["facility_distribution"])
|
| 25 |
-
response += "\n\n"
|
| 26 |
-
|
| 27 |
-
# Capacity Analysis
|
| 28 |
-
if "capacity_analysis" in analysis_results:
|
| 29 |
-
response += "## 3. Capacity Analysis\n\n"
|
| 30 |
-
response += ResponseFormatter._format_capacity_analysis(analysis_results["capacity_analysis"])
|
| 31 |
-
response += "\n\n"
|
| 32 |
-
|
| 33 |
-
# Long-Term Care Assessment
|
| 34 |
-
if "long_term_care_assessment" in analysis_results:
|
| 35 |
-
response += "## 4. Long-Term Care Capacity Assessment\n\n"
|
| 36 |
-
response += ResponseFormatter._format_long_term_care_assessment(analysis_results["long_term_care_assessment"])
|
| 37 |
-
response += "\n\n"
|
| 38 |
-
|
| 39 |
-
# Resource Allocation Analysis
|
| 40 |
-
if "resource_allocation" in analysis_results:
|
| 41 |
-
response += "## 5. Resource Allocation Analysis\n\n"
|
| 42 |
-
response += ResponseFormatter._format_resource_allocation(analysis_results["resource_allocation"])
|
| 43 |
-
response += "\n\n"
|
| 44 |
-
|
| 45 |
-
# Trend Analysis
|
| 46 |
-
if "trends" in analysis_results:
|
| 47 |
-
response += "## 6. Trend Analysis\n\n"
|
| 48 |
-
response += ResponseFormatter._format_trends(analysis_results["trends"])
|
| 49 |
-
response += "\n\n"
|
| 50 |
-
|
| 51 |
-
# Operational Recommendations
|
| 52 |
-
if "recommendations" in analysis_results:
|
| 53 |
-
response += "## 7. Operational Recommendations\n\n"
|
| 54 |
-
response += ResponseFormatter._format_recommendations(analysis_results["recommendations"])
|
| 55 |
-
response += "\n\n"
|
| 56 |
-
|
| 57 |
-
# Future Integration
|
| 58 |
-
if "future_integration" in analysis_results:
|
| 59 |
-
response += "## 8. Future Integration Opportunities\n\n"
|
| 60 |
-
response += ResponseFormatter._format_integration_opportunities(analysis_results["future_integration"])
|
| 61 |
-
response += "\n\n"
|
| 62 |
|
| 63 |
# Validation Results
|
| 64 |
if "validation" in analysis_results:
|
| 65 |
-
response += "##
|
| 66 |
response += ResponseFormatter._format_validation_results(analysis_results["validation"])
|
| 67 |
response += "\n\n"
|
| 68 |
|
| 69 |
# Provenance
|
| 70 |
-
response += "##
|
| 71 |
response += "This analysis is based on:\n"
|
| 72 |
response += "- Scenario description provided by the user\n"
|
| 73 |
response += "- Uploaded data files\n"
|
|
@@ -76,86 +38,104 @@ class ResponseFormatter:
|
|
| 76 |
return response
|
| 77 |
|
| 78 |
@staticmethod
|
| 79 |
-
def
|
| 80 |
-
"""
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
@staticmethod
|
| 109 |
-
def
|
| 110 |
-
"""
|
| 111 |
-
|
| 112 |
|
| 113 |
-
|
| 114 |
-
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
|
|
|
| 118 |
|
| 119 |
-
|
| 120 |
-
|
| 121 |
|
| 122 |
-
|
| 123 |
-
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
|
|
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
response += f"- {source}\n"
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
|
|
|
| 134 |
|
| 135 |
@staticmethod
|
| 136 |
-
def
|
| 137 |
-
"""
|
| 138 |
-
response = ""
|
| 139 |
|
| 140 |
-
if "
|
| 141 |
-
response
|
| 142 |
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
| 144 |
response += "### Facility Type Frequency\n\n"
|
| 145 |
response += "| Facility Type | Count |\n"
|
| 146 |
response += "|---------------|-------|\n"
|
| 147 |
-
for ftype, count in
|
| 148 |
response += f"| {ftype} | {count} |\n"
|
| 149 |
response += "\n"
|
| 150 |
|
| 151 |
-
|
|
|
|
| 152 |
response += "### Top Cities by Facility Count\n\n"
|
| 153 |
response += "| City | Hospitals | Nursing/Residential | Ambulatory | Total |\n"
|
| 154 |
response += "|------|-----------|-------------------|------------|-------|\n"
|
| 155 |
|
| 156 |
-
for city in
|
| 157 |
-
if city in
|
| 158 |
-
breakdown =
|
| 159 |
hospitals = breakdown.get("Hospitals", 0)
|
| 160 |
nursing = breakdown.get("Nursing and residential care facilities", 0)
|
| 161 |
ambulatory = breakdown.get("Ambulatory health care services", 0)
|
|
@@ -164,32 +144,95 @@ class ResponseFormatter:
|
|
| 164 |
|
| 165 |
response += "\n"
|
| 166 |
|
| 167 |
-
return response
|
| 168 |
|
| 169 |
@staticmethod
|
| 170 |
-
def
|
| 171 |
-
"""
|
| 172 |
-
response = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
-
|
| 175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
response += "### Facility Counts in Major City\n\n"
|
| 183 |
-
response += f"- Hospitals: {counts
|
| 184 |
-
response += f"- Nursing/Residential Care Facilities: {counts
|
| 185 |
-
response += f"- Ambulatory Health Services: {counts
|
| 186 |
|
| 187 |
-
|
| 188 |
-
|
|
|
|
| 189 |
response += f"**Nursing/Residential to Hospital Ratio**: {ratio:.2f}\n\n"
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
|
|
|
| 193 |
response += f"**Long-Term Care Capacity Assessment**: {assessment.upper()}\n\n"
|
| 194 |
|
| 195 |
if assessment == "insufficient":
|
|
@@ -199,7 +242,147 @@ class ResponseFormatter:
|
|
| 199 |
response += "The ratio of nursing/residential care facilities to hospitals meets or exceeds the recommended threshold. "
|
| 200 |
response += "This indicates sufficient long-term care capacity.\n\n"
|
| 201 |
|
| 202 |
-
return response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
@staticmethod
|
| 205 |
def _format_validation_results(validation: Dict[str, Any]) -> str:
|
|
@@ -216,6 +399,7 @@ class ResponseFormatter:
|
|
| 216 |
|
| 217 |
return response
|
| 218 |
|
|
|
|
| 219 |
@staticmethod
|
| 220 |
def _format_facility_distribution(dist_results: Dict[str, Any]) -> str:
|
| 221 |
"""Format facility distribution analysis results"""
|
|
@@ -237,77 +421,8 @@ class ResponseFormatter:
|
|
| 237 |
level = "High" if inequality > 0.4 else "Moderate" if inequality > 0.2 else "Low"
|
| 238 |
response += f"**Geographic Inequality**: {level} (Gini coefficient: {inequality:.2f})\n\n"
|
| 239 |
|
| 240 |
-
if "facility_type_distribution" in dist_results:
|
| 241 |
-
response += "### Facility Type Distribution\n\n"
|
| 242 |
-
response += "| Facility Type | Count |\n"
|
| 243 |
-
response += "|---------------|-------|\n"
|
| 244 |
-
for ftype, count in dist_results["facility_type_distribution"].items():
|
| 245 |
-
response += f"| {ftype} | {count} |\n"
|
| 246 |
-
response += "\n"
|
| 247 |
-
|
| 248 |
-
if "facility_diversity" in dist_results:
|
| 249 |
-
diversity = dist_results["facility_diversity"]
|
| 250 |
-
response += f"**Facility Diversity Index**: {diversity:.2f} (higher values indicate more diversity)\n\n"
|
| 251 |
-
|
| 252 |
-
if "top_cities" in dist_results and "city_breakdown" in dist_results:
|
| 253 |
-
response += "### Top Cities by Facility Count\n\n"
|
| 254 |
-
response += "| City | Hospitals | Nursing/Residential | Ambulatory | Total |\n"
|
| 255 |
-
response += "|------|-----------|-------------------|------------|-------|\n"
|
| 256 |
-
|
| 257 |
-
for city in dist_results["top_cities"]:
|
| 258 |
-
if city in dist_results["city_breakdown"]:
|
| 259 |
-
breakdown = dist_results["city_breakdown"][city]
|
| 260 |
-
hospitals = breakdown.get("Hospitals", 0)
|
| 261 |
-
nursing = breakdown.get("Nursing and residential care facilities", 0)
|
| 262 |
-
ambulatory = breakdown.get("Ambulatory health care services", 0)
|
| 263 |
-
total = hospitals + nursing + ambulatory
|
| 264 |
-
response += f"| {city} | {hospitals} | {nursing} | {ambulatory} | {total} |\n"
|
| 265 |
-
|
| 266 |
-
response += "\n"
|
| 267 |
-
|
| 268 |
return response if response else "No facility distribution data available"
|
| 269 |
|
| 270 |
-
@staticmethod
|
| 271 |
-
def _format_capacity_analysis(capacity_results: Dict[str, Any]) -> str:
|
| 272 |
-
"""Format capacity analysis results"""
|
| 273 |
-
response = ""
|
| 274 |
-
|
| 275 |
-
if "total_capacity" in capacity_results:
|
| 276 |
-
response += f"**Total System Capacity**: {capacity_results['total_capacity']:,} beds\n\n"
|
| 277 |
-
|
| 278 |
-
if "capacity_by_type" in capacity_results:
|
| 279 |
-
response += "### Capacity by Facility Type\n\n"
|
| 280 |
-
response += "| Facility Type | Capacity |\n"
|
| 281 |
-
response += "|---------------|----------|\n"
|
| 282 |
-
for ftype, capacity in capacity_results["capacity_by_type"].items():
|
| 283 |
-
response += f"| {ftype} | {capacity:,} |\n"
|
| 284 |
-
response += "\n"
|
| 285 |
-
|
| 286 |
-
if "average_utilization" in capacity_results:
|
| 287 |
-
response += f"**Average System Utilization**: {capacity_results['average_utilization']:.1%}\n\n"
|
| 288 |
-
|
| 289 |
-
if "utilization_by_type" in capacity_results:
|
| 290 |
-
response += "### Utilization by Facility Type\n\n"
|
| 291 |
-
response += "| Facility Type | Utilization |\n"
|
| 292 |
-
response += "|---------------|-------------|\n"
|
| 293 |
-
for ftype, util in capacity_results["utilization_by_type"].items():
|
| 294 |
-
response += f"| {ftype} | {util:.1%} |\n"
|
| 295 |
-
response += "\n"
|
| 296 |
-
|
| 297 |
-
if "capacity_trends" in capacity_results:
|
| 298 |
-
response += "### Capacity Trends\n\n"
|
| 299 |
-
response += "| Year | Capacity |\n"
|
| 300 |
-
response += "|------|----------|\n"
|
| 301 |
-
for year, capacity in capacity_results["capacity_trends"].items():
|
| 302 |
-
response += f"| {year} | {capacity:,} |\n"
|
| 303 |
-
response += "\n"
|
| 304 |
-
|
| 305 |
-
if "capacity_growth_rate" in capacity_results:
|
| 306 |
-
growth = capacity_results["capacity_growth_rate"]
|
| 307 |
-
response += f"**Annual Growth Rate**: {growth:.1%}\n\n"
|
| 308 |
-
|
| 309 |
-
return response if response else "No capacity analysis data available"
|
| 310 |
-
|
| 311 |
@staticmethod
|
| 312 |
def _format_resource_allocation(allocation_results: Dict[str, Any]) -> str:
|
| 313 |
"""Format resource allocation analysis results"""
|
|
@@ -325,26 +440,6 @@ class ResponseFormatter:
|
|
| 325 |
response += f"| {ftype} | {count:,} | {percentage:.1f}% |\n"
|
| 326 |
response += "\n"
|
| 327 |
|
| 328 |
-
if "allocation_by_region" in allocation_results:
|
| 329 |
-
response += "### Allocation by Region\n\n"
|
| 330 |
-
response += "| Region | Resource Count | Percentage |\n"
|
| 331 |
-
response += "|--------|----------------|------------|\n"
|
| 332 |
-
for region, count in allocation_results["allocation_by_region"].items():
|
| 333 |
-
percentage = count / allocation_results.get("total_resources", 1) * 100
|
| 334 |
-
response += f"| {region} | {count:,} | {percentage:.1f}% |\n"
|
| 335 |
-
response += "\n"
|
| 336 |
-
|
| 337 |
-
if "efficiency_metrics" in allocation_results:
|
| 338 |
-
response += "### Efficiency Metrics\n\n"
|
| 339 |
-
response += "| Metric | Value |\n"
|
| 340 |
-
response += "|--------|-------|\n"
|
| 341 |
-
for metric, value in allocation_results["efficiency_metrics"].items():
|
| 342 |
-
if isinstance(value, float):
|
| 343 |
-
response += f"| {metric} | {value:.2f} |\n"
|
| 344 |
-
else:
|
| 345 |
-
response += f"| {metric} | {value} |\n"
|
| 346 |
-
response += "\n"
|
| 347 |
-
|
| 348 |
return response if response else "No resource allocation data available"
|
| 349 |
|
| 350 |
@staticmethod
|
|
@@ -369,115 +464,4 @@ class ResponseFormatter:
|
|
| 369 |
prev_count = count
|
| 370 |
response += "\n"
|
| 371 |
|
| 372 |
-
if "
|
| 373 |
-
response += "### Capacity Trends\n\n"
|
| 374 |
-
response += "| Year | Total Capacity | Growth Rate |\n"
|
| 375 |
-
response += "|------|----------------|-------------|\n"
|
| 376 |
-
prev_capacity = None
|
| 377 |
-
for year, capacity in trends_results["capacity_trends"].items():
|
| 378 |
-
if prev_capacity is not None:
|
| 379 |
-
growth_rate = (capacity - prev_capacity) / prev_capacity
|
| 380 |
-
response += f"| {year} | {capacity:,} | {growth_rate:.1%} |\n"
|
| 381 |
-
else:
|
| 382 |
-
response += f"| {year} | {capacity:,} | N/A |\n"
|
| 383 |
-
prev_capacity = capacity
|
| 384 |
-
response += "\n"
|
| 385 |
-
|
| 386 |
-
if "utilization_trends" in trends_results:
|
| 387 |
-
response += "### Utilization Trends\n\n"
|
| 388 |
-
response += "| Year | Average Utilization |\n"
|
| 389 |
-
response += "|------|---------------------|\n"
|
| 390 |
-
for year, utilization in trends_results["utilization_trends"].items():
|
| 391 |
-
response += f"| {year} | {utilization:.1%} |\n"
|
| 392 |
-
response += "\n"
|
| 393 |
-
|
| 394 |
-
if "forecast" in trends_results:
|
| 395 |
-
response += "### Forecast\n\n"
|
| 396 |
-
response += trends_results["forecast"] + "\n\n"
|
| 397 |
-
|
| 398 |
-
return response if response else "No trend data available"
|
| 399 |
-
|
| 400 |
-
@staticmethod
|
| 401 |
-
def _format_recommendations(recommendations_results: List[Dict[str, Any]]) -> str:
|
| 402 |
-
"""Format operational recommendations"""
|
| 403 |
-
response = ""
|
| 404 |
-
|
| 405 |
-
if not recommendations_results:
|
| 406 |
-
return "No recommendations available"
|
| 407 |
-
|
| 408 |
-
# Group by priority
|
| 409 |
-
high_priority = [r for r in recommendations_results if r.get("priority") == "High"]
|
| 410 |
-
medium_priority = [r for r in recommendations_results if r.get("priority") == "Medium"]
|
| 411 |
-
low_priority = [r for r in recommendations_results if r.get("priority") == "Low"]
|
| 412 |
-
|
| 413 |
-
if high_priority:
|
| 414 |
-
response += "### High Priority Recommendations\n\n"
|
| 415 |
-
for i, rec in enumerate(high_priority, 1):
|
| 416 |
-
response += f"**{i}. {rec.get('title', 'Recommendation')}**\n\n"
|
| 417 |
-
response += f"{rec.get('description', '')}\n\n"
|
| 418 |
-
if "expected_impact" in rec:
|
| 419 |
-
response += f"Expected Impact: {rec['expected_impact']}\n\n"
|
| 420 |
-
if "implementation" in rec:
|
| 421 |
-
response += f"Implementation: {rec['implementation']}\n\n"
|
| 422 |
-
response += "---\n\n"
|
| 423 |
-
|
| 424 |
-
if medium_priority:
|
| 425 |
-
response += "### Medium Priority Recommendations\n\n"
|
| 426 |
-
for i, rec in enumerate(medium_priority, 1):
|
| 427 |
-
response += f"**{i}. {rec.get('title', 'Recommendation')}**\n\n"
|
| 428 |
-
response += f"{rec.get('description', '')}\n\n"
|
| 429 |
-
if "expected_impact" in rec:
|
| 430 |
-
response += f"Expected Impact: {rec['expected_impact']}\n\n"
|
| 431 |
-
if "implementation" in rec:
|
| 432 |
-
response += f"Implementation: {rec['implementation']}\n\n"
|
| 433 |
-
response += "---\n\n"
|
| 434 |
-
|
| 435 |
-
if low_priority:
|
| 436 |
-
response += "### Low Priority Recommendations\n\n"
|
| 437 |
-
for i, rec in enumerate(low_priority, 1):
|
| 438 |
-
response += f"**{i}. {rec.get('title', 'Recommendation')}**\n\n"
|
| 439 |
-
response += f"{rec.get('description', '')}\n\n"
|
| 440 |
-
if "expected_impact" in rec:
|
| 441 |
-
response += f"Expected Impact: {rec['expected_impact']}\n\n"
|
| 442 |
-
if "implementation" in rec:
|
| 443 |
-
response += f"Implementation: {rec['implementation']}\n\n"
|
| 444 |
-
response += "---\n\n"
|
| 445 |
-
|
| 446 |
-
return response
|
| 447 |
-
|
| 448 |
-
@staticmethod
|
| 449 |
-
def _format_integration_opportunities(integration_results: Dict[str, Any]) -> str:
|
| 450 |
-
"""Format future integration opportunities"""
|
| 451 |
-
response = ""
|
| 452 |
-
|
| 453 |
-
if "summary" in integration_results:
|
| 454 |
-
response += f"**Integration Summary**: {integration_results['summary']}\n\n"
|
| 455 |
-
|
| 456 |
-
if "opportunities" in integration_results:
|
| 457 |
-
response += "### Integration Opportunities\n\n"
|
| 458 |
-
for i, opp in enumerate(integration_results["opportunities"], 1):
|
| 459 |
-
response += f"**{i}. {opp.get('title', 'Opportunity')}**\n\n"
|
| 460 |
-
response += f"{opp.get('description', '')}\n\n"
|
| 461 |
-
if "benefits" in opp:
|
| 462 |
-
response += "**Benefits:**\n"
|
| 463 |
-
for benefit in opp["benefits"]:
|
| 464 |
-
response += f"- {benefit}\n"
|
| 465 |
-
response += "\n"
|
| 466 |
-
if "challenges" in opp:
|
| 467 |
-
response += "**Challenges:**\n"
|
| 468 |
-
for challenge in opp["challenges"]:
|
| 469 |
-
response += f"- {challenge}\n"
|
| 470 |
-
response += "\n"
|
| 471 |
-
if "timeline" in opp:
|
| 472 |
-
response += f"**Timeline:** {opp['timeline']}\n\n"
|
| 473 |
-
response += "---\n\n"
|
| 474 |
-
|
| 475 |
-
if "roadmap" in integration_results:
|
| 476 |
-
response += "### Integration Roadmap\n\n"
|
| 477 |
-
response += "| Phase | Activities | Timeline |\n"
|
| 478 |
-
response += "|-------|------------|----------|\n"
|
| 479 |
-
for phase in integration_results["roadmap"]:
|
| 480 |
-
response += f"| {phase['name']} | {phase['activities']} | {phase['timeline']} |\n"
|
| 481 |
-
response += "\n"
|
| 482 |
-
|
| 483 |
-
return response if response else "No integration opportunities identified"
|
|
|
|
| 1 |
# response_formatter.py
|
| 2 |
+
from typing import Dict, List, Any, Tuple
|
| 3 |
+
import re
|
| 4 |
|
| 5 |
class ResponseFormatter:
|
| 6 |
@staticmethod
|
| 7 |
def format_healthcare_response(scenario_text: str, analysis_results: Dict[str, Any]) -> str:
|
| 8 |
+
"""Format healthcare analysis response based on scenario tasks"""
|
| 9 |
+
# Extract tasks from scenario
|
| 10 |
+
tasks = ResponseFormatter._extract_scenario_tasks(scenario_text)
|
| 11 |
+
|
| 12 |
response = "# Healthcare Scenario Analysis\n\n"
|
| 13 |
|
| 14 |
# Executive Summary
|
|
|
|
| 16 |
response += ResponseFormatter._generate_executive_summary(analysis_results)
|
| 17 |
response += "\n\n"
|
| 18 |
|
| 19 |
+
# Process each task
|
| 20 |
+
for task in tasks:
|
| 21 |
+
section_content = ResponseFormatter._process_task(task, analysis_results)
|
| 22 |
+
if section_content:
|
| 23 |
+
response += section_content + "\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
# Validation Results
|
| 26 |
if "validation" in analysis_results:
|
| 27 |
+
response += "## Analysis Validation\n\n"
|
| 28 |
response += ResponseFormatter._format_validation_results(analysis_results["validation"])
|
| 29 |
response += "\n\n"
|
| 30 |
|
| 31 |
# Provenance
|
| 32 |
+
response += "## Provenance\n\n"
|
| 33 |
response += "This analysis is based on:\n"
|
| 34 |
response += "- Scenario description provided by the user\n"
|
| 35 |
response += "- Uploaded data files\n"
|
|
|
|
| 38 |
return response
|
| 39 |
|
| 40 |
@staticmethod
|
| 41 |
+
def _extract_scenario_tasks(scenario_text: str) -> List[str]:
|
| 42 |
+
"""Extract specific tasks from scenario text"""
|
| 43 |
+
tasks = []
|
| 44 |
+
lines = scenario_text.split('\n')
|
| 45 |
+
in_tasks = False
|
| 46 |
+
current_task = ""
|
| 47 |
+
|
| 48 |
+
for line in lines:
|
| 49 |
+
line = line.strip()
|
| 50 |
+
|
| 51 |
+
# Check if we're entering the tasks section
|
| 52 |
+
if line.lower().startswith('tasks'):
|
| 53 |
+
in_tasks = True
|
| 54 |
+
continue
|
| 55 |
+
|
| 56 |
+
# Check if we're leaving the tasks section
|
| 57 |
+
if in_tasks and (line.lower().startswith('operational recommendations') or
|
| 58 |
+
line.lower().startswith('future integration') or
|
| 59 |
+
line.lower().startswith('prepare your findings')):
|
| 60 |
+
in_tasks = False
|
| 61 |
+
if current_task:
|
| 62 |
+
tasks.append(current_task)
|
| 63 |
+
continue
|
| 64 |
+
|
| 65 |
+
# Collect task content
|
| 66 |
+
if in_tasks:
|
| 67 |
+
if line and (line.startswith(('1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.')) or
|
| 68 |
+
line.startswith(('•', '-', '*'))):
|
| 69 |
+
if current_task:
|
| 70 |
+
tasks.append(current_task)
|
| 71 |
+
current_task = line
|
| 72 |
+
elif line:
|
| 73 |
+
current_task += " " + line
|
| 74 |
+
|
| 75 |
+
# Add the last task if exists
|
| 76 |
+
if current_task:
|
| 77 |
+
tasks.append(current_task)
|
| 78 |
+
|
| 79 |
+
return tasks
|
| 80 |
|
| 81 |
@staticmethod
|
| 82 |
+
def _process_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 83 |
+
"""Process a single task and return formatted content"""
|
| 84 |
+
task_lower = task.lower()
|
| 85 |
|
| 86 |
+
# Data Preparation Task
|
| 87 |
+
if "data preparation" in task_lower or "load the data" in task_lower:
|
| 88 |
+
return ResponseFormatter._handle_data_preparation_task(task, analysis_results)
|
| 89 |
|
| 90 |
+
# Bed Capacity Analysis Tasks
|
| 91 |
+
elif "bed capacity" in task_lower or "summarize bed capacity" in task_lower:
|
| 92 |
+
return ResponseFormatter._handle_bed_capacity_task(task, analysis_results)
|
| 93 |
|
| 94 |
+
elif "facilities with the greatest declines" in task_lower:
|
| 95 |
+
return ResponseFormatter._handle_facility_declines_task(task, analysis_results)
|
| 96 |
|
| 97 |
+
elif "long-term care capacity" in task_lower or "assess long-term care" in task_lower:
|
| 98 |
+
return ResponseFormatter._handle_long_term_care_task(task, analysis_results)
|
| 99 |
|
| 100 |
+
# Operational Recommendations Tasks
|
| 101 |
+
elif "operational recommendations" in task_lower or "recommend actions" in task_lower:
|
| 102 |
+
return ResponseFormatter._handle_recommendations_task(task, analysis_results)
|
| 103 |
|
| 104 |
+
elif "future integration" in task_lower or "augmented ai" in task_lower:
|
| 105 |
+
return ResponseFormatter._handle_integration_task(task, analysis_results)
|
|
|
|
| 106 |
|
| 107 |
+
# Generic task handler
|
| 108 |
+
else:
|
| 109 |
+
return ResponseFormatter._handle_generic_task(task, analysis_results)
|
| 110 |
|
| 111 |
@staticmethod
|
| 112 |
+
def _handle_data_preparation_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 113 |
+
"""Handle data preparation task"""
|
| 114 |
+
response = "## Data Preparation\n\n"
|
| 115 |
|
| 116 |
+
if "data_preparation" not in analysis_results:
|
| 117 |
+
return response + "No data preparation results available"
|
| 118 |
|
| 119 |
+
data_prep = analysis_results["data_preparation"]
|
| 120 |
+
|
| 121 |
+
# Facility type frequency
|
| 122 |
+
if "facility_type_frequency" in data_prep:
|
| 123 |
response += "### Facility Type Frequency\n\n"
|
| 124 |
response += "| Facility Type | Count |\n"
|
| 125 |
response += "|---------------|-------|\n"
|
| 126 |
+
for ftype, count in data_prep["facility_type_frequency"].items():
|
| 127 |
response += f"| {ftype} | {count} |\n"
|
| 128 |
response += "\n"
|
| 129 |
|
| 130 |
+
# Top cities with facility breakdown
|
| 131 |
+
if "top_cities" in data_prep and "city_facility_breakdown" in data_prep:
|
| 132 |
response += "### Top Cities by Facility Count\n\n"
|
| 133 |
response += "| City | Hospitals | Nursing/Residential | Ambulatory | Total |\n"
|
| 134 |
response += "|------|-----------|-------------------|------------|-------|\n"
|
| 135 |
|
| 136 |
+
for city in data_prep["top_cities"]:
|
| 137 |
+
if city in data_prep["city_facility_breakdown"]:
|
| 138 |
+
breakdown = data_prep["city_facility_breakdown"][city]
|
| 139 |
hospitals = breakdown.get("Hospitals", 0)
|
| 140 |
nursing = breakdown.get("Nursing and residential care facilities", 0)
|
| 141 |
ambulatory = breakdown.get("Ambulatory health care services", 0)
|
|
|
|
| 144 |
|
| 145 |
response += "\n"
|
| 146 |
|
| 147 |
+
return response
|
| 148 |
|
| 149 |
@staticmethod
|
| 150 |
+
def _handle_bed_capacity_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 151 |
+
"""Handle bed capacity analysis task"""
|
| 152 |
+
response = "## Bed Capacity Analysis\n\n"
|
| 153 |
+
|
| 154 |
+
if "capacity_analysis" not in analysis_results:
|
| 155 |
+
return response + "No capacity analysis results available"
|
| 156 |
+
|
| 157 |
+
capacity = analysis_results["capacity_analysis"]
|
| 158 |
+
|
| 159 |
+
# Bed capacity by zone
|
| 160 |
+
if "bed_capacity_by_zone" in capacity:
|
| 161 |
+
response += "### Bed Capacity by Zone\n\n"
|
| 162 |
+
response += "| Zone | Current Beds | Previous Beds | Absolute Change | Percentage Change |\n"
|
| 163 |
+
response += "|------|--------------|---------------|-----------------|-------------------|\n"
|
| 164 |
+
for zone, data in capacity["bed_capacity_by_zone"].items():
|
| 165 |
+
current = data.get("beds_current", 0)
|
| 166 |
+
previous = data.get("beds_prev", 0)
|
| 167 |
+
abs_change = current - previous
|
| 168 |
+
perc_change = (abs_change / previous * 100) if previous > 0 else 0
|
| 169 |
+
response += f"| {zone} | {current:,} | {previous:,} | {abs_change:+,} | {perc_change:+.1f}% |\n"
|
| 170 |
+
response += "\n"
|
| 171 |
+
|
| 172 |
+
# Zones with largest decreases
|
| 173 |
+
if "zone_with_largest_absolute_decrease" in capacity:
|
| 174 |
+
response += f"**Zone with Largest Absolute Decrease**: {capacity['zone_with_largest_absolute_decrease']}\n\n"
|
| 175 |
+
|
| 176 |
+
if "zone_with_largest_percentage_decrease" in capacity:
|
| 177 |
+
response += f"**Zone with Largest Percentage Decrease**: {capacity['zone_with_largest_percentage_decrease']}\n\n"
|
| 178 |
+
|
| 179 |
+
return response
|
| 180 |
+
|
| 181 |
+
@staticmethod
|
| 182 |
+
def _handle_facility_declines_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 183 |
+
"""Handle facilities with greatest declines task"""
|
| 184 |
+
response = "## Facilities with Greatest Declines\n\n"
|
| 185 |
+
|
| 186 |
+
if "capacity_analysis" not in analysis_results or "facilities_with_declines" not in analysis_results["capacity_analysis"]:
|
| 187 |
+
return response + "No facility decline data available"
|
| 188 |
+
|
| 189 |
+
facilities = analysis_results["capacity_analysis"]["facilities_with_declines"]
|
| 190 |
|
| 191 |
+
response += "| Facility Name | Zone | Teaching Status | Bed Change |\n"
|
| 192 |
+
response += "|---------------|------|----------------|------------|\n"
|
| 193 |
+
for facility in facilities[:5]: # Top 5
|
| 194 |
+
name = facility.get("facility_name", "")
|
| 195 |
+
zone = facility.get("zone", "")
|
| 196 |
+
teaching = facility.get("teaching_status", "")
|
| 197 |
+
change = facility.get("bed_change", 0)
|
| 198 |
+
response += f"| {name} | {zone} | {teaching} | {change:+,} |\n"
|
| 199 |
|
| 200 |
+
return response
|
| 201 |
+
|
| 202 |
+
@staticmethod
|
| 203 |
+
def _handle_long_term_care_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 204 |
+
"""Handle long-term care capacity assessment task"""
|
| 205 |
+
response = "## Long-Term Care Capacity Assessment\n\n"
|
| 206 |
+
|
| 207 |
+
if "long_term_care_assessment" not in analysis_results:
|
| 208 |
+
return response + "No long-term care assessment results available"
|
| 209 |
+
|
| 210 |
+
ltc = analysis_results["long_term_care_assessment"]
|
| 211 |
|
| 212 |
+
# Zone with largest percentage decrease
|
| 213 |
+
if "zone_with_largest_percentage_decrease" in ltc:
|
| 214 |
+
response += f"**Zone with Largest Percentage Decrease**: {ltc['zone_with_largest_percentage_decrease']}\n\n"
|
| 215 |
+
|
| 216 |
+
# Major city in that zone
|
| 217 |
+
if "major_city_in_zone" in ltc:
|
| 218 |
+
response += f"**Major City in Zone**: {ltc['major_city_in_zone']}\n\n"
|
| 219 |
+
|
| 220 |
+
# Facility counts in major city
|
| 221 |
+
if "facility_counts" in ltc:
|
| 222 |
+
counts = ltc["facility_counts"]
|
| 223 |
response += "### Facility Counts in Major City\n\n"
|
| 224 |
+
response += f"- Hospitals: {counts.get('hospitals', 0)}\n"
|
| 225 |
+
response += f"- Nursing/Residential Care Facilities: {counts.get('nursing_residential_care', 0)}\n"
|
| 226 |
+
response += f"- Ambulatory Health Services: {counts.get('ambulatory', 0)}\n\n"
|
| 227 |
|
| 228 |
+
# Nursing to hospital ratio
|
| 229 |
+
if "nursing_to_hospital_ratio" in ltc:
|
| 230 |
+
ratio = ltc["nursing_to_hospital_ratio"]
|
| 231 |
response += f"**Nursing/Residential to Hospital Ratio**: {ratio:.2f}\n\n"
|
| 232 |
|
| 233 |
+
# Capacity assessment
|
| 234 |
+
if "capacity_assessment" in ltc:
|
| 235 |
+
assessment = ltc["capacity_assessment"]
|
| 236 |
response += f"**Long-Term Care Capacity Assessment**: {assessment.upper()}\n\n"
|
| 237 |
|
| 238 |
if assessment == "insufficient":
|
|
|
|
| 242 |
response += "The ratio of nursing/residential care facilities to hospitals meets or exceeds the recommended threshold. "
|
| 243 |
response += "This indicates sufficient long-term care capacity.\n\n"
|
| 244 |
|
| 245 |
+
return response
|
| 246 |
+
|
| 247 |
+
@staticmethod
|
| 248 |
+
def _handle_recommendations_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 249 |
+
"""Handle operational recommendations task"""
|
| 250 |
+
response = "## Operational Recommendations\n\n"
|
| 251 |
+
|
| 252 |
+
if "recommendations" not in analysis_results or not analysis_results["recommendations"]:
|
| 253 |
+
return response + "No recommendations available"
|
| 254 |
+
|
| 255 |
+
recommendations = analysis_results["recommendations"]
|
| 256 |
+
|
| 257 |
+
# Group by priority
|
| 258 |
+
high_priority = [r for r in recommendations if r.get("priority") == "High"]
|
| 259 |
+
medium_priority = [r for r in recommendations if r.get("priority") == "Medium"]
|
| 260 |
+
low_priority = [r for r in recommendations if r.get("priority") == "Low"]
|
| 261 |
+
|
| 262 |
+
if high_priority:
|
| 263 |
+
response += "### High Priority Recommendations\n\n"
|
| 264 |
+
for i, rec in enumerate(high_priority, 1):
|
| 265 |
+
response += f"**{i}. {rec.get('title', 'Recommendation')}**\n\n"
|
| 266 |
+
response += f"{rec.get('description', '')}\n\n"
|
| 267 |
+
if "justification" in rec:
|
| 268 |
+
response += f"**Justification**: {rec['justification']}\n\n"
|
| 269 |
+
if "expected_impact" in rec:
|
| 270 |
+
response += f"**Expected Impact**: {rec['expected_impact']}\n\n"
|
| 271 |
+
response += "---\n\n"
|
| 272 |
+
|
| 273 |
+
if medium_priority:
|
| 274 |
+
response += "### Medium Priority Recommendations\n\n"
|
| 275 |
+
for i, rec in enumerate(medium_priority, 1):
|
| 276 |
+
response += f"**{i}. {rec.get('title', 'Recommendation')}**\n\n"
|
| 277 |
+
response += f"{rec.get('description', '')}\n\n"
|
| 278 |
+
if "justification" in rec:
|
| 279 |
+
response += f"**Justification**: {rec['justification']}\n\n"
|
| 280 |
+
if "expected_impact" in rec:
|
| 281 |
+
response += f"**Expected Impact**: {rec['expected_impact']}\n\n"
|
| 282 |
+
response += "---\n\n"
|
| 283 |
+
|
| 284 |
+
if low_priority:
|
| 285 |
+
response += "### Low Priority Recommendations\n\n"
|
| 286 |
+
for i, rec in enumerate(low_priority, 1):
|
| 287 |
+
response += f"**{i}. {rec.get('title', 'Recommendation')}**\n\n"
|
| 288 |
+
response += f"{rec.get('description', '')}\n\n"
|
| 289 |
+
if "justification" in rec:
|
| 290 |
+
response += f"**Justification**: {rec['justification']}\n\n"
|
| 291 |
+
if "expected_impact" in rec:
|
| 292 |
+
response += f"**Expected Impact**: {rec['expected_impact']}\n\n"
|
| 293 |
+
response += "---\n\n"
|
| 294 |
+
|
| 295 |
+
return response
|
| 296 |
+
|
| 297 |
+
@staticmethod
|
| 298 |
+
def _handle_integration_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 299 |
+
"""Handle future integration opportunities task"""
|
| 300 |
+
response = "## Future Integration Opportunities\n\n"
|
| 301 |
+
|
| 302 |
+
if "future_integration" not in analysis_results:
|
| 303 |
+
return response + "No integration opportunities identified"
|
| 304 |
+
|
| 305 |
+
integration = analysis_results["future_integration"]
|
| 306 |
+
|
| 307 |
+
# Summary
|
| 308 |
+
if "summary" in integration:
|
| 309 |
+
response += f"{integration['summary']}\n\n"
|
| 310 |
+
|
| 311 |
+
# Opportunities
|
| 312 |
+
if "opportunities" in integration:
|
| 313 |
+
for i, opp in enumerate(integration["opportunities"], 1):
|
| 314 |
+
response += f"**{i}. {opp.get('title', 'Opportunity')}**\n\n"
|
| 315 |
+
response += f"{opp.get('description', '')}\n\n"
|
| 316 |
+
|
| 317 |
+
if "benefits" in opp:
|
| 318 |
+
response += "**Benefits:**\n"
|
| 319 |
+
for benefit in opp["benefits"]:
|
| 320 |
+
response += f"- {benefit}\n"
|
| 321 |
+
response += "\n"
|
| 322 |
+
|
| 323 |
+
if "challenges" in opp:
|
| 324 |
+
response += "**Challenges:**\n"
|
| 325 |
+
for challenge in opp["challenges"]:
|
| 326 |
+
response += f"- {challenge}\n"
|
| 327 |
+
response += "\n"
|
| 328 |
+
|
| 329 |
+
if "example_metric_or_model" in opp:
|
| 330 |
+
response += f"**Example Metric/Model**: {opp['example_metric_or_model']}\n\n"
|
| 331 |
+
|
| 332 |
+
if "timeline" in opp:
|
| 333 |
+
response += f"**Timeline**: {opp['timeline']}\n\n"
|
| 334 |
+
|
| 335 |
+
response += "---\n\n"
|
| 336 |
+
|
| 337 |
+
return response
|
| 338 |
+
|
| 339 |
+
@staticmethod
|
| 340 |
+
def _handle_generic_task(task: str, analysis_results: Dict[str, Any]) -> str:
|
| 341 |
+
"""Handle generic task"""
|
| 342 |
+
# Try to match task to any available analysis results
|
| 343 |
+
task_lower = task.lower()
|
| 344 |
+
|
| 345 |
+
if "distribution" in task_lower and "facility_distribution" in analysis_results:
|
| 346 |
+
return "## Facility Distribution Analysis\n\n" + ResponseFormatter._format_facility_distribution(analysis_results["facility_distribution"])
|
| 347 |
+
|
| 348 |
+
if "resource" in task_lower and "resource_allocation" in analysis_results:
|
| 349 |
+
return "## Resource Allocation Analysis\n\n" + ResponseFormatter._format_resource_allocation(analysis_results["resource_allocation"])
|
| 350 |
+
|
| 351 |
+
if "trend" in task_lower and "trends" in analysis_results:
|
| 352 |
+
return "## Trend Analysis\n\n" + ResponseFormatter._format_trends(analysis_results["trends"])
|
| 353 |
+
|
| 354 |
+
# If no specific match, return a generic section
|
| 355 |
+
return f"## {task}\n\nNo specific analysis results available for this task."
|
| 356 |
+
|
| 357 |
+
@staticmethod
|
| 358 |
+
def _generate_executive_summary(results: Dict[str, Any]) -> str:
|
| 359 |
+
"""Generate executive summary based on analysis results"""
|
| 360 |
+
summary = []
|
| 361 |
+
|
| 362 |
+
if "capacity_analysis" in results:
|
| 363 |
+
capacity = results["capacity_analysis"]
|
| 364 |
+
if "total_capacity" in capacity:
|
| 365 |
+
summary.append(f"Total system capacity: {capacity['total_capacity']:,} beds")
|
| 366 |
+
if "average_utilization" in capacity:
|
| 367 |
+
summary.append(f"Average utilization: {capacity['average_utilization']:.1%}")
|
| 368 |
+
|
| 369 |
+
if "facility_distribution" in results:
|
| 370 |
+
dist = results["facility_distribution"]
|
| 371 |
+
if "geographic_inequality" in dist:
|
| 372 |
+
inequality = dist["geographic_inequality"]
|
| 373 |
+
level = "High" if inequality > 0.4 else "Moderate" if inequality > 0.2 else "Low"
|
| 374 |
+
summary.append(f"Geographic distribution inequality: {level} (Gini: {inequality:.2f})")
|
| 375 |
+
|
| 376 |
+
if "recommendations" in results:
|
| 377 |
+
high_priority = [r for r in results["recommendations"] if r.get("priority") == "High"]
|
| 378 |
+
if high_priority:
|
| 379 |
+
summary.append(f"{len(high_priority)} high-priority recommendations identified")
|
| 380 |
+
|
| 381 |
+
if "validation" in results:
|
| 382 |
+
completion_rate = results["validation"].get("completion_rate", 0)
|
| 383 |
+
summary.append(f"Analysis completion rate: {completion_rate:.1%}")
|
| 384 |
+
|
| 385 |
+
return " | ".join(summary) if summary else "No key metrics identified"
|
| 386 |
|
| 387 |
@staticmethod
|
| 388 |
def _format_validation_results(validation: Dict[str, Any]) -> str:
|
|
|
|
| 399 |
|
| 400 |
return response
|
| 401 |
|
| 402 |
+
# Legacy formatting methods for generic tasks
|
| 403 |
@staticmethod
|
| 404 |
def _format_facility_distribution(dist_results: Dict[str, Any]) -> str:
|
| 405 |
"""Format facility distribution analysis results"""
|
|
|
|
| 421 |
level = "High" if inequality > 0.4 else "Moderate" if inequality > 0.2 else "Low"
|
| 422 |
response += f"**Geographic Inequality**: {level} (Gini coefficient: {inequality:.2f})\n\n"
|
| 423 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
return response if response else "No facility distribution data available"
|
| 425 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
@staticmethod
|
| 427 |
def _format_resource_allocation(allocation_results: Dict[str, Any]) -> str:
|
| 428 |
"""Format resource allocation analysis results"""
|
|
|
|
| 440 |
response += f"| {ftype} | {count:,} | {percentage:.1f}% |\n"
|
| 441 |
response += "\n"
|
| 442 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
return response if response else "No resource allocation data available"
|
| 444 |
|
| 445 |
@staticmethod
|
|
|
|
| 464 |
prev_count = count
|
| 465 |
response += "\n"
|
| 466 |
|
| 467 |
+
return response if response else "No trend data available"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|