pomera-ai-commander 1.1.1 → 1.2.2
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.
- package/LICENSE +21 -21
- package/README.md +105 -680
- package/bin/pomera-ai-commander.js +62 -62
- package/core/__init__.py +65 -65
- package/core/app_context.py +482 -482
- package/core/async_text_processor.py +421 -421
- package/core/backup_manager.py +655 -655
- package/core/backup_recovery_manager.py +1199 -1033
- package/core/content_hash_cache.py +508 -508
- package/core/context_menu.py +313 -313
- package/core/data_directory.py +549 -0
- package/core/data_validator.py +1066 -1066
- package/core/database_connection_manager.py +744 -744
- package/core/database_curl_settings_manager.py +608 -608
- package/core/database_promera_ai_settings_manager.py +446 -446
- package/core/database_schema.py +411 -411
- package/core/database_schema_manager.py +395 -395
- package/core/database_settings_manager.py +1507 -1507
- package/core/database_settings_manager_interface.py +456 -456
- package/core/dialog_manager.py +734 -734
- package/core/diff_utils.py +239 -0
- package/core/efficient_line_numbers.py +540 -510
- package/core/error_handler.py +746 -746
- package/core/error_service.py +431 -431
- package/core/event_consolidator.py +511 -511
- package/core/mcp/__init__.py +43 -43
- package/core/mcp/find_replace_diff.py +334 -0
- package/core/mcp/protocol.py +288 -288
- package/core/mcp/schema.py +251 -251
- package/core/mcp/server_stdio.py +299 -299
- package/core/mcp/tool_registry.py +2699 -2345
- package/core/memento.py +275 -0
- package/core/memory_efficient_text_widget.py +711 -711
- package/core/migration_manager.py +914 -914
- package/core/migration_test_suite.py +1085 -1085
- package/core/migration_validator.py +1143 -1143
- package/core/optimized_find_replace.py +714 -714
- package/core/optimized_pattern_engine.py +424 -424
- package/core/optimized_search_highlighter.py +552 -552
- package/core/performance_monitor.py +674 -674
- package/core/persistence_manager.py +712 -712
- package/core/progressive_stats_calculator.py +632 -632
- package/core/regex_pattern_cache.py +529 -529
- package/core/regex_pattern_library.py +350 -350
- package/core/search_operation_manager.py +434 -434
- package/core/settings_defaults_registry.py +1087 -1087
- package/core/settings_integrity_validator.py +1111 -1111
- package/core/settings_serializer.py +557 -557
- package/core/settings_validator.py +1823 -1823
- package/core/smart_stats_calculator.py +709 -709
- package/core/statistics_update_manager.py +619 -619
- package/core/stats_config_manager.py +858 -858
- package/core/streaming_text_handler.py +723 -723
- package/core/task_scheduler.py +596 -596
- package/core/update_pattern_library.py +168 -168
- package/core/visibility_monitor.py +596 -596
- package/core/widget_cache.py +498 -498
- package/mcp.json +51 -61
- package/migrate_data.py +127 -0
- package/package.json +64 -57
- package/pomera.py +7883 -7482
- package/pomera_mcp_server.py +183 -144
- package/requirements.txt +33 -0
- package/scripts/Dockerfile.alpine +43 -0
- package/scripts/Dockerfile.gui-test +54 -0
- package/scripts/Dockerfile.linux +43 -0
- package/scripts/Dockerfile.test-linux +80 -0
- package/scripts/Dockerfile.ubuntu +39 -0
- package/scripts/README.md +53 -0
- package/scripts/build-all.bat +113 -0
- package/scripts/build-docker.bat +53 -0
- package/scripts/build-docker.sh +55 -0
- package/scripts/build-optimized.bat +101 -0
- package/scripts/build.sh +78 -0
- package/scripts/docker-compose.test.yml +27 -0
- package/scripts/docker-compose.yml +32 -0
- package/scripts/postinstall.js +62 -0
- package/scripts/requirements-minimal.txt +33 -0
- package/scripts/test-linux-simple.bat +28 -0
- package/scripts/validate-release-workflow.py +450 -0
- package/tools/__init__.py +4 -4
- package/tools/ai_tools.py +2891 -2891
- package/tools/ascii_art_generator.py +352 -352
- package/tools/base64_tools.py +183 -183
- package/tools/base_tool.py +511 -511
- package/tools/case_tool.py +308 -308
- package/tools/column_tools.py +395 -395
- package/tools/cron_tool.py +884 -884
- package/tools/curl_history.py +600 -600
- package/tools/curl_processor.py +1207 -1207
- package/tools/curl_settings.py +502 -502
- package/tools/curl_tool.py +5467 -5467
- package/tools/diff_viewer.py +1817 -1072
- package/tools/email_extraction_tool.py +248 -248
- package/tools/email_header_analyzer.py +425 -425
- package/tools/extraction_tools.py +250 -250
- package/tools/find_replace.py +2289 -1750
- package/tools/folder_file_reporter.py +1463 -1463
- package/tools/folder_file_reporter_adapter.py +480 -480
- package/tools/generator_tools.py +1216 -1216
- package/tools/hash_generator.py +255 -255
- package/tools/html_tool.py +656 -656
- package/tools/jsonxml_tool.py +729 -729
- package/tools/line_tools.py +419 -419
- package/tools/markdown_tools.py +561 -561
- package/tools/mcp_widget.py +1417 -1417
- package/tools/notes_widget.py +978 -973
- package/tools/number_base_converter.py +372 -372
- package/tools/regex_extractor.py +571 -571
- package/tools/slug_generator.py +310 -310
- package/tools/sorter_tools.py +458 -458
- package/tools/string_escape_tool.py +392 -392
- package/tools/text_statistics_tool.py +365 -365
- package/tools/text_wrapper.py +430 -430
- package/tools/timestamp_converter.py +421 -421
- package/tools/tool_loader.py +710 -710
- package/tools/translator_tools.py +522 -522
- package/tools/url_link_extractor.py +261 -261
- package/tools/url_parser.py +204 -204
- package/tools/whitespace_tools.py +355 -355
- package/tools/word_frequency_counter.py +146 -146
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
package/tools/column_tools.py
CHANGED
|
@@ -1,396 +1,396 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Column/CSV Tools Module - Column manipulation utilities
|
|
3
|
-
|
|
4
|
-
This module provides column and CSV manipulation functionality
|
|
5
|
-
for the Pomera AI Commander application.
|
|
6
|
-
|
|
7
|
-
Features:
|
|
8
|
-
- Extract Column: Extract specific column by index
|
|
9
|
-
- Reorder Columns: Rearrange column order
|
|
10
|
-
- Delete Column: Remove column by index
|
|
11
|
-
- Transpose: Swap rows and columns
|
|
12
|
-
- CSV to Fixed Width: Convert to fixed-width format
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
import tkinter as tk
|
|
16
|
-
from tkinter import ttk
|
|
17
|
-
import csv
|
|
18
|
-
import io
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ColumnToolsProcessor:
|
|
22
|
-
"""Column tools processor with various column manipulation capabilities."""
|
|
23
|
-
|
|
24
|
-
@staticmethod
|
|
25
|
-
def parse_csv(text, delimiter=",", quote_char='"'):
|
|
26
|
-
"""Parse CSV text into rows."""
|
|
27
|
-
reader = csv.reader(io.StringIO(text), delimiter=delimiter, quotechar=quote_char)
|
|
28
|
-
return list(reader)
|
|
29
|
-
|
|
30
|
-
@staticmethod
|
|
31
|
-
def format_csv(rows, delimiter=",", quote_char='"'):
|
|
32
|
-
"""Format rows back to CSV text."""
|
|
33
|
-
output = io.StringIO()
|
|
34
|
-
writer = csv.writer(output, delimiter=delimiter, quotechar=quote_char, quoting=csv.QUOTE_MINIMAL)
|
|
35
|
-
writer.writerows(rows)
|
|
36
|
-
return output.getvalue().strip()
|
|
37
|
-
|
|
38
|
-
@staticmethod
|
|
39
|
-
def extract_column(text, column_index, delimiter=",", quote_char='"'):
|
|
40
|
-
"""Extract a specific column by index (0-based)."""
|
|
41
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
42
|
-
result = []
|
|
43
|
-
|
|
44
|
-
for row in rows:
|
|
45
|
-
if column_index < len(row):
|
|
46
|
-
result.append(row[column_index])
|
|
47
|
-
else:
|
|
48
|
-
result.append("")
|
|
49
|
-
|
|
50
|
-
return '\n'.join(result)
|
|
51
|
-
|
|
52
|
-
@staticmethod
|
|
53
|
-
def reorder_columns(text, order, delimiter=",", quote_char='"'):
|
|
54
|
-
"""Reorder columns based on specified order (e.g., "2,0,1")."""
|
|
55
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
indices = [int(i.strip()) for i in order.split(',')]
|
|
59
|
-
except ValueError:
|
|
60
|
-
return "Error: Invalid column order format. Use comma-separated indices (e.g., '2,0,1')"
|
|
61
|
-
|
|
62
|
-
result = []
|
|
63
|
-
for row in rows:
|
|
64
|
-
new_row = []
|
|
65
|
-
for idx in indices:
|
|
66
|
-
if idx < len(row):
|
|
67
|
-
new_row.append(row[idx])
|
|
68
|
-
else:
|
|
69
|
-
new_row.append("")
|
|
70
|
-
result.append(new_row)
|
|
71
|
-
|
|
72
|
-
return ColumnToolsProcessor.format_csv(result, delimiter, quote_char)
|
|
73
|
-
|
|
74
|
-
@staticmethod
|
|
75
|
-
def delete_column(text, column_index, delimiter=",", quote_char='"'):
|
|
76
|
-
"""Delete a column by index."""
|
|
77
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
78
|
-
result = []
|
|
79
|
-
|
|
80
|
-
for row in rows:
|
|
81
|
-
new_row = [cell for i, cell in enumerate(row) if i != column_index]
|
|
82
|
-
result.append(new_row)
|
|
83
|
-
|
|
84
|
-
return ColumnToolsProcessor.format_csv(result, delimiter, quote_char)
|
|
85
|
-
|
|
86
|
-
@staticmethod
|
|
87
|
-
def add_column(text, column_index, value="", delimiter=",", quote_char='"'):
|
|
88
|
-
"""Add a new column at specified index."""
|
|
89
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
90
|
-
result = []
|
|
91
|
-
|
|
92
|
-
for row in rows:
|
|
93
|
-
new_row = list(row)
|
|
94
|
-
# Ensure row is long enough
|
|
95
|
-
while len(new_row) < column_index:
|
|
96
|
-
new_row.append("")
|
|
97
|
-
new_row.insert(column_index, value)
|
|
98
|
-
result.append(new_row)
|
|
99
|
-
|
|
100
|
-
return ColumnToolsProcessor.format_csv(result, delimiter, quote_char)
|
|
101
|
-
|
|
102
|
-
@staticmethod
|
|
103
|
-
def transpose(text, delimiter=",", quote_char='"'):
|
|
104
|
-
"""Transpose rows and columns."""
|
|
105
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
106
|
-
|
|
107
|
-
if not rows:
|
|
108
|
-
return text
|
|
109
|
-
|
|
110
|
-
# Find max columns
|
|
111
|
-
max_cols = max(len(row) for row in rows)
|
|
112
|
-
|
|
113
|
-
# Pad rows to same length
|
|
114
|
-
padded = [row + [''] * (max_cols - len(row)) for row in rows]
|
|
115
|
-
|
|
116
|
-
# Transpose
|
|
117
|
-
transposed = list(map(list, zip(*padded)))
|
|
118
|
-
|
|
119
|
-
return ColumnToolsProcessor.format_csv(transposed, delimiter, quote_char)
|
|
120
|
-
|
|
121
|
-
@staticmethod
|
|
122
|
-
def to_fixed_width(text, delimiter=",", quote_char='"', padding=2):
|
|
123
|
-
"""Convert CSV to fixed-width format."""
|
|
124
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
125
|
-
|
|
126
|
-
if not rows:
|
|
127
|
-
return text
|
|
128
|
-
|
|
129
|
-
# Find max width for each column
|
|
130
|
-
max_cols = max(len(row) for row in rows)
|
|
131
|
-
col_widths = [0] * max_cols
|
|
132
|
-
|
|
133
|
-
for row in rows:
|
|
134
|
-
for i, cell in enumerate(row):
|
|
135
|
-
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
136
|
-
|
|
137
|
-
# Format with fixed widths
|
|
138
|
-
result = []
|
|
139
|
-
for row in rows:
|
|
140
|
-
formatted_cells = []
|
|
141
|
-
for i in range(max_cols):
|
|
142
|
-
cell = row[i] if i < len(row) else ""
|
|
143
|
-
formatted_cells.append(str(cell).ljust(col_widths[i] + padding))
|
|
144
|
-
result.append(''.join(formatted_cells).rstrip())
|
|
145
|
-
|
|
146
|
-
return '\n'.join(result)
|
|
147
|
-
|
|
148
|
-
@staticmethod
|
|
149
|
-
def get_column_count(text, delimiter=",", quote_char='"'):
|
|
150
|
-
"""Get the number of columns in the data."""
|
|
151
|
-
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
152
|
-
if rows:
|
|
153
|
-
return max(len(row) for row in rows)
|
|
154
|
-
return 0
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class ColumnToolsWidget(ttk.Frame):
|
|
158
|
-
"""Tabbed interface widget for column tools."""
|
|
159
|
-
|
|
160
|
-
def __init__(self, parent, app):
|
|
161
|
-
super().__init__(parent)
|
|
162
|
-
self.app = app
|
|
163
|
-
self.processor = ColumnToolsProcessor()
|
|
164
|
-
|
|
165
|
-
self.delimiter = tk.StringVar(value=",")
|
|
166
|
-
self.quote_char = tk.StringVar(value='"')
|
|
167
|
-
self.column_index = tk.IntVar(value=0)
|
|
168
|
-
self.column_order = tk.StringVar(value="")
|
|
169
|
-
self.new_column_value = tk.StringVar(value="")
|
|
170
|
-
|
|
171
|
-
self.create_widgets()
|
|
172
|
-
self.load_settings()
|
|
173
|
-
|
|
174
|
-
def create_widgets(self):
|
|
175
|
-
"""Creates the tabbed interface for column tools."""
|
|
176
|
-
# Common settings at top
|
|
177
|
-
settings_frame = ttk.LabelFrame(self, text="CSV Settings", padding=5)
|
|
178
|
-
settings_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
179
|
-
|
|
180
|
-
ttk.Label(settings_frame, text="Delimiter:").pack(side=tk.LEFT)
|
|
181
|
-
delimiter_combo = ttk.Combobox(settings_frame, textvariable=self.delimiter,
|
|
182
|
-
values=[",", ";", "\t", "|", " "], width=3)
|
|
183
|
-
delimiter_combo.pack(side=tk.LEFT, padx=5)
|
|
184
|
-
|
|
185
|
-
ttk.Label(settings_frame, text="Quote:").pack(side=tk.LEFT, padx=(10, 0))
|
|
186
|
-
quote_combo = ttk.Combobox(settings_frame, textvariable=self.quote_char,
|
|
187
|
-
values=['"', "'", ""], width=3)
|
|
188
|
-
quote_combo.pack(side=tk.LEFT, padx=5)
|
|
189
|
-
|
|
190
|
-
# Notebook for operations
|
|
191
|
-
self.notebook = ttk.Notebook(self)
|
|
192
|
-
self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
193
|
-
|
|
194
|
-
self.create_extract_tab()
|
|
195
|
-
self.create_reorder_tab()
|
|
196
|
-
self.create_delete_tab()
|
|
197
|
-
self.create_transpose_tab()
|
|
198
|
-
self.create_fixed_width_tab()
|
|
199
|
-
|
|
200
|
-
def create_extract_tab(self):
|
|
201
|
-
"""Creates the Extract Column tab."""
|
|
202
|
-
frame = ttk.Frame(self.notebook)
|
|
203
|
-
self.notebook.add(frame, text="Extract")
|
|
204
|
-
|
|
205
|
-
idx_frame = ttk.Frame(frame)
|
|
206
|
-
idx_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
207
|
-
|
|
208
|
-
ttk.Label(idx_frame, text="Column Index (0-based):").pack(side=tk.LEFT)
|
|
209
|
-
ttk.Spinbox(idx_frame, from_=0, to=100, width=5,
|
|
210
|
-
textvariable=self.column_index).pack(side=tk.LEFT, padx=5)
|
|
211
|
-
|
|
212
|
-
ttk.Button(frame, text="Extract Column",
|
|
213
|
-
command=lambda: self.process("extract")).pack(pady=10)
|
|
214
|
-
|
|
215
|
-
def create_reorder_tab(self):
|
|
216
|
-
"""Creates the Reorder Columns tab."""
|
|
217
|
-
frame = ttk.Frame(self.notebook)
|
|
218
|
-
self.notebook.add(frame, text="Reorder")
|
|
219
|
-
|
|
220
|
-
order_frame = ttk.Frame(frame)
|
|
221
|
-
order_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
222
|
-
|
|
223
|
-
ttk.Label(order_frame, text="New Order (e.g., 2,0,1):").pack(side=tk.LEFT)
|
|
224
|
-
ttk.Entry(order_frame, textvariable=self.column_order, width=20).pack(side=tk.LEFT, padx=5)
|
|
225
|
-
|
|
226
|
-
info = ttk.Label(frame, text="Enter column indices separated by commas.\n"
|
|
227
|
-
"Example: '2,0,1' moves column 2 first, then 0, then 1",
|
|
228
|
-
font=('TkDefaultFont', 8))
|
|
229
|
-
info.pack(pady=5)
|
|
230
|
-
|
|
231
|
-
ttk.Button(frame, text="Reorder Columns",
|
|
232
|
-
command=lambda: self.process("reorder")).pack(pady=10)
|
|
233
|
-
|
|
234
|
-
def create_delete_tab(self):
|
|
235
|
-
"""Creates the Delete Column tab."""
|
|
236
|
-
frame = ttk.Frame(self.notebook)
|
|
237
|
-
self.notebook.add(frame, text="Delete")
|
|
238
|
-
|
|
239
|
-
idx_frame = ttk.Frame(frame)
|
|
240
|
-
idx_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
241
|
-
|
|
242
|
-
ttk.Label(idx_frame, text="Column Index to Delete:").pack(side=tk.LEFT)
|
|
243
|
-
ttk.Spinbox(idx_frame, from_=0, to=100, width=5,
|
|
244
|
-
textvariable=self.column_index).pack(side=tk.LEFT, padx=5)
|
|
245
|
-
|
|
246
|
-
ttk.Button(frame, text="Delete Column",
|
|
247
|
-
command=lambda: self.process("delete")).pack(pady=10)
|
|
248
|
-
|
|
249
|
-
def create_transpose_tab(self):
|
|
250
|
-
"""Creates the Transpose tab."""
|
|
251
|
-
frame = ttk.Frame(self.notebook)
|
|
252
|
-
self.notebook.add(frame, text="Transpose")
|
|
253
|
-
|
|
254
|
-
info = ttk.Label(frame, text="Swaps rows and columns.\n"
|
|
255
|
-
"Row 1 becomes Column 1, etc.",
|
|
256
|
-
justify=tk.CENTER)
|
|
257
|
-
info.pack(pady=20)
|
|
258
|
-
|
|
259
|
-
ttk.Button(frame, text="Transpose",
|
|
260
|
-
command=lambda: self.process("transpose")).pack(pady=10)
|
|
261
|
-
|
|
262
|
-
def create_fixed_width_tab(self):
|
|
263
|
-
"""Creates the Fixed Width tab."""
|
|
264
|
-
frame = ttk.Frame(self.notebook)
|
|
265
|
-
self.notebook.add(frame, text="Fixed Width")
|
|
266
|
-
|
|
267
|
-
info = ttk.Label(frame, text="Converts CSV to fixed-width columns\n"
|
|
268
|
-
"for better readability.",
|
|
269
|
-
justify=tk.CENTER)
|
|
270
|
-
info.pack(pady=20)
|
|
271
|
-
|
|
272
|
-
ttk.Button(frame, text="Convert to Fixed Width",
|
|
273
|
-
command=lambda: self.process("fixed_width")).pack(pady=10)
|
|
274
|
-
|
|
275
|
-
def load_settings(self):
|
|
276
|
-
"""Load settings from the application."""
|
|
277
|
-
settings = self.app.settings.get("tool_settings", {}).get("Column Tools", {})
|
|
278
|
-
|
|
279
|
-
self.delimiter.set(settings.get("delimiter", ","))
|
|
280
|
-
self.quote_char.set(settings.get("quote_char", '"'))
|
|
281
|
-
|
|
282
|
-
def save_settings(self):
|
|
283
|
-
"""Save current settings to the application."""
|
|
284
|
-
if "Column Tools" not in self.app.settings["tool_settings"]:
|
|
285
|
-
self.app.settings["tool_settings"]["Column Tools"] = {}
|
|
286
|
-
|
|
287
|
-
self.app.settings["tool_settings"]["Column Tools"].update({
|
|
288
|
-
"delimiter": self.delimiter.get(),
|
|
289
|
-
"quote_char": self.quote_char.get()
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
self.app.save_settings()
|
|
293
|
-
|
|
294
|
-
def process(self, operation):
|
|
295
|
-
"""Process the input text."""
|
|
296
|
-
active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
|
|
297
|
-
input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
|
|
298
|
-
|
|
299
|
-
if not input_text.strip():
|
|
300
|
-
return
|
|
301
|
-
|
|
302
|
-
delimiter = self.delimiter.get()
|
|
303
|
-
if delimiter == "\\t":
|
|
304
|
-
delimiter = "\t"
|
|
305
|
-
quote_char = self.quote_char.get()
|
|
306
|
-
|
|
307
|
-
if operation == "extract":
|
|
308
|
-
result = ColumnToolsProcessor.extract_column(
|
|
309
|
-
input_text, self.column_index.get(), delimiter, quote_char)
|
|
310
|
-
elif operation == "reorder":
|
|
311
|
-
result = ColumnToolsProcessor.reorder_columns(
|
|
312
|
-
input_text, self.column_order.get(), delimiter, quote_char)
|
|
313
|
-
elif operation == "delete":
|
|
314
|
-
result = ColumnToolsProcessor.delete_column(
|
|
315
|
-
input_text, self.column_index.get(), delimiter, quote_char)
|
|
316
|
-
elif operation == "transpose":
|
|
317
|
-
result = ColumnToolsProcessor.transpose(input_text, delimiter, quote_char)
|
|
318
|
-
elif operation == "fixed_width":
|
|
319
|
-
result = ColumnToolsProcessor.to_fixed_width(input_text, delimiter, quote_char)
|
|
320
|
-
else:
|
|
321
|
-
result = input_text
|
|
322
|
-
|
|
323
|
-
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
324
|
-
active_output_tab.text.config(state="normal")
|
|
325
|
-
active_output_tab.text.delete("1.0", tk.END)
|
|
326
|
-
active_output_tab.text.insert("1.0", result)
|
|
327
|
-
active_output_tab.text.config(state="disabled")
|
|
328
|
-
|
|
329
|
-
self.save_settings()
|
|
330
|
-
self.app.update_all_stats()
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
class ColumnTools:
|
|
334
|
-
"""Main class for Column Tools integration."""
|
|
335
|
-
|
|
336
|
-
def __init__(self):
|
|
337
|
-
self.processor = ColumnToolsProcessor()
|
|
338
|
-
|
|
339
|
-
def create_widget(self, parent, app):
|
|
340
|
-
"""Create and return the Column Tools widget."""
|
|
341
|
-
return ColumnToolsWidget(parent, app)
|
|
342
|
-
|
|
343
|
-
def get_default_settings(self):
|
|
344
|
-
"""Return default settings for Column Tools."""
|
|
345
|
-
return {
|
|
346
|
-
"delimiter": ",",
|
|
347
|
-
"quote_char": '"',
|
|
348
|
-
"has_header": True
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
# BaseTool-compatible wrapper
|
|
353
|
-
try:
|
|
354
|
-
from tools.base_tool import ToolWithOptions
|
|
355
|
-
from typing import Dict, Any
|
|
356
|
-
import tkinter as tk
|
|
357
|
-
from tkinter import ttk
|
|
358
|
-
|
|
359
|
-
class ColumnToolsV2(ToolWithOptions):
|
|
360
|
-
"""
|
|
361
|
-
BaseTool-compatible version of ColumnTools.
|
|
362
|
-
"""
|
|
363
|
-
|
|
364
|
-
TOOL_NAME = "Column Tools"
|
|
365
|
-
TOOL_DESCRIPTION = "Manipulate CSV/column data"
|
|
366
|
-
TOOL_VERSION = "2.0.0"
|
|
367
|
-
|
|
368
|
-
OPTIONS = [
|
|
369
|
-
("Extract Column", "extract"),
|
|
370
|
-
("Delete Column", "delete"),
|
|
371
|
-
("Transpose", "transpose"),
|
|
372
|
-
("To Fixed Width", "fixed_width"),
|
|
373
|
-
]
|
|
374
|
-
OPTIONS_LABEL = "Operation"
|
|
375
|
-
USE_DROPDOWN = True
|
|
376
|
-
DEFAULT_OPTION = "extract"
|
|
377
|
-
|
|
378
|
-
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
379
|
-
"""Process CSV/column data."""
|
|
380
|
-
mode = settings.get("mode", "extract")
|
|
381
|
-
delimiter = settings.get("delimiter", ",")
|
|
382
|
-
column_index = settings.get("column_index", 0)
|
|
383
|
-
|
|
384
|
-
if mode == "extract":
|
|
385
|
-
return ColumnToolsProcessor.extract_column(input_text, column_index, delimiter)
|
|
386
|
-
elif mode == "delete":
|
|
387
|
-
return ColumnToolsProcessor.delete_column(input_text, column_index, delimiter)
|
|
388
|
-
elif mode == "transpose":
|
|
389
|
-
return ColumnToolsProcessor.transpose(input_text, delimiter)
|
|
390
|
-
elif mode == "fixed_width":
|
|
391
|
-
return ColumnToolsProcessor.to_fixed_width(input_text, delimiter)
|
|
392
|
-
else:
|
|
393
|
-
return input_text
|
|
394
|
-
|
|
395
|
-
except ImportError:
|
|
1
|
+
"""
|
|
2
|
+
Column/CSV Tools Module - Column manipulation utilities
|
|
3
|
+
|
|
4
|
+
This module provides column and CSV manipulation functionality
|
|
5
|
+
for the Pomera AI Commander application.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Extract Column: Extract specific column by index
|
|
9
|
+
- Reorder Columns: Rearrange column order
|
|
10
|
+
- Delete Column: Remove column by index
|
|
11
|
+
- Transpose: Swap rows and columns
|
|
12
|
+
- CSV to Fixed Width: Convert to fixed-width format
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import tkinter as tk
|
|
16
|
+
from tkinter import ttk
|
|
17
|
+
import csv
|
|
18
|
+
import io
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ColumnToolsProcessor:
|
|
22
|
+
"""Column tools processor with various column manipulation capabilities."""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def parse_csv(text, delimiter=",", quote_char='"'):
|
|
26
|
+
"""Parse CSV text into rows."""
|
|
27
|
+
reader = csv.reader(io.StringIO(text), delimiter=delimiter, quotechar=quote_char)
|
|
28
|
+
return list(reader)
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def format_csv(rows, delimiter=",", quote_char='"'):
|
|
32
|
+
"""Format rows back to CSV text."""
|
|
33
|
+
output = io.StringIO()
|
|
34
|
+
writer = csv.writer(output, delimiter=delimiter, quotechar=quote_char, quoting=csv.QUOTE_MINIMAL)
|
|
35
|
+
writer.writerows(rows)
|
|
36
|
+
return output.getvalue().strip()
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def extract_column(text, column_index, delimiter=",", quote_char='"'):
|
|
40
|
+
"""Extract a specific column by index (0-based)."""
|
|
41
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
42
|
+
result = []
|
|
43
|
+
|
|
44
|
+
for row in rows:
|
|
45
|
+
if column_index < len(row):
|
|
46
|
+
result.append(row[column_index])
|
|
47
|
+
else:
|
|
48
|
+
result.append("")
|
|
49
|
+
|
|
50
|
+
return '\n'.join(result)
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def reorder_columns(text, order, delimiter=",", quote_char='"'):
|
|
54
|
+
"""Reorder columns based on specified order (e.g., "2,0,1")."""
|
|
55
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
indices = [int(i.strip()) for i in order.split(',')]
|
|
59
|
+
except ValueError:
|
|
60
|
+
return "Error: Invalid column order format. Use comma-separated indices (e.g., '2,0,1')"
|
|
61
|
+
|
|
62
|
+
result = []
|
|
63
|
+
for row in rows:
|
|
64
|
+
new_row = []
|
|
65
|
+
for idx in indices:
|
|
66
|
+
if idx < len(row):
|
|
67
|
+
new_row.append(row[idx])
|
|
68
|
+
else:
|
|
69
|
+
new_row.append("")
|
|
70
|
+
result.append(new_row)
|
|
71
|
+
|
|
72
|
+
return ColumnToolsProcessor.format_csv(result, delimiter, quote_char)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def delete_column(text, column_index, delimiter=",", quote_char='"'):
|
|
76
|
+
"""Delete a column by index."""
|
|
77
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
78
|
+
result = []
|
|
79
|
+
|
|
80
|
+
for row in rows:
|
|
81
|
+
new_row = [cell for i, cell in enumerate(row) if i != column_index]
|
|
82
|
+
result.append(new_row)
|
|
83
|
+
|
|
84
|
+
return ColumnToolsProcessor.format_csv(result, delimiter, quote_char)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def add_column(text, column_index, value="", delimiter=",", quote_char='"'):
|
|
88
|
+
"""Add a new column at specified index."""
|
|
89
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
90
|
+
result = []
|
|
91
|
+
|
|
92
|
+
for row in rows:
|
|
93
|
+
new_row = list(row)
|
|
94
|
+
# Ensure row is long enough
|
|
95
|
+
while len(new_row) < column_index:
|
|
96
|
+
new_row.append("")
|
|
97
|
+
new_row.insert(column_index, value)
|
|
98
|
+
result.append(new_row)
|
|
99
|
+
|
|
100
|
+
return ColumnToolsProcessor.format_csv(result, delimiter, quote_char)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def transpose(text, delimiter=",", quote_char='"'):
|
|
104
|
+
"""Transpose rows and columns."""
|
|
105
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
106
|
+
|
|
107
|
+
if not rows:
|
|
108
|
+
return text
|
|
109
|
+
|
|
110
|
+
# Find max columns
|
|
111
|
+
max_cols = max(len(row) for row in rows)
|
|
112
|
+
|
|
113
|
+
# Pad rows to same length
|
|
114
|
+
padded = [row + [''] * (max_cols - len(row)) for row in rows]
|
|
115
|
+
|
|
116
|
+
# Transpose
|
|
117
|
+
transposed = list(map(list, zip(*padded)))
|
|
118
|
+
|
|
119
|
+
return ColumnToolsProcessor.format_csv(transposed, delimiter, quote_char)
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def to_fixed_width(text, delimiter=",", quote_char='"', padding=2):
|
|
123
|
+
"""Convert CSV to fixed-width format."""
|
|
124
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
125
|
+
|
|
126
|
+
if not rows:
|
|
127
|
+
return text
|
|
128
|
+
|
|
129
|
+
# Find max width for each column
|
|
130
|
+
max_cols = max(len(row) for row in rows)
|
|
131
|
+
col_widths = [0] * max_cols
|
|
132
|
+
|
|
133
|
+
for row in rows:
|
|
134
|
+
for i, cell in enumerate(row):
|
|
135
|
+
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
136
|
+
|
|
137
|
+
# Format with fixed widths
|
|
138
|
+
result = []
|
|
139
|
+
for row in rows:
|
|
140
|
+
formatted_cells = []
|
|
141
|
+
for i in range(max_cols):
|
|
142
|
+
cell = row[i] if i < len(row) else ""
|
|
143
|
+
formatted_cells.append(str(cell).ljust(col_widths[i] + padding))
|
|
144
|
+
result.append(''.join(formatted_cells).rstrip())
|
|
145
|
+
|
|
146
|
+
return '\n'.join(result)
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def get_column_count(text, delimiter=",", quote_char='"'):
|
|
150
|
+
"""Get the number of columns in the data."""
|
|
151
|
+
rows = ColumnToolsProcessor.parse_csv(text, delimiter, quote_char)
|
|
152
|
+
if rows:
|
|
153
|
+
return max(len(row) for row in rows)
|
|
154
|
+
return 0
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ColumnToolsWidget(ttk.Frame):
|
|
158
|
+
"""Tabbed interface widget for column tools."""
|
|
159
|
+
|
|
160
|
+
def __init__(self, parent, app):
|
|
161
|
+
super().__init__(parent)
|
|
162
|
+
self.app = app
|
|
163
|
+
self.processor = ColumnToolsProcessor()
|
|
164
|
+
|
|
165
|
+
self.delimiter = tk.StringVar(value=",")
|
|
166
|
+
self.quote_char = tk.StringVar(value='"')
|
|
167
|
+
self.column_index = tk.IntVar(value=0)
|
|
168
|
+
self.column_order = tk.StringVar(value="")
|
|
169
|
+
self.new_column_value = tk.StringVar(value="")
|
|
170
|
+
|
|
171
|
+
self.create_widgets()
|
|
172
|
+
self.load_settings()
|
|
173
|
+
|
|
174
|
+
def create_widgets(self):
|
|
175
|
+
"""Creates the tabbed interface for column tools."""
|
|
176
|
+
# Common settings at top
|
|
177
|
+
settings_frame = ttk.LabelFrame(self, text="CSV Settings", padding=5)
|
|
178
|
+
settings_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
179
|
+
|
|
180
|
+
ttk.Label(settings_frame, text="Delimiter:").pack(side=tk.LEFT)
|
|
181
|
+
delimiter_combo = ttk.Combobox(settings_frame, textvariable=self.delimiter,
|
|
182
|
+
values=[",", ";", "\t", "|", " "], width=3)
|
|
183
|
+
delimiter_combo.pack(side=tk.LEFT, padx=5)
|
|
184
|
+
|
|
185
|
+
ttk.Label(settings_frame, text="Quote:").pack(side=tk.LEFT, padx=(10, 0))
|
|
186
|
+
quote_combo = ttk.Combobox(settings_frame, textvariable=self.quote_char,
|
|
187
|
+
values=['"', "'", ""], width=3)
|
|
188
|
+
quote_combo.pack(side=tk.LEFT, padx=5)
|
|
189
|
+
|
|
190
|
+
# Notebook for operations
|
|
191
|
+
self.notebook = ttk.Notebook(self)
|
|
192
|
+
self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
193
|
+
|
|
194
|
+
self.create_extract_tab()
|
|
195
|
+
self.create_reorder_tab()
|
|
196
|
+
self.create_delete_tab()
|
|
197
|
+
self.create_transpose_tab()
|
|
198
|
+
self.create_fixed_width_tab()
|
|
199
|
+
|
|
200
|
+
def create_extract_tab(self):
|
|
201
|
+
"""Creates the Extract Column tab."""
|
|
202
|
+
frame = ttk.Frame(self.notebook)
|
|
203
|
+
self.notebook.add(frame, text="Extract")
|
|
204
|
+
|
|
205
|
+
idx_frame = ttk.Frame(frame)
|
|
206
|
+
idx_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
207
|
+
|
|
208
|
+
ttk.Label(idx_frame, text="Column Index (0-based):").pack(side=tk.LEFT)
|
|
209
|
+
ttk.Spinbox(idx_frame, from_=0, to=100, width=5,
|
|
210
|
+
textvariable=self.column_index).pack(side=tk.LEFT, padx=5)
|
|
211
|
+
|
|
212
|
+
ttk.Button(frame, text="Extract Column",
|
|
213
|
+
command=lambda: self.process("extract")).pack(pady=10)
|
|
214
|
+
|
|
215
|
+
def create_reorder_tab(self):
|
|
216
|
+
"""Creates the Reorder Columns tab."""
|
|
217
|
+
frame = ttk.Frame(self.notebook)
|
|
218
|
+
self.notebook.add(frame, text="Reorder")
|
|
219
|
+
|
|
220
|
+
order_frame = ttk.Frame(frame)
|
|
221
|
+
order_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
222
|
+
|
|
223
|
+
ttk.Label(order_frame, text="New Order (e.g., 2,0,1):").pack(side=tk.LEFT)
|
|
224
|
+
ttk.Entry(order_frame, textvariable=self.column_order, width=20).pack(side=tk.LEFT, padx=5)
|
|
225
|
+
|
|
226
|
+
info = ttk.Label(frame, text="Enter column indices separated by commas.\n"
|
|
227
|
+
"Example: '2,0,1' moves column 2 first, then 0, then 1",
|
|
228
|
+
font=('TkDefaultFont', 8))
|
|
229
|
+
info.pack(pady=5)
|
|
230
|
+
|
|
231
|
+
ttk.Button(frame, text="Reorder Columns",
|
|
232
|
+
command=lambda: self.process("reorder")).pack(pady=10)
|
|
233
|
+
|
|
234
|
+
def create_delete_tab(self):
|
|
235
|
+
"""Creates the Delete Column tab."""
|
|
236
|
+
frame = ttk.Frame(self.notebook)
|
|
237
|
+
self.notebook.add(frame, text="Delete")
|
|
238
|
+
|
|
239
|
+
idx_frame = ttk.Frame(frame)
|
|
240
|
+
idx_frame.pack(fill=tk.X, padx=5, pady=10)
|
|
241
|
+
|
|
242
|
+
ttk.Label(idx_frame, text="Column Index to Delete:").pack(side=tk.LEFT)
|
|
243
|
+
ttk.Spinbox(idx_frame, from_=0, to=100, width=5,
|
|
244
|
+
textvariable=self.column_index).pack(side=tk.LEFT, padx=5)
|
|
245
|
+
|
|
246
|
+
ttk.Button(frame, text="Delete Column",
|
|
247
|
+
command=lambda: self.process("delete")).pack(pady=10)
|
|
248
|
+
|
|
249
|
+
def create_transpose_tab(self):
|
|
250
|
+
"""Creates the Transpose tab."""
|
|
251
|
+
frame = ttk.Frame(self.notebook)
|
|
252
|
+
self.notebook.add(frame, text="Transpose")
|
|
253
|
+
|
|
254
|
+
info = ttk.Label(frame, text="Swaps rows and columns.\n"
|
|
255
|
+
"Row 1 becomes Column 1, etc.",
|
|
256
|
+
justify=tk.CENTER)
|
|
257
|
+
info.pack(pady=20)
|
|
258
|
+
|
|
259
|
+
ttk.Button(frame, text="Transpose",
|
|
260
|
+
command=lambda: self.process("transpose")).pack(pady=10)
|
|
261
|
+
|
|
262
|
+
def create_fixed_width_tab(self):
|
|
263
|
+
"""Creates the Fixed Width tab."""
|
|
264
|
+
frame = ttk.Frame(self.notebook)
|
|
265
|
+
self.notebook.add(frame, text="Fixed Width")
|
|
266
|
+
|
|
267
|
+
info = ttk.Label(frame, text="Converts CSV to fixed-width columns\n"
|
|
268
|
+
"for better readability.",
|
|
269
|
+
justify=tk.CENTER)
|
|
270
|
+
info.pack(pady=20)
|
|
271
|
+
|
|
272
|
+
ttk.Button(frame, text="Convert to Fixed Width",
|
|
273
|
+
command=lambda: self.process("fixed_width")).pack(pady=10)
|
|
274
|
+
|
|
275
|
+
def load_settings(self):
|
|
276
|
+
"""Load settings from the application."""
|
|
277
|
+
settings = self.app.settings.get("tool_settings", {}).get("Column Tools", {})
|
|
278
|
+
|
|
279
|
+
self.delimiter.set(settings.get("delimiter", ","))
|
|
280
|
+
self.quote_char.set(settings.get("quote_char", '"'))
|
|
281
|
+
|
|
282
|
+
def save_settings(self):
|
|
283
|
+
"""Save current settings to the application."""
|
|
284
|
+
if "Column Tools" not in self.app.settings["tool_settings"]:
|
|
285
|
+
self.app.settings["tool_settings"]["Column Tools"] = {}
|
|
286
|
+
|
|
287
|
+
self.app.settings["tool_settings"]["Column Tools"].update({
|
|
288
|
+
"delimiter": self.delimiter.get(),
|
|
289
|
+
"quote_char": self.quote_char.get()
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
self.app.save_settings()
|
|
293
|
+
|
|
294
|
+
def process(self, operation):
|
|
295
|
+
"""Process the input text."""
|
|
296
|
+
active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
|
|
297
|
+
input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
|
|
298
|
+
|
|
299
|
+
if not input_text.strip():
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
delimiter = self.delimiter.get()
|
|
303
|
+
if delimiter == "\\t":
|
|
304
|
+
delimiter = "\t"
|
|
305
|
+
quote_char = self.quote_char.get()
|
|
306
|
+
|
|
307
|
+
if operation == "extract":
|
|
308
|
+
result = ColumnToolsProcessor.extract_column(
|
|
309
|
+
input_text, self.column_index.get(), delimiter, quote_char)
|
|
310
|
+
elif operation == "reorder":
|
|
311
|
+
result = ColumnToolsProcessor.reorder_columns(
|
|
312
|
+
input_text, self.column_order.get(), delimiter, quote_char)
|
|
313
|
+
elif operation == "delete":
|
|
314
|
+
result = ColumnToolsProcessor.delete_column(
|
|
315
|
+
input_text, self.column_index.get(), delimiter, quote_char)
|
|
316
|
+
elif operation == "transpose":
|
|
317
|
+
result = ColumnToolsProcessor.transpose(input_text, delimiter, quote_char)
|
|
318
|
+
elif operation == "fixed_width":
|
|
319
|
+
result = ColumnToolsProcessor.to_fixed_width(input_text, delimiter, quote_char)
|
|
320
|
+
else:
|
|
321
|
+
result = input_text
|
|
322
|
+
|
|
323
|
+
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
324
|
+
active_output_tab.text.config(state="normal")
|
|
325
|
+
active_output_tab.text.delete("1.0", tk.END)
|
|
326
|
+
active_output_tab.text.insert("1.0", result)
|
|
327
|
+
active_output_tab.text.config(state="disabled")
|
|
328
|
+
|
|
329
|
+
self.save_settings()
|
|
330
|
+
self.app.update_all_stats()
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class ColumnTools:
|
|
334
|
+
"""Main class for Column Tools integration."""
|
|
335
|
+
|
|
336
|
+
def __init__(self):
|
|
337
|
+
self.processor = ColumnToolsProcessor()
|
|
338
|
+
|
|
339
|
+
def create_widget(self, parent, app):
|
|
340
|
+
"""Create and return the Column Tools widget."""
|
|
341
|
+
return ColumnToolsWidget(parent, app)
|
|
342
|
+
|
|
343
|
+
def get_default_settings(self):
|
|
344
|
+
"""Return default settings for Column Tools."""
|
|
345
|
+
return {
|
|
346
|
+
"delimiter": ",",
|
|
347
|
+
"quote_char": '"',
|
|
348
|
+
"has_header": True
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
# BaseTool-compatible wrapper
|
|
353
|
+
try:
|
|
354
|
+
from tools.base_tool import ToolWithOptions
|
|
355
|
+
from typing import Dict, Any
|
|
356
|
+
import tkinter as tk
|
|
357
|
+
from tkinter import ttk
|
|
358
|
+
|
|
359
|
+
class ColumnToolsV2(ToolWithOptions):
|
|
360
|
+
"""
|
|
361
|
+
BaseTool-compatible version of ColumnTools.
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
TOOL_NAME = "Column Tools"
|
|
365
|
+
TOOL_DESCRIPTION = "Manipulate CSV/column data"
|
|
366
|
+
TOOL_VERSION = "2.0.0"
|
|
367
|
+
|
|
368
|
+
OPTIONS = [
|
|
369
|
+
("Extract Column", "extract"),
|
|
370
|
+
("Delete Column", "delete"),
|
|
371
|
+
("Transpose", "transpose"),
|
|
372
|
+
("To Fixed Width", "fixed_width"),
|
|
373
|
+
]
|
|
374
|
+
OPTIONS_LABEL = "Operation"
|
|
375
|
+
USE_DROPDOWN = True
|
|
376
|
+
DEFAULT_OPTION = "extract"
|
|
377
|
+
|
|
378
|
+
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
379
|
+
"""Process CSV/column data."""
|
|
380
|
+
mode = settings.get("mode", "extract")
|
|
381
|
+
delimiter = settings.get("delimiter", ",")
|
|
382
|
+
column_index = settings.get("column_index", 0)
|
|
383
|
+
|
|
384
|
+
if mode == "extract":
|
|
385
|
+
return ColumnToolsProcessor.extract_column(input_text, column_index, delimiter)
|
|
386
|
+
elif mode == "delete":
|
|
387
|
+
return ColumnToolsProcessor.delete_column(input_text, column_index, delimiter)
|
|
388
|
+
elif mode == "transpose":
|
|
389
|
+
return ColumnToolsProcessor.transpose(input_text, delimiter)
|
|
390
|
+
elif mode == "fixed_width":
|
|
391
|
+
return ColumnToolsProcessor.to_fixed_width(input_text, delimiter)
|
|
392
|
+
else:
|
|
393
|
+
return input_text
|
|
394
|
+
|
|
395
|
+
except ImportError:
|
|
396
396
|
pass
|