165 lines
7.2 KiB
Python
165 lines
7.2 KiB
Python
# #region DictionaryEntryCRUD [C:3] [TYPE Class] [SEMANTICS dictionary, entries, crud]
|
|
# @BRIEF CRUD operations for DictionaryEntry records.
|
|
# @RELATION DEPENDS_ON -> [DictionaryEntry]
|
|
# @RELATION DEPENDS_ON -> [TerminologyDictionary]
|
|
|
|
from sqlalchemy import func
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ...core.logger import belief_scope, logger
|
|
from ...models.translate import DictionaryEntry, TerminologyDictionary
|
|
from .[EXT:internal:_utils] import _normalize_term
|
|
from .dictionary_validation import _validate_bcp47
|
|
|
|
|
|
class DictionaryEntryCRUD:
|
|
"""CRUD operations for dictionary entries."""
|
|
|
|
# region add_entry [TYPE Function]
|
|
# @PURPOSE: Add an entry to a dictionary with language-pair-aware uniqueness validation.
|
|
@staticmethod
|
|
def add_entry(
|
|
db: Session, dict_id: str, source_term: str, target_term: str,
|
|
context_notes: str | None = None,
|
|
source_language: str = "und", target_language: str = "und",
|
|
) -> DictionaryEntry:
|
|
with belief_scope("DictionaryEntryCRUD.add_entry"):
|
|
_validate_bcp47(source_language, "source_language")
|
|
_validate_bcp47(target_language, "target_language")
|
|
|
|
normalized = _normalize_term(source_term)
|
|
existing = (
|
|
db.query(DictionaryEntry)
|
|
.filter(
|
|
DictionaryEntry.dictionary_id == dict_id,
|
|
DictionaryEntry.source_term_normalized == normalized,
|
|
DictionaryEntry.source_language == source_language,
|
|
DictionaryEntry.target_language == target_language,
|
|
)
|
|
.first()
|
|
)
|
|
if existing:
|
|
raise ValueError(
|
|
f"Duplicate entry: '{source_term}' (lang: {source_language}→{target_language}) "
|
|
f"already exists in this dictionary (id={existing.id}). "
|
|
"Use overwrite or keep_existing conflict mode."
|
|
)
|
|
|
|
logger.reason("Adding dictionary entry", {"dict_id": dict_id, "term": source_term})
|
|
entry = DictionaryEntry(
|
|
dictionary_id=dict_id,
|
|
source_term=source_term.strip(),
|
|
source_term_normalized=normalized,
|
|
target_term=target_term.strip(),
|
|
context_notes=context_notes.strip() if context_notes else None,
|
|
source_language=source_language.strip(),
|
|
target_language=target_language.strip(),
|
|
)
|
|
db.add(entry)
|
|
db.commit()
|
|
db.refresh(entry)
|
|
logger.reflect("Entry added", {"entry_id": entry.id})
|
|
return entry
|
|
# endregion add_entry
|
|
|
|
# region edit_entry [TYPE Function]
|
|
# @PURPOSE: Edit an existing dictionary entry with language-pair-aware duplicate check.
|
|
@staticmethod
|
|
def edit_entry(
|
|
db: Session, entry_id: str, source_term: str | None = None,
|
|
target_term: str | None = None, context_notes: str | None = None,
|
|
source_language: str | None = None, target_language: str | None = None,
|
|
) -> DictionaryEntry:
|
|
with belief_scope("DictionaryEntryCRUD.edit_entry"):
|
|
entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()
|
|
if not entry:
|
|
raise ValueError(f"Entry not found: {entry_id}")
|
|
logger.reason("Editing dictionary entry", {"entry_id": entry_id})
|
|
|
|
if source_language is not None:
|
|
_validate_bcp47(source_language, "source_language")
|
|
entry.source_language = source_language.strip()
|
|
if target_language is not None:
|
|
_validate_bcp47(target_language, "target_language")
|
|
entry.target_language = target_language.strip()
|
|
|
|
if source_term is not None:
|
|
normalized = _normalize_term(source_term)
|
|
existing = (
|
|
db.query(DictionaryEntry)
|
|
.filter(
|
|
DictionaryEntry.dictionary_id == entry.dictionary_id,
|
|
DictionaryEntry.source_term_normalized == normalized,
|
|
DictionaryEntry.source_language == entry.source_language,
|
|
DictionaryEntry.target_language == entry.target_language,
|
|
DictionaryEntry.id != entry_id,
|
|
)
|
|
.first()
|
|
)
|
|
if existing:
|
|
raise ValueError(f"Duplicate entry: '{source_term}' already exists in this dictionary (id={existing.id}).")
|
|
entry.source_term = source_term.strip()
|
|
entry.source_term_normalized = normalized
|
|
if target_term is not None:
|
|
entry.target_term = target_term.strip()
|
|
if context_notes is not None:
|
|
entry.context_notes = context_notes.strip() if context_notes else None
|
|
db.commit()
|
|
db.refresh(entry)
|
|
logger.reflect("Entry updated", {"entry_id": entry.id})
|
|
return entry
|
|
# endregion edit_entry
|
|
|
|
# region delete_entry [TYPE Function]
|
|
# @PURPOSE: Delete a single dictionary entry.
|
|
@staticmethod
|
|
def delete_entry(db: Session, entry_id: str) -> None:
|
|
with belief_scope("DictionaryEntryCRUD.delete_entry"):
|
|
entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()
|
|
if not entry:
|
|
raise ValueError(f"Entry not found: {entry_id}")
|
|
logger.reason("Deleting dictionary entry", {"entry_id": entry_id})
|
|
db.delete(entry)
|
|
db.commit()
|
|
logger.reflect("Entry deleted", {"entry_id": entry_id})
|
|
# endregion delete_entry
|
|
|
|
# region clear_entries [TYPE Function]
|
|
# @PURPOSE: Delete all entries for a dictionary.
|
|
@staticmethod
|
|
def clear_entries(db: Session, dict_id: str) -> int:
|
|
with belief_scope("DictionaryEntryCRUD.clear_entries"):
|
|
dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()
|
|
if not dictionary:
|
|
raise ValueError(f"Dictionary not found: {dict_id}")
|
|
logger.reason("Clearing all entries", {"dict_id": dict_id})
|
|
deleted = db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()
|
|
db.commit()
|
|
logger.reflect("Entries cleared", {"dict_id": dict_id, "count": deleted})
|
|
return deleted
|
|
# endregion clear_entries
|
|
|
|
# region list_entries [TYPE Function]
|
|
# @PURPOSE: List entries for a dictionary with pagination.
|
|
@staticmethod
|
|
def list_entries(
|
|
db: Session, dict_id: str, page: int = 1, page_size: int = 50,
|
|
) -> tuple[list[DictionaryEntry], int]:
|
|
total = (
|
|
db.query(func.count(DictionaryEntry.id))
|
|
.filter(DictionaryEntry.dictionary_id == dict_id)
|
|
.scalar() or 0
|
|
)
|
|
entries = (
|
|
db.query(DictionaryEntry)
|
|
.filter(DictionaryEntry.dictionary_id == dict_id)
|
|
.order_by(DictionaryEntry.source_term.asc())
|
|
.offset((page - 1) * page_size)
|
|
.limit(page_size)
|
|
.all()
|
|
)
|
|
return entries, total
|
|
# endregion list_entries
|
|
|
|
# #endregion DictionaryEntryCRUD
|