""" سكربت تجهيز بيانات المعجم: يحوّل ملف خام (raw_lexicon.csv) إلى الصيغة الموحَّدة data/lexicon_source.json التي يستخدمها scripts/build_embeddings.py. المدخل المتوقع: data/raw_lexicon.csv أعمدة الملف: _id, sense_id, lemma, stem, definition, example, translation, pos_label lemma -> الكلمة العربية (مُشكَّلة) definition -> نص التعريف (مُشكَّل) stem -> نص JSON لمصفوفة بعنصر واحد أو لا شيء، مثل '["ش ر ب"]' أو '[null]' المخرج: data/lexicon_source.json [ {"word": "...", "definition": "...", "stem": "..." | null}, ... ] word وdefinition يُحفظان بتشكيلهما الكامل كما في المصدر؛ إزالة التشكيل تتم فقط عند حساب متجهات الـembedding في build_embeddings.py، وليس هنا. ملاحظات التنظيف المطبَّقة: - إزالة المسافات الزائدة من بداية/نهاية النصوص - تجاهل الصفوف الفارغة (lemma أو definition خاليان) - إزالة التكرارات المتطابقة تماماً (نفس الكلمة + نفس التعريف) الاستخدام: python -m scripts.prepare_data """ import csv import json import sys from pathlib import Path sys.path.append(str(Path(__file__).resolve().parent.parent)) from app import config # noqa: E402 def get_pos_group(pos_label: str) -> str: if not pos_label or str(pos_label).strip() in ("", "nan", "غير معروف"): return "غير معروف" pos = str(pos_label).strip() NOUN = { "اسم", "أداة", "اسم آلة", "اسم جنس", "اسم ذات", "اسم زمان", "اسم مبهم", "اسم مرة", "اسم معنى", "اسم مكان", "اسم هيئة", } VERB = { "فعل", "فعل لازم", "فعل متعدي", "أفعل تفضيل", "انفعال تعجب", "انفعال ذم", "انفعال صوتي", "انفعال فعلي", "انفعال مدح", } ADJ = { "صفة", "صفة فاعل", "صفة مشبهة", "صفة مفعول", "صيغة مبالغة", "منسوب", } PRON = {"ضمير إشارة", "ضمير شخصي", "ضمير موصول"} ADV = {"ظرف", "ظرف زمان", "ظرف مكان"} if pos in NOUN: return "اسم" if pos in VERB: return "فعل" if pos in ADJ: return "صفة" if pos in PRON: return "ضمير" if pos in ADV: return "ظرف" return "غير معروف" def parse_stem(raw_stem: str) -> str | None: try: items = json.loads(raw_stem) except (json.JSONDecodeError, TypeError): return None return items[0] if items else None def main() -> None: raw_path = config.DATA_DIR / "raw_lexicon.csv" if not raw_path.exists(): raise FileNotFoundError( f"الملف الخام غير موجود: {raw_path}\n" "ضعي ملف raw_lexicon.csv بأعمدة: lemma, stem, definition" ) seen: set[tuple[str, str]] = set() lexicon: list[dict] = [] with open(raw_path, "r", encoding="utf-8") as f: reader = csv.DictReader(f) required = {"lemma", "stem", "definition"} if not required.issubset(reader.fieldnames or []): raise ValueError( f"أعمدة الملف غير صحيحة: {reader.fieldnames}\n" f"يجب أن يحتوي الملف على الأعمدة: {required}" ) for row in reader: word = (row.get("lemma") or "").strip() definition = (row.get("definition") or "").strip() if not word or not definition: continue key = (word, definition) if key in seen: continue seen.add(key) lexicon.append({ "word": word, "definition": definition, "stem": parse_stem(row.get("stem") or ""), "sense_id": (row.get("sense_id") or "").strip(), "pos_label": (row.get("pos_label") or "").strip(), "pos_group": get_pos_group(row.get("pos_label") or ""), }) if not lexicon: raise ValueError("لم يتم العثور على أي صفوف صالحة في الملف الخام") output_path = config.DATA_DIR / "lexicon_source.json" with open(output_path, "w", encoding="utf-8") as f: json.dump(lexicon, f, ensure_ascii=False, indent=2) print(f"تم حفظ {len(lexicon)} عنصر في: {output_path}") print("اكتمل التجهيز بنجاح ✅") if __name__ == "__main__": main()