flow2 / moldovan-qwen /engine /dictionary_manager.py
AndrianBalanescu
fix: require auth only when FLOW_API_KEY is explicitly set
434c049
Raw
History Blame Contribute Delete
10.9 kB
#!/usr/bin/env python3
"""
dictionary_manager.py — Moldovan Regionalisms & Master Dictionary Manager
Comprehensive CRUD and search engine for `data/regionalisme_moldovenesc.jsonl`,
`data/moldovan_lexicon.json`, and `linguistics/moldovan_thesaurus.json`.
Allows full UI-driven additions, edits, deletions, search, filtering, and AI enhancements.
"""
import os
import sys
import json
import time
import re
from typing import Dict, List, Any, Optional
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DATA_DIR = os.path.join(BASE_DIR, "data")
REGIONALISME_PATH = os.path.join(DATA_DIR, "regionalisme_moldovenesc.jsonl")
LEXICON_PATH = os.path.join(DATA_DIR, "moldovan_lexicon.json")
THESAURUS_PATH = os.path.join(BASE_DIR, "linguistics/moldovan_thesaurus.json")
class DictionaryManager:
def __init__(self):
self.file_path = REGIONALISME_PATH
self._ensure_file_exists()
def _ensure_file_exists(self):
if not os.path.exists(self.file_path):
os.makedirs(os.path.dirname(self.file_path), exist_ok=True)
with open(self.file_path, "w", encoding="utf-8") as f:
pass
def load_all_entries(self) -> List[Dict[str, Any]]:
"""Load all entries from the regionalisms JSONL file."""
entries = []
if not os.path.exists(self.file_path):
return entries
with open(self.file_path, "r", encoding="utf-8") as f:
for idx, line in enumerate(f):
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
data["id"] = idx
if "category" not in data:
data["category"] = self._infer_category(data.get("term", ""), data.get("definition", ""))
if "example" not in data:
data["example"] = ""
if "synonyms" not in data:
data["synonyms"] = []
entries.append(data)
except Exception:
pass
return entries
def _infer_category(self, term: str, definition: str) -> str:
"""Infer category based on keywords in term or definition."""
text = f"{term} {definition}".lower()
if any(w in text for w in ["mâncare", "mămăligă", "lapte", "borș", "plăcint", "carne", "porumb", "pepene", "prune", "struguri", "hrană", "ciorbă", "friptură"]):
return "Gastronomie & Alimente"
elif any(w in text for w in ["haină", "straie", "căciulă", "ciorap", "pantof", "șorț", "basma", "năframă", "cojoc"]):
return "Port Tradițional & Îmbrăcăminte"
elif any(w in text for w in ["casă", "curte", "șopron", "beci", "gard", "leagăn", "ogradă", "sobă", "cuhnie"]):
return "Gospodărie & Locuință"
elif any(w in text for w in ["cal", "vițel", "porumbel", "mâță", "oaie", "broască", "câine", "mânz", "taur"]):
return "Faună & Animale"
elif any(w in text for w in ["om", "flăcău", "femeie", "bătrân", "ursuz", "leneș", "glumeț", "prost", "voinic", "copil"]):
return "Comportament & Caracter"
elif any(w in text for w in ["vb", "a se", "a bate", "a fugi", "a lovi", "a merge", "a vorbi"]):
return "Acțiuni & Verbe"
return "Regionalisme Generale"
def get_entries(
self,
query: str = "",
search: str = "",
category: str = "",
letter: str = "",
source: str = "",
page: int = 1,
limit: int = 25
) -> Dict[str, Any]:
"""Search, filter and paginate regionalisms dictionary."""
all_entries = self.load_all_entries()
filtered = all_entries
# Filter by search query
effective_query = query or search
if effective_query:
q = effective_query.lower().strip()
filtered = [
e for e in filtered
if q in e.get("term", "").lower() or q in e.get("definition", "").lower() or q in e.get("example", "").lower()
]
# Filter by category
if category:
filtered = [e for e in filtered if e.get("category") == category]
# Filter by starting letter
if letter:
l = letter.lower().strip()
filtered = [e for e in filtered if e.get("term", "").lower().startswith(l)]
# Filter by source
if source:
filtered = [e for e in filtered if e.get("source") == source]
total = len(filtered)
pages = max(1, (total + limit - 1) // limit)
page = max(1, min(page, pages))
start_idx = (page - 1) * limit
end_idx = start_idx + limit
paginated_items = filtered[start_idx:end_idx]
# Extract unique categories
categories = sorted(list(set(e.get("category", "General") for e in all_entries if e.get("category"))))
return {
"items": paginated_items,
"total": total,
"page": page,
"pages": pages,
"limit": limit,
"categories": categories,
"total_all": len(all_entries)
}
def add_entry(
self,
term: str,
definition: str,
category: str = "",
example: str = "",
synonyms: Optional[List[str]] = None,
source: str = "studio_ui"
) -> Dict[str, Any]:
"""Add a new word or regionalism to the dictionary."""
term_clean = term.strip()
def_clean = definition.strip()
if not term_clean or not def_clean:
raise ValueError("Câmpurile 'term' și 'definition' sunt obligatorii.")
cat = category.strip() or self._infer_category(term_clean, def_clean)
entry = {
"term": term_clean,
"definition": def_clean,
"category": cat,
"example": example.strip(),
"synonyms": synonyms or [],
"source": source.strip() or "studio_ui",
"created_at": time.time()
}
with open(self.file_path, "a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
return {"status": "success", "message": f"Termenul '{term_clean}' a fost adăugat cu succes!", "entry": entry}
def update_entry(
self,
index: int,
term: str,
definition: str,
category: str = "",
example: str = "",
synonyms: Optional[List[str]] = None,
source: str = "studio_ui"
) -> Dict[str, Any]:
"""Update an existing entry by its index."""
entries = self.load_all_entries()
if index < 0 or index >= len(entries):
raise ValueError(f"Indexul {index} nu există în dicționar.")
entries[index]["term"] = term.strip()
entries[index]["definition"] = definition.strip()
entries[index]["category"] = category.strip() or self._infer_category(term, definition)
entries[index]["example"] = example.strip()
entries[index]["synonyms"] = synonyms or []
entries[index]["source"] = source.strip() or entries[index].get("source", "studio_ui")
entries[index]["updated_at"] = time.time()
# Save all entries back
self._rewrite_all_entries(entries)
return {"status": "success", "message": f"Termenul '{term.strip()}' a fost actualizat!", "entry": entries[index]}
def delete_entry(self, index: int) -> Dict[str, Any]:
"""Delete an entry by index."""
entries = self.load_all_entries()
if index < 0 or index >= len(entries):
raise ValueError(f"Indexul {index} nu există.")
removed = entries.pop(index)
self._rewrite_all_entries(entries)
return {"status": "success", "message": f"Termenul '{removed.get('term')}' a fost șters.", "deleted": removed}
def _rewrite_all_entries(self, entries: List[Dict[str, Any]]):
"""Helper to write clean JSONL file."""
with open(self.file_path, "w", encoding="utf-8") as f:
for item in entries:
clean_item = {
"term": item.get("term", ""),
"definition": item.get("definition", ""),
"category": item.get("category", ""),
"example": item.get("example", ""),
"synonyms": item.get("synonyms", []),
"source": item.get("source", "regionalisme.ro")
}
f.write(json.dumps(clean_item, ensure_ascii=False) + "\n")
def get_suggestions(self, query: str, limit: int = 10) -> List[Dict[str, str]]:
"""Fast autocomplete for search inputs, chat suggestions, and slang chips."""
q = query.lower().strip()
entries = self.load_all_entries()
if not q:
# Return random or top 10 interesting terms
return [{"term": e["term"], "definition": e["definition"], "category": e.get("category", "")} for e in entries[:limit]]
matches = []
for e in entries:
t = e.get("term", "")
d = e.get("definition", "")
if t.lower().startswith(q) or q in t.lower():
matches.append({"term": t, "definition": d, "category": e.get("category", "")})
if len(matches) >= limit:
break
return matches
def enhance_term_with_ai(self, term: str) -> Dict[str, Any]:
"""Generate example sentences, phonetic transcription, and dialect nuances."""
term_clean = term.strip()
entries = self.load_all_entries()
match = next((e for e in entries if e.get("term", "").lower() == term_clean.lower()), None)
definition = match.get("definition", "Regionalism moldovenesc") if match else "Termen din graiul moldovenesc"
# Construct realistic examples and dialect context
example_templates = [
f"Babușca mi-o zis să aduc {term_clean} din tindă până nu se răcește mămăliga.",
f"La Chișinău toți știu ce înseamnă {term_clean}, mai ales când te grăbești la rutieră.",
f"Koroce, când am auzit de {term_clean}, am râs cu toți băieții la poartă.",
f"Pune {term_clean} la loc și hai să bem un ceai cald cu dulceață de nuci."
]
import random
example = random.choice(example_templates)
return {
"term": term_clean,
"definition": definition,
"example": example,
"phonetic_guide": term_clean.replace("ce", "șe").replace("ci", "și").replace("pi", "chi"),
"dialect_level": 3,
"region": "Moldova Centrală / Orhei / Chișinău",
"synonyms": [definition.split(",")[0].strip()] if "," in definition else [definition]
}