aiecs 1.0.0__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 (90) hide show
  1. aiecs/__init__.py +75 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +295 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +341 -0
  7. aiecs/config/__init__.py +15 -0
  8. aiecs/config/config.py +117 -0
  9. aiecs/config/registry.py +19 -0
  10. aiecs/core/__init__.py +46 -0
  11. aiecs/core/interface/__init__.py +34 -0
  12. aiecs/core/interface/execution_interface.py +150 -0
  13. aiecs/core/interface/storage_interface.py +214 -0
  14. aiecs/domain/__init__.py +20 -0
  15. aiecs/domain/context/__init__.py +28 -0
  16. aiecs/domain/context/content_engine.py +982 -0
  17. aiecs/domain/context/conversation_models.py +306 -0
  18. aiecs/domain/execution/__init__.py +12 -0
  19. aiecs/domain/execution/model.py +49 -0
  20. aiecs/domain/task/__init__.py +13 -0
  21. aiecs/domain/task/dsl_processor.py +460 -0
  22. aiecs/domain/task/model.py +50 -0
  23. aiecs/domain/task/task_context.py +257 -0
  24. aiecs/infrastructure/__init__.py +26 -0
  25. aiecs/infrastructure/messaging/__init__.py +13 -0
  26. aiecs/infrastructure/messaging/celery_task_manager.py +341 -0
  27. aiecs/infrastructure/messaging/websocket_manager.py +289 -0
  28. aiecs/infrastructure/monitoring/__init__.py +12 -0
  29. aiecs/infrastructure/monitoring/executor_metrics.py +138 -0
  30. aiecs/infrastructure/monitoring/structured_logger.py +50 -0
  31. aiecs/infrastructure/monitoring/tracing_manager.py +376 -0
  32. aiecs/infrastructure/persistence/__init__.py +12 -0
  33. aiecs/infrastructure/persistence/database_manager.py +286 -0
  34. aiecs/infrastructure/persistence/file_storage.py +671 -0
  35. aiecs/infrastructure/persistence/redis_client.py +162 -0
  36. aiecs/llm/__init__.py +54 -0
  37. aiecs/llm/base_client.py +99 -0
  38. aiecs/llm/client_factory.py +339 -0
  39. aiecs/llm/custom_callbacks.py +228 -0
  40. aiecs/llm/openai_client.py +125 -0
  41. aiecs/llm/vertex_client.py +186 -0
  42. aiecs/llm/xai_client.py +184 -0
  43. aiecs/main.py +351 -0
  44. aiecs/scripts/DEPENDENCY_SYSTEM_SUMMARY.md +241 -0
  45. aiecs/scripts/README_DEPENDENCY_CHECKER.md +309 -0
  46. aiecs/scripts/README_WEASEL_PATCH.md +126 -0
  47. aiecs/scripts/__init__.py +3 -0
  48. aiecs/scripts/dependency_checker.py +825 -0
  49. aiecs/scripts/dependency_fixer.py +348 -0
  50. aiecs/scripts/download_nlp_data.py +348 -0
  51. aiecs/scripts/fix_weasel_validator.py +121 -0
  52. aiecs/scripts/fix_weasel_validator.sh +82 -0
  53. aiecs/scripts/patch_weasel_library.sh +188 -0
  54. aiecs/scripts/quick_dependency_check.py +269 -0
  55. aiecs/scripts/run_weasel_patch.sh +41 -0
  56. aiecs/scripts/setup_nlp_data.sh +217 -0
  57. aiecs/tasks/__init__.py +2 -0
  58. aiecs/tasks/worker.py +111 -0
  59. aiecs/tools/__init__.py +196 -0
  60. aiecs/tools/base_tool.py +202 -0
  61. aiecs/tools/langchain_adapter.py +361 -0
  62. aiecs/tools/task_tools/__init__.py +82 -0
  63. aiecs/tools/task_tools/chart_tool.py +704 -0
  64. aiecs/tools/task_tools/classfire_tool.py +901 -0
  65. aiecs/tools/task_tools/image_tool.py +397 -0
  66. aiecs/tools/task_tools/office_tool.py +600 -0
  67. aiecs/tools/task_tools/pandas_tool.py +565 -0
  68. aiecs/tools/task_tools/report_tool.py +499 -0
  69. aiecs/tools/task_tools/research_tool.py +363 -0
  70. aiecs/tools/task_tools/scraper_tool.py +548 -0
  71. aiecs/tools/task_tools/search_api.py +7 -0
  72. aiecs/tools/task_tools/stats_tool.py +513 -0
  73. aiecs/tools/temp_file_manager.py +126 -0
  74. aiecs/tools/tool_executor/__init__.py +35 -0
  75. aiecs/tools/tool_executor/tool_executor.py +518 -0
  76. aiecs/utils/LLM_output_structor.py +409 -0
  77. aiecs/utils/__init__.py +23 -0
  78. aiecs/utils/base_callback.py +50 -0
  79. aiecs/utils/execution_utils.py +158 -0
  80. aiecs/utils/logging.py +1 -0
  81. aiecs/utils/prompt_loader.py +13 -0
  82. aiecs/utils/token_usage_repository.py +279 -0
  83. aiecs/ws/__init__.py +0 -0
  84. aiecs/ws/socket_server.py +41 -0
  85. aiecs-1.0.0.dist-info/METADATA +610 -0
  86. aiecs-1.0.0.dist-info/RECORD +90 -0
  87. aiecs-1.0.0.dist-info/WHEEL +5 -0
  88. aiecs-1.0.0.dist-info/entry_points.txt +7 -0
  89. aiecs-1.0.0.dist-info/licenses/LICENSE +225 -0
  90. aiecs-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,499 @@
1
+ """
2
+ Report Tool - A multi-format report generation tool supporting HTML, PDF, Excel, PowerPoint, Markdown, Word, and image-based reports.
3
+
4
+ This module provides a comprehensive report generation tool that can be used to create various types of reports
5
+ in different formats. It supports template-based rendering, data visualization, and batch processing.
6
+
7
+ Author: Your Organization
8
+ Version: 1.0.0
9
+ """
10
+ import os
11
+ import bleach
12
+ from typing import Dict, Any, List, Optional, Union, Tuple, Set
13
+ from jinja2 import FileSystemLoader, sandbox
14
+ # from weasyprint import HTML # TODO: Re-enable when deployment issues are resolved
15
+ import pandas as pd
16
+ from pptx import Presentation
17
+ from pptx.util import Pt
18
+ from docx import Document
19
+ from docx.shared import Pt as DocxPt, RGBColor
20
+ import markdown
21
+ import matplotlib.pyplot as plt
22
+ from pydantic import ValidationError, ConfigDict
23
+ from pydantic_settings import BaseSettings
24
+ import tempfile
25
+ import logging
26
+
27
+ from aiecs.tools.base_tool import BaseTool
28
+ from aiecs.tools import register_tool
29
+ from aiecs.tools.temp_file_manager import TempFileManager
30
+
31
+ # Configuration for ReportTool
32
+ class ReportSettings(BaseSettings):
33
+ """
34
+ Configuration for ReportTool.
35
+
36
+ Attributes:
37
+ templates_dir (str): Directory for Jinja2 templates.
38
+ default_output_dir (str): Default directory for output files.
39
+ allowed_extensions (List[str]): Allowed file extensions for outputs.
40
+ pdf_page_size (str): Default PDF page size.
41
+ default_font (str): Default font for documents.
42
+ default_font_size (int): Default font size in points.
43
+ allowed_html_tags (Set[str]): Allowed HTML tags for sanitization.
44
+ allowed_html_attributes (Dict[str, List[str]]): Allowed HTML attributes for sanitization.
45
+ temp_files_max_age (int): Maximum age of temporary files in seconds.
46
+ env_prefix (str): Environment variable prefix for settings.
47
+ """
48
+ templates_dir: str = os.getcwd()
49
+ default_output_dir: str = os.path.join(tempfile.gettempdir(), 'reports')
50
+ allowed_extensions: List[str] = ['.html', '.pdf', '.xlsx', '.pptx', '.docx', '.md', '.png']
51
+ pdf_page_size: str = 'A4'
52
+ default_font: str = 'Arial'
53
+ default_font_size: int = 12
54
+ allowed_html_tags: Set[str] = {
55
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'br', 'a', 'ul', 'ol', 'li',
56
+ 'strong', 'em', 'b', 'i', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
57
+ 'span', 'div', 'img', 'hr', 'code', 'pre'
58
+ }
59
+ allowed_html_attributes: Dict[str, List[str]] = {
60
+ 'a': ['href', 'title', 'target'],
61
+ 'img': ['src', 'alt', 'title', 'width', 'height'],
62
+ 'td': ['colspan', 'rowspan', 'align'],
63
+ 'th': ['colspan', 'rowspan', 'align'],
64
+ '*': ['class', 'id', 'style']
65
+ }
66
+ temp_files_max_age: int = 3600 # 1 hour in seconds
67
+ env_prefix: str = 'REPORT_TOOL_'
68
+
69
+ model_config = ConfigDict(env_prefix='REPORT_TOOL_')
70
+
71
+ # Exceptions
72
+ class ReportToolError(Exception):
73
+ """Base exception for ReportTool errors."""
74
+ pass
75
+
76
+ class FileOperationError(ReportToolError):
77
+ """Raised when file operations fail."""
78
+ pass
79
+
80
+ # Helper function for HTML sanitization
81
+ def sanitize_html(html_content: str, allowed_tags: Set[str], allowed_attributes: Dict[str, List[str]]) -> str:
82
+ """
83
+ Sanitize HTML content to prevent XSS attacks.
84
+
85
+ Args:
86
+ html_content (str): The HTML content to sanitize.
87
+ allowed_tags (Set[str]): Set of allowed HTML tags.
88
+ allowed_attributes (Dict[str, List[str]]): Dictionary of allowed attributes for each tag.
89
+
90
+ Returns:
91
+ str: Sanitized HTML content.
92
+ """
93
+ return bleach.clean(
94
+ html_content,
95
+ tags=allowed_tags,
96
+ attributes=allowed_attributes,
97
+ strip=True
98
+ )
99
+
100
+ # Type alias for dataset entries
101
+ DatasetType = Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]]
102
+
103
+ @register_tool('report')
104
+ class ReportTool(BaseTool):
105
+ """
106
+ Multi-format report generation tool supporting HTML, Excel, PowerPoint, Markdown, Word, and image-based reports.
107
+
108
+ NOTE: PDF generation is temporarily disabled due to weasyprint deployment complexity.
109
+
110
+ Operations:
111
+ - generate_html: Render HTML report using Jinja2.
112
+ - generate_pdf: Currently disabled - will be re-enabled in future release.
113
+ - generate_excel: Create Excel workbook with multiple sheets and styling.
114
+ - generate_pptx: Create PowerPoint presentation with customizable slides.
115
+ - generate_markdown: Render Markdown report using Jinja2.
116
+ - generate_word: Create Word document with customizable styles.
117
+ - generate_image: Generate charts (bar, line, pie) using Matplotlib.
118
+ - batch_generate: Generate multiple reports in parallel.
119
+
120
+ Inherits from BaseTool.
121
+ """
122
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
123
+ """
124
+ Initialize ReportTool with settings and resources.
125
+
126
+ Args:
127
+ config (Dict, optional): Configuration overrides for ReportSettings.
128
+
129
+ Raises:
130
+ ValueError: If config contains invalid settings.
131
+ """
132
+ super().__init__(config)
133
+ self.settings = ReportSettings()
134
+ if config:
135
+ try:
136
+ self.settings = self.settings.model_validate({**self.settings.model_dump(), **config})
137
+ except ValidationError as e:
138
+ raise ValueError(f"Invalid configuration: {e}")
139
+ self.logger = logging.getLogger(__name__)
140
+ if not self.logger.handlers:
141
+ handler = logging.StreamHandler()
142
+ handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
143
+ self.logger.addHandler(handler)
144
+ self.logger.setLevel(logging.INFO)
145
+ self._jinja_env = sandbox.SandboxedEnvironment(
146
+ loader=FileSystemLoader(self.settings.templates_dir),
147
+ autoescape=True
148
+ )
149
+ self._temp_manager = TempFileManager(self.settings.default_output_dir, self.settings.temp_files_max_age)
150
+
151
+ def generate_html(self, template_path: Optional[str], template_str: Optional[str], context: Dict[str, Any], output_path: str, template_variables: Optional[Dict[str, str]] = None) -> str:
152
+ """
153
+ Render an HTML report using a Jinja2 template.
154
+
155
+ Args:
156
+ template_path (Optional[str]): Path to the template file.
157
+ template_str (Optional[str]): Template string content.
158
+ context (Dict[str, Any]): Template context data.
159
+ output_path (str): Path to save the HTML file.
160
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
161
+
162
+ Returns:
163
+ str: Path to the generated HTML file.
164
+
165
+ Raises:
166
+ FileOperationError: If template file is not found or writing fails.
167
+ """
168
+ try:
169
+ if template_path:
170
+ path = os.path.join(self.settings.templates_dir, template_path)
171
+ tmpl = self._jinja_env.get_template(template_path)
172
+ else:
173
+ tmpl = self._jinja_env.from_string(template_str)
174
+ html = tmpl.render(**context)
175
+ csrf_meta = '<meta http-equiv="Content-Security-Policy" content="default-src \'self\'; script-src \'self\'; object-src \'none\'">\n'
176
+ csrf_meta += '<meta name="referrer" content="no-referrer">\n'
177
+ if '<head>' in html:
178
+ html = html.replace('<head>', '<head>\n' + csrf_meta)
179
+ else:
180
+ html = csrf_meta + html
181
+ html = sanitize_html(html, self.settings.allowed_html_tags, self.settings.allowed_html_attributes)
182
+ with open(output_path, 'w', encoding='utf-8') as f:
183
+ f.write(html)
184
+ self._temp_manager.register_file(output_path)
185
+ return output_path
186
+ except Exception as e:
187
+ raise FileOperationError(f"Failed to generate HTML: {str(e)}")
188
+
189
+ def generate_pdf(self, html: Optional[str], html_schema: Optional[Dict], output_path: str, page_size: Optional[str] = None, template_variables: Optional[Dict[str, str]] = None) -> str:
190
+ """
191
+ Generate a PDF report from HTML content or a Jinja2 template.
192
+
193
+ NOTE: PDF generation is currently disabled due to weasyprint deployment complexity.
194
+ This feature will be re-enabled in a future release.
195
+
196
+ Args:
197
+ html (Optional[str]): HTML content.
198
+ html_schema (Optional[Dict]): Dict for HTML generation.
199
+ output_path (str): Path to save the PDF file.
200
+ page_size (Optional[str]): PDF page size.
201
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
202
+
203
+ Returns:
204
+ str: Path to the generated PDF file.
205
+
206
+ Raises:
207
+ FileOperationError: PDF generation is currently disabled.
208
+ """
209
+ raise FileOperationError(
210
+ "PDF generation is currently disabled due to weasyprint deployment complexity. "
211
+ "Please use generate_html() to create HTML reports instead. "
212
+ "PDF functionality will be restored in a future release."
213
+ )
214
+
215
+ # TODO: Re-enable when weasyprint deployment issues are resolved
216
+ # try:
217
+ # if not html and html_schema:
218
+ # html_path = self.generate_html(**html_schema)
219
+ # with open(html_path, 'r', encoding='utf-8') as f:
220
+ # html = f.read()
221
+ # HTML(string=html).write_pdf(
222
+ # output_path,
223
+ # stylesheets=[{'page_size': page_size or self.settings.pdf_page_size}]
224
+ # )
225
+ # self._temp_manager.register_file(output_path)
226
+ # return output_path
227
+ # except Exception as e:
228
+ # raise FileOperationError(f"Failed to generate PDF: {str(e)}")
229
+
230
+ def generate_excel(self, sheets: Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]], output_path: str, styles: Optional[Dict[str, Dict[str, Any]]] = None, template_variables: Optional[Dict[str, str]] = None) -> str:
231
+ """
232
+ Generate an Excel workbook with multiple sheets and optional styling.
233
+
234
+ Args:
235
+ sheets (Dict[str, Union[pd.DataFrame, List[Dict[str, Any]]]]): Sheet data.
236
+ output_path (str): Path to save the Excel file.
237
+ styles (Optional[Dict[str, Dict[str, Any]]]): Cell styling.
238
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
239
+
240
+ Returns:
241
+ str: Path to the generated Excel file.
242
+
243
+ Raises:
244
+ FileOperationError: If Excel generation fails.
245
+ """
246
+ try:
247
+ writer = pd.ExcelWriter(output_path, engine='xlsxwriter')
248
+ workbook = writer.book
249
+ for name, data in sheets.items():
250
+ df = data if isinstance(data, pd.DataFrame) else pd.DataFrame(data)
251
+ df.to_excel(writer, sheet_name=name[:31], index=False)
252
+ if styles and name in styles:
253
+ worksheet = writer.sheets[name[:31]]
254
+ for cell, style in styles[name].items():
255
+ format_dict = {}
256
+ if style.get('bold'):
257
+ format_dict['bold'] = True
258
+ if style.get('font_size'):
259
+ format_dict['font_size'] = style['font_size']
260
+ if style.get('bg_color'):
261
+ format_dict['bg_color'] = style['bg_color']
262
+ worksheet.write(cell, df.loc[int(cell[1:]) - 1, cell[0]], workbook.add_format(format_dict))
263
+ writer.close()
264
+ self._temp_manager.register_file(output_path)
265
+ return output_path
266
+ except Exception as e:
267
+ raise FileOperationError(f"Failed to generate Excel: {str(e)}")
268
+
269
+ def generate_pptx(self, slides: List[Dict], output_path: str, default_font: Optional[str] = None, default_font_size: Optional[int] = None, default_font_color: Optional[Tuple[int, int, int]] = None, template_variables: Optional[Dict[str, str]] = None) -> str:
270
+ """
271
+ Generate a PowerPoint presentation with customizable slides.
272
+
273
+ Args:
274
+ slides (List[Dict]): List of slide data.
275
+ output_path (str): Path to save the PPTX file.
276
+ default_font (Optional[str]): Default font for slides.
277
+ default_font_size (Optional[int]): Default font size.
278
+ default_font_color (Optional[Tuple[int, int, int]]): Default font color.
279
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
280
+
281
+ Returns:
282
+ str: Path to the generated PPTX file.
283
+
284
+ Raises:
285
+ FileOperationError: If PPTX generation fails.
286
+ """
287
+ try:
288
+ prs = Presentation()
289
+ for slide in slides:
290
+ s = prs.slides.add_slide(prs.slide_layouts[1])
291
+ title_shape = s.shapes.title
292
+ title_shape.text = slide['title']
293
+ font = slide.get('font') or default_font or self.settings.default_font
294
+ font_size = slide.get('font_size') or default_font_size or self.settings.default_font_size
295
+ font_color = slide.get('font_color') or default_font_color or (0, 0, 0)
296
+ title_shape.text_frame.paragraphs[0].font.name = font
297
+ title_shape.text_frame.paragraphs[0].font.size = Pt(font_size)
298
+ # Set font color safely - skip color setting for now to avoid library issues
299
+ # Font color setting in python-pptx can be problematic, focusing on core functionality
300
+ pass
301
+ body = s.shapes.placeholders[1].text_frame
302
+ for bullet in slide['bullets']:
303
+ p = body.add_paragraph()
304
+ p.text = bullet
305
+ p.level = 0
306
+ p.font.name = font
307
+ p.font.size = Pt(font_size)
308
+ # Skip font color setting for bullet points to avoid library issues
309
+ pass
310
+ prs.save(output_path)
311
+ self._temp_manager.register_file(output_path)
312
+ return output_path
313
+ except Exception as e:
314
+ raise FileOperationError(f"Failed to generate PPTX: {str(e)}")
315
+
316
+ def generate_markdown(self, template_path: Optional[str], template_str: Optional[str], context: Dict[str, Any], output_path: str, template_variables: Optional[Dict[str, str]] = None) -> str:
317
+ """
318
+ Render a Markdown report using a Jinja2 template.
319
+
320
+ Args:
321
+ template_path (Optional[str]): Path to the template file.
322
+ template_str (Optional[str]): Template string content.
323
+ context (Dict[str, Any]): Template context data.
324
+ output_path (str): Path to save the Markdown file.
325
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
326
+
327
+ Returns:
328
+ str: Path to the generated Markdown file.
329
+
330
+ Raises:
331
+ FileOperationError: If rendering or writing fails.
332
+ """
333
+ try:
334
+ if template_path:
335
+ tmpl = self._jinja_env.get_template(template_path)
336
+ else:
337
+ tmpl = self._jinja_env.from_string(template_str)
338
+ markdown_content = tmpl.render(**context)
339
+ with open(output_path, 'w', encoding='utf-8') as f:
340
+ f.write(markdown_content)
341
+ self._temp_manager.register_file(output_path)
342
+ return output_path
343
+ except Exception as e:
344
+ raise FileOperationError(f"Failed to generate Markdown: {str(e)}")
345
+
346
+ def generate_word(self, template_path: Optional[str], template_str: Optional[str], context: Dict[str, Any], output_path: str, font: Optional[str] = None, font_size: Optional[int] = None, font_color: Optional[Tuple[int, int, int]] = None, template_variables: Optional[Dict[str, str]] = None) -> str:
347
+ """
348
+ Generate a Word document from a Jinja2 template with customizable styles.
349
+
350
+ Args:
351
+ template_path (Optional[str]): Path to the template file.
352
+ template_str (Optional[str]): Template string content.
353
+ context (Dict[str, Any]): Template context data.
354
+ output_path (str): Path to save the DOCX file.
355
+ font (Optional[str]): Font for the document.
356
+ font_size (Optional[int]): Font size.
357
+ font_color (Optional[Tuple[int, int, int]]): Font color.
358
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
359
+
360
+ Returns:
361
+ str: Path to the generated DOCX file.
362
+
363
+ Raises:
364
+ FileOperationError: If Word generation fails.
365
+ """
366
+ try:
367
+ if template_path:
368
+ tmpl = self._jinja_env.get_template(template_path)
369
+ else:
370
+ tmpl = self._jinja_env.from_string(template_str)
371
+ content = tmpl.render(**context)
372
+ doc = Document()
373
+ font = font or self.settings.default_font
374
+ font_size = font_size or self.settings.default_font_size
375
+ font_color = font_color or (0, 0, 0)
376
+ for line in content.splitlines():
377
+ p = doc.add_paragraph()
378
+ run = p.add_run(line)
379
+ run.font.name = font
380
+ run.font.size = DocxPt(font_size)
381
+ # Skip font color setting for Word documents to avoid library issues
382
+ pass
383
+ doc.save(output_path)
384
+ self._temp_manager.register_file(output_path)
385
+ return output_path
386
+ except Exception as e:
387
+ raise FileOperationError(f"Failed to generate Word: {str(e)}")
388
+
389
+ def generate_image(self, chart_type: str, data: Union[pd.DataFrame, List[Dict[str, Any]]], output_path: str, x_col: Optional[str] = None, y_col: Optional[str] = None, labels: Optional[List[str]] = None, title: Optional[str] = None, width: int = 8, height: int = 6, template_variables: Optional[Dict[str, str]] = None) -> str:
390
+ """
391
+ Generate a chart (bar, line, pie) using Matplotlib.
392
+
393
+ Args:
394
+ chart_type (str): Type of chart ('bar', 'line', 'pie').
395
+ data (Union[pd.DataFrame, List[Dict[str, Any]]]): Chart data.
396
+ output_path (str): Path to save the image file.
397
+ x_col (Optional[str]): X-axis column name.
398
+ y_col (Optional[str]): Y-axis column name.
399
+ labels (Optional[List[str]]): Labels for pie chart.
400
+ title (Optional[str]): Chart title.
401
+ width (int): Chart width.
402
+ height (int): Chart height.
403
+ template_variables (Optional[Dict[str, str]]): Variables for dynamic output path.
404
+
405
+ Returns:
406
+ str: Path to the generated image file.
407
+
408
+ Raises:
409
+ FileOperationError: If chart generation fails.
410
+ """
411
+ try:
412
+ df = data if isinstance(data, pd.DataFrame) else pd.DataFrame(data)
413
+ plt.figure(figsize=(width, height))
414
+ if chart_type == 'bar':
415
+ df.plot.bar(x=x_col, y=y_col, title=title)
416
+ elif chart_type == 'line':
417
+ df.plot.line(x=x_col, y=y_col, title=title)
418
+ elif chart_type == 'pie':
419
+ plt.pie(df[y_col], labels=df[x_col] if x_col else labels, autopct='%1.1f%%')
420
+ plt.title(title)
421
+ plt.savefig(output_path)
422
+ plt.close()
423
+ self._temp_manager.register_file(output_path)
424
+ return output_path
425
+ except Exception as e:
426
+ raise FileOperationError(f"Failed to generate image: {str(e)}")
427
+
428
+ def batch_generate(self, operation: str, contexts: List[Dict[str, Any]], output_paths: List[str], datasets: Optional[List[DatasetType]] = None, slides: Optional[List[List[Dict]]] = None) -> List[str]:
429
+ """
430
+ Generate multiple reports in parallel for different contexts or datasets.
431
+
432
+ Args:
433
+ operation (str): Operation to perform.
434
+ contexts (List[Dict[str, Any]]): Contexts for HTML, Markdown, Word, PDF.
435
+ output_paths (List[str]): Paths for generated files.
436
+ datasets (Optional[List[DatasetType]]): Datasets for Excel, Image.
437
+ slides (Optional[List[List[Dict]]]): Slides for PPTX.
438
+
439
+ Returns:
440
+ List[str]: List of generated file paths.
441
+
442
+ Raises:
443
+ FileOperationError: If batch generation fails.
444
+ """
445
+ try:
446
+ tasks = []
447
+ input_data = contexts or datasets or slides
448
+ for i, output_path in enumerate(output_paths):
449
+ op_params = {'output_path': output_path}
450
+ if operation in ('generate_html', 'generate_markdown', 'generate_word'):
451
+ op_params.update(input_data[i])
452
+ op_params['template_path'] = input_data[i].get('template_path')
453
+ op_params['template_str'] = input_data[i].get('template_str')
454
+ if operation == 'generate_word':
455
+ op_params['font'] = input_data[i].get('font')
456
+ op_params['font_size'] = input_data[i].get('font_size')
457
+ op_params['font_color'] = input_data[i].get('font_color')
458
+ elif operation == 'generate_excel':
459
+ op_params['sheets'] = input_data[i]
460
+ op_params['styles'] = input_data[i].get('styles')
461
+ elif operation == 'generate_pptx':
462
+ op_params['slides'] = input_data[i]
463
+ op_params['default_font'] = input_data[i][0].get('font') if input_data[i] else None
464
+ op_params['default_font_size'] = input_data[i][0].get('font_size') if input_data[i] else None
465
+ op_params['default_font_color'] = input_data[i][0].get('font_color') if input_data[i] else None
466
+ elif operation == 'generate_image':
467
+ op_params.update(input_data[i])
468
+ elif operation == 'generate_pdf':
469
+ op_params['html'] = input_data[i].get('html')
470
+ op_params['html_schema'] = input_data[i] if input_data[i].get('context') else None
471
+ op_params['page_size'] = input_data[i].get('page_size')
472
+ tasks.append({'op': operation, 'kwargs': op_params})
473
+ # Execute tasks synchronously for batch generation
474
+ results = []
475
+ for task in tasks:
476
+ op_name = task['op']
477
+ kwargs = task['kwargs']
478
+
479
+ if op_name == 'generate_html':
480
+ result = self.generate_html(**kwargs)
481
+ elif op_name == 'generate_excel':
482
+ result = self.generate_excel(**kwargs)
483
+ elif op_name == 'generate_pptx':
484
+ result = self.generate_pptx(**kwargs)
485
+ elif op_name == 'generate_markdown':
486
+ result = self.generate_markdown(**kwargs)
487
+ elif op_name == 'generate_word':
488
+ result = self.generate_word(**kwargs)
489
+ elif op_name == 'generate_image':
490
+ result = self.generate_image(**kwargs)
491
+ elif op_name == 'generate_pdf':
492
+ result = self.generate_pdf(**kwargs)
493
+ else:
494
+ raise FileOperationError(f"Unsupported operation: {op_name}")
495
+
496
+ results.append(result)
497
+ return results
498
+ except Exception as e:
499
+ raise FileOperationError(f"Failed to generate batch reports: {str(e)}")