aiecs 1.1.0__py3-none-any.whl → 1.2.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.

Potentially problematic release.


This version of aiecs might be problematic. Click here for more details.

Files changed (81) hide show
  1. aiecs/__init__.py +1 -1
  2. aiecs/aiecs_client.py +1 -1
  3. aiecs/config/config.py +38 -0
  4. aiecs/domain/__init__.py +95 -0
  5. aiecs/domain/community/__init__.py +159 -0
  6. aiecs/domain/community/agent_adapter.py +516 -0
  7. aiecs/domain/community/analytics.py +465 -0
  8. aiecs/domain/community/collaborative_workflow.py +99 -7
  9. aiecs/domain/community/communication_hub.py +649 -0
  10. aiecs/domain/community/community_builder.py +322 -0
  11. aiecs/domain/community/community_integration.py +365 -12
  12. aiecs/domain/community/community_manager.py +481 -5
  13. aiecs/domain/community/decision_engine.py +459 -13
  14. aiecs/domain/community/exceptions.py +238 -0
  15. aiecs/domain/community/models/__init__.py +36 -0
  16. aiecs/domain/community/resource_manager.py +1 -1
  17. aiecs/domain/community/shared_context_manager.py +621 -0
  18. aiecs/domain/context/context_engine.py +37 -33
  19. aiecs/infrastructure/monitoring/__init__.py +22 -0
  20. aiecs/infrastructure/monitoring/global_metrics_manager.py +207 -0
  21. aiecs/infrastructure/persistence/file_storage.py +41 -28
  22. aiecs/llm/__init__.py +44 -7
  23. aiecs/llm/callbacks/__init__.py +12 -0
  24. aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +1 -1
  25. aiecs/llm/client_factory.py +23 -6
  26. aiecs/llm/clients/__init__.py +35 -0
  27. aiecs/llm/{base_client.py → clients/base_client.py} +73 -1
  28. aiecs/llm/{googleai_client.py → clients/googleai_client.py} +19 -15
  29. aiecs/llm/{openai_client.py → clients/openai_client.py} +9 -14
  30. aiecs/llm/{vertex_client.py → clients/vertex_client.py} +15 -15
  31. aiecs/llm/{xai_client.py → clients/xai_client.py} +36 -50
  32. aiecs/llm/config/__init__.py +54 -0
  33. aiecs/llm/config/config_loader.py +275 -0
  34. aiecs/llm/config/config_validator.py +237 -0
  35. aiecs/llm/config/model_config.py +132 -0
  36. aiecs/llm/utils/__init__.py +11 -0
  37. aiecs/llm/utils/validate_config.py +91 -0
  38. aiecs/main.py +32 -2
  39. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  40. aiecs/scripts/aid/__init__.py +15 -0
  41. aiecs/scripts/aid/version_manager.py +224 -0
  42. aiecs/scripts/dependance_check/download_nlp_data.py +1 -0
  43. aiecs/tools/__init__.py +23 -23
  44. aiecs/tools/docs/__init__.py +5 -2
  45. aiecs/tools/docs/ai_document_orchestrator.py +39 -26
  46. aiecs/tools/docs/ai_document_writer_orchestrator.py +61 -38
  47. aiecs/tools/docs/content_insertion_tool.py +48 -28
  48. aiecs/tools/docs/document_creator_tool.py +47 -29
  49. aiecs/tools/docs/document_layout_tool.py +35 -20
  50. aiecs/tools/docs/document_parser_tool.py +56 -36
  51. aiecs/tools/docs/document_writer_tool.py +115 -62
  52. aiecs/tools/schema_generator.py +56 -56
  53. aiecs/tools/statistics/__init__.py +82 -0
  54. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +581 -0
  55. aiecs/tools/statistics/ai_insight_generator_tool.py +473 -0
  56. aiecs/tools/statistics/ai_report_orchestrator_tool.py +629 -0
  57. aiecs/tools/statistics/data_loader_tool.py +518 -0
  58. aiecs/tools/statistics/data_profiler_tool.py +599 -0
  59. aiecs/tools/statistics/data_transformer_tool.py +531 -0
  60. aiecs/tools/statistics/data_visualizer_tool.py +460 -0
  61. aiecs/tools/statistics/model_trainer_tool.py +470 -0
  62. aiecs/tools/statistics/statistical_analyzer_tool.py +426 -0
  63. aiecs/tools/task_tools/chart_tool.py +2 -1
  64. aiecs/tools/task_tools/image_tool.py +43 -43
  65. aiecs/tools/task_tools/office_tool.py +39 -36
  66. aiecs/tools/task_tools/pandas_tool.py +37 -33
  67. aiecs/tools/task_tools/report_tool.py +67 -56
  68. aiecs/tools/task_tools/research_tool.py +32 -31
  69. aiecs/tools/task_tools/scraper_tool.py +53 -46
  70. aiecs/tools/task_tools/search_tool.py +1123 -0
  71. aiecs/tools/task_tools/stats_tool.py +20 -15
  72. aiecs/tools/tool_executor/__init__.py +2 -2
  73. aiecs/tools/tool_executor/tool_executor.py +3 -3
  74. {aiecs-1.1.0.dist-info → aiecs-1.2.1.dist-info}/METADATA +5 -1
  75. aiecs-1.2.1.dist-info/RECORD +144 -0
  76. {aiecs-1.1.0.dist-info → aiecs-1.2.1.dist-info}/entry_points.txt +1 -0
  77. aiecs/tools/task_tools/search_api.py +0 -7
  78. aiecs-1.1.0.dist-info/RECORD +0 -114
  79. {aiecs-1.1.0.dist-info → aiecs-1.2.1.dist-info}/WHEEL +0 -0
  80. {aiecs-1.1.0.dist-info → aiecs-1.2.1.dist-info}/licenses/LICENSE +0 -0
  81. {aiecs-1.1.0.dist-info → aiecs-1.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,629 @@
1
+ """
2
+ AI Report Orchestrator Tool - AI-powered comprehensive report generation
3
+
4
+ This tool provides advanced report generation with:
5
+ - Automated analysis report creation
6
+ - Multiple report types and formats
7
+ - Integration with analysis results
8
+ - Visualization embedding
9
+ - Export to multiple formats
10
+ """
11
+
12
+ import os
13
+ import logging
14
+ import tempfile
15
+ from typing import Dict, Any, List, Optional
16
+ from enum import Enum
17
+ from datetime import datetime
18
+
19
+ from pydantic import BaseModel, Field, ValidationError, ConfigDict
20
+
21
+ from aiecs.tools.base_tool import BaseTool
22
+ from aiecs.tools import register_tool
23
+
24
+
25
+ class ReportType(str, Enum):
26
+ """Types of reports"""
27
+ EXECUTIVE_SUMMARY = "executive_summary"
28
+ TECHNICAL_REPORT = "technical_report"
29
+ BUSINESS_REPORT = "business_report"
30
+ RESEARCH_PAPER = "research_paper"
31
+ DATA_QUALITY_REPORT = "data_quality_report"
32
+
33
+
34
+ class ReportFormat(str, Enum):
35
+ """Report output formats"""
36
+ MARKDOWN = "markdown"
37
+ HTML = "html"
38
+ PDF = "pdf"
39
+ WORD = "word"
40
+ JSON = "json"
41
+
42
+
43
+
44
+
45
+ class ReportOrchestratorError(Exception):
46
+ """Base exception for Report Orchestrator errors"""
47
+ pass
48
+
49
+
50
+ class ReportGenerationError(ReportOrchestratorError):
51
+ """Raised when report generation fails"""
52
+ pass
53
+
54
+
55
+ @register_tool('ai_report_orchestrator')
56
+ class AIReportOrchestratorTool(BaseTool):
57
+ """
58
+ AI-powered analysis report generator that can:
59
+ 1. Generate comprehensive analysis reports
60
+ 2. Customize report structure and style
61
+ 3. Include visualizations and tables
62
+ 4. Export to multiple formats
63
+ 5. Integrate analysis results and insights
64
+
65
+ Integrates with report_tool for document generation.
66
+ """
67
+
68
+ # Configuration schema
69
+ class Config(BaseModel):
70
+ """Configuration for the AI report orchestrator tool"""
71
+ model_config = ConfigDict(env_prefix="AI_REPORT_ORCHESTRATOR_")
72
+
73
+ default_report_type: str = Field(
74
+ default="business_report",
75
+ description="Default report type to generate"
76
+ )
77
+ default_format: str = Field(
78
+ default="markdown",
79
+ description="Default report output format"
80
+ )
81
+ output_directory: str = Field(
82
+ default=tempfile.gettempdir(),
83
+ description="Directory for report output files"
84
+ )
85
+ include_code: bool = Field(
86
+ default=False,
87
+ description="Whether to include code snippets in reports"
88
+ )
89
+ include_visualizations: bool = Field(
90
+ default=True,
91
+ description="Whether to include visualizations in reports"
92
+ )
93
+ max_insights_per_report: int = Field(
94
+ default=20,
95
+ description="Maximum number of insights to include per report"
96
+ )
97
+
98
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
99
+ """Initialize AI Report Orchestrator Tool"""
100
+ super().__init__(config)
101
+
102
+ # Parse configuration
103
+ self.config = self.Config(**(config or {}))
104
+
105
+ self.logger = logging.getLogger(__name__)
106
+ if not self.logger.handlers:
107
+ handler = logging.StreamHandler()
108
+ handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
109
+ self.logger.addHandler(handler)
110
+ self.logger.setLevel(logging.INFO)
111
+
112
+ self._init_external_tools()
113
+
114
+ # Ensure output directory exists
115
+ os.makedirs(self.config.output_directory, exist_ok=True)
116
+
117
+ def _init_external_tools(self):
118
+ """Initialize external task tools"""
119
+ self.external_tools = {}
120
+
121
+ # Initialize ReportTool for document generation
122
+ try:
123
+ from aiecs.tools.task_tools.report_tool import ReportTool
124
+ self.external_tools['report'] = ReportTool()
125
+ self.logger.info("ReportTool initialized successfully")
126
+ except ImportError:
127
+ self.logger.warning("ReportTool not available")
128
+ self.external_tools['report'] = None
129
+
130
+ # Schema definitions
131
+ class GenerateReportSchema(BaseModel):
132
+ """Schema for generate_report operation"""
133
+ analysis_results: Dict[str, Any] = Field(description="Analysis results to include")
134
+ insights: Optional[Dict[str, Any]] = Field(default=None, description="Generated insights")
135
+ report_type: ReportType = Field(default=ReportType.BUSINESS_REPORT, description="Type of report")
136
+ output_format: ReportFormat = Field(default=ReportFormat.MARKDOWN, description="Output format")
137
+ title: Optional[str] = Field(default=None, description="Report title")
138
+ include_code: bool = Field(default=False, description="Include code snippets")
139
+
140
+ class FormatReportSchema(BaseModel):
141
+ """Schema for format_report operation"""
142
+ report_content: str = Field(description="Report content to format")
143
+ output_format: ReportFormat = Field(description="Desired output format")
144
+ output_path: Optional[str] = Field(default=None, description="Output file path")
145
+
146
+ class ExportReportSchema(BaseModel):
147
+ """Schema for export_report operation"""
148
+ report_content: str = Field(description="Report content")
149
+ output_format: ReportFormat = Field(description="Export format")
150
+ output_path: str = Field(description="Output file path")
151
+
152
+ def generate_report(
153
+ self,
154
+ analysis_results: Dict[str, Any],
155
+ insights: Optional[Dict[str, Any]] = None,
156
+ report_type: ReportType = ReportType.BUSINESS_REPORT,
157
+ output_format: ReportFormat = ReportFormat.MARKDOWN,
158
+ title: Optional[str] = None,
159
+ include_code: bool = False
160
+ ) -> Dict[str, Any]:
161
+ """
162
+ Generate comprehensive analysis report.
163
+
164
+ Args:
165
+ analysis_results: Results from data analysis
166
+ insights: Generated insights to include
167
+ report_type: Type of report to generate
168
+ output_format: Output format
169
+ title: Report title
170
+ include_code: Whether to include code snippets
171
+
172
+ Returns:
173
+ Dict containing:
174
+ - report_content: Generated report content
175
+ - sections: Report sections
176
+ - export_path: Path to exported report
177
+ - metadata: Report metadata
178
+ """
179
+ try:
180
+ self.logger.info(f"Generating {report_type.value} report in {output_format.value} format")
181
+
182
+ # Generate report title
183
+ if title is None:
184
+ title = self._generate_title(report_type, analysis_results)
185
+
186
+ # Build report sections
187
+ sections = self._build_report_sections(
188
+ analysis_results,
189
+ insights,
190
+ report_type,
191
+ include_code
192
+ )
193
+
194
+ # Compile report content
195
+ report_content = self._compile_report(title, sections, report_type)
196
+
197
+ # Format report for output format
198
+ formatted_content = self._format_content(report_content, output_format)
199
+
200
+ # Export report
201
+ export_path = self._export_report(formatted_content, output_format, title)
202
+
203
+ # Generate metadata
204
+ metadata = {
205
+ 'generated_at': datetime.now().isoformat(),
206
+ 'report_type': report_type.value,
207
+ 'output_format': output_format.value,
208
+ 'sections_count': len(sections),
209
+ 'word_count': len(report_content.split())
210
+ }
211
+
212
+ return {
213
+ 'report_content': report_content,
214
+ 'sections': {s['title']: s['content'] for s in sections},
215
+ 'export_path': export_path,
216
+ 'metadata': metadata,
217
+ 'title': title
218
+ }
219
+
220
+ except Exception as e:
221
+ self.logger.error(f"Error generating report: {e}")
222
+ raise ReportGenerationError(f"Report generation failed: {e}")
223
+
224
+ def format_report(
225
+ self,
226
+ report_content: str,
227
+ output_format: ReportFormat,
228
+ output_path: Optional[str] = None
229
+ ) -> Dict[str, Any]:
230
+ """
231
+ Format report content to specified format.
232
+
233
+ Args:
234
+ report_content: Report content to format
235
+ output_format: Desired output format
236
+ output_path: Optional output file path
237
+
238
+ Returns:
239
+ Dict containing formatted report info
240
+ """
241
+ try:
242
+ formatted_content = self._format_content(report_content, output_format)
243
+
244
+ if output_path:
245
+ with open(output_path, 'w', encoding='utf-8') as f:
246
+ f.write(formatted_content)
247
+ export_path = output_path
248
+ else:
249
+ export_path = self._export_report(formatted_content, output_format, "report")
250
+
251
+ return {
252
+ 'formatted_content': formatted_content,
253
+ 'output_format': output_format.value,
254
+ 'export_path': export_path
255
+ }
256
+
257
+ except Exception as e:
258
+ self.logger.error(f"Error formatting report: {e}")
259
+ raise ReportGenerationError(f"Report formatting failed: {e}")
260
+
261
+ def export_report(
262
+ self,
263
+ report_content: str,
264
+ output_format: ReportFormat,
265
+ output_path: str
266
+ ) -> Dict[str, Any]:
267
+ """
268
+ Export report to file.
269
+
270
+ Args:
271
+ report_content: Report content
272
+ output_format: Export format
273
+ output_path: Output file path
274
+
275
+ Returns:
276
+ Dict containing export information
277
+ """
278
+ try:
279
+ formatted_content = self._format_content(report_content, output_format)
280
+
281
+ with open(output_path, 'w', encoding='utf-8') as f:
282
+ f.write(formatted_content)
283
+
284
+ file_size = os.path.getsize(output_path)
285
+
286
+ return {
287
+ 'export_path': output_path,
288
+ 'format': output_format.value,
289
+ 'file_size_bytes': file_size,
290
+ 'success': True
291
+ }
292
+
293
+ except Exception as e:
294
+ self.logger.error(f"Error exporting report: {e}")
295
+ raise ReportGenerationError(f"Report export failed: {e}")
296
+
297
+ # Internal report generation methods
298
+
299
+ def _generate_title(self, report_type: ReportType, analysis_results: Dict[str, Any]) -> str:
300
+ """Generate appropriate report title"""
301
+ if report_type == ReportType.EXECUTIVE_SUMMARY:
302
+ return "Executive Summary: Data Analysis Report"
303
+ elif report_type == ReportType.TECHNICAL_REPORT:
304
+ return "Technical Data Analysis Report"
305
+ elif report_type == ReportType.BUSINESS_REPORT:
306
+ return "Business Intelligence Report"
307
+ elif report_type == ReportType.RESEARCH_PAPER:
308
+ return "Data Analysis Research Paper"
309
+ elif report_type == ReportType.DATA_QUALITY_REPORT:
310
+ return "Data Quality Assessment Report"
311
+ else:
312
+ return "Data Analysis Report"
313
+
314
+ def _build_report_sections(
315
+ self,
316
+ analysis_results: Dict[str, Any],
317
+ insights: Optional[Dict[str, Any]],
318
+ report_type: ReportType,
319
+ include_code: bool
320
+ ) -> List[Dict[str, str]]:
321
+ """Build report sections based on type"""
322
+ sections = []
323
+
324
+ # Executive Summary (for all report types)
325
+ sections.append({
326
+ 'title': 'Executive Summary',
327
+ 'content': self._generate_executive_summary(analysis_results, insights)
328
+ })
329
+
330
+ # Methodology section (for technical and research reports)
331
+ if report_type in [ReportType.TECHNICAL_REPORT, ReportType.RESEARCH_PAPER]:
332
+ sections.append({
333
+ 'title': 'Methodology',
334
+ 'content': self._generate_methodology_section(analysis_results)
335
+ })
336
+
337
+ # Data Overview
338
+ sections.append({
339
+ 'title': 'Data Overview',
340
+ 'content': self._generate_data_overview(analysis_results)
341
+ })
342
+
343
+ # Findings section
344
+ sections.append({
345
+ 'title': 'Key Findings',
346
+ 'content': self._generate_findings_section(analysis_results, insights)
347
+ })
348
+
349
+ # Statistical Analysis (for technical reports)
350
+ if report_type == ReportType.TECHNICAL_REPORT and 'statistical_analysis' in analysis_results:
351
+ sections.append({
352
+ 'title': 'Statistical Analysis',
353
+ 'content': self._generate_statistics_section(analysis_results.get('statistical_analysis', {}))
354
+ })
355
+
356
+ # Insights section
357
+ if insights:
358
+ sections.append({
359
+ 'title': 'Insights and Patterns',
360
+ 'content': self._generate_insights_section(insights)
361
+ })
362
+
363
+ # Recommendations
364
+ sections.append({
365
+ 'title': 'Recommendations',
366
+ 'content': self._generate_recommendations_section(analysis_results, insights)
367
+ })
368
+
369
+ # Conclusion
370
+ sections.append({
371
+ 'title': 'Conclusion',
372
+ 'content': self._generate_conclusion(analysis_results, insights)
373
+ })
374
+
375
+ # Appendix (if code included)
376
+ if include_code:
377
+ sections.append({
378
+ 'title': 'Appendix: Technical Details',
379
+ 'content': self._generate_appendix(analysis_results)
380
+ })
381
+
382
+ return sections
383
+
384
+ def _generate_executive_summary(self, analysis_results: Dict[str, Any], insights: Optional[Dict[str, Any]]) -> str:
385
+ """Generate executive summary"""
386
+ lines = []
387
+
388
+ # Get data profile if available
389
+ data_profile = analysis_results.get('data_profile', {})
390
+ summary = data_profile.get('summary', {})
391
+
392
+ if summary:
393
+ lines.append(f"This report presents a comprehensive analysis of a dataset containing {summary.get('rows', 'N/A')} rows and {summary.get('columns', 'N/A')} columns.")
394
+
395
+ missing_pct = summary.get('missing_percentage', 0)
396
+ if missing_pct > 0:
397
+ lines.append(f"The dataset has {missing_pct:.2f}% missing values.")
398
+
399
+ # Add insight summary
400
+ if insights and 'summary' in insights:
401
+ lines.append(f"\n{insights['summary']}")
402
+
403
+ # Add key metrics
404
+ if insights and 'priority_insights' in insights:
405
+ top_insights = insights['priority_insights'][:3]
406
+ if top_insights:
407
+ lines.append("\nKey highlights:")
408
+ for i, insight in enumerate(top_insights, 1):
409
+ lines.append(f"{i}. {insight.get('title', 'Insight')}")
410
+
411
+ return "\n".join(lines) if lines else "Analysis completed successfully."
412
+
413
+ def _generate_methodology_section(self, analysis_results: Dict[str, Any]) -> str:
414
+ """Generate methodology section"""
415
+ lines = [
416
+ "The analysis was conducted using a systematic approach:",
417
+ "",
418
+ "1. **Data Loading**: Data was loaded and validated for quality",
419
+ "2. **Data Profiling**: Comprehensive statistical profiling was performed",
420
+ "3. **Data Transformation**: Necessary transformations and cleaning were applied",
421
+ "4. **Statistical Analysis**: Various statistical tests and analyses were conducted",
422
+ "5. **Insight Generation**: Patterns, trends, and anomalies were identified",
423
+ "6. **Visualization**: Key findings were visualized for clarity"
424
+ ]
425
+
426
+ return "\n".join(lines)
427
+
428
+ def _generate_data_overview(self, analysis_results: Dict[str, Any]) -> str:
429
+ """Generate data overview section"""
430
+ lines = []
431
+
432
+ data_profile = analysis_results.get('data_profile', {})
433
+ summary = data_profile.get('summary', {})
434
+
435
+ if summary:
436
+ lines.append("**Dataset Characteristics:**")
437
+ lines.append(f"- Total Records: {summary.get('rows', 'N/A')}")
438
+ lines.append(f"- Total Columns: {summary.get('columns', 'N/A')}")
439
+ lines.append(f"- Numeric Columns: {summary.get('numeric_columns', 'N/A')}")
440
+ lines.append(f"- Categorical Columns: {summary.get('categorical_columns', 'N/A')}")
441
+ lines.append(f"- Missing Values: {summary.get('missing_percentage', 0):.2f}%")
442
+ lines.append(f"- Duplicate Rows: {summary.get('duplicate_rows', 0)}")
443
+ else:
444
+ lines.append("Data overview not available.")
445
+
446
+ return "\n".join(lines)
447
+
448
+ def _generate_findings_section(self, analysis_results: Dict[str, Any], insights: Optional[Dict[str, Any]]) -> str:
449
+ """Generate findings section"""
450
+ lines = []
451
+
452
+ # Extract findings from analysis results
453
+ if 'findings' in analysis_results:
454
+ findings = analysis_results['findings']
455
+ for i, finding in enumerate(findings[:10], 1):
456
+ lines.append(f"{i}. **{finding.get('title', 'Finding')}**: {finding.get('description', 'No description')}")
457
+
458
+ # Add insights if available
459
+ if insights and 'insights' in insights:
460
+ insight_list = insights['insights'][:self.config.max_insights_per_report]
461
+ if insight_list and not lines:
462
+ lines.append("**Key Insights:**")
463
+ for insight in insight_list:
464
+ lines.append(f"- {insight.get('title', 'Insight')}: {insight.get('description', '')}")
465
+
466
+ return "\n".join(lines) if lines else "No significant findings to report."
467
+
468
+ def _generate_statistics_section(self, stats_results: Dict[str, Any]) -> str:
469
+ """Generate statistics section"""
470
+ lines = ["**Statistical Analysis Results:**", ""]
471
+
472
+ if 'correlation_matrix' in stats_results:
473
+ lines.append("Correlation analysis was performed to identify relationships between variables.")
474
+
475
+ if 'hypothesis_tests' in stats_results:
476
+ lines.append("Hypothesis testing was conducted to validate statistical significance.")
477
+
478
+ return "\n".join(lines)
479
+
480
+ def _generate_insights_section(self, insights: Dict[str, Any]) -> str:
481
+ """Generate insights section"""
482
+ lines = []
483
+
484
+ insight_list = insights.get('insights', [])
485
+ priority_insights = insights.get('priority_insights', [])
486
+
487
+ if priority_insights:
488
+ lines.append("**Priority Insights:**")
489
+ for i, insight in enumerate(priority_insights, 1):
490
+ title = insight.get('title', 'Insight')
491
+ description = insight.get('description', '')
492
+ confidence = insight.get('confidence', 0)
493
+ impact = insight.get('impact', 'medium')
494
+
495
+ lines.append(f"\n{i}. **{title}** (Confidence: {confidence:.0%}, Impact: {impact})")
496
+ lines.append(f" {description}")
497
+
498
+ if 'recommendation' in insight:
499
+ lines.append(f" *Recommendation: {insight['recommendation']}*")
500
+
501
+ return "\n".join(lines) if lines else "No insights generated."
502
+
503
+ def _generate_recommendations_section(self, analysis_results: Dict[str, Any], insights: Optional[Dict[str, Any]]) -> str:
504
+ """Generate recommendations section"""
505
+ lines = []
506
+
507
+ # Get recommendations from analysis results
508
+ if 'recommendations' in analysis_results:
509
+ recs = analysis_results['recommendations']
510
+ for i, rec in enumerate(recs[:10], 1):
511
+ action = rec.get('action', 'Action')
512
+ reason = rec.get('reason', '')
513
+ priority = rec.get('priority', 'medium')
514
+ lines.append(f"{i}. **{action}** (Priority: {priority})")
515
+ if reason:
516
+ lines.append(f" {reason}")
517
+
518
+ # Get recommendations from data profiler
519
+ data_profile = analysis_results.get('data_profile', {})
520
+ if 'recommendations' in data_profile:
521
+ if not lines:
522
+ lines.append("**Data Quality Recommendations:**")
523
+ for rec in data_profile['recommendations'][:5]:
524
+ lines.append(f"- {rec.get('action', 'Action')}: {rec.get('reason', '')}")
525
+
526
+ return "\n".join(lines) if lines else "No specific recommendations at this time."
527
+
528
+ def _generate_conclusion(self, analysis_results: Dict[str, Any], insights: Optional[Dict[str, Any]]) -> str:
529
+ """Generate conclusion"""
530
+ lines = []
531
+
532
+ lines.append("This comprehensive analysis has provided valuable insights into the dataset.")
533
+
534
+ if insights:
535
+ total_insights = insights.get('total_insights', 0)
536
+ lines.append(f"A total of {total_insights} insights were generated through systematic analysis.")
537
+
538
+ lines.append("\nThe findings and recommendations presented in this report should be carefully considered in the context of your specific business objectives and constraints.")
539
+
540
+ return "\n".join(lines)
541
+
542
+ def _generate_appendix(self, analysis_results: Dict[str, Any]) -> str:
543
+ """Generate appendix with technical details"""
544
+ lines = [
545
+ "**Technical Details:**",
546
+ "",
547
+ "This section contains technical information about the analysis process.",
548
+ "",
549
+ "Analysis was performed using the AIECS Data Analysis Orchestrator framework."
550
+ ]
551
+
552
+ return "\n".join(lines)
553
+
554
+ def _compile_report(self, title: str, sections: List[Dict[str, str]], report_type: ReportType) -> str:
555
+ """Compile report sections into final document"""
556
+ lines = [
557
+ f"# {title}",
558
+ "",
559
+ f"*Report Type: {report_type.value.replace('_', ' ').title()}*",
560
+ f"*Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*",
561
+ "",
562
+ "---",
563
+ ""
564
+ ]
565
+
566
+ for section in sections:
567
+ lines.append(f"## {section['title']}")
568
+ lines.append("")
569
+ lines.append(section['content'])
570
+ lines.append("")
571
+ lines.append("---")
572
+ lines.append("")
573
+
574
+ return "\n".join(lines)
575
+
576
+ def _format_content(self, content: str, output_format: ReportFormat) -> str:
577
+ """Format content for specified output format"""
578
+ if output_format == ReportFormat.MARKDOWN:
579
+ return content
580
+ elif output_format == ReportFormat.HTML:
581
+ return self._markdown_to_html(content)
582
+ elif output_format == ReportFormat.JSON:
583
+ return self._content_to_json(content)
584
+ else:
585
+ # For PDF and Word, return markdown (would need additional libraries for conversion)
586
+ self.logger.warning(f"Format {output_format.value} not fully implemented, returning markdown")
587
+ return content
588
+
589
+ def _markdown_to_html(self, markdown_content: str) -> str:
590
+ """Convert markdown to HTML (basic implementation)"""
591
+ html = markdown_content
592
+ # Basic conversions
593
+ html = html.replace('# ', '<h1>').replace('\n', '</h1>\n', 1)
594
+ html = html.replace('## ', '<h2>').replace('\n', '</h2>\n')
595
+ html = html.replace('**', '<strong>').replace('**', '</strong>')
596
+ html = html.replace('*', '<em>').replace('*', '</em>')
597
+ html = f"<html><body>{html}</body></html>"
598
+ return html
599
+
600
+ def _content_to_json(self, content: str) -> str:
601
+ """Convert content to JSON format"""
602
+ import json
603
+ return json.dumps({'content': content, 'format': 'markdown'}, indent=2)
604
+
605
+ def _export_report(self, content: str, output_format: ReportFormat, title: str) -> str:
606
+ """Export report to file"""
607
+ # Generate filename
608
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
609
+ safe_title = title.replace(' ', '_').replace(':', '').lower()
610
+
611
+ extension_map = {
612
+ ReportFormat.MARKDOWN: '.md',
613
+ ReportFormat.HTML: '.html',
614
+ ReportFormat.PDF: '.pdf',
615
+ ReportFormat.WORD: '.docx',
616
+ ReportFormat.JSON: '.json'
617
+ }
618
+
619
+ extension = extension_map.get(output_format, '.txt')
620
+ filename = f"{safe_title}_{timestamp}{extension}"
621
+ filepath = os.path.join(self.config.output_directory, filename)
622
+
623
+ # Write file
624
+ with open(filepath, 'w', encoding='utf-8') as f:
625
+ f.write(content)
626
+
627
+ self.logger.info(f"Report exported to: {filepath}")
628
+ return filepath
629
+