"""Aggregator tab for combining markdown files."""
import os
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QProgressBar, QFileDialog, QMessageBox
)
from PyQt6.QtCore import Qt, QCoreApplication
from core.aggregator import Aggregator
from core.exceptions import ObsidianToolException
class AggregatorTab(QWidget):
"""Tab for aggregating multiple markdown files."""
def __init__(self):
"""Initialize the aggregator tab."""
super().__init__()
self.aggregator = Aggregator()
self.init_ui()
def init_ui(self):
"""Initialize the user interface."""
layout = QVBoxLayout(self)
layout.setSpacing(15)
layout.setContentsMargins(20, 20, 20, 20)
# Source Directory section
layout.addWidget(QLabel("Source Directory:"))
source_layout = QHBoxLayout()
self.source_edit = QLineEdit()
self.source_edit.setReadOnly(True)
self.source_edit.setPlaceholderText("Select a directory containing .md files...")
source_layout.addWidget(self.source_edit)
self.source_btn = QPushButton("Browse...")
self.source_btn.setMaximumWidth(100)
self.source_btn.clicked.connect(self.browse_source)
source_layout.addWidget(self.source_btn)
layout.addLayout(source_layout)
# Output File section
layout.addWidget(QLabel("Output File:"))
output_layout = QHBoxLayout()
self.output_edit = QLineEdit()
self.output_edit.setReadOnly(True)
self.output_edit.setPlaceholderText("Select output file location...")
output_layout.addWidget(self.output_edit)
self.output_btn = QPushButton("Browse...")
self.output_btn.setMaximumWidth(100)
self.output_btn.clicked.connect(self.browse_output)
output_layout.addWidget(self.output_btn)
layout.addLayout(output_layout)
# Spacer
layout.addSpacing(20)
# Action button
self.aggregate_btn = QPushButton("Aggregate Files")
self.aggregate_btn.setMinimumHeight(40)
self.aggregate_btn.clicked.connect(self.aggregate_files)
layout.addWidget(self.aggregate_btn)
# Progress bar
self.progress_bar = QProgressBar()
self.progress_bar.setValue(0)
layout.addWidget(self.progress_bar)
# Status label
self.status_label = QLabel("Ready")
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.status_label.setStyleSheet("color: #666; font-style: italic;")
layout.addWidget(self.status_label)
# Add stretch to push everything to the top
layout.addStretch()
def browse_source(self):
"""Open dialog to select source directory."""
directory = QFileDialog.getExistingDirectory(
self,
"Select Source Directory",
os.path.expanduser("~")
)
if directory:
self.source_edit.setText(directory)
self.status_label.setText("Source directory selected")
def browse_output(self):
"""Open dialog to select output file."""
file_path, _ = QFileDialog.getSaveFileName(
self,
"Select Output File",
os.path.expanduser("~/aggregated.md"),
"Markdown Files (*.md);;All Files (*)"
)
if file_path:
# Ensure .md extension
if not file_path.endswith('.md'):
file_path += '.md'
self.output_edit.setText(file_path)
self.status_label.setText("Output file selected")
def aggregate_files(self):
"""Perform the aggregation operation."""
source_dir = self.source_edit.text()
output_file = self.output_edit.text()
# Validate inputs
if not source_dir:
QMessageBox.warning(
self,
"Input Required",
"Please select a source directory."
)
return
if not output_file:
QMessageBox.warning(
self,
"Input Required",
"Please select an output file."
)
return
# Disable UI during operation
self.set_ui_enabled(False)
self.progress_bar.setValue(0)
self.status_label.setText("Aggregating files...")
try:
# Progress callback
def update_progress(percentage, message):
self.progress_bar.setValue(percentage)
self.status_label.setText(message)
QCoreApplication.processEvents() # Keep UI responsive
# Perform aggregation
result = self.aggregator.aggregate(
source_dir,
output_file,
update_progress
)
# Show success message
QMessageBox.information(
self,
"Success",
f"Aggregation complete!\n\n"
f"Files processed: {result['files_processed']}\n"
f"Output file: {result['output_file']}"
)
self.status_label.setText("Aggregation completed successfully")
except ObsidianToolException as e:
QMessageBox.critical(
self,
"Error",
f"Aggregation failed:\n\n{str(e)}"
)
self.status_label.setText("Aggregation failed")
self.progress_bar.setValue(0)
except Exception as e:
QMessageBox.critical(
self,
"Unexpected Error",
f"An unexpected error occurred:\n\n{str(e)}"
)
self.status_label.setText("Aggregation failed")
self.progress_bar.setValue(0)
finally:
# Re-enable UI
self.set_ui_enabled(True)
def set_ui_enabled(self, enabled: bool):
"""Enable or disable UI elements during operation."""
self.source_btn.setEnabled(enabled)
self.output_btn.setEnabled(enabled)
self.aggregate_btn.setEnabled(enabled)