mohammed777's picture
Update app.py
f4dae3c verified
import gradio as gr
import json
import matplotlib.pyplot as plt # استيراد مكتبة Matplotlib لرسم المخططات
import numpy as np # قد نحتاجه لبعض العمليات الحسابية البسيطة
import matplotlib.font_manager as fm # استيراد font_manager للتحكم في الخطوط
import arabic_reshaper # لاستعادة شكل الحروف العربية
from bidi.algorithm import get_display # لضبط اتجاه النص RTL
# استيراد الدالة الرئيسية لتقييم قائمة تشغيل YouTube من ملف main.py
# تأكد أن ملف main.py موجود في نفس المجلد وأن النماذج (.pkl) متاحة له
from main import evaluate_youtube_playlist_individually_same_method2
# =======================================================
# إعداد الخطوط لـ Matplotlib لدعم اللغة العربية
# =======================================================
# يجب التأكد من تثبيت خط عربي في بيئة Docker الخاصة بـ Hugging Face Space.
# مثال على التثبيت في Dockerfile: RUN apt-get update && apt-get install -y fonts-noto-arabic
# سنبحث عن خط Noto Sans Arabic، وإذا لم نجده، سيظل Matplotlib يستخدم الخط الافتراضي
font_path = None
# البحث عن خط Noto Sans Arabic أو Tajawal (يمكنك تغيير هذا بناءً على الخط الذي تفضله وتثبته)
for font in fm.findSystemFonts(fontpaths=None):
if 'NotoSansArabic' in font or 'Tajawal' in font or 'Amiri' in font:
font_path = font
break
if font_path:
# إضافة الخط العربي إلى قائمة الخطوط الافتراضية لـ Matplotlib
plt.rcParams['font.sans-serif'] = [fm.FontProperties(fname=font_path).get_name()] + plt.rcParams['font.sans-serif']
# تعطيل عرض علامة السالب كـ "صندوق" في بعض الخطوط
plt.rcParams['axes.unicode_minus'] = False
# تعيين عائلة الخط الافتراضية
plt.rcParams['font.family'] = 'sans-serif'
print(f"تم تعيين الخط العربي لـ Matplotlib: {font_path}") # لغرض التصحيح في السجلات
else:
print("تحذير: لم يتم العثور على خط عربي مناسب. قد لا يظهر النص العربي بشكل صحيح في المخطط.")
gr.Warning("تحذير: لم يتم العثور على خط عربي مناسب. قد لا يظهر النص العربي بشكل صحيح في المخطط.")
async def evaluate_playlist_local(youtube_url: str, max_comments_per_video: int = 50, max_workers: int = 3):
"""
تستدعي دالة تقييم قائمة تشغيل YouTube مباشرة من ملف main.py.
وتعيد النتائج النصية مجمعة في صندوق واحد، بالإضافة إلى مخطط Matplotlib يوضح نسب التعليقات.
"""
error_message = ""
# تهيئة المخرجات الافتراضية في حالة وجود خطأ أو عدم وجود رابط
default_text_output = "الرجاء إدخال رابط صحيح وتشغيل التقييم."
# إنشاء شكل Matplotlib فارغ لتجنب الأخطاء في حالة عدم وجود بيانات للمخطط
default_plot = plt.figure()
plt.close(default_plot) # إغلاق الشكل الافتراضي لمنع عرضه غير الضروري
# التحقق من أن الرابط ليس فارغًا قبل المتابعة
if not youtube_url:
error_message = "الرجاء إدخال رابط قائمة تشغيل أو فيديو يوتيوب."
gr.Warning(error_message) # عرض تحذير للمستخدم في واجهة Gradio
return error_message, default_plot # إرجاع رسالة خطأ ومخطط فارغ
try:
# استدعاء الدالة الرئيسية لمعالجة قائمة التشغيل/الفيديو
result = await evaluate_youtube_playlist_individually_same_method2(
youtube_url=youtube_url,
max_comments_per_video=max_comments_per_video,
max_workers=max_workers
)
# التحقق مما إذا كانت الدالة قد أعادت خطأ
if "error" in result:
error_message = f"خطأ في المعالجة: {result['error']}"
gr.Error(error_message) # عرض الخطأ كرسالة خطأ حمراء في Gradio
return error_message, default_plot # إرجاع رسالة الخطأ ومخطط فارغ
# استخراج النتائج المطلوبة من كائن JSON المُعاد
overall_quality = result.get("overall_quality", "غير متوفر")
composite_quality = result.get("composite_quality", "غير متوفر")
composite_score = result.get("composite_score", "غير متوفر")
percent_good_videos = result.get("percent_good_videos", "غير متوفر")
# التأكد من أن النسب عددية، وإلا سيتم تعيينها إلى 0.0
positive_ratio = result.get("positive_ratio", 0.0)
negative_ratio = result.get("negative_ratio", 0.0)
# =======================================================
# 1. إعداد النص الموحد ليتم عرضه في صندوق واحد (باستخدام Markdown للتنسيق)
# =======================================================
formatted_output_text = f"""
## 📊 نتائج تقييم قائمة التشغيل:
- **الجودة العامة:** **{overall_quality}**
- **الجودة المركبة:** **{composite_quality}**
- **النقاط المركبة:** **{composite_score} / 100**
- **نسبة الفيديوهات الجيدة:** **{percent_good_videos}%**
- **نسبة التعليقات الإيجابية:** **{positive_ratio}%**
- **نسبة التعليقات السلبية:** **{negative_ratio}%**
"""
# =======================================================
# 2. رسم المخطط باستخدام Matplotlib (مخطط دائري)
# =======================================================
# إنشاء شكل ومحاور للمخطط بحجم مناسب
fig, ax = plt.subplots(figsize=(6, 4))
# النصوص العربية التي ستظهر على المخطط
labels_arabic = ['إيجابية', 'سلبية']
# تطبيق arabic_reshaper و get_display على التسميات العربية
# هذا هو الجزء الذي يحل مشكلة قلب النص وتقطيعه
reshaped_labels = [get_display(arabic_reshaper.reshape(label)) for label in labels_arabic]
sizes = [positive_ratio, negative_ratio] # قيم النسب المئوية
colors = ['#4CAF50', '#F44336'] # ألوان الشرائح (أخضر للإيجابي، أحمر للسلبي)
explode = (0.1, 0) # "تفجير" الشريحة الإيجابية قليلاً لإبرازها (اختياري)
# رسم المخطط الدائري باستخدام التسميات المصححة
ax.pie(sizes, explode=explode, labels=reshaped_labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=90, textprops={'fontsize': 12})
ax.axis('equal') # يضمن أن يكون المخطط دائريًا متناسبًا.
# تطبيق arabic_reshaper و get_display على عنوان المخطط
reshaped_title = get_display(arabic_reshaper.reshape('توزيع التعليقات'))
ax.set_title(reshaped_title, fontsize=14, pad=20)
# إغلاق الشكل لمنع Matplotlib من عرضه تلقائيًا في الخلفية، Gradio سيتولى العرض.
plt.close(fig)
# إرجاع النص المنسق والمخطط كناتجين للدالة
return formatted_output_text, fig
except Exception as e:
# التقاط أي أخطاء غير متوقعة أثناء المعالجة
error_message = f"حدث خطأ غير متوقع أثناء المعالجة: {e}"
gr.Error(error_message) # عرض رسالة الخطأ للمستخدم
return error_message, default_plot # إرجاع رسالة الخطأ ومخطط فارغ في حالة الفشل
# بناء واجهة Gradio باستخدام gr.Blocks للتحكم في التخطيط
with gr.Blocks(title="تقييم قائمة تشغيل يوتيوب") as demo:
# عنوان ووصف الواجهة
gr.Markdown("# 📊 تقييم جودة قائمة تشغيل يوتيوب")
gr.Markdown("أدخل رابط قائمة تشغيل أو فيديو يوتيوب لتقييم جودتها بناءً على التحليلات والمشاعر.")
# تقسيم الواجهة إلى صفين (Row) لأعمدة المدخلات والمخرجات
with gr.Row():
# العمود الأول للمدخلات
with gr.Column(scale=1):
youtube_url_input = gr.Textbox(label="رابط قائمة تشغيل/فيديو يوتيوب", placeholder="الصق رابط يوتيوب هنا...", interactive=True)
max_comments_input = gr.Slider(minimum=10, maximum=200, value=50, step=10, label="الحد الأقصى للتعليقات لكل فيديو", interactive=True)
max_workers_input = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="الحد الأقصى للعمال المتوازيين", interactive=True)
submit_button = gr.Button("🚀 تقييم قائمة التشغيل", variant="primary") # زر الإرسال الرئيسي
# العمود الثاني للمخرجات (بجعل scale=2 ليكون أكبر)
with gr.Column(scale=2):
# صندوق نصي واحد لعرض جميع النتائج النصية المنسقة
output_text_box = gr.Markdown(label="النتائج النهائية للتقييم")
# عنصر Gradio لعرض مخطط Matplotlib
output_plot = gr.Plot(label="توزيع التعليقات (إيجابية مقابل سلبية)")
# ربط زر الإرسال بالدالة evaluate_playlist_local
# المدخلات هي عناصر واجهة المستخدم التي توفر البيانات للدالة
# المخرجات هي عناصر واجهة المستخدم التي ستعرض النتائج من الدالة
submit_button.click(
evaluate_playlist_local,
inputs=[youtube_url_input, max_comments_input, max_workers_input],
outputs=[output_text_box, output_plot]
)
# تشغيل واجهة Gradio
demo.launch(debug=True)