Spaces:
Configuration error
Configuration error
File size: 13,313 Bytes
ceaf3bb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | import tempfile
import os
import shutil
from io import BytesIO
from zipfile import ZipFile
from typing import Dict, Callable, Any, List
from datetime import datetime
from system_chart_extraction import system_chart_extraction
class SystemChartService:
"""
Service class for System Chart Extraction.
Handles the orchestration of system chart extraction, visualization creation,
and result packaging.
"""
def __init__(self):
pass
async def extract_system_charts(
self,
category: str,
llm,
pdf_dict: Dict[str, str],
enable_visualization: bool = False,
run_system_chart_normalization: bool = False,
progress_callback: Callable[[Dict[str, Any]], None] = None,
extraction_id: str = None
) -> Dict[str, Any]:
"""
Main system chart extraction pipeline.
Args:
category: Database category for extraction
llm: Language model for extraction
pdf_dict: Dictionary mapping document titles to full text content
enable_visualization: Whether to create visualization diagrams
progress_callback: Function to report progress updates
Returns:
Dict containing extraction results and metadata
"""
try:
# Validate input
if not pdf_dict:
return {
"success": False,
"message": "No files found in the specified category.",
"zip_data": None,
"filename": None
}
print(f"π Found {len(pdf_dict)} papers to process")
# Initial progress update
if progress_callback:
progress_callback({
"status": "starting",
"progress": 0,
"message": "Getting PDF files..."
})
# Create progress callback function for the extraction process
def extraction_progress_callback(progress_data):
if progress_callback:
print(f"π Progress update: {progress_data}")
progress_callback(progress_data)
print(f"π Progress updated: {progress_data}")
# Update progress before starting extraction
if progress_callback:
progress_callback({
"status": "starting",
"progress": 5,
"message": f"Found {len(pdf_dict)} papers. Starting extraction..."
})
print(f"π¬ Starting system chart extraction...")
# Create temporary directory for visualization files if needed
temp_dir = None
if enable_visualization:
temp_dir = tempfile.mkdtemp(prefix="system_chart_viz_")
print(f"π Created temporary directory: {temp_dir}")
# Run system chart extraction with progress tracking
output_system_charts, output_all_details, created_visualizations = await system_chart_extraction(
pdf_dict,
llm,
extraction_progress_callback,
enable_visualization,
temp_dir,
extraction_id, # Pass extraction_id to save conversations
run_normalization=run_system_chart_normalization # β only normalize when asked/needed
)
# Create ZIP file in memory
zip_data = self._create_zip_file(
output_system_charts,
output_all_details,
created_visualizations
)
# Clean up temporary directory
if temp_dir:
self._cleanup_temp_directory(temp_dir)
# Prepare result
viz_message = f" with {len(created_visualizations)} visualizations" if created_visualizations else ""
return {
"success": True,
"message": f"System chart extraction completed successfully{viz_message}!",
"zip_data": zip_data,
"filename": "system_chart_extraction.zip",
"charts_count": len(pdf_dict),
"visualizations_count": len(created_visualizations) if created_visualizations else 0
}
except Exception as e:
# Clean up on error
if 'temp_dir' in locals() and temp_dir:
self._cleanup_temp_directory(temp_dir)
return {
"success": False,
"message": f"Error during system chart extraction: {str(e)}",
"zip_data": None,
"filename": None
}
def _create_zip_file(
self,
output_system_charts: str,
output_all_details: str,
created_visualizations: List[str]
) -> bytes:
"""Create ZIP file containing extraction results and visualizations."""
zip_buffer = BytesIO()
with ZipFile(zip_buffer, "w") as zipf:
# Add text results
zipf.writestr("system_charts.txt", output_system_charts)
zipf.writestr("all_details.txt", output_all_details)
# Add visualization files if any were created
if created_visualizations:
for viz_path in created_visualizations:
if os.path.exists(viz_path):
# Extract just the filename for the ZIP entry
viz_filename = os.path.basename(viz_path)
zipf.write(viz_path, f"visualizations/{viz_filename}")
print(f"π¦ Added visualization to ZIP: {viz_filename}")
# Get ZIP data and close buffer
zip_data = zip_buffer.getvalue()
zip_buffer.close()
return zip_data
def _cleanup_temp_directory(self, temp_dir: str) -> None:
"""Clean up temporary directory and its contents."""
if temp_dir and os.path.exists(temp_dir):
try:
shutil.rmtree(temp_dir)
print(f"ποΈ Cleaned up temporary directory: {temp_dir}")
except OSError as e:
print(f"β οΈ Could not clean up temporary directory {temp_dir}: {e}")
# Don't fail the whole process if cleanup fails
pass
@staticmethod
def parse_system_charts_file(file_content: str) -> List[Dict[str, str]]:
"""
Parse a delimited system charts file into individual chart objects.
Args:
file_content: Content of the system_charts.txt file
Returns:
List of dictionaries, each containing:
- 'chart_number': The chart number (auto-generated)
- 'title': Paper title
- 'citation': Citation format (same as title for compatibility)
- 'content': System chart table content
"""
charts = []
lines = file_content.split('\n')
current_chart = None
chart_number = 1
for i, line in enumerate(lines):
line = line.strip()
# Check for delimiter line (100 '=' characters)
if line == '=' * 100:
# Look for PAPER: line after delimiter
if i + 1 < len(lines):
next_line = lines[i + 1].strip()
if next_line.startswith('PAPER:'):
# Save previous chart if exists
if current_chart:
charts.append(current_chart)
# Start new chart
paper_title = next_line.replace('PAPER:', '').strip()
current_chart = {
'chart_number': str(chart_number),
'title': paper_title,
'citation': f'[{paper_title}]',
'content': ''
}
chart_number += 1
elif line.startswith('SYSTEM_CHART:') and current_chart:
# Extract the system chart content
chart_content = line.replace('SYSTEM_CHART:', '').strip()
current_chart['content'] = chart_content
# Handle last chart if file doesn't end with proper delimiter
if current_chart:
charts.append(current_chart)
print(f"π Parsed {len(charts)} system charts from file")
return charts
@staticmethod
def combine_system_charts_files(file_contents: List[str]) -> str:
"""
Combine multiple system chart txt files into a single formatted string.
Preserves the one-to-one relationship between system charts and titles.
Args:
file_contents: List of system chart file contents to combine
Returns:
Combined system chart content in the new format
"""
combined_charts = []
# Parse each file and collect all charts
for file_content in file_contents:
charts = SystemChartService.parse_system_charts_file(file_content)
combined_charts.extend(charts)
# Generate combined output in new format
combined_output = "COMBINED SYSTEM CHARTS EXTRACTION RESULTS\n"
combined_output += "=" * 100 + "\n"
for chart in combined_charts:
delimiter = "=" * 100
combined_output += f'\n{delimiter}\n'
combined_output += f'PAPER: {chart["title"]}\n'
combined_output += f'SYSTEM_CHART: {chart["content"]}\n'
combined_output += f'{delimiter}\n\n'
# Add summary
summary_delimiter = "=" * 100
combined_output += f'\n{summary_delimiter}\n'
combined_output += f'COMBINATION SUMMARY\n'
combined_output += f'{summary_delimiter}\n'
combined_output += f'Total Files Combined: {len(file_contents)}\n'
combined_output += f'Total System Charts: {len(combined_charts)}\n'
combined_output += f'Combination Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n'
print(f"π Combined {len(file_contents)} files into {len(combined_charts)} system charts")
return combined_output
async def combine_multiple_system_chart_files(
self,
file_paths: List[str],
output_filename: str = "combined_system_charts.txt"
) -> Dict[str, Any]:
"""
Combine multiple system chart txt files into a single ZIP file.
Args:
file_paths: List of file paths to system chart txt files
output_filename: Name for the output file
Returns:
Dict containing success status, message, and ZIP data
"""
try:
file_contents = []
# Read all files
for file_path in file_paths:
if not os.path.exists(file_path):
return {
"success": False,
"message": f"File not found: {file_path}",
"zip_data": None,
"filename": None
}
with open(file_path, 'r', encoding='utf-8') as f:
file_contents.append(f.read())
# Combine the files
combined_content = self.combine_system_charts_files(file_contents)
# Create ZIP file
zip_buffer = BytesIO()
with ZipFile(zip_buffer, "w") as zipf:
zipf.writestr(output_filename, combined_content)
zip_data = zip_buffer.getvalue()
zip_buffer.close()
return {
"success": True,
"message": f"Successfully combined {len(file_paths)} system chart files!",
"zip_data": zip_data,
"filename": "combined_system_charts.zip",
"files_combined": len(file_paths)
}
except Exception as e:
return {
"success": False,
"message": f"Error combining system chart files: {str(e)}",
"zip_data": None,
"filename": None
}
@staticmethod
def get_chart_citations(file_content: str) -> List[str]:
"""
Extract just the citations from a system charts file.
Args:
file_content: Content of the system_charts.txt file
Returns:
List of citation strings
"""
charts = SystemChartService.parse_system_charts_file(file_content)
return [chart.get('citation', chart.get('title', f"Chart {chart['chart_number']}"))
for chart in charts if chart.get('chart_number')] |