Mongoosetross's picture
Fast deploy (index 51f60c37cb32)
4d4cb57 verified
Raw
History Blame Contribute Delete
5.22 kB
from __future__ import annotations
"""Serve short glosses from the local Kaikki dump baked into atlas.sqlite."""
import json
import sqlite3
MAX_SENSES = 4
MAX_GLOSSES = 4
def _trim_senses(raw: object) -> list[dict]:
if isinstance(raw, str):
try:
raw = json.loads(raw)
except json.JSONDecodeError:
return []
if not isinstance(raw, list):
return []
senses: list[dict] = []
for block in raw:
if not isinstance(block, dict):
continue
pos = str(block.get("pos") or "Sense").strip() or "Sense"
glosses: list[str] = []
for g in block.get("glosses") or []:
text = " ".join(str(g).split()).strip()
if text:
glosses.append(text)
if len(glosses) >= MAX_GLOSSES:
break
if glosses:
senses.append({"pos": pos, "glosses": glosses})
if len(senses) >= MAX_SENSES:
break
return senses
def _candidate_langs(lang: str, iso_639_3: str | None, lang_meta: dict[str, dict] | None) -> list[str]:
out: list[str] = []
key = (lang or "").strip()
if key:
out.append(key)
folded = key.casefold()
if folded != key:
out.append(folded)
iso = (iso_639_3 or "").strip().casefold()
if lang_meta and iso:
for lk, meta in lang_meta.items():
if (meta.get("iso_639_3") or "").casefold() == iso:
out.append(lk)
if iso:
out.append(iso)
seen: set[str] = set()
uniq: list[str] = []
for c in out:
if c and c not in seen:
seen.add(c)
uniq.append(c)
return uniq
def lookup_definitions(
conn: sqlite3.Connection,
term: str,
lang: str = "",
ety: str | None = None,
iso_639_3: str | None = None,
lang_meta: dict[str, dict] | None = None,
) -> dict:
term = (term or "").strip()
if not term:
return {"term": term, "senses": [], "error": "empty_term", "source": "kaikki"}
candidates = _candidate_langs(lang, iso_639_3, lang_meta)
row = None
matched_lang = None
matched_ety = None
for cand in candidates:
if ety is not None:
row = conn.execute(
"SELECT lang, ety, senses_json FROM definitions WHERE term = ? AND lang = ? AND ety = ?",
(term, cand, ety),
).fetchone()
else:
row = conn.execute(
"""
SELECT lang, ety, senses_json FROM definitions
WHERE term = ? AND lang = ?
ORDER BY (ety = '') DESC, ety
LIMIT 1
""",
(term, cand),
).fetchone()
if row is not None:
matched_lang = row["lang"] if isinstance(row, sqlite3.Row) else row[0]
matched_ety = row["ety"] if isinstance(row, sqlite3.Row) else row[1]
break
fallback = False
if row is None:
rows = conn.execute(
"SELECT lang, ety, senses_json FROM definitions WHERE term = ? ORDER BY lang, ety LIMIT 8",
(term,),
).fetchall()
if rows:
prefer = {c.casefold() for c in candidates}
chosen = None
for r in rows:
rlang = r["lang"] if isinstance(r, sqlite3.Row) else r[0]
if rlang.casefold() in prefer or rlang.casefold() == "english":
chosen = r
break
row = chosen or rows[0]
matched_lang = row["lang"] if isinstance(row, sqlite3.Row) else row[0]
matched_ety = row["ety"] if isinstance(row, sqlite3.Row) else row[1]
fallback = bool(candidates) and matched_lang not in candidates
if row is None:
return {
"term": term,
"lang": lang,
"ety": ety or "",
"matched_code": None,
"senses": [],
"fallback": False,
"source": "kaikki",
"error": "not_found",
}
senses_json = row["senses_json"] if isinstance(row, sqlite3.Row) else row[2]
senses = _trim_senses(senses_json)
return {
"term": term,
"lang": lang,
"ety": matched_ety or "",
"matched_code": matched_lang,
"senses": senses,
"fallback": fallback,
"source": "kaikki",
"error": None if senses else "no_senses",
}
def fetch_definitions(
term: str,
lang: str = "",
ety: str | None = None,
iso_639_3: str | None = None,
*,
atlas=None,
) -> dict:
"""Lookup glosses from the baked index. ``atlas`` must be a loaded Atlas."""
if atlas is None or getattr(atlas, "conn", None) is None:
return {
"term": term,
"lang": lang,
"ety": ety or "",
"matched_code": None,
"senses": [],
"fallback": False,
"source": "kaikki",
"error": "index_unavailable",
}
return lookup_definitions(
atlas.conn,
term=term,
lang=lang,
ety=ety,
iso_639_3=iso_639_3,
lang_meta=getattr(atlas, "lang_meta", None),
)