AiOCR / admin_panel.py
Asem75's picture
Update admin_panel.py
56de8a1 verified
Raw
History Blame Contribute Delete
12.8 kB
#!/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