runbooks 1.1.4__py3-none-any.whl → 1.1.5__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.
Files changed (228) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +138 -35
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +11 -0
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +63 -74
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +201 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/cloud_foundations_integration.py +144 -149
  113. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  114. runbooks/inventory/collectors/aws_networking.py +109 -99
  115. runbooks/inventory/collectors/base.py +4 -0
  116. runbooks/inventory/core/collector.py +495 -313
  117. runbooks/inventory/drift_detection_cli.py +69 -96
  118. runbooks/inventory/inventory_mcp_cli.py +48 -46
  119. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  120. runbooks/inventory/mcp_inventory_validator.py +549 -465
  121. runbooks/inventory/mcp_vpc_validator.py +359 -442
  122. runbooks/inventory/organizations_discovery.py +55 -51
  123. runbooks/inventory/rich_inventory_display.py +33 -32
  124. runbooks/inventory/unified_validation_engine.py +278 -251
  125. runbooks/inventory/vpc_analyzer.py +732 -695
  126. runbooks/inventory/vpc_architecture_validator.py +293 -348
  127. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  128. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  129. runbooks/main.py +49 -34
  130. runbooks/main_final.py +91 -60
  131. runbooks/main_minimal.py +22 -10
  132. runbooks/main_optimized.py +131 -100
  133. runbooks/main_ultra_minimal.py +7 -2
  134. runbooks/mcp/__init__.py +36 -0
  135. runbooks/mcp/integration.py +679 -0
  136. runbooks/monitoring/performance_monitor.py +9 -4
  137. runbooks/operate/dynamodb_operations.py +3 -1
  138. runbooks/operate/ec2_operations.py +145 -137
  139. runbooks/operate/iam_operations.py +146 -152
  140. runbooks/operate/networking_cost_heatmap.py +29 -8
  141. runbooks/operate/rds_operations.py +223 -254
  142. runbooks/operate/s3_operations.py +107 -118
  143. runbooks/operate/vpc_operations.py +646 -616
  144. runbooks/remediation/base.py +1 -1
  145. runbooks/remediation/commons.py +10 -7
  146. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  147. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  148. runbooks/remediation/multi_account.py +24 -21
  149. runbooks/remediation/rds_snapshot_list.py +86 -60
  150. runbooks/remediation/remediation_cli.py +92 -146
  151. runbooks/remediation/universal_account_discovery.py +83 -79
  152. runbooks/remediation/workspaces_list.py +46 -41
  153. runbooks/security/__init__.py +19 -0
  154. runbooks/security/assessment_runner.py +1150 -0
  155. runbooks/security/baseline_checker.py +812 -0
  156. runbooks/security/cloudops_automation_security_validator.py +509 -535
  157. runbooks/security/compliance_automation_engine.py +17 -17
  158. runbooks/security/config/__init__.py +2 -2
  159. runbooks/security/config/compliance_config.py +50 -50
  160. runbooks/security/config_template_generator.py +63 -76
  161. runbooks/security/enterprise_security_framework.py +1 -1
  162. runbooks/security/executive_security_dashboard.py +519 -508
  163. runbooks/security/multi_account_security_controls.py +959 -1210
  164. runbooks/security/real_time_security_monitor.py +422 -444
  165. runbooks/security/security_baseline_tester.py +1 -1
  166. runbooks/security/security_cli.py +143 -112
  167. runbooks/security/test_2way_validation.py +439 -0
  168. runbooks/security/two_way_validation_framework.py +852 -0
  169. runbooks/sre/production_monitoring_framework.py +167 -177
  170. runbooks/tdd/__init__.py +15 -0
  171. runbooks/tdd/cli.py +1071 -0
  172. runbooks/utils/__init__.py +14 -17
  173. runbooks/utils/logger.py +7 -2
  174. runbooks/utils/version_validator.py +50 -47
  175. runbooks/validation/__init__.py +6 -6
  176. runbooks/validation/cli.py +9 -3
  177. runbooks/validation/comprehensive_2way_validator.py +745 -704
  178. runbooks/validation/mcp_validator.py +906 -228
  179. runbooks/validation/terraform_citations_validator.py +104 -115
  180. runbooks/validation/terraform_drift_detector.py +447 -451
  181. runbooks/vpc/README.md +617 -0
  182. runbooks/vpc/__init__.py +8 -1
  183. runbooks/vpc/analyzer.py +577 -0
  184. runbooks/vpc/cleanup_wrapper.py +476 -413
  185. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  186. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  187. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  188. runbooks/vpc/config.py +92 -97
  189. runbooks/vpc/cost_engine.py +411 -148
  190. runbooks/vpc/cost_explorer_integration.py +553 -0
  191. runbooks/vpc/cross_account_session.py +101 -106
  192. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  193. runbooks/vpc/eni_gate_validator.py +961 -0
  194. runbooks/vpc/heatmap_engine.py +185 -160
  195. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  196. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  197. runbooks/vpc/networking_wrapper.py +15 -8
  198. runbooks/vpc/pdca_remediation_planner.py +528 -0
  199. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  200. runbooks/vpc/runbooks_adapter.py +1167 -241
  201. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  202. runbooks/vpc/test_data_loader.py +358 -0
  203. runbooks/vpc/tests/conftest.py +314 -4
  204. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  205. runbooks/vpc/tests/test_cost_engine.py +0 -2
  206. runbooks/vpc/topology_generator.py +326 -0
  207. runbooks/vpc/unified_scenarios.py +1297 -1124
  208. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  209. runbooks-1.1.5.dist-info/METADATA +328 -0
  210. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/RECORD +214 -193
  211. runbooks/finops/README.md +0 -414
  212. runbooks/finops/accuracy_cross_validator.py +0 -647
  213. runbooks/finops/business_cases.py +0 -950
  214. runbooks/finops/dashboard_router.py +0 -922
  215. runbooks/finops/ebs_optimizer.py +0 -973
  216. runbooks/finops/embedded_mcp_validator.py +0 -1629
  217. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  218. runbooks/finops/finops_dashboard.py +0 -584
  219. runbooks/finops/finops_scenarios.py +0 -1218
  220. runbooks/finops/legacy_migration.py +0 -730
  221. runbooks/finops/multi_dashboard.py +0 -1519
  222. runbooks/finops/single_dashboard.py +0 -1113
  223. runbooks/finops/unlimited_scenarios.py +0 -393
  224. runbooks-1.1.4.dist-info/METADATA +0 -800
  225. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  226. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  227. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  228. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Dynamic Business Case Configuration - Enterprise Template System
3
3
 
4
- Strategic Achievement: Replace hardcoded JIRA references with dynamic business case templates
4
+ Strategic Achievement: Replace hardcoded references with dynamic business case templates
5
5
  - Enterprise naming conventions with configurable business scenarios
6
6
  - Dynamic financial targets and achievement tracking
7
7
  - Reusable template system for unlimited business case scaling
@@ -14,12 +14,13 @@ This module provides configurable business case templates following enterprise s
14
14
 
15
15
  import os
16
16
  from dataclasses import dataclass
17
- from typing import Dict, List, Optional, Any, Union, NamedTuple
18
17
  from enum import Enum
18
+ from typing import Any, Dict, List, NamedTuple, Optional, Union
19
19
 
20
20
 
21
21
  class BusinessCaseType(Enum):
22
22
  """Standard business case types for enterprise scenarios."""
23
+
23
24
  COST_OPTIMIZATION = "cost_optimization"
24
25
  RESOURCE_CLEANUP = "resource_cleanup"
25
26
  COMPLIANCE_FRAMEWORK = "compliance_framework"
@@ -30,6 +31,7 @@ class BusinessCaseType(Enum):
30
31
  @dataclass
31
32
  class BusinessScenario:
32
33
  """Dynamic business scenario configuration."""
34
+
33
35
  scenario_id: str
34
36
  display_name: str
35
37
  business_case_type: BusinessCaseType
@@ -40,12 +42,12 @@ class BusinessScenario:
40
42
  risk_level: str = "Medium"
41
43
  implementation_status: str = "Analysis"
42
44
  cli_command_suffix: str = ""
43
-
45
+
44
46
  @property
45
47
  def scenario_display_id(self) -> str:
46
48
  """Generate enterprise-friendly scenario display ID."""
47
49
  return f"{self.business_case_type.value.replace('_', '-').title()}-{self.scenario_id}"
48
-
50
+
49
51
  @property
50
52
  def savings_range_display(self) -> str:
51
53
  """Generate savings range display for business presentations."""
@@ -62,18 +64,18 @@ class BusinessScenario:
62
64
 
63
65
  class BusinessCaseConfigManager:
64
66
  """Enterprise business case configuration manager."""
65
-
67
+
66
68
  def __init__(self, config_source: Optional[str] = None):
67
69
  """
68
70
  Initialize business case configuration manager.
69
-
71
+
70
72
  Args:
71
73
  config_source: Optional path to configuration file or environment variable prefix
72
74
  """
73
75
  self.config_source = config_source or "RUNBOOKS_BUSINESS_CASE"
74
76
  self.scenarios = self._load_default_scenarios()
75
77
  self._load_environment_overrides()
76
-
78
+
77
79
  def _load_default_scenarios(self) -> Dict[str, BusinessScenario]:
78
80
  """Load default enterprise business scenarios."""
79
81
  return {
@@ -86,7 +88,7 @@ class BusinessCaseConfigManager:
86
88
  business_description="Identify and optimize unused Amazon WorkSpaces for cost efficiency",
87
89
  technical_focus="Zero-usage WorkSpaces detection and cost analysis",
88
90
  risk_level="Low",
89
- cli_command_suffix="workspaces"
91
+ cli_command_suffix="workspaces",
90
92
  ),
91
93
  "rds-snapshots": BusinessScenario(
92
94
  scenario_id="rds-snapshots",
@@ -97,7 +99,7 @@ class BusinessCaseConfigManager:
97
99
  business_description="Optimize manual RDS snapshots to reduce storage costs",
98
100
  technical_focus="Manual RDS snapshot lifecycle management",
99
101
  risk_level="Medium",
100
- cli_command_suffix="rds-snapshots"
102
+ cli_command_suffix="rds-snapshots",
101
103
  ),
102
104
  "backup-investigation": BusinessScenario(
103
105
  scenario_id="backup-investigation",
@@ -107,7 +109,7 @@ class BusinessCaseConfigManager:
107
109
  technical_focus="Backup infrastructure resource utilization analysis",
108
110
  risk_level="Medium",
109
111
  implementation_status="Framework",
110
- cli_command_suffix="backup-investigation"
112
+ cli_command_suffix="backup-investigation",
111
113
  ),
112
114
  "nat-gateway": BusinessScenario(
113
115
  scenario_id="nat-gateway",
@@ -117,7 +119,7 @@ class BusinessCaseConfigManager:
117
119
  target_savings_max=12000,
118
120
  business_description="Optimize NAT Gateway configurations for cost efficiency",
119
121
  technical_focus="NAT Gateway usage analysis and rightsizing",
120
- cli_command_suffix="nat-gateway"
122
+ cli_command_suffix="nat-gateway",
121
123
  ),
122
124
  "elastic-ip": BusinessScenario(
123
125
  scenario_id="elastic-ip",
@@ -127,7 +129,7 @@ class BusinessCaseConfigManager:
127
129
  business_description="Optimize unattached Elastic IP addresses",
128
130
  technical_focus="Elastic IP attachment analysis and cleanup recommendations",
129
131
  risk_level="Low",
130
- cli_command_suffix="elastic-ip"
132
+ cli_command_suffix="elastic-ip",
131
133
  ),
132
134
  "ebs-optimization": BusinessScenario(
133
135
  scenario_id="ebs-optimization",
@@ -135,7 +137,7 @@ class BusinessCaseConfigManager:
135
137
  business_case_type=BusinessCaseType.COST_OPTIMIZATION,
136
138
  business_description="Optimize EBS volume types and utilization for cost efficiency",
137
139
  technical_focus="EBS volume rightsizing and type optimization (15-20% potential)",
138
- cli_command_suffix="ebs-optimization"
140
+ cli_command_suffix="ebs-optimization",
139
141
  ),
140
142
  "vpc-cleanup": BusinessScenario(
141
143
  scenario_id="vpc-cleanup",
@@ -144,10 +146,10 @@ class BusinessCaseConfigManager:
144
146
  target_savings_min=5869,
145
147
  business_description="Clean up unused VPC resources and infrastructure",
146
148
  technical_focus="VPC resource utilization analysis and cleanup recommendations",
147
- cli_command_suffix="vpc-cleanup"
148
- )
149
+ cli_command_suffix="vpc-cleanup",
150
+ ),
149
151
  }
150
-
152
+
151
153
  def _load_environment_overrides(self) -> None:
152
154
  """
153
155
  Load configuration overrides and discover new scenarios from environment variables.
@@ -206,7 +208,7 @@ class BusinessCaseConfigManager:
206
208
  if env_var.startswith(prefix) and "_DISPLAY_NAME" in env_var:
207
209
  # Extract scenario key from pattern: RUNBOOKS_BUSINESS_CASE_[SCENARIO]_DISPLAY_NAME
208
210
  scenario_part = env_var.replace(prefix, "").replace("_DISPLAY_NAME", "")
209
- scenario_key = scenario_part.lower().replace('_', '-')
211
+ scenario_key = scenario_part.lower().replace("_", "-")
210
212
  discovered_scenarios.add((scenario_key, scenario_part))
211
213
 
212
214
  # Create new scenarios for discovered patterns
@@ -216,7 +218,9 @@ class BusinessCaseConfigManager:
216
218
  if new_scenario:
217
219
  self.scenarios[scenario_key] = new_scenario
218
220
 
219
- def _create_scenario_from_environment(self, scenario_key: str, env_scenario_key: str, prefix: str) -> Optional[BusinessScenario]:
221
+ def _create_scenario_from_environment(
222
+ self, scenario_key: str, env_scenario_key: str, prefix: str
223
+ ) -> Optional[BusinessScenario]:
220
224
  """
221
225
  Create a new BusinessScenario from environment variables.
222
226
 
@@ -266,21 +270,21 @@ class BusinessCaseConfigManager:
266
270
  technical_focus=technical_focus,
267
271
  risk_level=risk_level,
268
272
  implementation_status=implementation_status,
269
- cli_command_suffix=cli_suffix
273
+ cli_command_suffix=cli_suffix,
270
274
  )
271
-
275
+
272
276
  def get_scenario(self, scenario_key: str) -> Optional[BusinessScenario]:
273
277
  """Get business scenario by key."""
274
278
  return self.scenarios.get(scenario_key)
275
-
279
+
276
280
  def get_all_scenarios(self) -> Dict[str, BusinessScenario]:
277
281
  """Get all configured business scenarios."""
278
282
  return self.scenarios
279
-
283
+
280
284
  def get_scenario_choices(self) -> List[str]:
281
285
  """Get list of valid scenario keys for CLI choice options."""
282
286
  return list(self.scenarios.keys())
283
-
287
+
284
288
  def get_scenario_help_text(self) -> str:
285
289
  """Generate help text for CLI scenario option."""
286
290
  help_parts = []
@@ -288,25 +292,28 @@ class BusinessCaseConfigManager:
288
292
  savings_display = scenario.savings_range_display
289
293
  help_parts.append(f"{key} ({scenario.display_name}: {savings_display})")
290
294
  return "Business scenario analysis: " + ", ".join(help_parts)
291
-
292
- def format_scenario_for_display(self, scenario_key: str,
293
- achieved_savings: Optional[float] = None,
294
- achievement_percentage: Optional[float] = None) -> str:
295
+
296
+ def format_scenario_for_display(
297
+ self,
298
+ scenario_key: str,
299
+ achieved_savings: Optional[float] = None,
300
+ achievement_percentage: Optional[float] = None,
301
+ ) -> str:
295
302
  """Format scenario for display in tables and reports."""
296
303
  scenario = self.get_scenario(scenario_key)
297
304
  if not scenario:
298
305
  return f"Unknown scenario: {scenario_key}"
299
-
306
+
300
307
  base_info = f"{scenario.display_name} ({scenario.savings_range_display})"
301
-
308
+
302
309
  if achieved_savings:
303
310
  base_info += f" - Achieved: ${achieved_savings:,.0f}"
304
-
311
+
305
312
  if achievement_percentage:
306
313
  base_info += f" ({achievement_percentage:.0f}% of target)"
307
-
314
+
308
315
  return base_info
309
-
316
+
310
317
  def add_dynamic_scenario(self, scenario: BusinessScenario) -> None:
311
318
  """
312
319
  Add a new business scenario dynamically.
@@ -316,7 +323,9 @@ class BusinessCaseConfigManager:
316
323
  """
317
324
  self.scenarios[scenario.scenario_id] = scenario
318
325
 
319
- def create_scenario_from_template(self, scenario_id: str, template_type: str = "aws_resource_optimization") -> BusinessScenario:
326
+ def create_scenario_from_template(
327
+ self, scenario_id: str, template_type: str = "aws_resource_optimization"
328
+ ) -> BusinessScenario:
320
329
  """
321
330
  Create a business scenario from predefined templates.
322
331
 
@@ -344,7 +353,7 @@ class BusinessCaseConfigManager:
344
353
  technical_focus=f"{scenario_id.title().replace('-', ' ')} resource analysis with automated recommendations",
345
354
  risk_level="Medium",
346
355
  implementation_status="Template Ready",
347
- cli_command_suffix=scenario_id
356
+ cli_command_suffix=scenario_id,
348
357
  ),
349
358
  "lambda_rightsizing": BusinessScenario(
350
359
  scenario_id=scenario_id,
@@ -353,7 +362,7 @@ class BusinessCaseConfigManager:
353
362
  business_description="Optimize Lambda function memory allocation and timeout settings",
354
363
  technical_focus="CloudWatch metrics analysis for memory and duration optimization",
355
364
  risk_level="Low",
356
- cli_command_suffix=scenario_id
365
+ cli_command_suffix=scenario_id,
357
366
  ),
358
367
  "s3_storage_optimization": BusinessScenario(
359
368
  scenario_id=scenario_id,
@@ -362,7 +371,7 @@ class BusinessCaseConfigManager:
362
371
  business_description="Optimize S3 storage classes based on access patterns",
363
372
  technical_focus="S3 access pattern analysis with intelligent storage class recommendations",
364
373
  risk_level="Low",
365
- cli_command_suffix=scenario_id
374
+ cli_command_suffix=scenario_id,
366
375
  ),
367
376
  "healthcare_compliance": BusinessScenario(
368
377
  scenario_id=scenario_id,
@@ -371,7 +380,7 @@ class BusinessCaseConfigManager:
371
380
  business_description="HIPAA compliance optimization with cost considerations",
372
381
  technical_focus="Healthcare data security analysis with cost-effective compliance solutions",
373
382
  risk_level="High",
374
- cli_command_suffix=scenario_id
383
+ cli_command_suffix=scenario_id,
375
384
  ),
376
385
  "finance_cost_governance": BusinessScenario(
377
386
  scenario_id=scenario_id,
@@ -380,7 +389,7 @@ class BusinessCaseConfigManager:
380
389
  business_description="Financial industry cost governance and audit readiness",
381
390
  technical_focus="SOX compliance cost optimization with audit trail requirements",
382
391
  risk_level="High",
383
- cli_command_suffix=scenario_id
392
+ cli_command_suffix=scenario_id,
384
393
  ),
385
394
  "manufacturing_automation": BusinessScenario(
386
395
  scenario_id=scenario_id,
@@ -389,8 +398,8 @@ class BusinessCaseConfigManager:
389
398
  business_description="Manufacturing workflow automation with cost optimization",
390
399
  technical_focus="IoT and automation pipeline cost optimization analysis",
391
400
  risk_level="Medium",
392
- cli_command_suffix=scenario_id
393
- )
401
+ cli_command_suffix=scenario_id,
402
+ ),
394
403
  }
395
404
 
396
405
  template = templates.get(template_type, templates["aws_resource_optimization"])
@@ -405,7 +414,7 @@ class BusinessCaseConfigManager:
405
414
  technical_focus=template.technical_focus,
406
415
  risk_level=template.risk_level,
407
416
  implementation_status=template.implementation_status,
408
- cli_command_suffix=template.cli_command_suffix
417
+ cli_command_suffix=template.cli_command_suffix,
409
418
  )
410
419
 
411
420
  def get_template_types(self) -> List[str]:
@@ -416,11 +425,12 @@ class BusinessCaseConfigManager:
416
425
  "s3_storage_optimization",
417
426
  "healthcare_compliance",
418
427
  "finance_cost_governance",
419
- "manufacturing_automation"
428
+ "manufacturing_automation",
420
429
  ]
421
430
 
422
- def calculate_roi_projection(self, scenario_key: str, current_monthly_cost: float,
423
- optimization_percentage: float = 0.20) -> Dict[str, float]:
431
+ def calculate_roi_projection(
432
+ self, scenario_key: str, current_monthly_cost: float, optimization_percentage: float = 0.20
433
+ ) -> Dict[str, float]:
424
434
  """
425
435
  Calculate ROI projection for a business scenario.
426
436
 
@@ -440,20 +450,15 @@ class BusinessCaseConfigManager:
440
450
  "monthly_savings": monthly_savings,
441
451
  "annual_savings": annual_savings,
442
452
  "optimization_percentage": optimization_percentage * 100,
443
- "roi_12_month": annual_savings # Assuming minimal implementation cost for analysis
453
+ "roi_12_month": annual_savings, # Assuming minimal implementation cost for analysis
444
454
  }
445
455
 
446
456
  def create_business_case_summary(self) -> Dict[str, Any]:
447
457
  """Create executive summary of all business cases."""
448
- total_min_savings = sum(
449
- scenario.target_savings_min or 0
450
- for scenario in self.scenarios.values()
451
- )
458
+ total_min_savings = sum(scenario.target_savings_min or 0 for scenario in self.scenarios.values())
452
459
 
453
460
  total_max_savings = sum(
454
- scenario.target_savings_max or 0
455
- for scenario in self.scenarios.values()
456
- if scenario.target_savings_max
461
+ scenario.target_savings_max or 0 for scenario in self.scenarios.values() if scenario.target_savings_max
457
462
  )
458
463
 
459
464
  return {
@@ -462,24 +467,22 @@ class BusinessCaseConfigManager:
462
467
  "total_potential_max": total_max_savings,
463
468
  "potential_range": f"${total_min_savings:,.0f}-${total_max_savings:,.0f}",
464
469
  "scenarios_by_type": {
465
- case_type.value: [
466
- s.display_name for s in self.scenarios.values()
467
- if s.business_case_type == case_type
468
- ]
470
+ case_type.value: [s.display_name for s in self.scenarios.values() if s.business_case_type == case_type]
469
471
  for case_type in BusinessCaseType
470
472
  },
471
473
  "scenario_discovery": {
472
474
  "default_scenarios": 7,
473
475
  "environment_discovered": len(self.scenarios) - 7,
474
476
  "total_active": len(self.scenarios),
475
- "unlimited_expansion": True
476
- }
477
+ "unlimited_expansion": True,
478
+ },
477
479
  }
478
480
 
479
481
 
480
482
  # Global configuration manager instance
481
483
  _config_manager = None
482
484
 
485
+
483
486
  def get_business_case_config() -> BusinessCaseConfigManager:
484
487
  """Get global business case configuration manager."""
485
488
  global _config_manager
@@ -506,17 +509,17 @@ def format_business_achievement(scenario_key: str, achieved_savings: float) -> s
506
509
  """Format business achievement for executive reporting."""
507
510
  config = get_business_case_config()
508
511
  scenario = config.get_scenario(scenario_key)
509
-
512
+
510
513
  if not scenario:
511
514
  return f"{scenario_key}: ${achieved_savings:,.0f} annual savings"
512
-
515
+
513
516
  # Calculate achievement percentage if target is available
514
517
  achievement_text = f"{scenario.display_name}: ${achieved_savings:,.0f} annual savings"
515
-
518
+
516
519
  if scenario.target_savings_min:
517
520
  percentage = (achieved_savings / scenario.target_savings_min) * 100
518
521
  achievement_text += f" ({percentage:.0f}% of target)"
519
-
522
+
520
523
  return achievement_text
521
524
 
522
525
 
@@ -539,12 +542,10 @@ def create_scenario_from_environment_variables(scenario_id: str) -> Optional[Bus
539
542
  BusinessScenario object or None if required fields missing
540
543
  """
541
544
  config_manager = get_business_case_config()
542
- env_key = scenario_id.upper().replace('-', '_')
545
+ env_key = scenario_id.upper().replace("-", "_")
543
546
 
544
547
  return config_manager._create_scenario_from_environment(
545
- scenario_id.lower().replace('_', '-'),
546
- env_key,
547
- "RUNBOOKS_BUSINESS_CASE_"
548
+ scenario_id.lower().replace("_", "-"), env_key, "RUNBOOKS_BUSINESS_CASE_"
548
549
  )
549
550
 
550
551
 
@@ -571,8 +572,9 @@ def get_available_templates() -> List[str]:
571
572
  return config_manager.get_template_types()
572
573
 
573
574
 
574
- def calculate_scenario_roi(scenario_id: str, current_monthly_cost: float,
575
- optimization_percentage: float = 0.20) -> Dict[str, float]:
575
+ def calculate_scenario_roi(
576
+ scenario_id: str, current_monthly_cost: float, optimization_percentage: float = 0.20
577
+ ) -> Dict[str, float]:
576
578
  """
577
579
  Calculate ROI projection for any business scenario.
578
580
 
@@ -622,7 +624,7 @@ def discover_scenarios_summary() -> Dict[str, Any]:
622
624
  # Migration helper functions for existing hardcoded patterns
623
625
  def migrate_legacy_scenario_reference(legacy_ref: str) -> str:
624
626
  """
625
- Migrate legacy JIRA references to dynamic business case keys.
627
+ Migrate legacy references to dynamic business case keys.
626
628
 
627
629
  Args:
628
630
  legacy_ref: Legacy reference like "FinOps-24", "finops-23", etc.
@@ -644,7 +646,7 @@ def migrate_legacy_scenario_reference(legacy_ref: str) -> str:
644
646
  "finops-ebs": "ebs-optimization",
645
647
  "FinOps-EBS": "ebs-optimization",
646
648
  "awso-05": "vpc-cleanup",
647
- "AWSO-05": "vpc-cleanup"
649
+ "AWSO-05": "vpc-cleanup",
648
650
  }
649
651
 
650
652
  return legacy_mapping.get(legacy_ref, legacy_ref.lower())
@@ -654,8 +656,10 @@ def migrate_legacy_scenario_reference(legacy_ref: str) -> str:
654
656
  # Business Scenario Matrix - Phase 1 Priority 2 Implementation
655
657
  # ============================================================================
656
658
 
659
+
657
660
  class ScenarioParameter(NamedTuple):
658
661
  """Parameter recommendation for business scenarios."""
662
+
659
663
  name: str
660
664
  optimal_value: Union[str, int, float]
661
665
  business_justification: str
@@ -665,6 +669,7 @@ class ScenarioParameter(NamedTuple):
665
669
  @dataclass
666
670
  class ScenarioParameterMatrix:
667
671
  """Intelligent parameter defaults for business scenarios."""
672
+
668
673
  timerange_days: Optional[int] = None
669
674
  regional_scope: Optional[str] = None # single, multi, global
670
675
  cost_focus: Optional[str] = None # unblended, amortized, dual-metrics
@@ -678,40 +683,40 @@ class ScenarioParameterMatrix:
678
683
  recommendations = {}
679
684
 
680
685
  if self.timerange_days:
681
- recommendations['timerange'] = ScenarioParameter(
682
- name='--time-range',
686
+ recommendations["timerange"] = ScenarioParameter(
687
+ name="--time-range",
683
688
  optimal_value=self.timerange_days,
684
689
  business_justification=f"Optimal analysis period: {self.timerange_days} days. {self.business_justification}",
685
- alternative_values=[7, 30, 60, 90, 180] if self.timerange_days not in [7, 30, 60, 90, 180] else None
690
+ alternative_values=[7, 30, 60, 90, 180] if self.timerange_days not in [7, 30, 60, 90, 180] else None,
686
691
  )
687
692
 
688
693
  if self.regional_scope:
689
694
  scope_mapping = {
690
- 'single': ['--region', 'us-east-1'],
691
- 'multi': ['--all-regions', True],
692
- 'global': ['--global-scope', True]
695
+ "single": ["--region", "us-east-1"],
696
+ "multi": ["--all-regions", True],
697
+ "global": ["--global-scope", True],
693
698
  }
694
699
  if self.regional_scope in scope_mapping:
695
700
  param_name, param_value = scope_mapping[self.regional_scope]
696
- recommendations['regional_scope'] = ScenarioParameter(
701
+ recommendations["regional_scope"] = ScenarioParameter(
697
702
  name=param_name,
698
703
  optimal_value=param_value,
699
- business_justification=f"Regional scope: {self.regional_scope} - {self.business_justification}"
704
+ business_justification=f"Regional scope: {self.regional_scope} - {self.business_justification}",
700
705
  )
701
706
 
702
707
  if self.cost_focus:
703
- recommendations['cost_focus'] = ScenarioParameter(
704
- name=f'--{self.cost_focus}',
708
+ recommendations["cost_focus"] = ScenarioParameter(
709
+ name=f"--{self.cost_focus}",
705
710
  optimal_value=True,
706
- business_justification=f"Cost perspective: {self.cost_focus} - {self.business_justification}"
711
+ business_justification=f"Cost perspective: {self.cost_focus} - {self.business_justification}",
707
712
  )
708
713
 
709
714
  if self.export_priority:
710
- recommendations['export_format'] = ScenarioParameter(
711
- name='--' + self.export_priority.replace('_', '-'),
715
+ recommendations["export_format"] = ScenarioParameter(
716
+ name="--" + self.export_priority.replace("_", "-"),
712
717
  optimal_value=True,
713
718
  business_justification=f"Export format: {self.export_priority} - {self.business_justification}",
714
- alternative_values=['csv', 'json', 'pdf', 'markdown'] if self.export_priority else None
719
+ alternative_values=["csv", "json", "pdf", "markdown"] if self.export_priority else None,
715
720
  )
716
721
 
717
722
  return recommendations
@@ -737,70 +742,64 @@ class BusinessScenarioMatrix:
737
742
  """Build the complete business scenario parameter matrix."""
738
743
  return {
739
744
  # TIER 1 HIGH-VALUE SCENARIOS
740
- 'workspaces': ScenarioParameterMatrix(
745
+ "workspaces": ScenarioParameterMatrix(
741
746
  timerange_days=90,
742
- regional_scope='single',
743
- cost_focus='unblended',
744
- export_priority='pdf',
745
- validation_level='comprehensive',
746
- business_justification="WorkSpaces require quarterly analysis for usage pattern detection. Single-region focus optimizes analysis speed. Unblended costs show true resource utilization. PDF format ideal for management review."
747
+ regional_scope="single",
748
+ cost_focus="unblended",
749
+ export_priority="pdf",
750
+ validation_level="comprehensive",
751
+ business_justification="WorkSpaces require quarterly analysis for usage pattern detection. Single-region focus optimizes analysis speed. Unblended costs show true resource utilization. PDF format ideal for management review.",
747
752
  ),
748
-
749
- 'nat-gateway': ScenarioParameterMatrix(
753
+ "nat-gateway": ScenarioParameterMatrix(
750
754
  timerange_days=30,
751
- regional_scope='multi',
752
- cost_focus='amortized',
753
- export_priority='json',
754
- validation_level='enhanced',
755
- business_justification="NAT Gateways require monthly analysis for traffic optimization. Multi-region analysis essential for comprehensive network cost optimization. Amortized costs account for data transfer pricing. JSON format enables automation integration."
755
+ regional_scope="multi",
756
+ cost_focus="amortized",
757
+ export_priority="json",
758
+ validation_level="enhanced",
759
+ business_justification="NAT Gateways require monthly analysis for traffic optimization. Multi-region analysis essential for comprehensive network cost optimization. Amortized costs account for data transfer pricing. JSON format enables automation integration.",
756
760
  ),
757
-
758
- 'rds-snapshots': ScenarioParameterMatrix(
761
+ "rds-snapshots": ScenarioParameterMatrix(
759
762
  timerange_days=90,
760
- regional_scope='multi',
761
- cost_focus='dual-metrics',
762
- export_priority='csv',
763
- validation_level='comprehensive',
764
- business_justification="RDS snapshots require quarterly analysis for retention policy optimization. Multi-region scope captures all backup strategies. Dual metrics provide complete cost visibility. CSV format enables spreadsheet analysis."
763
+ regional_scope="multi",
764
+ cost_focus="dual-metrics",
765
+ export_priority="csv",
766
+ validation_level="comprehensive",
767
+ business_justification="RDS snapshots require quarterly analysis for retention policy optimization. Multi-region scope captures all backup strategies. Dual metrics provide complete cost visibility. CSV format enables spreadsheet analysis.",
765
768
  ),
766
-
767
769
  # TIER 2 STRATEGIC SCENARIOS
768
- 'ebs-optimization': ScenarioParameterMatrix(
770
+ "ebs-optimization": ScenarioParameterMatrix(
769
771
  timerange_days=180,
770
- regional_scope='multi',
771
- cost_focus='dual-metrics',
772
- export_priority='pdf',
773
- validation_level='comprehensive',
774
- business_justification="EBS optimization requires extended analysis to identify usage patterns. Multi-region scope captures all storage. Dual metrics show both immediate and amortized costs. PDF format suitable for capacity planning presentations."
772
+ regional_scope="multi",
773
+ cost_focus="dual-metrics",
774
+ export_priority="pdf",
775
+ validation_level="comprehensive",
776
+ business_justification="EBS optimization requires extended analysis to identify usage patterns. Multi-region scope captures all storage. Dual metrics show both immediate and amortized costs. PDF format suitable for capacity planning presentations.",
775
777
  ),
776
-
777
- 'vpc-cleanup': ScenarioParameterMatrix(
778
+ "vpc-cleanup": ScenarioParameterMatrix(
778
779
  timerange_days=30,
779
- regional_scope='multi',
780
- cost_focus='unblended',
781
- export_priority='csv',
782
- validation_level='enhanced',
783
- business_justification="VPC cleanup requires recent data for active resource identification. Multi-region analysis captures all network resources. Unblended costs show direct infrastructure impact. CSV enables detailed resource tracking."
780
+ regional_scope="multi",
781
+ cost_focus="unblended",
782
+ export_priority="csv",
783
+ validation_level="enhanced",
784
+ business_justification="VPC cleanup requires recent data for active resource identification. Multi-region analysis captures all network resources. Unblended costs show direct infrastructure impact. CSV enables detailed resource tracking.",
784
785
  ),
785
-
786
- 'elastic-ip': ScenarioParameterMatrix(
786
+ "elastic-ip": ScenarioParameterMatrix(
787
787
  timerange_days=7,
788
- regional_scope='multi',
789
- cost_focus='unblended',
790
- export_priority='json',
791
- validation_level='basic',
792
- business_justification="Elastic IP analysis requires recent data for attachment status. Multi-region scope captures all IP allocations. Unblended costs show direct charges. JSON format enables automated cleanup workflows."
788
+ regional_scope="multi",
789
+ cost_focus="unblended",
790
+ export_priority="json",
791
+ validation_level="basic",
792
+ business_justification="Elastic IP analysis requires recent data for attachment status. Multi-region scope captures all IP allocations. Unblended costs show direct charges. JSON format enables automated cleanup workflows.",
793
793
  ),
794
-
795
794
  # TIER 3 FRAMEWORK SCENARIOS
796
- 'backup-investigation': ScenarioParameterMatrix(
795
+ "backup-investigation": ScenarioParameterMatrix(
797
796
  timerange_days=None, # Framework-based
798
- regional_scope='multi',
799
- cost_focus='amortized',
800
- export_priority='markdown',
801
- validation_level='basic',
802
- business_justification="Backup investigation uses framework-based timerange analysis. Multi-region scope for comprehensive backup strategy. Amortized costs for long-term planning. Markdown format for documentation and reporting."
803
- )
797
+ regional_scope="multi",
798
+ cost_focus="amortized",
799
+ export_priority="markdown",
800
+ validation_level="basic",
801
+ business_justification="Backup investigation uses framework-based timerange analysis. Multi-region scope for comprehensive backup strategy. Amortized costs for long-term planning. Markdown format for documentation and reporting.",
802
+ ),
804
803
  }
805
804
 
806
805
  def _extend_matrix_with_discovered_scenarios(self) -> None:
@@ -833,59 +832,60 @@ class BusinessScenarioMatrix:
833
832
  type_defaults = {
834
833
  BusinessCaseType.COST_OPTIMIZATION: ScenarioParameterMatrix(
835
834
  timerange_days=30,
836
- regional_scope='multi',
837
- cost_focus='dual-metrics',
838
- export_priority='pdf',
839
- validation_level='enhanced',
840
- business_justification=f"Cost optimization requires comprehensive analysis with dual cost perspectives for {scenario.display_name}"
835
+ regional_scope="multi",
836
+ cost_focus="dual-metrics",
837
+ export_priority="pdf",
838
+ validation_level="enhanced",
839
+ business_justification=f"Cost optimization requires comprehensive analysis with dual cost perspectives for {scenario.display_name}",
841
840
  ),
842
841
  BusinessCaseType.RESOURCE_CLEANUP: ScenarioParameterMatrix(
843
842
  timerange_days=7,
844
- regional_scope='multi',
845
- cost_focus='unblended',
846
- export_priority='csv',
847
- validation_level='basic',
848
- business_justification=f"Resource cleanup requires recent data for active resource identification in {scenario.display_name}"
843
+ regional_scope="multi",
844
+ cost_focus="unblended",
845
+ export_priority="csv",
846
+ validation_level="basic",
847
+ business_justification=f"Resource cleanup requires recent data for active resource identification in {scenario.display_name}",
849
848
  ),
850
849
  BusinessCaseType.COMPLIANCE_FRAMEWORK: ScenarioParameterMatrix(
851
850
  timerange_days=90,
852
- regional_scope='global',
853
- cost_focus='amortized',
854
- export_priority='pdf',
855
- validation_level='comprehensive',
856
- business_justification=f"Compliance frameworks require extended analysis with comprehensive reporting for {scenario.display_name}"
851
+ regional_scope="global",
852
+ cost_focus="amortized",
853
+ export_priority="pdf",
854
+ validation_level="comprehensive",
855
+ business_justification=f"Compliance frameworks require extended analysis with comprehensive reporting for {scenario.display_name}",
857
856
  ),
858
857
  BusinessCaseType.SECURITY_ENHANCEMENT: ScenarioParameterMatrix(
859
858
  timerange_days=30,
860
- regional_scope='global',
861
- cost_focus='unblended',
862
- export_priority='markdown',
863
- validation_level='comprehensive',
864
- business_justification=f"Security enhancements require thorough analysis with documentation focus for {scenario.display_name}"
859
+ regional_scope="global",
860
+ cost_focus="unblended",
861
+ export_priority="markdown",
862
+ validation_level="comprehensive",
863
+ business_justification=f"Security enhancements require thorough analysis with documentation focus for {scenario.display_name}",
865
864
  ),
866
865
  BusinessCaseType.AUTOMATION_DEPLOYMENT: ScenarioParameterMatrix(
867
866
  timerange_days=60,
868
- regional_scope='multi',
869
- cost_focus='amortized',
870
- export_priority='json',
871
- validation_level='enhanced',
872
- business_justification=f"Automation deployment requires extended analysis with machine-readable output for {scenario.display_name}"
873
- )
867
+ regional_scope="multi",
868
+ cost_focus="amortized",
869
+ export_priority="json",
870
+ validation_level="enhanced",
871
+ business_justification=f"Automation deployment requires extended analysis with machine-readable output for {scenario.display_name}",
872
+ ),
874
873
  }
875
874
 
876
875
  # Use type-based defaults or fallback to generic cost optimization pattern
877
- default_matrix = type_defaults.get(scenario.business_case_type,
878
- type_defaults[BusinessCaseType.COST_OPTIMIZATION])
876
+ default_matrix = type_defaults.get(
877
+ scenario.business_case_type, type_defaults[BusinessCaseType.COST_OPTIMIZATION]
878
+ )
879
879
 
880
880
  # Override with scenario-specific intelligence where available
881
- if scenario.risk_level.lower() == 'high':
881
+ if scenario.risk_level.lower() == "high":
882
882
  # High risk scenarios need more comprehensive validation
883
- default_matrix.validation_level = 'comprehensive'
883
+ default_matrix.validation_level = "comprehensive"
884
884
  default_matrix.timerange_days = max(default_matrix.timerange_days or 30, 90)
885
885
 
886
- if scenario.risk_level.lower() == 'low':
886
+ if scenario.risk_level.lower() == "low":
887
887
  # Low risk scenarios can use basic validation
888
- default_matrix.validation_level = 'basic'
888
+ default_matrix.validation_level = "basic"
889
889
  default_matrix.timerange_days = min(default_matrix.timerange_days or 30, 14)
890
890
 
891
891
  return default_matrix
@@ -932,7 +932,7 @@ class BusinessScenarioMatrix:
932
932
  help_lines.append(f" → {param.business_justification}")
933
933
 
934
934
  if param.alternative_values:
935
- alternatives = ', '.join(str(v) for v in param.alternative_values)
935
+ alternatives = ", ".join(str(v) for v in param.alternative_values)
936
936
  help_lines.append(f" Alternatives: {alternatives}")
937
937
  help_lines.append("")
938
938
 
@@ -951,25 +951,29 @@ class BusinessScenarioMatrix:
951
951
  suggestions = {}
952
952
 
953
953
  # Check timerange optimization
954
- if 'timerange' in recommendations:
955
- optimal_timerange = recommendations['timerange'].optimal_value
956
- provided_timerange = provided_params.get('time_range')
954
+ if "timerange" in recommendations:
955
+ optimal_timerange = recommendations["timerange"].optimal_value
956
+ provided_timerange = provided_params.get("time_range")
957
957
 
958
958
  if provided_timerange and provided_timerange != optimal_timerange:
959
- suggestions['timerange'] = f"Consider --time-range {optimal_timerange} for optimal {scenario_key} analysis (current: {provided_timerange})"
959
+ suggestions["timerange"] = (
960
+ f"Consider --time-range {optimal_timerange} for optimal {scenario_key} analysis (current: {provided_timerange})"
961
+ )
960
962
 
961
963
  # Check export format optimization
962
- if 'export_format' in recommendations:
963
- optimal_export = recommendations['export_format'].name.replace('--', '')
964
- export_formats = provided_params.get('export_formats', [])
964
+ if "export_format" in recommendations:
965
+ optimal_export = recommendations["export_format"].name.replace("--", "")
966
+ export_formats = provided_params.get("export_formats", [])
965
967
 
966
968
  if export_formats and optimal_export not in export_formats:
967
- suggestions['export_format'] = f"Consider {optimal_export} export format for {scenario_key} analysis (optimal for business case)"
969
+ suggestions["export_format"] = (
970
+ f"Consider {optimal_export} export format for {scenario_key} analysis (optimal for business case)"
971
+ )
968
972
 
969
973
  # Check cost focus optimization
970
- if 'cost_focus' in recommendations:
971
- optimal_focus = recommendations['cost_focus'].name.replace('--', '')
972
- cost_focus_params = ['unblended', 'amortized', 'dual_metrics']
974
+ if "cost_focus" in recommendations:
975
+ optimal_focus = recommendations["cost_focus"].name.replace("--", "")
976
+ cost_focus_params = ["unblended", "amortized", "dual_metrics"]
973
977
  provided_focus = None
974
978
 
975
979
  for focus_type in cost_focus_params:
@@ -978,7 +982,7 @@ class BusinessScenarioMatrix:
978
982
  break
979
983
 
980
984
  if not provided_focus:
981
- suggestions['cost_focus'] = f"Consider {optimal_focus} cost perspective for {scenario_key} analysis"
985
+ suggestions["cost_focus"] = f"Consider {optimal_focus} cost perspective for {scenario_key} analysis"
982
986
 
983
987
  return suggestions
984
988
 
@@ -1010,9 +1014,10 @@ class BusinessScenarioMatrix:
1010
1014
  # Global scenario matrix instance
1011
1015
  _scenario_matrix = None
1012
1016
 
1017
+
1013
1018
  def get_business_scenario_matrix() -> BusinessScenarioMatrix:
1014
1019
  """Get global business scenario matrix instance."""
1015
1020
  global _scenario_matrix
1016
1021
  if _scenario_matrix is None:
1017
1022
  _scenario_matrix = BusinessScenarioMatrix()
1018
- return _scenario_matrix
1023
+ return _scenario_matrix