Spaces:
Sleeping
Sleeping
| # ============================================================ | |
| # 📄 الملف: app/assets.py | |
| # 🎯 الغرض: تنزيل الملفات الكبيرة تلقائيًا عند النشر (Streamlit Cloud). | |
| # | |
| # الملفات الكبيرة (النماذج المُكمَّمة + الـ embeddings + البيانات) لا تُرفع | |
| # على GitHub (تتجاوز حد 1GB). نخزّنها في مستودع Hugging Face Hub عام، | |
| # والتطبيق ينزّلها أول تشغيل فقط إن لم تكن موجودة محليًا. | |
| # | |
| # اسم مستودع Hugging Face يُقرأ من الأسرار: st.secrets["HF_DATA_REPO"] | |
| # (مثال: "username/reverse-dictionary-assets"). المستودع عام فلا يحتاج توكن. | |
| # | |
| # محليًا (الملفات موجودة) لا يحدث أي تنزيل — الدالة ترجع فورًا. | |
| # ============================================================ | |
| import os | |
| from pathlib import Path | |
| import streamlit as st | |
| import config | |
| # ما الذي نُنزّله (أنماط المسارات داخل مستودع HF): | |
| # - data/embeddings/ : المتجهات المحفوظة (.npy) | |
| # - data/processed/ : البيانات المنظّفة (data.csv) | |
| # - models/** : كل النماذج المحلية (الثالث وأي نموذج محلي إضافي). | |
| # النماذج من HuggingFace (الأول/الثاني) تُنزَّل تلقائيًا. | |
| DOWNLOAD_PATTERNS = [ | |
| "data/embeddings/**", | |
| "data/processed/data.csv", | |
| "models/**", | |
| ] | |
| def _setting(key: str) -> str | None: | |
| """يقرأ إعدادًا من متغيّرات البيئة أولًا (HF Spaces)، ثم من st.secrets | |
| فقط إن وُجد ملف secrets.toml فعلًا — لتفادي تحذير «No secrets found».""" | |
| val = os.getenv(key) | |
| if val: | |
| return val | |
| secret_paths = ( | |
| "/root/.streamlit/secrets.toml", | |
| "/app/.streamlit/secrets.toml", | |
| os.path.expanduser("~/.streamlit/secrets.toml"), | |
| ".streamlit/secrets.toml", | |
| ) | |
| if any(os.path.exists(p) for p in secret_paths): | |
| try: | |
| return st.secrets[key] | |
| except Exception: | |
| return None | |
| return None | |
| def _repo_id() -> str | None: | |
| """اسم مستودع HF الذي يحوي الملفات الكبيرة.""" | |
| return _setting("HF_DATA_REPO") | |
| def assets_ready() -> bool: | |
| """هل كل الملفات الكبيرة موجودة محليًا؟""" | |
| if not config.PROCESSED_DATA_PATH.exists(): | |
| return False | |
| for m in config.MODELS: | |
| # ملف الـ embeddings مطلوب لكل نموذج | |
| if not config.embedding_path(m["key"]).exists(): | |
| return False | |
| # النماذج المحلية (مسار مطلق) لازم يكون مجلدها موجودًا. | |
| # نماذج HuggingFace (مثل "org/name") تُنزَّل تلقائيًا فلا نفحصها. | |
| if os.path.isabs(m["hf_id"]) and not Path(m["hf_id"]).exists(): | |
| return False | |
| return True | |
| def ensure_assets() -> None: | |
| """يُنزّل الملفات الكبيرة من Hugging Face Hub إن لم تكن موجودة.""" | |
| if assets_ready(): | |
| return | |
| repo = _repo_id() | |
| if not repo: | |
| raise RuntimeError( | |
| "الملفات الكبيرة (النماذج/الـ embeddings) غير موجودة، و HF_DATA_REPO " | |
| "غير مضبوط في الأسرار. راجع DEPLOY.md." | |
| ) | |
| from huggingface_hub import snapshot_download | |
| # تُنزَّل مباشرة إلى جذر المشروع بنفس البنية (data/، models/...) | |
| snapshot_download( | |
| repo_id=repo, | |
| repo_type="model", | |
| local_dir=str(config.BASE_DIR), | |
| allow_patterns=DOWNLOAD_PATTERNS, | |
| ) | |