- Removes source_dialect and target_dialect from TerminologyDictionary model, schemas, routes, helper serialization, and all tests - Adds allowed_languages config field (BCP-47 language codes) to GlobalSettings with validation, consolidated settings response, and API endpoint - Adds alembic migration f1a2b3c4d5e6 to drop the two columns
130 lines
5.3 KiB
Python
130 lines
5.3 KiB
Python
# #region DictionaryCRUD [C:3] [TYPE Class] [SEMANTICS dictionary, crud, terminology]
|
|
# @BRIEF CRUD operations for TerminologyDictionary records.
|
|
# @RELATION DEPENDS_ON -> [TerminologyDictionary]
|
|
# @RELATION DEPENDS_ON -> [TranslationJob]
|
|
# @RELATION DEPENDS_ON -> [TranslationJobDictionary]
|
|
|
|
from sqlalchemy import func
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ...core.logger import belief_scope, logger
|
|
from ...models.translate import (
|
|
DictionaryEntry,
|
|
TerminologyDictionary,
|
|
TranslationJob,
|
|
TranslationJobDictionary,
|
|
)
|
|
|
|
|
|
class DictionaryCRUD:
|
|
"""CRUD operations for terminology dictionaries."""
|
|
|
|
# region create_dictionary [TYPE Function]
|
|
# @PURPOSE: Create a new terminology dictionary.
|
|
@staticmethod
|
|
def create_dictionary(
|
|
db: Session, name: str,
|
|
created_by: str | None = None, description: str | None = None,
|
|
is_active: bool = True,
|
|
) -> TerminologyDictionary:
|
|
with belief_scope("DictionaryCRUD.create_dictionary"):
|
|
logger.reason("Creating dictionary", {"name": name})
|
|
dictionary = TerminologyDictionary(
|
|
name=name, description=description,
|
|
is_active=is_active, created_by=created_by,
|
|
)
|
|
db.add(dictionary)
|
|
db.commit()
|
|
db.refresh(dictionary)
|
|
logger.reflect("Dictionary created", {"id": dictionary.id})
|
|
return dictionary
|
|
# endregion create_dictionary
|
|
|
|
# region update_dictionary [TYPE Function]
|
|
# @PURPOSE: Update an existing terminology dictionary.
|
|
@staticmethod
|
|
def update_dictionary(
|
|
db: Session, dict_id: str, name: str | None = None,
|
|
description: str | None = None, is_active: bool | None = None,
|
|
) -> TerminologyDictionary:
|
|
with belief_scope("DictionaryCRUD.update_dictionary"):
|
|
dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()
|
|
if not dictionary:
|
|
raise ValueError(f"Dictionary not found: {dict_id}")
|
|
logger.reason("Updating dictionary", {"id": dict_id})
|
|
if name is not None:
|
|
dictionary.name = name
|
|
if description is not None:
|
|
dictionary.description = description
|
|
if is_active is not None:
|
|
dictionary.is_active = is_active
|
|
db.commit()
|
|
db.refresh(dictionary)
|
|
logger.reflect("Dictionary updated", {"id": dictionary.id})
|
|
return dictionary
|
|
# endregion update_dictionary
|
|
|
|
# region delete_dictionary [TYPE Function]
|
|
# @PURPOSE: Delete a dictionary, blocked if attached to active/scheduled jobs.
|
|
@staticmethod
|
|
def delete_dictionary(db: Session, dict_id: str) -> None:
|
|
with belief_scope("DictionaryCRUD.delete_dictionary"):
|
|
dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()
|
|
if not dictionary:
|
|
raise ValueError(f"Dictionary not found: {dict_id}")
|
|
|
|
blocked_statuses = ["ACTIVE", "READY", "RUNNING", "SCHEDULED"]
|
|
attached_jobs = (
|
|
db.query(TranslationJobDictionary)
|
|
.filter(
|
|
TranslationJobDictionary.dictionary_id == dict_id,
|
|
TranslationJobDictionary.job_id.in_(
|
|
db.query(TranslationJob.id).filter(TranslationJob.status.in_(blocked_statuses))
|
|
),
|
|
)
|
|
.count()
|
|
)
|
|
if attached_jobs > 0:
|
|
logger.explore("Delete blocked: dictionary attached to active jobs", {"dict_id": dict_id, "jobs": attached_jobs})
|
|
raise ValueError(
|
|
f"Cannot delete dictionary attached to {attached_jobs} active/scheduled job(s). "
|
|
"Remove dictionary associations from jobs first."
|
|
)
|
|
|
|
logger.reason("Deleting dictionary", {"id": dict_id})
|
|
db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()
|
|
db.query(TranslationJobDictionary).filter(TranslationJobDictionary.dictionary_id == dict_id).delete()
|
|
db.delete(dictionary)
|
|
db.commit()
|
|
logger.reflect("Dictionary deleted", {"id": dict_id})
|
|
# endregion delete_dictionary
|
|
|
|
# region get_dictionary [TYPE Function]
|
|
# @PURPOSE: Get a single dictionary by ID.
|
|
@staticmethod
|
|
def get_dictionary(db: Session, dict_id: str) -> TerminologyDictionary:
|
|
dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()
|
|
if not dictionary:
|
|
raise ValueError(f"Dictionary not found: {dict_id}")
|
|
return dictionary
|
|
# endregion get_dictionary
|
|
|
|
# region list_dictionaries [TYPE Function]
|
|
# @PURPOSE: List dictionaries with pagination.
|
|
@staticmethod
|
|
def list_dictionaries(
|
|
db: Session, page: int = 1, page_size: int = 20,
|
|
) -> tuple[list[TerminologyDictionary], int]:
|
|
total = db.query(func.count(TerminologyDictionary.id)).scalar() or 0
|
|
dictionaries = (
|
|
db.query(TerminologyDictionary)
|
|
.order_by(TerminologyDictionary.created_at.desc())
|
|
.offset((page - 1) * page_size)
|
|
.limit(page_size)
|
|
.all()
|
|
)
|
|
return dictionaries, total
|
|
# endregion list_dictionaries
|
|
|
|
# #endregion DictionaryCRUD
|