Spaces:
Paused
Paused
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| import os | |
| import json | |
| import time | |
| from datetime import datetime as dt | |
| from typing import Optional, Any, Dict, List | |
| import gradio as gr | |
| import huggingface_hub | |
| from huggingface_hub import HfApi, hf_hub_download | |
| # ================= إعدادات الاتصال بالخزنة والسيرفر الفوقي ================= | |
| TOKEN = os.getenv("AIOCR_KEY") | |
| REPO_ID = "Asem75/aiocr_asistant" | |
| REPO_TYPE = "dataset" | |
| if TOKEN: | |
| TOKEN = TOKEN.strip().split()[0] | |
| api = HfApi(token=TOKEN) | |
| else: | |
| api = None | |
| # مسارات ملفات البيانات المشتركة داخل الـ Dataset السحابي | |
| USER_REGISTRY_FILE = "system/user_registry.json" | |
| BANNER_CONFIG_FILE = "system/banner_config.json" | |
| MESSAGES_FILE = "system/messages.json" | |
| ABOUT_APP_FILE = "system/about_app.json" | |
| LIVE_STATUS_FILE = "process/live_status.json" | |
| FINAL_OUTPUT_FILE = "process/final_output.json" | |
| # ================= الدوال المساعدة لعمليات القراءة والكتابة بالسيرفر ================= | |
| def read_vault_file(path_in_repo: str) -> Optional[Dict[str, Any]]: | |
| try: | |
| local_path = hf_hub_download(repo_id=REPO_ID, filename=path_in_repo, repo_type=REPO_TYPE, token=TOKEN) | |
| with open(local_path, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| except Exception: | |
| return None | |
| def save_json_to_repo(path: str, data: Any, commit_msg: str = "update") -> bool: | |
| try: | |
| content = json.dumps(data, ensure_ascii=False, indent=4).encode("utf-8") | |
| huggingface_hub.upload_file( | |
| path_or_fileobj=content, | |
| path_in_repo=path, | |
| repo_id=REPO_ID, | |
| repo_type=REPO_TYPE, | |
| token=TOKEN, | |
| commit_message=commit_msg | |
| ) | |
| return True | |
| except Exception: | |
| return False | |
| # ================= الدوال الإدارية للوحة الإدارة والتقارير ================= | |
| def fetch_live_processors_status() -> List[List]: | |
| status_data = read_vault_file(LIVE_STATUS_FILE) or {} | |
| ocr = status_data.get("ocr_engine", {}) | |
| ocr_row = [ | |
| "🔮 Sapphire OCR Engine", | |
| ocr.get("status", "🟢 مستقر وبانتظار سحب الملفات..."), | |
| str(ocr.get("processed_files", 0)), | |
| str(ocr.get("total_words", 0)), | |
| ocr.get("last_file", "لا يوجد"), | |
| ocr.get("last_pulse", "N/A") | |
| ] | |
| qwen = status_data.get("qwen_engine", {}) | |
| qwen_row = [ | |
| "👑 Qwen Golden Processor", | |
| qwen.get("status", "🟢 جاهز ومستقر عبر الرام..."), | |
| f"VIP: {qwen.get('vip_fixed', 0)} | عادي: {qwen.get('free_fixed', 0)}", | |
| str(qwen.get("total_words", 0)), | |
| f"سرعة آخر طلب: {qwen.get('last_speed', 0)} ثانية", | |
| qwen.get("last_pulse", "N/A") | |
| ] | |
| return [ocr_row, qwen_row] | |
| def get_all_messages() -> List[Dict]: | |
| msgs = read_vault_file(MESSAGES_FILE) or [] | |
| if isinstance(msgs, list): | |
| msgs.sort(key=lambda x: x.get("timestamp", ""), reverse=True) | |
| return msgs | |
| return [] | |
| def update_message_reply(msg_id: str, reply_text: str) -> str: | |
| if not msg_id or not reply_text.strip(): | |
| return "⚠️ يجب إدخال معرف الرسالة ونص الرد الشرعي." | |
| messages = read_vault_file(MESSAGES_FILE) or [] | |
| updated = False | |
| for msg in messages: | |
| if str(msg.get("id")) == str(msg_id): | |
| msg["admin_reply"] = reply_text.strip() | |
| msg["replied_at"] = dt.now().isoformat() | |
| updated = True | |
| break | |
| if not updated: | |
| return "❌ لم يتم العثور على الرسالة المطلوبة بالخزنة." | |
| if save_json_to_repo(MESSAGES_FILE, messages, f"رد إداري على رسالة {msg_id}"): | |
| return f"✅ تم حفظ الرد على الرسالة {msg_id} بنجاح." | |
| return "❌ فشل رفع التحديث السحابي للخزنة." | |
| def update_about_app(info: str, version: str) -> str: | |
| if not info.strip() or not version.strip(): | |
| return "⚠️ يجب ملء حقلي المعلومات والإصدار سيادياً." | |
| data = {"info": info.strip(), "version": version.strip(), "last_updated": dt.now().isoformat()} | |
| if save_json_to_repo(ABOUT_APP_FILE, data, "تحديث معلومات التطبيق الاستراتيجية"): | |
| return "✅ تم مزامنة وتحديث معلومات 'حول التطبيق' بنجاح." | |
| return "❌ فشل نقل الملف السحابي للخزنة." | |
| def get_users_table() -> List[List]: | |
| registry = read_vault_file(USER_REGISTRY_FILE) or {} | |
| table = [] | |
| for username, data in registry.items(): | |
| table.append([ | |
| username, | |
| data.get("email", ""), | |
| data.get("tier", "free"), | |
| data.get("fingerprint", "")[:20] + "..." if data.get("fingerprint") else "N/A", | |
| data.get("registered_at", "") | |
| ]) | |
| return table | |
| def update_user_tier(username: str, new_tier: str) -> str: | |
| if not username.strip(): | |
| return "⚠️ يرجى تزويد المنظومة باسم المستخدم المستهدف." | |
| registry = read_vault_file(USER_REGISTRY_FILE) or {} | |
| if username not in registry: | |
| return f"❌ المستخدم [{username}] غير مسجل في القيود الفوقية للمنظومة." | |
| registry[username]["tier"] = new_tier | |
| if save_json_to_repo(USER_REGISTRY_FILE, registry, f"تغيير رتبة {username} إلى {new_tier}"): | |
| return f"✅ تم ترقية رتبة {username} إلى [{new_tier}] بنجاح." | |
| return "❌ فشل تعديل القيود السحابية لملف المستخدم." | |
| def fetch_all_raw_data() -> Dict[str, Any]: | |
| return { | |
| "user_registry": read_vault_file(USER_REGISTRY_FILE) or {}, | |
| "banner_config": read_vault_file(BANNER_CONFIG_FILE) or {}, | |
| "messages": read_vault_file(MESSAGES_FILE) or [], | |
| "about_app": read_vault_file(ABOUT_APP_FILE) or {}, | |
| "final_output": read_vault_file(FINAL_OUTPUT_FILE) or {}, | |
| "live_status": read_vault_file(LIVE_STATUS_FILE) or {} | |
| } | |
| def format_raw_data(data: Dict) -> str: | |
| return json.dumps(data, ensure_ascii=False, indent=4) | |
| def load_messages_grid(): | |
| msgs = get_all_messages() | |
| return [[m.get("id",""), m.get("user",""), m.get("message",""), m.get("admin_reply",""), m.get("timestamp","")] for m in msgs] | |
| def radar_trigger_center(): | |
| return fetch_live_processors_status(), format_raw_data(fetch_all_raw_data()) | |
| # ================= دالة بناء واجهة التحكم الكاملة للأدمن (معدلة) ================= | |
| def build_admin_ui(about_info_input, about_version_input): | |
| # 🔥 التعديل الأساسي: لا نقوم بتحميل البيانات عند بدء التشغيل | |
| # نضع قيماً فارغة وسيتم تحديثها عبر الأزرار | |
| initial_radar = [] # فارغة بدلاً من استدعاء radar_trigger_center() | |
| initial_raw = "{}" # فارغة بدلاً من استدعاء radar_trigger_center() | |
| with gr.Tabs(): | |
| # 1) تبويب الرادار والتقارير الحية | |
| with gr.Tab("📡 رادار المراقبة وتقارير الأداء"): | |
| gr.Markdown("### 👁️ نافذة الرصد الفوري لأداء المعالجات المنفصلة (OCR & Qwen)") | |
| refresh_radar_btn = gr.Button("📡 تحديث فوري وسحب مؤشرات الأداء الحية", variant="secondary") | |
| processors_table = gr.Dataframe( | |
| value=initial_radar, | |
| headers=["المعالج المركزي", "الحالة التشغيلية", "الملفات المعالجة", "إجمالي الكلمات", "مؤشرات السرعة / آخر مستند", "آخر نبض اتصال"], | |
| datatype=["str", "str", "str", "str", "str", "str"], | |
| interactive=False | |
| ) | |
| gr.Markdown("### 📝 فحص البيانات الخام والمؤشرات الكلية للـ Dataset (JSON)") | |
| raw_data_display = gr.TextArea( | |
| value=initial_raw, | |
| label="ملخص البنى الهيكلية لكافة الملفات السحابية بالخزنة", | |
| lines=10, | |
| interactive=False | |
| ) | |
| # 2) تبويب إدارة المشتركين والتحكم برتب الـ VIP والعادي | |
| with gr.Tab("👥 إدارة المستخدمين والرتب"): | |
| gr.Markdown("### 📊 جدول فرز المستخدمين المسجلين وتغيير رتب العبور للـ VIP") | |
| refresh_users_btn = gr.Button("🔄 تحديث جدول القيود والمستخدمين", variant="secondary") | |
| users_table = gr.Dataframe( | |
| headers=["اسم المستخدم", "البريد الإلكتروني", "الرتبة", "البصمة المشفرة", "تاريخ التسجيل"], | |
| datatype=["str", "str", "str", "str", "str"], | |
| interactive=False, | |
| wrap=True | |
| ) | |
| # نضع جدول فارغ مبدئياً (سيتم ملؤه عند النقر على تحديث) | |
| users_table.value = [] | |
| with gr.Row(): | |
| user_to_upgrade = gr.Textbox(label="👤 اسم المستخدم الدقيق لتعديله") | |
| tier_selector = gr.Radio(["free", "vip"], label="👑 رتبة حساب العبور الممنوحة", value="free") | |
| upgrade_btn = gr.Button("🛠️ تنفيذ الترقية / سحب الرتبة فوراً", variant="primary") | |
| upgrade_result = gr.Textbox(label="حالة تحديث الرتبة في القاعدة", interactive=False) | |
| # 3) تبويب سجل مراسلات المستخدمين والرد الفوري عليها | |
| with gr.Tab("💬 القناة الثنائية (الرسائل والردود)"): | |
| gr.Markdown("### ✉️ سجل رسائل المستخدمين المستلمة مع الرد المباشر السحابي") | |
| refresh_msgs_btn = gr.Button("🔄 تحديث يدوي لقائمة الرسائل", variant="secondary") | |
| msgs_table = gr.Dataframe( | |
| headers=["ID", "المستخدم", "الرسالة", "الرد الحالي", "التوقيت"], | |
| datatype=["str", "str", "str", "str", "str"], | |
| interactive=False, | |
| wrap=True | |
| ) | |
| msgs_table.value = [] # فارغ مبدئياً | |
| with gr.Row(): | |
| msg_id_input = gr.Textbox(label="🆔 معرف الرسالة (ID)") | |
| reply_input = gr.Textbox(label="✍️ نص الرد الإداري الموجه للمستخدم", lines=2) | |
| send_reply_btn = gr.Button("🚀 إرسال الرد وتثبيته في الخزنة السحابية", variant="primary") | |
| reply_result = gr.Textbox(label="حالة معالجة الإرسال", interactive=False) | |
| # 4) تبويب إدارة وتحديث معلومات التطبيق الاستراتيجية للواجهة المشتركة | |
| with gr.Tab("ℹ️ تحديث معلومات 'حول التطبيق'"): | |
| gr.Markdown("### 📝 تعديل ديناميكي لوثيقة التعريف الاستراتيجية والنسخة") | |
| admin_info_box = about_info_input | |
| admin_ver_box = about_version_input | |
| update_about_btn = gr.Button("💾 حفظ وتعميم التحديثات على السيرفر الأساسي", variant="primary") | |
| about_result = gr.Textbox(label="حالة حفظ البيانات", interactive=False) | |
| # === ربط أحداث النقر والتحديث التلقائي لكافة التبويبات === | |
| refresh_radar_btn.click(fn=radar_trigger_center, inputs=None, outputs=[processors_table, raw_data_display]) | |
| refresh_users_btn.click(get_users_table, outputs=[users_table]) | |
| refresh_msgs_btn.click(load_messages_grid, outputs=[msgs_table]) | |
| send_reply_btn.click(update_message_reply, inputs=[msg_id_input, reply_input], outputs=[reply_result]) | |
| update_about_btn.click(update_about_app, inputs=[admin_info_box, admin_ver_box], outputs=[about_result]) | |
| upgrade_btn.click(update_user_tier, inputs=[user_to_upgrade, tier_selector], outputs=[upgrade_result]) | |
| # نعيد المكونات التي قد تحتاجها الصفحة الرئيسية (اختياري) | |
| return refresh_msgs_btn, msgs_table, refresh_users_btn, users_table |