Spaces:
Sleeping
Sleeping
| 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), | |
| ) | |