2026-04-08 19:26:26 +00:00
|
|
|
import uuid
|
|
|
|
|
from typing import Protocol
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from ..entities.dictionary_entities import (
|
|
|
|
|
DictionaryLemmaEntity,
|
|
|
|
|
DictionarySenseEntity,
|
|
|
|
|
DictionaryWordformEntity,
|
|
|
|
|
)
|
|
|
|
|
from ....domain.models.dictionary import Lemma, Sense, Wordform
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DictionaryRepository(Protocol):
|
|
|
|
|
async def get_senses_for_headword(self, headword: str, language: str) -> list[Sense]: ...
|
|
|
|
|
async def find_senses_by_english_gloss(self, text: str, target_lang: str) -> list[Sense]: ...
|
2026-04-09 19:40:11 +00:00
|
|
|
async def get_sense(self, sense_id: uuid.UUID) -> Sense | None: ...
|
|
|
|
|
async def get_lemma(self, lemma_id: uuid.UUID) -> Lemma | None: ...
|
2026-04-08 19:26:26 +00:00
|
|
|
async def get_wordforms_for_lemma(self, lemma_id: uuid.UUID) -> list[Wordform]: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _sense_to_model(entity: DictionarySenseEntity) -> Sense:
|
|
|
|
|
return Sense(
|
|
|
|
|
id=str(entity.id),
|
|
|
|
|
lemma_id=str(entity.lemma_id),
|
|
|
|
|
sense_index=entity.sense_index,
|
|
|
|
|
gloss=entity.gloss,
|
|
|
|
|
topics=entity.topics or [],
|
|
|
|
|
tags=entity.tags or [],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-04-09 19:40:11 +00:00
|
|
|
def _lemma_to_model(entity: DictionaryLemmaEntity) -> Lemma:
|
|
|
|
|
return Lemma(
|
|
|
|
|
id=str(entity.id),
|
|
|
|
|
headword=entity.headword,
|
|
|
|
|
language=entity.language,
|
|
|
|
|
pos_raw=entity.pos_raw,
|
|
|
|
|
pos_normalised=entity.pos_normalised,
|
|
|
|
|
gender=entity.gender,
|
|
|
|
|
tags=entity.tags or [],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-04-08 19:26:26 +00:00
|
|
|
def _wordform_to_model(entity: DictionaryWordformEntity) -> Wordform:
|
|
|
|
|
return Wordform(
|
|
|
|
|
id=str(entity.id),
|
|
|
|
|
lemma_id=str(entity.lemma_id),
|
|
|
|
|
form=entity.form,
|
|
|
|
|
tags=entity.tags or [],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PostgresDictionaryRepository:
|
|
|
|
|
def __init__(self, db: AsyncSession) -> None:
|
|
|
|
|
self.db = db
|
|
|
|
|
|
|
|
|
|
async def get_senses_for_headword(self, headword: str, language: str) -> list[Sense]:
|
|
|
|
|
result = await self.db.execute(
|
|
|
|
|
select(DictionarySenseEntity)
|
|
|
|
|
.join(DictionaryLemmaEntity, DictionarySenseEntity.lemma_id == DictionaryLemmaEntity.id)
|
|
|
|
|
.where(
|
|
|
|
|
DictionaryLemmaEntity.headword == headword,
|
|
|
|
|
DictionaryLemmaEntity.language == language,
|
|
|
|
|
)
|
|
|
|
|
.order_by(DictionarySenseEntity.sense_index)
|
|
|
|
|
)
|
|
|
|
|
return [_sense_to_model(e) for e in result.scalars().all()]
|
|
|
|
|
|
|
|
|
|
async def find_senses_by_english_gloss(self, text: str, target_lang: str) -> list[Sense]:
|
|
|
|
|
"""EN→target direction: find senses whose gloss matches the given English text.
|
|
|
|
|
|
|
|
|
|
Uses a case-insensitive exact match on the gloss column, filtered to the
|
|
|
|
|
target language via the joined lemma row.
|
|
|
|
|
"""
|
|
|
|
|
result = await self.db.execute(
|
|
|
|
|
select(DictionarySenseEntity)
|
|
|
|
|
.join(DictionaryLemmaEntity, DictionarySenseEntity.lemma_id == DictionaryLemmaEntity.id)
|
|
|
|
|
.where(
|
|
|
|
|
DictionarySenseEntity.gloss.ilike(text),
|
|
|
|
|
DictionaryLemmaEntity.language == target_lang,
|
|
|
|
|
)
|
|
|
|
|
.order_by(DictionarySenseEntity.sense_index)
|
|
|
|
|
)
|
|
|
|
|
return [_sense_to_model(e) for e in result.scalars().all()]
|
|
|
|
|
|
2026-04-09 19:40:11 +00:00
|
|
|
async def get_sense(self, sense_id: uuid.UUID) -> Sense | None:
|
|
|
|
|
result = await self.db.execute(
|
|
|
|
|
select(DictionarySenseEntity).where(DictionarySenseEntity.id == sense_id)
|
|
|
|
|
)
|
|
|
|
|
entity = result.scalar_one_or_none()
|
|
|
|
|
return _sense_to_model(entity) if entity else None
|
|
|
|
|
|
|
|
|
|
async def get_lemma(self, lemma_id: uuid.UUID) -> Lemma | None:
|
|
|
|
|
result = await self.db.execute(
|
|
|
|
|
select(DictionaryLemmaEntity).where(DictionaryLemmaEntity.id == lemma_id)
|
|
|
|
|
)
|
|
|
|
|
entity = result.scalar_one_or_none()
|
|
|
|
|
return _lemma_to_model(entity) if entity else None
|
|
|
|
|
|
2026-04-08 19:26:26 +00:00
|
|
|
async def get_wordforms_for_lemma(self, lemma_id: uuid.UUID) -> list[Wordform]:
|
|
|
|
|
result = await self.db.execute(
|
|
|
|
|
select(DictionaryWordformEntity).where(
|
|
|
|
|
DictionaryWordformEntity.lemma_id == lemma_id
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
return [_wordform_to_model(e) for e in result.scalars().all()]
|