-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsettings_manager.py
More file actions
540 lines (443 loc) · 23.5 KB
/
settings_manager.py
File metadata and controls
540 lines (443 loc) · 23.5 KB
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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
"""
Settings Manager for Contract Processing System
Author: Martin Bacigal, 01/2025 @ https://procureai.tech
License: MIT License
"""
import json
import logging
from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import dataclass, field, asdict
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import os
@dataclass
class AppSettings:
"""Application settings structure"""
# Processing settings
max_workers: int = 4
batch_size: int = 10
memory_limit_mb: int = 4096
processing_timeout: int = 300 # seconds
# File type settings
file_types: Dict[str, bool] = field(default_factory=lambda: {
"PDF": True,
"DOCX": True,
"DOC": True,
"XLSX": True,
"XLS": True
})
# Azure/OpenAI settings
use_azure: bool = False
azure_endpoint: str = ""
azure_deployment: str = ""
azure_api_key: str = ""
azure_api_version: str = "2024-02-01"
openai_api_key: str = ""
# Database settings
db_type: str = "postgresql"
db_host: str = "localhost"
db_port: int = 5432
db_name: str = "contract_processor"
db_username: str = "postgres"
db_password: str = ""
# Redis settings
redis_enabled: bool = False
redis_host: str = "localhost"
redis_port: int = 6379
# Neo4j settings
neo4j_enabled: bool = False
neo4j_uri: str = "bolt://localhost:7687"
neo4j_username: str = "neo4j"
neo4j_password: str = ""
# Output settings
output_directory: str = "output"
save_to_excel: bool = True
save_to_database: bool = True
# Document metadata settings
default_cw_prefix: str = "CW"
default_company_id: str = ""
default_company_group: str = ""
# Processing behavior
skip_processed_files: bool = True
auto_detect_relationships: bool = True
extract_all_metadata: bool = True
# UI settings
theme: str = "default"
show_advanced_options: bool = False
log_level: str = "INFO"
def to_dict(self) -> Dict[str, Any]:
"""Convert settings to dictionary"""
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'AppSettings':
"""Create settings from dictionary"""
return cls(**data)
class SettingsManager:
"""Manages application settings with JSON persistence"""
DEFAULT_SETTINGS_FILE = "contract_processor_settings.json"
def __init__(self, settings_file: Optional[Path] = None):
self.settings_file = Path(settings_file or self.DEFAULT_SETTINGS_FILE)
self.settings = self.load_settings()
self.recent_directories: List[str] = []
self.load_recent_directories()
def load_settings(self) -> AppSettings:
"""Load settings from JSON file"""
if self.settings_file.exists():
try:
with open(self.settings_file, 'r') as f:
data = json.load(f)
logging.info(f"Settings loaded from {self.settings_file}")
return AppSettings.from_dict(data.get('settings', {}))
except Exception as e:
logging.error(f"Error loading settings: {e}")
return AppSettings()
else:
logging.info("No settings file found, using defaults")
return AppSettings()
def save_settings(self):
"""Save settings to JSON file"""
try:
data = {
'settings': self.settings.to_dict(),
'recent_directories': self.recent_directories,
'version': '1.0'
}
with open(self.settings_file, 'w') as f:
json.dump(data, f, indent=2)
logging.info(f"Settings saved to {self.settings_file}")
except Exception as e:
logging.error(f"Error saving settings: {e}")
def load_recent_directories(self):
"""Load recent directories from settings file"""
if self.settings_file.exists():
try:
with open(self.settings_file, 'r') as f:
data = json.load(f)
self.recent_directories = data.get('recent_directories', [])
except:
pass
def add_recent_directory(self, directory: str):
"""Add a directory to recent directories list"""
if directory in self.recent_directories:
self.recent_directories.remove(directory)
self.recent_directories.insert(0, directory)
self.recent_directories = self.recent_directories[:10] # Keep only 10 recent
self.save_settings()
def get_database_config(self):
"""Get database configuration from settings"""
from database_manager import DatabaseConfig
return DatabaseConfig(
db_type=self.settings.db_type,
host=self.settings.db_host,
port=self.settings.db_port,
database=self.settings.db_name,
username=self.settings.db_username,
password=self.settings.db_password,
redis_host=self.settings.redis_host,
redis_port=self.settings.redis_port,
neo4j_uri=self.settings.neo4j_uri,
neo4j_username=self.settings.neo4j_username,
neo4j_password=self.settings.neo4j_password
)
def get_azure_config(self):
"""Get Azure configuration from settings"""
from contract_processor import AzureConfig
return AzureConfig(
endpoint=self.settings.azure_endpoint or os.getenv("AZURE_ENDPOINT", ""),
deployment=self.settings.azure_deployment or os.getenv("AZURE_DEPLOYMENT", ""),
api_key=self.settings.azure_api_key or os.getenv("AZURE_API_KEY", ""),
api_version=self.settings.azure_api_version
)
def get_openai_config(self):
"""Get OpenAI configuration from settings"""
from contract_processor import OpenAIConfig
return OpenAIConfig(
api_key=self.settings.openai_api_key or os.getenv("OPENAI_API_KEY", "")
)
def get_endpoint_config(self):
"""Get endpoint configuration from settings"""
from contract_processor import EndpointConfig
return EndpointConfig(
use_azure=self.settings.use_azure,
azure_config=self.get_azure_config(),
openai_config=self.get_openai_config()
)
class SettingsDialog(tk.Toplevel):
"""Settings dialog window"""
def __init__(self, parent, settings_manager: SettingsManager):
super().__init__(parent)
self.settings_manager = settings_manager
self.settings = settings_manager.settings
self.title("Settings")
self.geometry("600x700")
self.resizable(True, True)
# Create notebook for tabbed interface
self.notebook = ttk.Notebook(self)
self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Create tabs
self._create_general_tab()
self._create_api_tab()
self._create_database_tab()
self._create_processing_tab()
self._create_output_tab()
# Create buttons
self._create_buttons()
# Center the dialog
self.transient(parent)
self.grab_set()
self._center_window()
def _center_window(self):
"""Center the window on screen"""
self.update_idletasks()
width = self.winfo_width()
height = self.winfo_height()
x = (self.winfo_screenwidth() // 2) - (width // 2)
y = (self.winfo_screenheight() // 2) - (height // 2)
self.geometry(f'{width}x{height}+{x}+{y}')
def _create_general_tab(self):
"""Create general settings tab"""
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text="General")
# File types
file_types_frame = ttk.LabelFrame(frame, text="File Types to Process", padding=10)
file_types_frame.pack(fill=tk.X, padx=10, pady=5)
self.file_type_vars = {}
for file_type, enabled in self.settings.file_types.items():
var = tk.BooleanVar(value=enabled)
self.file_type_vars[file_type] = var
ttk.Checkbutton(file_types_frame, text=file_type, variable=var).pack(anchor=tk.W)
# Processing behavior
behavior_frame = ttk.LabelFrame(frame, text="Processing Behavior", padding=10)
behavior_frame.pack(fill=tk.X, padx=10, pady=5)
self.skip_processed_var = tk.BooleanVar(value=self.settings.skip_processed_files)
ttk.Checkbutton(behavior_frame, text="Skip already processed files",
variable=self.skip_processed_var).pack(anchor=tk.W)
self.auto_relationships_var = tk.BooleanVar(value=self.settings.auto_detect_relationships)
ttk.Checkbutton(behavior_frame, text="Auto-detect document relationships",
variable=self.auto_relationships_var).pack(anchor=tk.W)
self.extract_metadata_var = tk.BooleanVar(value=self.settings.extract_all_metadata)
ttk.Checkbutton(behavior_frame, text="Extract all metadata",
variable=self.extract_metadata_var).pack(anchor=tk.W)
# Logging
log_frame = ttk.LabelFrame(frame, text="Logging", padding=10)
log_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(log_frame, text="Log Level:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.log_level_var = tk.StringVar(value=self.settings.log_level)
log_combo = ttk.Combobox(log_frame, textvariable=self.log_level_var,
values=["DEBUG", "INFO", "WARNING", "ERROR"],
state="readonly", width=15)
log_combo.grid(row=0, column=1, padx=5)
def _create_api_tab(self):
"""Create API settings tab"""
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text="API Settings")
# API type selection
api_frame = ttk.LabelFrame(frame, text="API Configuration", padding=10)
api_frame.pack(fill=tk.X, padx=10, pady=5)
self.use_azure_var = tk.BooleanVar(value=self.settings.use_azure)
ttk.Radiobutton(api_frame, text="Use OpenAI", variable=self.use_azure_var,
value=False, command=self._toggle_api_fields).pack(anchor=tk.W)
ttk.Radiobutton(api_frame, text="Use Azure OpenAI", variable=self.use_azure_var,
value=True, command=self._toggle_api_fields).pack(anchor=tk.W)
# OpenAI settings
self.openai_frame = ttk.LabelFrame(frame, text="OpenAI Settings", padding=10)
self.openai_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(self.openai_frame, text="API Key:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.openai_key_var = tk.StringVar(value=self.settings.openai_api_key)
ttk.Entry(self.openai_frame, textvariable=self.openai_key_var, width=40, show="*").grid(row=0, column=1, padx=5)
# Azure settings
self.azure_frame = ttk.LabelFrame(frame, text="Azure OpenAI Settings", padding=10)
self.azure_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(self.azure_frame, text="Endpoint:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.azure_endpoint_var = tk.StringVar(value=self.settings.azure_endpoint)
ttk.Entry(self.azure_frame, textvariable=self.azure_endpoint_var, width=40).grid(row=0, column=1, padx=5)
ttk.Label(self.azure_frame, text="Deployment:").grid(row=1, column=0, sticky=tk.W, padx=5)
self.azure_deployment_var = tk.StringVar(value=self.settings.azure_deployment)
ttk.Entry(self.azure_frame, textvariable=self.azure_deployment_var, width=40).grid(row=1, column=1, padx=5)
ttk.Label(self.azure_frame, text="API Key:").grid(row=2, column=0, sticky=tk.W, padx=5)
self.azure_key_var = tk.StringVar(value=self.settings.azure_api_key)
ttk.Entry(self.azure_frame, textvariable=self.azure_key_var, width=40, show="*").grid(row=2, column=1, padx=5)
ttk.Label(self.azure_frame, text="API Version:").grid(row=3, column=0, sticky=tk.W, padx=5)
self.azure_version_var = tk.StringVar(value=self.settings.azure_api_version)
ttk.Entry(self.azure_frame, textvariable=self.azure_version_var, width=40).grid(row=3, column=1, padx=5)
self._toggle_api_fields()
def _create_database_tab(self):
"""Create database settings tab"""
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text="Database")
# Main database
db_frame = ttk.LabelFrame(frame, text="Database Connection", padding=10)
db_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(db_frame, text="Database Type:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.db_type_var = tk.StringVar(value=self.settings.db_type)
db_combo = ttk.Combobox(db_frame, textvariable=self.db_type_var,
values=["postgresql", "mysql", "sqlite"],
state="readonly", width=20)
db_combo.grid(row=0, column=1, padx=5)
ttk.Label(db_frame, text="Host:").grid(row=1, column=0, sticky=tk.W, padx=5)
self.db_host_var = tk.StringVar(value=self.settings.db_host)
ttk.Entry(db_frame, textvariable=self.db_host_var, width=30).grid(row=1, column=1, padx=5)
ttk.Label(db_frame, text="Port:").grid(row=2, column=0, sticky=tk.W, padx=5)
self.db_port_var = tk.IntVar(value=self.settings.db_port)
ttk.Entry(db_frame, textvariable=self.db_port_var, width=10).grid(row=2, column=1, sticky=tk.W, padx=5)
ttk.Label(db_frame, text="Database Name:").grid(row=3, column=0, sticky=tk.W, padx=5)
self.db_name_var = tk.StringVar(value=self.settings.db_name)
ttk.Entry(db_frame, textvariable=self.db_name_var, width=30).grid(row=3, column=1, padx=5)
ttk.Label(db_frame, text="Username:").grid(row=4, column=0, sticky=tk.W, padx=5)
self.db_username_var = tk.StringVar(value=self.settings.db_username)
ttk.Entry(db_frame, textvariable=self.db_username_var, width=30).grid(row=4, column=1, padx=5)
ttk.Label(db_frame, text="Password:").grid(row=5, column=0, sticky=tk.W, padx=5)
self.db_password_var = tk.StringVar(value=self.settings.db_password)
ttk.Entry(db_frame, textvariable=self.db_password_var, width=30, show="*").grid(row=5, column=1, padx=5)
# Redis settings
redis_frame = ttk.LabelFrame(frame, text="Redis Cache (Optional)", padding=10)
redis_frame.pack(fill=tk.X, padx=10, pady=5)
self.redis_enabled_var = tk.BooleanVar(value=self.settings.redis_enabled)
ttk.Checkbutton(redis_frame, text="Enable Redis caching",
variable=self.redis_enabled_var).grid(row=0, column=0, columnspan=2, sticky=tk.W)
ttk.Label(redis_frame, text="Host:").grid(row=1, column=0, sticky=tk.W, padx=5)
self.redis_host_var = tk.StringVar(value=self.settings.redis_host)
ttk.Entry(redis_frame, textvariable=self.redis_host_var, width=30).grid(row=1, column=1, padx=5)
ttk.Label(redis_frame, text="Port:").grid(row=2, column=0, sticky=tk.W, padx=5)
self.redis_port_var = tk.IntVar(value=self.settings.redis_port)
ttk.Entry(redis_frame, textvariable=self.redis_port_var, width=10).grid(row=2, column=1, sticky=tk.W, padx=5)
def _create_processing_tab(self):
"""Create processing settings tab"""
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text="Processing")
# Performance settings
perf_frame = ttk.LabelFrame(frame, text="Performance Settings", padding=10)
perf_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(perf_frame, text="Max Workers:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.max_workers_var = tk.IntVar(value=self.settings.max_workers)
ttk.Spinbox(perf_frame, from_=1, to=16, textvariable=self.max_workers_var,
width=10).grid(row=0, column=1, sticky=tk.W, padx=5)
ttk.Label(perf_frame, text="Batch Size:").grid(row=1, column=0, sticky=tk.W, padx=5)
self.batch_size_var = tk.IntVar(value=self.settings.batch_size)
ttk.Spinbox(perf_frame, from_=1, to=100, textvariable=self.batch_size_var,
width=10).grid(row=1, column=1, sticky=tk.W, padx=5)
ttk.Label(perf_frame, text="Memory Limit (MB):").grid(row=2, column=0, sticky=tk.W, padx=5)
self.memory_limit_var = tk.IntVar(value=self.settings.memory_limit_mb)
ttk.Spinbox(perf_frame, from_=512, to=16384, increment=512,
textvariable=self.memory_limit_var, width=10).grid(row=2, column=1, sticky=tk.W, padx=5)
ttk.Label(perf_frame, text="Processing Timeout (sec):").grid(row=3, column=0, sticky=tk.W, padx=5)
self.timeout_var = tk.IntVar(value=self.settings.processing_timeout)
ttk.Spinbox(perf_frame, from_=60, to=3600, increment=60,
textvariable=self.timeout_var, width=10).grid(row=3, column=1, sticky=tk.W, padx=5)
# Document metadata
meta_frame = ttk.LabelFrame(frame, text="Document Metadata Defaults", padding=10)
meta_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(meta_frame, text="CW Prefix:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.cw_prefix_var = tk.StringVar(value=self.settings.default_cw_prefix)
ttk.Entry(meta_frame, textvariable=self.cw_prefix_var, width=20).grid(row=0, column=1, padx=5)
ttk.Label(meta_frame, text="Company ID:").grid(row=1, column=0, sticky=tk.W, padx=5)
self.company_id_var = tk.StringVar(value=self.settings.default_company_id)
ttk.Entry(meta_frame, textvariable=self.company_id_var, width=20).grid(row=1, column=1, padx=5)
ttk.Label(meta_frame, text="Company Group:").grid(row=2, column=0, sticky=tk.W, padx=5)
self.company_group_var = tk.StringVar(value=self.settings.default_company_group)
ttk.Entry(meta_frame, textvariable=self.company_group_var, width=20).grid(row=2, column=1, padx=5)
def _create_output_tab(self):
"""Create output settings tab"""
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text="Output")
# Output options
output_frame = ttk.LabelFrame(frame, text="Output Options", padding=10)
output_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(output_frame, text="Output Directory:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.output_dir_var = tk.StringVar(value=self.settings.output_directory)
ttk.Entry(output_frame, textvariable=self.output_dir_var, width=30).grid(row=0, column=1, padx=5)
ttk.Button(output_frame, text="Browse",
command=self._browse_output_dir).grid(row=0, column=2, padx=5)
self.save_excel_var = tk.BooleanVar(value=self.settings.save_to_excel)
ttk.Checkbutton(output_frame, text="Save results to Excel",
variable=self.save_excel_var).grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=5)
self.save_db_var = tk.BooleanVar(value=self.settings.save_to_database)
ttk.Checkbutton(output_frame, text="Save results to database",
variable=self.save_db_var).grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=5)
def _create_buttons(self):
"""Create dialog buttons"""
button_frame = ttk.Frame(self)
button_frame.pack(fill=tk.X, padx=10, pady=10)
ttk.Button(button_frame, text="Save", command=self._save_settings).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="Cancel", command=self.destroy).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="Test Database", command=self._test_database).pack(side=tk.LEFT, padx=5)
def _toggle_api_fields(self):
"""Toggle between OpenAI and Azure fields"""
if self.use_azure_var.get():
self._disable_frame(self.openai_frame)
self._enable_frame(self.azure_frame)
else:
self._enable_frame(self.openai_frame)
self._disable_frame(self.azure_frame)
def _disable_frame(self, frame):
"""Disable all widgets in a frame"""
for child in frame.winfo_children():
child.configure(state="disabled")
def _enable_frame(self, frame):
"""Enable all widgets in a frame"""
for child in frame.winfo_children():
if isinstance(child, (ttk.Entry, ttk.Combobox, ttk.Spinbox)):
child.configure(state="normal")
elif isinstance(child, ttk.Checkbutton):
child.configure(state="normal")
def _browse_output_dir(self):
"""Browse for output directory"""
directory = filedialog.askdirectory(initialdir=self.output_dir_var.get())
if directory:
self.output_dir_var.set(directory)
def _test_database(self):
"""Test database connection"""
try:
from database_manager import DatabaseConfig, DatabaseManager
config = DatabaseConfig(
db_type=self.db_type_var.get(),
host=self.db_host_var.get(),
port=self.db_port_var.get(),
database=self.db_name_var.get(),
username=self.db_username_var.get(),
password=self.db_password_var.get()
)
db_manager = DatabaseManager(config)
db_manager.initialize()
db_manager.close()
messagebox.showinfo("Success", "Database connection successful!")
except Exception as e:
messagebox.showerror("Error", f"Database connection failed:\n{str(e)}")
def _save_settings(self):
"""Save settings and close dialog"""
# Update settings from UI
self.settings.file_types = {k: v.get() for k, v in self.file_type_vars.items()}
self.settings.skip_processed_files = self.skip_processed_var.get()
self.settings.auto_detect_relationships = self.auto_relationships_var.get()
self.settings.extract_all_metadata = self.extract_metadata_var.get()
self.settings.log_level = self.log_level_var.get()
self.settings.use_azure = self.use_azure_var.get()
self.settings.openai_api_key = self.openai_key_var.get()
self.settings.azure_endpoint = self.azure_endpoint_var.get()
self.settings.azure_deployment = self.azure_deployment_var.get()
self.settings.azure_api_key = self.azure_key_var.get()
self.settings.azure_api_version = self.azure_version_var.get()
self.settings.db_type = self.db_type_var.get()
self.settings.db_host = self.db_host_var.get()
self.settings.db_port = self.db_port_var.get()
self.settings.db_name = self.db_name_var.get()
self.settings.db_username = self.db_username_var.get()
self.settings.db_password = self.db_password_var.get()
self.settings.redis_enabled = self.redis_enabled_var.get()
self.settings.redis_host = self.redis_host_var.get()
self.settings.redis_port = self.redis_port_var.get()
self.settings.max_workers = self.max_workers_var.get()
self.settings.batch_size = self.batch_size_var.get()
self.settings.memory_limit_mb = self.memory_limit_var.get()
self.settings.processing_timeout = self.timeout_var.get()
self.settings.default_cw_prefix = self.cw_prefix_var.get()
self.settings.default_company_id = self.company_id_var.get()
self.settings.default_company_group = self.company_group_var.get()
self.settings.output_directory = self.output_dir_var.get()
self.settings.save_to_excel = self.save_excel_var.get()
self.settings.save_to_database = self.save_db_var.get()
# Save to file
self.settings_manager.save_settings()
messagebox.showinfo("Success", "Settings saved successfully!")
self.destroy()