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,545 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Translation Memory Metadata Manager Module
|
|
3
|
+
|
|
4
|
+
Handles TM metadata operations: creation, activation, TM management.
|
|
5
|
+
Works alongside the existing translation_memory.py module which handles TM matching/searching.
|
|
6
|
+
|
|
7
|
+
TMs can be activated/deactivated per project (similar to termbases).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sqlite3
|
|
11
|
+
from typing import List, Dict, Optional
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TMMetadataManager:
|
|
16
|
+
"""Manages translation memory metadata and activation"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, db_manager, log_callback=None):
|
|
19
|
+
"""
|
|
20
|
+
Initialize TM metadata manager
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
db_manager: DatabaseManager instance
|
|
24
|
+
log_callback: Optional logging function
|
|
25
|
+
"""
|
|
26
|
+
self.db_manager = db_manager
|
|
27
|
+
self.log = log_callback if log_callback else print
|
|
28
|
+
|
|
29
|
+
# ========================================================================
|
|
30
|
+
# TM MANAGEMENT
|
|
31
|
+
# ========================================================================
|
|
32
|
+
|
|
33
|
+
def tm_id_exists(self, tm_id: str) -> bool:
|
|
34
|
+
"""Check if a tm_id already exists in the database"""
|
|
35
|
+
try:
|
|
36
|
+
cursor = self.db_manager.cursor
|
|
37
|
+
cursor.execute("SELECT 1 FROM translation_memories WHERE tm_id = ?", (tm_id,))
|
|
38
|
+
return cursor.fetchone() is not None
|
|
39
|
+
except Exception:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
def get_unique_tm_id(self, base_tm_id: str) -> str:
|
|
43
|
+
"""
|
|
44
|
+
Get a unique tm_id by appending a number suffix if needed.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
base_tm_id: The desired tm_id base (e.g., 'my_tm')
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
A unique tm_id (e.g., 'my_tm' or 'my_tm_2' if 'my_tm' exists)
|
|
51
|
+
"""
|
|
52
|
+
if not self.tm_id_exists(base_tm_id):
|
|
53
|
+
return base_tm_id
|
|
54
|
+
|
|
55
|
+
# Find a unique suffix
|
|
56
|
+
suffix = 2
|
|
57
|
+
while True:
|
|
58
|
+
candidate = f"{base_tm_id}_{suffix}"
|
|
59
|
+
if not self.tm_id_exists(candidate):
|
|
60
|
+
return candidate
|
|
61
|
+
suffix += 1
|
|
62
|
+
if suffix > 1000: # Safety limit
|
|
63
|
+
import uuid
|
|
64
|
+
return f"{base_tm_id}_{uuid.uuid4().hex[:8]}"
|
|
65
|
+
|
|
66
|
+
def create_tm(self, name: str, tm_id: str, source_lang: Optional[str] = None,
|
|
67
|
+
target_lang: Optional[str] = None, description: str = "",
|
|
68
|
+
is_project_tm: bool = False, read_only: bool = False,
|
|
69
|
+
project_id: Optional[int] = None, auto_unique_id: bool = True) -> Optional[int]:
|
|
70
|
+
"""
|
|
71
|
+
Create a new TM metadata entry
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
name: Display name for the TM (e.g., "ClientX_Medical_2024")
|
|
75
|
+
tm_id: Unique identifier used in translation_units.tm_id field
|
|
76
|
+
source_lang: Source language code (e.g., 'en', 'nl')
|
|
77
|
+
target_lang: Target language code
|
|
78
|
+
description: Optional description
|
|
79
|
+
is_project_tm: Whether this is the special project TM (only one per project)
|
|
80
|
+
read_only: Whether this TM should not be updated
|
|
81
|
+
project_id: Which project this TM belongs to (NULL = global)
|
|
82
|
+
auto_unique_id: If True, automatically make tm_id unique by appending suffix
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
TM database ID or None if failed
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
cursor = self.db_manager.cursor
|
|
89
|
+
now = datetime.now().isoformat()
|
|
90
|
+
|
|
91
|
+
# If this is a project TM, check if one already exists for this project
|
|
92
|
+
if is_project_tm and project_id:
|
|
93
|
+
cursor.execute("""
|
|
94
|
+
SELECT id, name FROM translation_memories
|
|
95
|
+
WHERE project_id = ? AND is_project_tm = 1
|
|
96
|
+
""", (project_id,))
|
|
97
|
+
existing = cursor.fetchone()
|
|
98
|
+
if existing:
|
|
99
|
+
self.log(f"✗ Project {project_id} already has a project TM: {existing[1]}")
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
# Make tm_id unique if it already exists
|
|
103
|
+
if auto_unique_id and self.tm_id_exists(tm_id):
|
|
104
|
+
original_tm_id = tm_id
|
|
105
|
+
tm_id = self.get_unique_tm_id(tm_id)
|
|
106
|
+
self.log(f"ℹ️ TM ID '{original_tm_id}' already exists, using '{tm_id}' instead")
|
|
107
|
+
|
|
108
|
+
cursor.execute("""
|
|
109
|
+
INSERT INTO translation_memories
|
|
110
|
+
(name, tm_id, source_lang, target_lang, description, created_date, modified_date, entry_count,
|
|
111
|
+
is_project_tm, read_only, project_id)
|
|
112
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)
|
|
113
|
+
""", (name, tm_id, source_lang, target_lang, description, now, now, is_project_tm, read_only, project_id))
|
|
114
|
+
|
|
115
|
+
self.db_manager.connection.commit()
|
|
116
|
+
db_id = cursor.lastrowid
|
|
117
|
+
tm_type = "project TM" if is_project_tm else "TM"
|
|
118
|
+
self.log(f"✓ Created {tm_type}: {name} (ID: {db_id}, tm_id: {tm_id})")
|
|
119
|
+
return db_id
|
|
120
|
+
except Exception as e:
|
|
121
|
+
self.log(f"✗ Error creating TM: {e}")
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def get_all_tms(self) -> List[Dict]:
|
|
125
|
+
"""
|
|
126
|
+
Get all TMs with metadata
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
List of TM dictionaries with fields: id, name, tm_id, source_lang, target_lang,
|
|
130
|
+
description, entry_count, created_date, modified_date, last_used,
|
|
131
|
+
is_project_tm, read_only, project_id
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
cursor = self.db_manager.cursor
|
|
135
|
+
|
|
136
|
+
# Get TM metadata with actual entry counts from translation_units
|
|
137
|
+
cursor.execute("""
|
|
138
|
+
SELECT
|
|
139
|
+
tm.id, tm.name, tm.tm_id, tm.source_lang, tm.target_lang,
|
|
140
|
+
tm.description, tm.created_date, tm.modified_date, tm.last_used,
|
|
141
|
+
COUNT(tu.id) as actual_count,
|
|
142
|
+
tm.is_project_tm, tm.read_only, tm.project_id
|
|
143
|
+
FROM translation_memories tm
|
|
144
|
+
LEFT JOIN translation_units tu ON tm.tm_id = tu.tm_id
|
|
145
|
+
GROUP BY tm.id
|
|
146
|
+
ORDER BY tm.is_project_tm DESC, tm.name ASC
|
|
147
|
+
""")
|
|
148
|
+
|
|
149
|
+
tms = []
|
|
150
|
+
for row in cursor.fetchall():
|
|
151
|
+
tms.append({
|
|
152
|
+
'id': row[0],
|
|
153
|
+
'name': row[1],
|
|
154
|
+
'tm_id': row[2],
|
|
155
|
+
'source_lang': row[3],
|
|
156
|
+
'target_lang': row[4],
|
|
157
|
+
'description': row[5],
|
|
158
|
+
'created_date': row[6],
|
|
159
|
+
'modified_date': row[7],
|
|
160
|
+
'last_used': row[8],
|
|
161
|
+
'entry_count': row[9],
|
|
162
|
+
'is_project_tm': bool(row[10]) if len(row) > 10 else False,
|
|
163
|
+
'read_only': bool(row[11]) if len(row) > 11 else False,
|
|
164
|
+
'project_id': row[12] if len(row) > 12 else None
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
return tms
|
|
168
|
+
except Exception as e:
|
|
169
|
+
self.log(f"✗ Error fetching TMs: {e}")
|
|
170
|
+
import traceback
|
|
171
|
+
traceback.print_exc()
|
|
172
|
+
return []
|
|
173
|
+
|
|
174
|
+
def get_tm(self, tm_db_id: int) -> Optional[Dict]:
|
|
175
|
+
"""Get single TM by database ID"""
|
|
176
|
+
try:
|
|
177
|
+
cursor = self.db_manager.cursor
|
|
178
|
+
|
|
179
|
+
cursor.execute("""
|
|
180
|
+
SELECT
|
|
181
|
+
tm.id, tm.name, tm.tm_id, tm.source_lang, tm.target_lang,
|
|
182
|
+
tm.description, tm.created_date, tm.modified_date, tm.last_used,
|
|
183
|
+
COUNT(tu.id) as actual_count
|
|
184
|
+
FROM translation_memories tm
|
|
185
|
+
LEFT JOIN translation_units tu ON tm.tm_id = tu.tm_id
|
|
186
|
+
WHERE tm.id = ?
|
|
187
|
+
GROUP BY tm.id
|
|
188
|
+
""", (tm_db_id,))
|
|
189
|
+
|
|
190
|
+
row = cursor.fetchone()
|
|
191
|
+
if row:
|
|
192
|
+
return {
|
|
193
|
+
'id': row[0],
|
|
194
|
+
'name': row[1],
|
|
195
|
+
'tm_id': row[2],
|
|
196
|
+
'source_lang': row[3],
|
|
197
|
+
'target_lang': row[4],
|
|
198
|
+
'description': row[5],
|
|
199
|
+
'created_date': row[6],
|
|
200
|
+
'modified_date': row[7],
|
|
201
|
+
'last_used': row[8],
|
|
202
|
+
'entry_count': row[9] or 0
|
|
203
|
+
}
|
|
204
|
+
return None
|
|
205
|
+
except Exception as e:
|
|
206
|
+
self.log(f"✗ Error fetching TM: {e}")
|
|
207
|
+
return None
|
|
208
|
+
|
|
209
|
+
def update_tm(self, tm_db_id: int, name: Optional[str] = None,
|
|
210
|
+
description: Optional[str] = None, source_lang: Optional[str] = None,
|
|
211
|
+
target_lang: Optional[str] = None) -> bool:
|
|
212
|
+
"""Update TM metadata"""
|
|
213
|
+
try:
|
|
214
|
+
cursor = self.db_manager.cursor
|
|
215
|
+
updates = []
|
|
216
|
+
values = []
|
|
217
|
+
|
|
218
|
+
if name is not None:
|
|
219
|
+
updates.append("name = ?")
|
|
220
|
+
values.append(name)
|
|
221
|
+
if description is not None:
|
|
222
|
+
updates.append("description = ?")
|
|
223
|
+
values.append(description)
|
|
224
|
+
if source_lang is not None:
|
|
225
|
+
updates.append("source_lang = ?")
|
|
226
|
+
values.append(source_lang)
|
|
227
|
+
if target_lang is not None:
|
|
228
|
+
updates.append("target_lang = ?")
|
|
229
|
+
values.append(target_lang)
|
|
230
|
+
|
|
231
|
+
if not updates:
|
|
232
|
+
return True
|
|
233
|
+
|
|
234
|
+
updates.append("modified_date = ?")
|
|
235
|
+
values.append(datetime.now().isoformat())
|
|
236
|
+
values.append(tm_db_id)
|
|
237
|
+
|
|
238
|
+
sql = f"UPDATE translation_memories SET {', '.join(updates)} WHERE id = ?"
|
|
239
|
+
cursor.execute(sql, values)
|
|
240
|
+
self.db_manager.connection.commit()
|
|
241
|
+
|
|
242
|
+
self.log(f"✓ Updated TM (ID: {tm_db_id})")
|
|
243
|
+
return True
|
|
244
|
+
except Exception as e:
|
|
245
|
+
self.log(f"✗ Error updating TM: {e}")
|
|
246
|
+
return False
|
|
247
|
+
|
|
248
|
+
def delete_tm(self, tm_db_id: int, delete_entries: bool = False) -> bool:
|
|
249
|
+
"""
|
|
250
|
+
Delete TM metadata (and optionally its translation units)
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
tm_db_id: Database ID of the TM
|
|
254
|
+
delete_entries: If True, also delete all translation_units with this tm_id
|
|
255
|
+
"""
|
|
256
|
+
try:
|
|
257
|
+
cursor = self.db_manager.cursor
|
|
258
|
+
|
|
259
|
+
# Get tm_id first
|
|
260
|
+
cursor.execute("SELECT tm_id FROM translation_memories WHERE id = ?", (tm_db_id,))
|
|
261
|
+
row = cursor.fetchone()
|
|
262
|
+
if not row:
|
|
263
|
+
self.log(f"✗ TM not found: {tm_db_id}")
|
|
264
|
+
return False
|
|
265
|
+
|
|
266
|
+
tm_id = row[0]
|
|
267
|
+
|
|
268
|
+
# Delete translation units if requested
|
|
269
|
+
if delete_entries:
|
|
270
|
+
cursor.execute("DELETE FROM translation_units WHERE tm_id = ?", (tm_id,))
|
|
271
|
+
self.log(f"✓ Deleted translation units for tm_id: {tm_id}")
|
|
272
|
+
|
|
273
|
+
# Delete TM metadata (this will cascade delete tm_activation entries)
|
|
274
|
+
cursor.execute("DELETE FROM translation_memories WHERE id = ?", (tm_db_id,))
|
|
275
|
+
|
|
276
|
+
self.db_manager.connection.commit()
|
|
277
|
+
self.log(f"✓ Deleted TM (ID: {tm_db_id})")
|
|
278
|
+
return True
|
|
279
|
+
except Exception as e:
|
|
280
|
+
self.log(f"✗ Error deleting TM: {e}")
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
def update_entry_count(self, tm_id: str) -> bool:
|
|
284
|
+
"""Update cached entry count for a TM"""
|
|
285
|
+
try:
|
|
286
|
+
cursor = self.db_manager.cursor
|
|
287
|
+
|
|
288
|
+
cursor.execute("""
|
|
289
|
+
UPDATE translation_memories
|
|
290
|
+
SET entry_count = (
|
|
291
|
+
SELECT COUNT(*) FROM translation_units WHERE tm_id = ?
|
|
292
|
+
),
|
|
293
|
+
modified_date = ?
|
|
294
|
+
WHERE tm_id = ?
|
|
295
|
+
""", (tm_id, datetime.now().isoformat(), tm_id))
|
|
296
|
+
|
|
297
|
+
self.db_manager.connection.commit()
|
|
298
|
+
return True
|
|
299
|
+
except Exception as e:
|
|
300
|
+
self.log(f"✗ Error updating entry count: {e}")
|
|
301
|
+
return False
|
|
302
|
+
|
|
303
|
+
# ========================================================================
|
|
304
|
+
# TM ACTIVATION (per-project)
|
|
305
|
+
# ========================================================================
|
|
306
|
+
|
|
307
|
+
def activate_tm(self, tm_db_id: int, project_id: int) -> bool:
|
|
308
|
+
"""Activate a TM for a specific project"""
|
|
309
|
+
try:
|
|
310
|
+
cursor = self.db_manager.cursor
|
|
311
|
+
now = datetime.now().isoformat()
|
|
312
|
+
|
|
313
|
+
cursor.execute("""
|
|
314
|
+
INSERT OR REPLACE INTO tm_activation (tm_id, project_id, is_active, activated_date)
|
|
315
|
+
VALUES (?, ?, 1, ?)
|
|
316
|
+
""", (tm_db_id, project_id, now))
|
|
317
|
+
|
|
318
|
+
self.db_manager.connection.commit()
|
|
319
|
+
self.log(f"✓ Activated TM {tm_db_id} for project {project_id}")
|
|
320
|
+
return True
|
|
321
|
+
except Exception as e:
|
|
322
|
+
self.log(f"✗ Error activating TM: {e}")
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
def deactivate_tm(self, tm_db_id: int, project_id: int) -> bool:
|
|
326
|
+
"""Deactivate a TM for a specific project"""
|
|
327
|
+
try:
|
|
328
|
+
cursor = self.db_manager.cursor
|
|
329
|
+
|
|
330
|
+
cursor.execute("""
|
|
331
|
+
UPDATE tm_activation
|
|
332
|
+
SET is_active = 0
|
|
333
|
+
WHERE tm_id = ? AND project_id = ?
|
|
334
|
+
""", (tm_db_id, project_id))
|
|
335
|
+
|
|
336
|
+
self.db_manager.connection.commit()
|
|
337
|
+
self.log(f"✓ Deactivated TM {tm_db_id} for project {project_id}")
|
|
338
|
+
return True
|
|
339
|
+
except Exception as e:
|
|
340
|
+
self.log(f"✗ Error deactivating TM: {e}")
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
def is_tm_active(self, tm_db_id: int, project_id: Optional[int]) -> bool:
|
|
344
|
+
"""Check if a TM is active for a project (or global when project_id=0)"""
|
|
345
|
+
if project_id is None:
|
|
346
|
+
return False # If None (not 0), default to inactive
|
|
347
|
+
|
|
348
|
+
try:
|
|
349
|
+
cursor = self.db_manager.cursor
|
|
350
|
+
|
|
351
|
+
cursor.execute("""
|
|
352
|
+
SELECT is_active FROM tm_activation
|
|
353
|
+
WHERE tm_id = ? AND project_id = ?
|
|
354
|
+
""", (tm_db_id, project_id))
|
|
355
|
+
|
|
356
|
+
row = cursor.fetchone()
|
|
357
|
+
if row:
|
|
358
|
+
return bool(row[0])
|
|
359
|
+
|
|
360
|
+
# If no activation record exists, TM is inactive by default
|
|
361
|
+
return False
|
|
362
|
+
except Exception as e:
|
|
363
|
+
self.log(f"✗ Error checking TM activation: {e}")
|
|
364
|
+
return False
|
|
365
|
+
|
|
366
|
+
def get_active_tm_ids(self, project_id: Optional[int]) -> List[str]:
|
|
367
|
+
"""
|
|
368
|
+
Get list of active tm_id strings for a project
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
List of tm_id strings that are active for the project
|
|
372
|
+
"""
|
|
373
|
+
if project_id is None:
|
|
374
|
+
# No project - return all TMs
|
|
375
|
+
try:
|
|
376
|
+
cursor = self.db_manager.cursor
|
|
377
|
+
cursor.execute("SELECT tm_id FROM translation_memories")
|
|
378
|
+
return [row[0] for row in cursor.fetchall()]
|
|
379
|
+
except Exception as e:
|
|
380
|
+
self.log(f"✗ Error fetching all tm_ids: {e}")
|
|
381
|
+
return []
|
|
382
|
+
|
|
383
|
+
try:
|
|
384
|
+
cursor = self.db_manager.cursor
|
|
385
|
+
|
|
386
|
+
# Only return TMs that have been explicitly activated (is_active = 1)
|
|
387
|
+
cursor.execute("""
|
|
388
|
+
SELECT tm.tm_id
|
|
389
|
+
FROM translation_memories tm
|
|
390
|
+
INNER JOIN tm_activation ta ON tm.id = ta.tm_id
|
|
391
|
+
WHERE ta.project_id = ? AND ta.is_active = 1
|
|
392
|
+
""", (project_id,))
|
|
393
|
+
|
|
394
|
+
return [row[0] for row in cursor.fetchall()]
|
|
395
|
+
except Exception as e:
|
|
396
|
+
self.log(f"✗ Error fetching active tm_ids: {e}")
|
|
397
|
+
return []
|
|
398
|
+
|
|
399
|
+
# ========================================================================
|
|
400
|
+
# PROJECT TM MANAGEMENT (similar to termbases)
|
|
401
|
+
# ========================================================================
|
|
402
|
+
|
|
403
|
+
def set_as_project_tm(self, tm_db_id: int, project_id: int) -> bool:
|
|
404
|
+
"""
|
|
405
|
+
Set a TM as the project TM for a specific project.
|
|
406
|
+
Only one TM can be the project TM per project (automatically unsets others).
|
|
407
|
+
"""
|
|
408
|
+
try:
|
|
409
|
+
cursor = self.db_manager.cursor
|
|
410
|
+
|
|
411
|
+
# First, unset any existing project TM for this project
|
|
412
|
+
cursor.execute("""
|
|
413
|
+
UPDATE translation_memories
|
|
414
|
+
SET is_project_tm = 0
|
|
415
|
+
WHERE project_id = ? AND is_project_tm = 1
|
|
416
|
+
""", (project_id,))
|
|
417
|
+
|
|
418
|
+
# Then set the new one
|
|
419
|
+
cursor.execute("""
|
|
420
|
+
UPDATE translation_memories
|
|
421
|
+
SET is_project_tm = 1, project_id = ?
|
|
422
|
+
WHERE id = ?
|
|
423
|
+
""", (project_id, tm_db_id))
|
|
424
|
+
|
|
425
|
+
self.db_manager.connection.commit()
|
|
426
|
+
self.log(f"✓ Set TM {tm_db_id} as project TM for project {project_id}")
|
|
427
|
+
return True
|
|
428
|
+
except Exception as e:
|
|
429
|
+
self.log(f"✗ Error setting project TM: {e}")
|
|
430
|
+
return False
|
|
431
|
+
|
|
432
|
+
def unset_project_tm(self, tm_db_id: int) -> bool:
|
|
433
|
+
"""Unset a TM as project TM"""
|
|
434
|
+
try:
|
|
435
|
+
cursor = self.db_manager.cursor
|
|
436
|
+
|
|
437
|
+
cursor.execute("""
|
|
438
|
+
UPDATE translation_memories
|
|
439
|
+
SET is_project_tm = 0
|
|
440
|
+
WHERE id = ?
|
|
441
|
+
""", (tm_db_id,))
|
|
442
|
+
|
|
443
|
+
self.db_manager.connection.commit()
|
|
444
|
+
self.log(f"✓ Unset TM {tm_db_id} as project TM")
|
|
445
|
+
return True
|
|
446
|
+
except Exception as e:
|
|
447
|
+
self.log(f"✗ Error unsetting project TM: {e}")
|
|
448
|
+
return False
|
|
449
|
+
|
|
450
|
+
def get_project_tm(self, project_id: int) -> Optional[Dict]:
|
|
451
|
+
"""Get the project TM for a specific project"""
|
|
452
|
+
try:
|
|
453
|
+
cursor = self.db_manager.cursor
|
|
454
|
+
|
|
455
|
+
cursor.execute("""
|
|
456
|
+
SELECT
|
|
457
|
+
tm.id, tm.name, tm.tm_id, tm.source_lang, tm.target_lang,
|
|
458
|
+
tm.description, tm.created_date, tm.modified_date, tm.last_used,
|
|
459
|
+
COUNT(tu.id) as actual_count,
|
|
460
|
+
tm.is_project_tm, tm.read_only, tm.project_id
|
|
461
|
+
FROM translation_memories tm
|
|
462
|
+
LEFT JOIN translation_units tu ON tm.tm_id = tu.tm_id
|
|
463
|
+
WHERE tm.project_id = ? AND tm.is_project_tm = 1
|
|
464
|
+
GROUP BY tm.id
|
|
465
|
+
""", (project_id,))
|
|
466
|
+
|
|
467
|
+
row = cursor.fetchone()
|
|
468
|
+
if row:
|
|
469
|
+
return {
|
|
470
|
+
'id': row[0],
|
|
471
|
+
'name': row[1],
|
|
472
|
+
'tm_id': row[2],
|
|
473
|
+
'source_lang': row[3],
|
|
474
|
+
'target_lang': row[4],
|
|
475
|
+
'description': row[5],
|
|
476
|
+
'created_date': row[6],
|
|
477
|
+
'modified_date': row[7],
|
|
478
|
+
'last_used': row[8],
|
|
479
|
+
'entry_count': row[9],
|
|
480
|
+
'is_project_tm': bool(row[10]),
|
|
481
|
+
'read_only': bool(row[11]),
|
|
482
|
+
'project_id': row[12]
|
|
483
|
+
}
|
|
484
|
+
return None
|
|
485
|
+
except Exception as e:
|
|
486
|
+
self.log(f"✗ Error fetching project TM: {e}")
|
|
487
|
+
return None
|
|
488
|
+
|
|
489
|
+
def get_tm_by_tm_id(self, tm_id: str) -> Optional[Dict]:
|
|
490
|
+
"""Get TM by its tm_id string"""
|
|
491
|
+
try:
|
|
492
|
+
cursor = self.db_manager.cursor
|
|
493
|
+
|
|
494
|
+
cursor.execute("""
|
|
495
|
+
SELECT
|
|
496
|
+
tm.id, tm.name, tm.tm_id, tm.source_lang, tm.target_lang,
|
|
497
|
+
tm.description, tm.created_date, tm.modified_date, tm.last_used,
|
|
498
|
+
COUNT(tu.id) as actual_count,
|
|
499
|
+
tm.is_project_tm, tm.read_only, tm.project_id
|
|
500
|
+
FROM translation_memories tm
|
|
501
|
+
LEFT JOIN translation_units tu ON tm.tm_id = tu.tm_id
|
|
502
|
+
WHERE tm.tm_id = ?
|
|
503
|
+
GROUP BY tm.id
|
|
504
|
+
""", (tm_id,))
|
|
505
|
+
|
|
506
|
+
row = cursor.fetchone()
|
|
507
|
+
if row:
|
|
508
|
+
return {
|
|
509
|
+
'id': row[0],
|
|
510
|
+
'name': row[1],
|
|
511
|
+
'tm_id': row[2],
|
|
512
|
+
'source_lang': row[3],
|
|
513
|
+
'target_lang': row[4],
|
|
514
|
+
'description': row[5],
|
|
515
|
+
'created_date': row[6],
|
|
516
|
+
'modified_date': row[7],
|
|
517
|
+
'last_used': row[8],
|
|
518
|
+
'entry_count': row[9],
|
|
519
|
+
'is_project_tm': bool(row[10]) if len(row) > 10 else False,
|
|
520
|
+
'read_only': bool(row[11]) if len(row) > 11 else False,
|
|
521
|
+
'project_id': row[12] if len(row) > 12 else None
|
|
522
|
+
}
|
|
523
|
+
return None
|
|
524
|
+
except Exception as e:
|
|
525
|
+
self.log(f"✗ Error fetching TM by tm_id: {e}")
|
|
526
|
+
return None
|
|
527
|
+
|
|
528
|
+
def set_read_only(self, tm_db_id: int, read_only: bool) -> bool:
|
|
529
|
+
"""Set whether a TM is read-only (cannot be updated)"""
|
|
530
|
+
try:
|
|
531
|
+
cursor = self.db_manager.cursor
|
|
532
|
+
|
|
533
|
+
cursor.execute("""
|
|
534
|
+
UPDATE translation_memories
|
|
535
|
+
SET read_only = ?
|
|
536
|
+
WHERE id = ?
|
|
537
|
+
""", (read_only, tm_db_id))
|
|
538
|
+
|
|
539
|
+
self.db_manager.connection.commit()
|
|
540
|
+
status = "read-only" if read_only else "writable"
|
|
541
|
+
self.log(f"✓ Set TM {tm_db_id} as {status}")
|
|
542
|
+
return True
|
|
543
|
+
except Exception as e:
|
|
544
|
+
self.log(f"✗ Error setting read-only status: {e}")
|
|
545
|
+
return False
|