supervertaler 1.9.163__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.
- Supervertaler.py +48473 -0
- modules/__init__.py +10 -0
- modules/ai_actions.py +964 -0
- modules/ai_attachment_manager.py +343 -0
- modules/ai_file_viewer_dialog.py +210 -0
- modules/autofingers_engine.py +466 -0
- modules/cafetran_docx_handler.py +379 -0
- modules/config_manager.py +469 -0
- modules/database_manager.py +1911 -0
- modules/database_migrations.py +417 -0
- modules/dejavurtf_handler.py +779 -0
- modules/document_analyzer.py +427 -0
- modules/docx_handler.py +689 -0
- modules/encoding_repair.py +319 -0
- modules/encoding_repair_Qt.py +393 -0
- modules/encoding_repair_ui.py +481 -0
- modules/feature_manager.py +350 -0
- modules/figure_context_manager.py +340 -0
- modules/file_dialog_helper.py +148 -0
- modules/find_replace.py +164 -0
- modules/find_replace_qt.py +457 -0
- modules/glossary_manager.py +433 -0
- modules/image_extractor.py +188 -0
- modules/keyboard_shortcuts_widget.py +571 -0
- modules/llm_clients.py +1211 -0
- modules/llm_leaderboard.py +737 -0
- modules/llm_superbench_ui.py +1401 -0
- modules/local_llm_setup.py +1104 -0
- modules/model_update_dialog.py +381 -0
- modules/model_version_checker.py +373 -0
- modules/mqxliff_handler.py +638 -0
- modules/non_translatables_manager.py +743 -0
- modules/pdf_rescue_Qt.py +1822 -0
- modules/pdf_rescue_tkinter.py +909 -0
- modules/phrase_docx_handler.py +516 -0
- modules/project_home_panel.py +209 -0
- modules/prompt_assistant.py +357 -0
- modules/prompt_library.py +689 -0
- modules/prompt_library_migration.py +447 -0
- modules/quick_access_sidebar.py +282 -0
- modules/ribbon_widget.py +597 -0
- modules/sdlppx_handler.py +874 -0
- modules/setup_wizard.py +353 -0
- modules/shortcut_manager.py +932 -0
- modules/simple_segmenter.py +128 -0
- modules/spellcheck_manager.py +727 -0
- modules/statuses.py +207 -0
- modules/style_guide_manager.py +315 -0
- modules/superbench_ui.py +1319 -0
- modules/superbrowser.py +329 -0
- modules/supercleaner.py +600 -0
- modules/supercleaner_ui.py +444 -0
- modules/superdocs.py +19 -0
- modules/superdocs_viewer_qt.py +382 -0
- modules/superlookup.py +252 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +351 -0
- modules/term_extractor.py +270 -0
- modules/termbase_entry_editor.py +842 -0
- modules/termbase_import_export.py +488 -0
- modules/termbase_manager.py +1060 -0
- modules/termview_widget.py +1176 -0
- modules/theme_manager.py +499 -0
- modules/tm_editor_dialog.py +99 -0
- modules/tm_manager_qt.py +1280 -0
- modules/tm_metadata_manager.py +545 -0
- modules/tmx_editor.py +1461 -0
- modules/tmx_editor_qt.py +2784 -0
- modules/tmx_generator.py +284 -0
- modules/tracked_changes.py +900 -0
- modules/trados_docx_handler.py +430 -0
- modules/translation_memory.py +715 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +659 -0
- modules/unified_prompt_manager_qt.py +3951 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.163.dist-info/METADATA +906 -0
- supervertaler-1.9.163.dist-info/RECORD +85 -0
- supervertaler-1.9.163.dist-info/WHEEL +5 -0
- supervertaler-1.9.163.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.163.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.163.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Migration Script: 4-Layer to Unified Prompt Library
|
|
3
|
+
|
|
4
|
+
Migrates from old structure:
|
|
5
|
+
1_System_Prompts/
|
|
6
|
+
2_Domain_Prompts/
|
|
7
|
+
3_Project_Prompts/
|
|
8
|
+
4_Style_Guides/
|
|
9
|
+
|
|
10
|
+
To new unified structure:
|
|
11
|
+
Library/
|
|
12
|
+
Style Guides/
|
|
13
|
+
Domain Expertise/
|
|
14
|
+
Project Prompts/
|
|
15
|
+
Active Projects/
|
|
16
|
+
|
|
17
|
+
System Prompts are moved to settings storage (handled separately).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import shutil
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PromptLibraryMigration:
|
|
27
|
+
"""Handles one-time migration from 4-layer to unified structure"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, prompt_library_dir: str, log_callback=None):
|
|
30
|
+
"""
|
|
31
|
+
Args:
|
|
32
|
+
prompt_library_dir: Path to user_data/prompt_library
|
|
33
|
+
log_callback: Function for logging
|
|
34
|
+
"""
|
|
35
|
+
self.prompt_library_dir = Path(prompt_library_dir)
|
|
36
|
+
self.log = log_callback if log_callback else print
|
|
37
|
+
|
|
38
|
+
# Old structure paths
|
|
39
|
+
self.old_system_prompts = self.prompt_library_dir / "1_System_Prompts"
|
|
40
|
+
self.old_domain_prompts = self.prompt_library_dir / "2_Domain_Prompts"
|
|
41
|
+
self.old_project_prompts = self.prompt_library_dir / "3_Project_Prompts"
|
|
42
|
+
self.old_style_guides = self.prompt_library_dir / "4_Style_Guides"
|
|
43
|
+
|
|
44
|
+
# New unified library
|
|
45
|
+
self.new_library = self.prompt_library_dir / "Library"
|
|
46
|
+
|
|
47
|
+
# Migration flag
|
|
48
|
+
self.migration_completed_file = self.prompt_library_dir / ".migration_completed"
|
|
49
|
+
|
|
50
|
+
def needs_migration(self) -> bool:
|
|
51
|
+
"""Check if migration is needed"""
|
|
52
|
+
# Already migrated?
|
|
53
|
+
if self.migration_completed_file.exists():
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
# Old structure exists?
|
|
57
|
+
has_old_structure = (
|
|
58
|
+
self.old_domain_prompts.exists() or
|
|
59
|
+
self.old_project_prompts.exists() or
|
|
60
|
+
self.old_style_guides.exists()
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return has_old_structure
|
|
64
|
+
|
|
65
|
+
def migrate(self) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Perform migration from old to new structure.
|
|
68
|
+
|
|
69
|
+
Steps:
|
|
70
|
+
1. Create new Library/ structure
|
|
71
|
+
2. Copy Domain Prompts → Library/Domain Expertise/
|
|
72
|
+
3. Copy Project Prompts → Library/Project Prompts/
|
|
73
|
+
4. Copy Style Guides → Library/Style Guides/
|
|
74
|
+
5. Backup old folders with .old extension
|
|
75
|
+
6. Mark migration as completed
|
|
76
|
+
|
|
77
|
+
Note: System Prompts are NOT migrated here (moved to settings separately)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
True if successful
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
self.log("=" * 60)
|
|
84
|
+
self.log("🔄 Starting Prompt Library Migration")
|
|
85
|
+
self.log("=" * 60)
|
|
86
|
+
|
|
87
|
+
# Create new library structure
|
|
88
|
+
self.log("\n📁 Creating new unified library structure...")
|
|
89
|
+
self.new_library.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
|
|
91
|
+
# Migrate each layer
|
|
92
|
+
migrated_counts = {}
|
|
93
|
+
|
|
94
|
+
# Layer 2: Domain Prompts → Domain Expertise
|
|
95
|
+
if self.old_domain_prompts.exists():
|
|
96
|
+
self.log("\n📚 Migrating Domain Prompts...")
|
|
97
|
+
count = self._migrate_folder(
|
|
98
|
+
self.old_domain_prompts,
|
|
99
|
+
self.new_library / "Domain Expertise",
|
|
100
|
+
add_metadata={'folder': 'Domain Expertise'}
|
|
101
|
+
)
|
|
102
|
+
migrated_counts['domain'] = count
|
|
103
|
+
self.log(f" ✓ Migrated {count} domain prompts")
|
|
104
|
+
|
|
105
|
+
# Layer 3: Project Prompts → Project Prompts
|
|
106
|
+
if self.old_project_prompts.exists():
|
|
107
|
+
self.log("\n📋 Migrating Project Prompts...")
|
|
108
|
+
count = self._migrate_folder(
|
|
109
|
+
self.old_project_prompts,
|
|
110
|
+
self.new_library / "Project Prompts",
|
|
111
|
+
add_metadata={'folder': 'Project Prompts'}
|
|
112
|
+
)
|
|
113
|
+
migrated_counts['project'] = count
|
|
114
|
+
self.log(f" ✓ Migrated {count} project prompts")
|
|
115
|
+
|
|
116
|
+
# Layer 4: Style Guides → Style Guides
|
|
117
|
+
if self.old_style_guides.exists():
|
|
118
|
+
self.log("\n🎨 Migrating Style Guides...")
|
|
119
|
+
count = self._migrate_folder(
|
|
120
|
+
self.old_style_guides,
|
|
121
|
+
self.new_library / "Style Guides",
|
|
122
|
+
add_metadata={'folder': 'Style Guides'}
|
|
123
|
+
)
|
|
124
|
+
migrated_counts['style'] = count
|
|
125
|
+
self.log(f" ✓ Migrated {count} style guides")
|
|
126
|
+
|
|
127
|
+
# Backup old folders
|
|
128
|
+
self.log("\n💾 Creating backups of old folders...")
|
|
129
|
+
self._backup_old_folders()
|
|
130
|
+
|
|
131
|
+
# Create Active Projects folder for user
|
|
132
|
+
active_projects = self.new_library / "Active Projects"
|
|
133
|
+
active_projects.mkdir(exist_ok=True)
|
|
134
|
+
self.log(f" ✓ Created 'Active Projects' folder for user-created prompts")
|
|
135
|
+
|
|
136
|
+
# Mark migration as completed
|
|
137
|
+
self.migration_completed_file.write_text(
|
|
138
|
+
f"Migration completed on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
|
139
|
+
f"Migrated: {sum(migrated_counts.values())} prompts\n"
|
|
140
|
+
f"Details: {migrated_counts}\n",
|
|
141
|
+
encoding='utf-8'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Summary
|
|
145
|
+
total = sum(migrated_counts.values())
|
|
146
|
+
self.log("\n" + "=" * 60)
|
|
147
|
+
self.log(f"✅ Migration Complete! Migrated {total} prompts")
|
|
148
|
+
self.log("=" * 60)
|
|
149
|
+
self.log(f"\nDomain Expertise: {migrated_counts.get('domain', 0)} prompts")
|
|
150
|
+
self.log(f"Project Prompts: {migrated_counts.get('project', 0)} prompts")
|
|
151
|
+
self.log(f"Style Guides: {migrated_counts.get('style', 0)} prompts")
|
|
152
|
+
self.log(f"\n📂 New location: {self.new_library}")
|
|
153
|
+
self.log(f"💾 Backups: Old folders renamed with .old extension")
|
|
154
|
+
self.log("\n⚠️ Note: System Prompts moved to Settings (not in Library)")
|
|
155
|
+
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
except Exception as e:
|
|
159
|
+
self.log(f"\n❌ Migration failed: {e}")
|
|
160
|
+
import traceback
|
|
161
|
+
self.log(traceback.format_exc())
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
def _migrate_folder(self, source: Path, destination: Path, add_metadata: dict = None) -> int:
|
|
165
|
+
"""
|
|
166
|
+
Migrate prompts from source to destination folder.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
source: Source folder path
|
|
170
|
+
destination: Destination folder path
|
|
171
|
+
add_metadata: Additional metadata to add to YAML frontmatter
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Number of files migrated
|
|
175
|
+
"""
|
|
176
|
+
count = 0
|
|
177
|
+
|
|
178
|
+
if not source.exists():
|
|
179
|
+
return count
|
|
180
|
+
|
|
181
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
|
|
183
|
+
for item in source.iterdir():
|
|
184
|
+
# Skip hidden files, README, and format_examples
|
|
185
|
+
if item.name.startswith('.') or item.name == 'README.md' or item.name == 'format_examples':
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
# Handle subdirectories (recursive)
|
|
189
|
+
if item.is_dir():
|
|
190
|
+
sub_dest = destination / item.name
|
|
191
|
+
count += self._migrate_folder(item, sub_dest, add_metadata)
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
# Only process .svprompt, .md, .txt, .json files
|
|
195
|
+
if item.suffix.lower() not in ['.svprompt', '.md', '.txt', '.json']:
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
# Convert JSON to Markdown if needed
|
|
200
|
+
if item.suffix.lower() == '.json':
|
|
201
|
+
count += self._migrate_json_prompt(item, destination, add_metadata)
|
|
202
|
+
else:
|
|
203
|
+
# Copy and update metadata if needed (.svprompt, .md, .txt are all markdown-based)
|
|
204
|
+
count += self._migrate_markdown_prompt(item, destination, add_metadata)
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
self.log(f" ⚠ Failed to migrate {item.name}: {e}")
|
|
208
|
+
|
|
209
|
+
return count
|
|
210
|
+
|
|
211
|
+
def _migrate_markdown_prompt(self, source: Path, destination: Path, add_metadata: dict = None) -> int:
|
|
212
|
+
"""Migrate a Markdown prompt file, adding metadata if needed"""
|
|
213
|
+
try:
|
|
214
|
+
content = source.read_text(encoding='utf-8')
|
|
215
|
+
|
|
216
|
+
# Parse existing frontmatter if present
|
|
217
|
+
if content.startswith('---'):
|
|
218
|
+
content = content[3:].lstrip('\n')
|
|
219
|
+
|
|
220
|
+
if '---' in content:
|
|
221
|
+
frontmatter_str, prompt_content = content.split('---', 1)
|
|
222
|
+
prompt_content = prompt_content.lstrip('\n')
|
|
223
|
+
else:
|
|
224
|
+
frontmatter_str = ""
|
|
225
|
+
prompt_content = content
|
|
226
|
+
else:
|
|
227
|
+
frontmatter_str = ""
|
|
228
|
+
prompt_content = content
|
|
229
|
+
|
|
230
|
+
# Parse existing metadata
|
|
231
|
+
metadata = self._parse_simple_yaml(frontmatter_str) if frontmatter_str else {}
|
|
232
|
+
|
|
233
|
+
# Add new metadata fields
|
|
234
|
+
if add_metadata:
|
|
235
|
+
metadata.update(add_metadata)
|
|
236
|
+
|
|
237
|
+
# Ensure required fields
|
|
238
|
+
if 'name' not in metadata:
|
|
239
|
+
metadata['name'] = source.stem
|
|
240
|
+
|
|
241
|
+
metadata.setdefault('favorite', False)
|
|
242
|
+
metadata.setdefault('quick_run', False)
|
|
243
|
+
metadata.setdefault('tags', [])
|
|
244
|
+
|
|
245
|
+
if 'created' not in metadata:
|
|
246
|
+
metadata['created'] = datetime.now().strftime("%Y-%m-%d")
|
|
247
|
+
|
|
248
|
+
# Build new file with updated metadata
|
|
249
|
+
new_content = self._build_markdown_with_frontmatter(metadata, prompt_content)
|
|
250
|
+
|
|
251
|
+
# Write to destination
|
|
252
|
+
dest_file = destination / source.name
|
|
253
|
+
dest_file.write_text(new_content, encoding='utf-8')
|
|
254
|
+
|
|
255
|
+
return 1
|
|
256
|
+
|
|
257
|
+
except Exception as e:
|
|
258
|
+
self.log(f" ⚠ Error migrating {source.name}: {e}")
|
|
259
|
+
return 0
|
|
260
|
+
|
|
261
|
+
def _migrate_json_prompt(self, source: Path, destination: Path, add_metadata: dict = None) -> int:
|
|
262
|
+
"""Migrate a JSON prompt file, converting to Markdown"""
|
|
263
|
+
try:
|
|
264
|
+
import json
|
|
265
|
+
|
|
266
|
+
with open(source, 'r', encoding='utf-8') as f:
|
|
267
|
+
data = json.load(f)
|
|
268
|
+
|
|
269
|
+
# Extract content (prefer translate_prompt)
|
|
270
|
+
content = data.get('translate_prompt', data.get('content', ''))
|
|
271
|
+
|
|
272
|
+
if not content:
|
|
273
|
+
self.log(f" ⚠ No content in {source.name}, skipping")
|
|
274
|
+
return 0
|
|
275
|
+
|
|
276
|
+
# Build metadata
|
|
277
|
+
metadata = {
|
|
278
|
+
'name': data.get('name', source.stem),
|
|
279
|
+
'description': data.get('description', ''),
|
|
280
|
+
'domain': data.get('domain', ''),
|
|
281
|
+
'version': data.get('version', '1.0'),
|
|
282
|
+
'task_type': data.get('task_type', 'Translation'),
|
|
283
|
+
'favorite': False,
|
|
284
|
+
'quick_run': False,
|
|
285
|
+
'tags': [],
|
|
286
|
+
'created': data.get('created', datetime.now().strftime("%Y-%m-%d"))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# Add migration-specific metadata
|
|
290
|
+
if add_metadata:
|
|
291
|
+
metadata.update(add_metadata)
|
|
292
|
+
|
|
293
|
+
# Build Markdown content
|
|
294
|
+
new_content = self._build_markdown_with_frontmatter(metadata, content)
|
|
295
|
+
|
|
296
|
+
# Write as .md file
|
|
297
|
+
dest_file = destination / f"{source.stem}.md"
|
|
298
|
+
dest_file.write_text(new_content, encoding='utf-8')
|
|
299
|
+
|
|
300
|
+
self.log(f" ✓ Converted {source.name} → {dest_file.name}")
|
|
301
|
+
return 1
|
|
302
|
+
|
|
303
|
+
except Exception as e:
|
|
304
|
+
self.log(f" ⚠ Error converting {source.name}: {e}")
|
|
305
|
+
return 0
|
|
306
|
+
|
|
307
|
+
def _build_markdown_with_frontmatter(self, metadata: dict, content: str) -> str:
|
|
308
|
+
"""Build Markdown file content with YAML frontmatter"""
|
|
309
|
+
lines = ['---']
|
|
310
|
+
|
|
311
|
+
# Ordered fields
|
|
312
|
+
field_order = [
|
|
313
|
+
'name', 'description', 'domain', 'version', 'task_type',
|
|
314
|
+
'favorite', 'quick_run', 'folder', 'tags', 'created', 'modified'
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
for field in field_order:
|
|
318
|
+
if field in metadata:
|
|
319
|
+
value = metadata[field]
|
|
320
|
+
|
|
321
|
+
if isinstance(value, bool):
|
|
322
|
+
lines.append(f'{field}: {str(value).lower()}')
|
|
323
|
+
elif isinstance(value, list):
|
|
324
|
+
if value: # Non-empty list
|
|
325
|
+
items = ', '.join([f'"{item}"' for item in value])
|
|
326
|
+
lines.append(f'{field}: [{items}]')
|
|
327
|
+
else:
|
|
328
|
+
lines.append(f'{field}: []')
|
|
329
|
+
elif isinstance(value, str):
|
|
330
|
+
lines.append(f'{field}: "{value}"')
|
|
331
|
+
else:
|
|
332
|
+
lines.append(f'{field}: {value}')
|
|
333
|
+
|
|
334
|
+
lines.append('---')
|
|
335
|
+
|
|
336
|
+
return '\n'.join(lines) + '\n\n' + content.strip()
|
|
337
|
+
|
|
338
|
+
def _parse_simple_yaml(self, yaml_str: str) -> dict:
|
|
339
|
+
"""Simple YAML parser for frontmatter"""
|
|
340
|
+
data = {}
|
|
341
|
+
|
|
342
|
+
for line in yaml_str.strip().split('\n'):
|
|
343
|
+
line = line.strip()
|
|
344
|
+
if not line or line.startswith('#') or ':' not in line:
|
|
345
|
+
continue
|
|
346
|
+
|
|
347
|
+
key, value = line.split(':', 1)
|
|
348
|
+
key = key.strip()
|
|
349
|
+
value = value.strip()
|
|
350
|
+
|
|
351
|
+
# Handle arrays
|
|
352
|
+
if value.startswith('[') and value.endswith(']'):
|
|
353
|
+
array_str = value[1:-1]
|
|
354
|
+
items = [item.strip().strip('"').strip("'") for item in array_str.split(',')]
|
|
355
|
+
data[key] = [item for item in items if item]
|
|
356
|
+
continue
|
|
357
|
+
|
|
358
|
+
# Handle booleans
|
|
359
|
+
if value.lower() in ['true', 'false']:
|
|
360
|
+
data[key] = value.lower() == 'true'
|
|
361
|
+
continue
|
|
362
|
+
|
|
363
|
+
# Remove quotes
|
|
364
|
+
if (value.startswith('"') and value.endswith('"')) or \
|
|
365
|
+
(value.startswith("'") and value.endswith("'")):
|
|
366
|
+
value = value[1:-1]
|
|
367
|
+
|
|
368
|
+
data[key] = value
|
|
369
|
+
|
|
370
|
+
return data
|
|
371
|
+
|
|
372
|
+
def _backup_old_folders(self):
|
|
373
|
+
"""Rename old folders with .old extension"""
|
|
374
|
+
folders_to_backup = [
|
|
375
|
+
self.old_system_prompts,
|
|
376
|
+
self.old_domain_prompts,
|
|
377
|
+
self.old_project_prompts,
|
|
378
|
+
self.old_style_guides
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
for folder in folders_to_backup:
|
|
382
|
+
if folder.exists():
|
|
383
|
+
backup_path = folder.with_suffix('.old')
|
|
384
|
+
|
|
385
|
+
# If backup already exists, remove it first
|
|
386
|
+
if backup_path.exists():
|
|
387
|
+
shutil.rmtree(backup_path)
|
|
388
|
+
|
|
389
|
+
folder.rename(backup_path)
|
|
390
|
+
self.log(f" ✓ Backed up: {folder.name} → {backup_path.name}")
|
|
391
|
+
|
|
392
|
+
def rollback(self):
|
|
393
|
+
"""Rollback migration (restore from .old backups)"""
|
|
394
|
+
try:
|
|
395
|
+
self.log("🔄 Rolling back migration...")
|
|
396
|
+
|
|
397
|
+
# Remove new library
|
|
398
|
+
if self.new_library.exists():
|
|
399
|
+
shutil.rmtree(self.new_library)
|
|
400
|
+
self.log(" ✓ Removed new library structure")
|
|
401
|
+
|
|
402
|
+
# Restore from backups
|
|
403
|
+
backup_folders = [
|
|
404
|
+
(self.prompt_library_dir / "1_System_Prompts.old", self.old_system_prompts),
|
|
405
|
+
(self.prompt_library_dir / "2_Domain_Prompts.old", self.old_domain_prompts),
|
|
406
|
+
(self.prompt_library_dir / "3_Project_Prompts.old", self.old_project_prompts),
|
|
407
|
+
(self.prompt_library_dir / "4_Style_Guides.old", self.old_style_guides)
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
for backup, original in backup_folders:
|
|
411
|
+
if backup.exists():
|
|
412
|
+
if original.exists():
|
|
413
|
+
shutil.rmtree(original)
|
|
414
|
+
backup.rename(original)
|
|
415
|
+
self.log(f" ✓ Restored: {original.name}")
|
|
416
|
+
|
|
417
|
+
# Remove migration flag
|
|
418
|
+
if self.migration_completed_file.exists():
|
|
419
|
+
self.migration_completed_file.unlink()
|
|
420
|
+
|
|
421
|
+
self.log("✓ Rollback complete")
|
|
422
|
+
return True
|
|
423
|
+
|
|
424
|
+
except Exception as e:
|
|
425
|
+
self.log(f"❌ Rollback failed: {e}")
|
|
426
|
+
return False
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def migrate_prompt_library(prompt_library_dir: str, log_callback=None) -> bool:
|
|
430
|
+
"""
|
|
431
|
+
Convenience function to perform migration.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
prompt_library_dir: Path to user_data/Prompt_Library
|
|
435
|
+
log_callback: Function for logging
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
True if migration successful or not needed
|
|
439
|
+
"""
|
|
440
|
+
migrator = PromptLibraryMigration(prompt_library_dir, log_callback)
|
|
441
|
+
|
|
442
|
+
if not migrator.needs_migration():
|
|
443
|
+
if log_callback:
|
|
444
|
+
log_callback("✓ Prompt library already migrated (or no old structure found)")
|
|
445
|
+
return True
|
|
446
|
+
|
|
447
|
+
return migrator.migrate()
|