ahmedmegahed commited on
Commit
cbcd82f
·
verified ·
1 Parent(s): f8d6953

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +384 -417
app.py CHANGED
@@ -1,439 +1,406 @@
1
- import gradio as gr
2
- import torch
3
- # We will use a mock predictor as the original library might not be available in this environment
4
- # from transformers import AutoTokenizer, AutoModelForSequenceClassification
5
- import pandas as pd
6
- import plotly.express as px
7
- import json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  import os
9
- import google.generativeai as genai
10
- import warnings
11
  import logging
12
- import html
13
- import datetime
14
- from markdown_it import MarkdownIt
15
  import base64
 
 
 
 
16
 
17
- # --- الخطوة الأهم: الإعدادات والتهيئة ---
 
 
 
18
 
19
- # ==============================================================================
20
- # ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
21
- # الصق مفتاح Gemini API الخاص بك هنا بين علامتي التنصيص
22
- # يمكنك الحصول عليه من Google AI Studio
23
- GEMINI_API_KEY = "AIzaSyC5k5j07zeDWxJATTzx6zNABXwXQWvLIqw"
24
- # ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
25
- # ==============================================================================
26
 
 
 
27
 
28
- # --- بقية الإعدادات (لا تحتاج لتعديلها) ---
29
- warnings.filterwarnings("ignore", category=UserWarning)
30
- logging.getLogger("transformers").setLevel(logging.ERROR)
31
 
32
- # A mock class to simulate the original ActoraPredictor if it's not available
33
- class MockActoraPredictor:
34
- """A mock predictor that returns dummy data for UI testing."""
35
- def predict(self, text):
36
- return {
37
- 'liked': 150, 'loved': 70, 'haha': 20, 'wow': 15, 'sad': 5, 'angry': 2,
38
- 'comments': 25, 'shares': 10, 'interactions': 297
39
- }
40
 
41
- # إعداد واجهة برمجة تطبيقات Gemini
42
- try:
43
- if not GEMINI_API_KEY or GEMINI_API_KEY == "PASTE_YOUR_KEY_HERE":
44
- print("تحذير: لم يتم توفير مفتاح GEMINI_API_KEY. ميزة التحليل الاستراتيجي لن تعمل.")
45
- GEMINI_ENABLED = False
46
- else:
47
- genai.configure(api_key=GEMINI_API_KEY)
48
- GEMINI_ENABLED = True
49
- except Exception as e:
50
- print(f"خطأ في إعداد Gemini: {e}")
51
- GEMINI_ENABLED = False
52
-
53
- actora_emoji_map = {
54
- "liked": "👍", "loved": "❤️", "haha": "😂", "wow": "😮", "sad": "😢", "angry": "😠",
55
- "comments": "💬", "shares": "🔗", "interactions": "📊"
56
- }
57
-
58
- # Use the Mock Predictor for stability in this environment
59
- predictor = MockActoraPredictor()
60
- MODEL_LOADED = True
61
- MODEL_ERROR_MESSAGE = ""
62
-
63
-
64
- # أيقونات SVG للتفاعلات (تم التحقق منها وتصحيحها)
65
- EMOJI_MAP_SVG = {
66
- "liked": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' fill='%230866FF'/%3E%3Cpath fill='%23fff' d='M7.2 3.2c0-.44.36-.8.8-.8.74 0 1.34.6 1.34 1.34v1.86h1.76c.96 0 1.66.9 1.44 1.86l-.45 1.88a2.2 2.2 0 01-2.14 1.69H6.9a.55.55 0 01-.55-.55V7.9c0-.4.09-.8.27-1.16l.27-.55c.27-.55.41-1.16.41-1.77V3.2zM3.7 7.05c-.42 0-.76.34-.76.76v3.2c0 .42.34.76.76.76h.76c.2 0 .37-.17.37-.37V7.42c0-.2-.17-.37-.37-.37H3.7z'/%3E%3C/svg%3E",
67
- "loved": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' fill='%23FA2E3E'/%3E%3Cpath fill='%23fff' d='M8 11.1c2.1-1.43 3.9-2.67 3.9-4.36 0-.97-.73-1.92-1.82-1.92-.74 0-1.37.47-1.64 1.14-.27-.67-.9-1.14-1.64-1.14-1.09 0-1.82.95-1.82 1.92 0 1.7 1.8 2.94 3.9 4.36z'/%3E%3C/svg%3E",
68
- "haha": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' fill='%23F6A623'/%3E%3Ccircle cx='5.2' cy='5.6' r='1' fill='%231C1C1D'/%3E%3Ccircle cx='10.8' cy='5.6' r='1' fill='%231C1C1D'/%3E%3Cpath fill='%231C1C1D' d='M4.6 9.6c.5 1.1 1.9 1.7 3.4 1.7s2.9-.6 3.4-1.7c.1-.3-.1-.6-.5-.6H5c-.4 0-.6.3-.4.6z'/%3E%3C/svg%3E",
69
- "wow": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' fill='%23FFB169'/%3E%3Ccircle cx='5.3' cy='5.6' r='1.2' fill='%231C1C1D'/%3E%3Ccircle cx='10.7' cy='5.6' r='1.2' fill='%231C1C1D'/%3E%3Ccircle cx='8' cy='10.2' r='1.6' fill='%231C1C1D'/%3E%3C/svg%3E",
70
- "sad": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' fill='%237A7F85'/%3E%3Ccircle cx='5.3' cy='6.1' r='.9' fill='%23fff'/%3E%3Ccircle cx='10.7' cy='6.1' r='.9' fill='%23fff'/%3E%3Cpath stroke='%23fff' stroke-width='1.2' d='M5 11c.8-1 2-1.4 3-1.4s2.2.4 3 1.4'/%3E%3C/svg%3E",
71
- "angry": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='8' cy='8' r='7.5' fill='%23E11731'/%3E%3Cpath stroke='%23fff' stroke-width='1.2' d='M5 6l2 1M11 6l-2 1'/%3E%3Cpath stroke='%23fff' stroke-width='1.2' d='M5 11c1-.6 2-.8 3-.8s2 .2 3 .8'/%3E%3C/svg%3E",
72
- "shares": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M10.6 4.7 14 8l-3.4 3.3M14 8H2' stroke='%2394A3B8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E",
73
- "comments": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M8 14.5c-.6 0-1.2-.1-1.7-.3-.5-.2-1-.4-1.4-.7L1.5 15l.7-3.8C1.7 10.7 1.1 9.6 1.1 8.4c0-3 2.2-5.4 6-5.4 3 0 5.8 2 6.2 5 .2 1.2 0 2.4-.6 3.4-.6.9-1.5 1.6-2.6 1.9-.8.2-1.6.3-2.3.3Z' stroke='%2394A3B8' stroke-width='1.4'/%3E%3C/svg%3E",
74
- "interactions": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M2 8a6 6 0 1 1 9.4 4.8' stroke='%2394A3B8' stroke-width='1.2'/%3E%3Cpath d='M8 15H5' stroke='%2394A3B8' stroke-width='1.2'/%3E%3Cpath d='M8 1H5' stroke='%2394A3B8' stroke-width='1.2'/%3E%3C/svg%3E"
75
- }
76
-
77
- # Base64 encoded logo
78
- LOGO_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAZlBMVEX///8zNTYtLzQpc7cvNDYnca4rcrYsMjYnc7QqcLIncLQrcrMrbrQAAAAAAAAAAAAAAAAAAAAAADsAADwAACYAADO/v78AABkAAD+vr68AADba2toAACqAgIAAACHA29uAgIAAAAB+fn45OTn1RfeBAAAAB3RSTlMA+9k2F9/Y2QftfmcAAAbrSURBVHja7d1rZ5pIEAZgEgRR8E1EFARUERBFe//fK2B3N00GBCVVT//f0+nJJjGZTM5sWcPhx4/oK27j3f67i+9p/b4+3u0/vL/9Yj1j/9d31z+6+S5A+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Qp-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+...QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+A+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+A+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+...QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+...QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+...QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+-QpA+QpA+QpA+-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+-QpA+QpA+-QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+-QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+Q-Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+QpA+QpA+QpA+QpA+Q-QpA+... "
79
-
80
- # --- الدوال المنطقية والتحليلية ---
81
-
82
- def get_prediction_results(text: str):
83
- if not MODEL_LOADED: raise gr.Error(MODEL_ERROR_MESSAGE)
84
- if not text or not text.strip(): return None
85
  try:
86
- raw_predictions = predictor.predict(text)
87
- reverse_map = {v: k for k, v in actora_emoji_map.items()}
88
- return {reverse_map.get(k, k): v for k, v in raw_predictions.items()}
89
- except Exception as e:
90
- raise gr.Error(f"خطأ أثناء تحليل النص مع Actora: {e}")
91
-
92
- def get_gemini_insights(results: dict, text: str) -> str:
93
- if not GEMINI_ENABLED:
94
- return "<p>ميزة التحليل المتقدم معطلة. يرجى إعداد مفتاح API الخاص بـ Gemini في ملف app.py.</p>"
95
- prompt = f"""
96
- # المهمة
97
- أنت "MegoFlow AI"، مستشار تسويق رقمي وخبير في تحليل النصوص الإعلانية. تحليلك يجب أن يكون دقيقاً، موجزاً، وقابلاً للتنفيذ بشكل مباشر.
98
-
99
- # السياق
100
- لدي منشور على وسائل التواصل الاجتماعي أريد تحليله. قام نموذج تنبؤي بمنحه النتائج التالية (من إجمالي {results.get('interactions', 0)} تفاعل متوقع):
101
- - إعجابات متوقعة: {results.get('liked', 0)}
102
- - قلوب متوقعة: {results.get('loved', 0)}
103
- - تعليقات متوقعة: {results.get('comments', 0)}
104
- - مشاركات متوقعة: {results.get('shares', 0)}
105
- - تفاعلات سلبية (حزن/غضب): {results.get('sad', 0) + results.get('angry', 0)}
106
-
107
- # محتوى المنشور
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  ---
109
- {text}
110
  ---
111
-
112
- # المطلوب
113
- بناءً على نص المنشور ونتائجه المتوقعة، قدم تحليلاً نقدياً باللغة العربية في قسمين. استخدم تنسيق Markdown:
114
- 1. **"تشخيص الأداء":** في جملة واحدة، اشرح ماذا تخبرنا هذه الأرقام عن الاستقبال المحتمل للمنشور من قبل الجمهور.
115
- 2. **"مقترحات للنمو":** قدم 3 نقاط بالضبط على شكل قائمة. كل نقطة يجب أن تكون اقتراحاً ملموساً ومبدعاً لتحسين المنشور. لا تقدم نصائح عامة مثل "أضف CTA"، بل اقترح CTA *محدد* أو إعادة صياغة *محددة* لجملة في النص. يجب أن تكون النصائح مرتبطة مباشرة بالنص والنتائج.
 
 
 
 
116
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  try:
118
- model = genai.GenerativeModel('gemini-1.5-flash-latest')
119
- response = model.generate_content(prompt)
120
- md = MarkdownIt()
121
- return md.render(response.text)
122
  except Exception as e:
123
- return f"<p style='color: #ff9999;'>حدث خطأ أثناء استدعاء Gemini: {e}</p>"
124
-
125
- def create_results_card(title: str, text: str, results: dict) -> str:
126
- if not results: return ""
127
- safe_text = html.escape(text)
128
- formatted_text = safe_text.replace("\n", "<br>")
129
- rankables = {k: v for k, v in results.items() if k not in ["comments", "shares", "interactions"]}
130
- sorted_reactions = sorted(rankables.items(), key=lambda item: item[1], reverse=True)
131
- total_interactions = results.get("interactions", 0)
132
- comments = results.get("comments", 0)
133
- shares = results.get("shares", 0)
134
- insights_html = get_gemini_insights(results, text)
135
- html_content = f"""
136
- <div class='result-card'>
137
- <div class='card-header'>{html.escape(title)}</div>
138
- <div class='post-preview'>{formatted_text}</div>
139
- <div class='card-body'>
140
- <div class='total-score'>
141
- <img src='{EMOJI_MAP_SVG['interactions']}' class='icon-total'/>
142
- <div><span class='total-value'>{total_interactions}</span><span class='total-label'>إجمالي التفاعل المتوقع</span></div>
143
- </div>
144
- <div class='breakdown-title'>توزيع التفاعلات</div>
145
- <div class='breakdown-grid'>
146
- """
147
- for name, value in sorted_reactions:
148
- if value > 0 and name in EMOJI_MAP_SVG:
149
- percentage = (value / total_interactions * 100) if total_interactions > 0 else 0
150
- html_content += f"""
151
- <div class='reaction-row'>
152
- <img src='{EMOJI_MAP_SVG[name]}' class='icon-reaction'/>
153
- <span class='reaction-name'>{name.capitalize()}</span>
154
- <div class='progress-bar-container'><div class='progress-bar' style='width: {percentage}%;'></div></div>
155
- <span class='reaction-value'>{value}</span>
156
- </div>
157
- """
158
- html_content += "</div>"
159
- if comments > 0 or shares > 0:
160
- html_content += "<div class='secondary-stats'>"
161
- if comments > 0: html_content += f"<span><img src='{EMOJI_MAP_SVG['comments']}' class='icon-secondary'/> {comments} تعليق</span>"
162
- if shares > 0: html_content += f"<span><img src='{EMOJI_MAP_SVG['shares']}' class='icon-secondary'/> {shares} مشاركة</span>"
163
- html_content += "</div>"
164
- html_content += f"""
165
- <div class='insights-section'>
166
- <div class='breakdown-title insights-title'>💡 تحليل المستشار الآلي (Gemini)</div>
167
- <div class='gemini-insights'>{insights_html}</div>
168
- </div>
169
- """
170
- html_content += "</div></div>"
171
- return html_content
172
-
173
- def update_history_display(history: list) -> str:
174
- if not history: return "<div class='placeholder'>لا توجد تحليلات في السجل حتى الآن.</div>"
175
- history_html = "<div class='history-container'>"
176
- for entry in reversed(history):
177
- preview = html.escape(entry['text'][:100]) + ('...' if len(entry['text']) > 100 else '')
178
- score = entry['results'].get('interactions', 0)
179
- history_html += f"""
180
- <div class='history-item'>
181
- <div class='history-score'>{score}</div>
182
- <div class='history-details'>
183
- <div class='history-text'>{preview}</div>
184
- <div class='history-time'>{entry['timestamp']}</div>
185
- </div>
186
- </div>
187
- """
188
- history_html += "</div>"
189
- return history_html
190
-
191
- def analyze_single_post(text: str, current_history: list):
192
- if not text or not text.strip():
193
- placeholder_html = "<div class='placeholder'>ابدأ بكتابة منشور في الصندوق أعلاه ثم اضغط 'حلّل الآن'.</div>"
194
- return placeholder_html, update_history_display(current_history), current_history
195
- results = get_prediction_results(text)
196
- if results:
197
- card_html = create_results_card("تحليل المنشور", text, results)
198
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
199
- new_entry = {'text': text, 'results': results, 'timestamp': timestamp}
200
- current_history.append(new_entry)
201
- history_html = update_history_display(current_history)
202
- return card_html, history_html, current_history
203
- no_results_html = "<div class='placeholder'>لا توجد نتائج لعرضها.</div>"
204
- return no_results_html, update_history_display(current_history), current_history
205
-
206
- def analyze_ab_comparison(text_a: str, text_b: str):
207
- if (not text_a or not text_a.strip()) and (not text_b or not text_b.strip()):
208
- return "<div class='placeholder'>اكتب في صندوق واحد على الأقل للمقارنة.</div>"
209
- html_output = "<div class='comparison-container'>"
210
- if text_a and text_a.strip():
211
- results_a = get_prediction_results(text_a)
212
- html_output += create_results_card("المنشور (أ)", text_a, results_a)
213
- else:
214
- html_output += "<div class='result-card placeholder-card'>اكتب المنشور (أ) هنا</div>"
215
- if text_b and text_b.strip():
216
- results_b = get_prediction_results(text_b)
217
- html_output += create_results_card("المنشور (ب)", text_b, results_b)
218
- else:
219
- html_output += "<div class='result-card placeholder-card'>اكتب المنشور (ب) هنا</div>"
220
- html_output += "</div>"
221
- return html_output
222
-
223
- # --- بناء واجهة المستخدم التفاعلية ---
224
-
225
- CSS = """
226
- @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;700;900&display=swap');
227
-
228
- :root {
229
- --brand-dark-blue: #042A5C;
230
- --brand-teal-green: #248277;
231
- --bg-main: #0a101f;
232
- --bg-panel: #192233;
233
- --text-main: #f0f4f8;
234
- --text-secondary: #a0aec0;
235
- --border-color: #313c52;
236
- --brand-gradient: linear-gradient(90deg, var(--brand-teal-green), var(--brand-dark-blue));
237
- }
238
-
239
- body, .gradio-container {
240
- font-family: 'Cairo', sans-serif;
241
- background: var(--bg-main) !important;
242
- color: var(--text-main);
243
- }
244
-
245
- .panel {
246
- background: var(--bg-panel);
247
- border: 1px solid var(--border-color);
248
- border-radius: 16px;
249
- padding: 24px;
250
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -2px rgba(0, 0, 0, 0.2);
251
- }
252
- .panel-title {
253
- font-size: 20px;
254
- font-weight: 700;
255
- color: var(--text-main);
256
- margin-bottom: 16px;
257
- border-bottom: 1px solid var(--border-color);
258
- padding-bottom: 12px;
259
- }
260
- .placeholder {
261
- text-align: center;
262
- color: var(--text-secondary);
263
- padding: 40px;
264
- font-size: 16px;
265
- min-height: 200px;
266
- display: flex;
267
- justify-content: center;
268
- align-items: center;
269
- }
270
-
271
- .custom-btn {
272
- background: var(--brand-gradient) !important;
273
- color: white !important;
274
- font-weight: bold !important;
275
- border: none !important;
276
- border-radius: 8px !important;
277
- padding: 12px 24px !important;
278
- transition: all 0.2s ease-in-out;
279
- }
280
- .custom-btn:hover {
281
- box-shadow: 0 0 15px rgba(36, 130, 119, 0.6);
282
- transform: translateY(-2px);
283
- }
284
-
285
- .result-card {
286
- direction: rtl;
287
- background: #1c283d;
288
- color: var(--text-main);
289
- border-radius: 12px;
290
- overflow: hidden;
291
- border: 1px solid var(--border-color);
292
- margin-top: 10px;
293
- }
294
- .card-header {
295
- padding: 12px 16px;
296
- font-weight: 700;
297
- font-size: 18px;
298
- color: white;
299
- background: var(--brand-dark-blue);
300
- }
301
- .post-preview {
302
- padding: 16px;
303
- font-size: 15px;
304
- line-height: 1.6;
305
- background: rgba(10, 16, 31, 0.5);
306
- border-bottom: 1px solid var(--border-color);
307
- white-space: pre-wrap;
308
- word-wrap: break-word;
309
- }
310
- .card-body { padding: 16px; }
311
- .total-score {
312
- display: flex;
313
- align-items: center;
314
- gap: 12px;
315
- border-bottom: 1px solid var(--border-color);
316
- padding-bottom: 12px;
317
- margin-bottom: 12px;
318
- }
319
- .icon-total { width: 36px; height: 36px; }
320
- .total-value { font-size: 28px; font-weight: 900; color: white; }
321
- .total-label { font-size: 12px; color: var(--text-secondary); }
322
- .breakdown-title {
323
- font-weight: 700;
324
- margin-bottom: 12px;
325
- font-size: 14px;
326
- color: var(--text-secondary);
327
- text-transform: uppercase;
328
- }
329
- .breakdown-grid { display: grid; gap: 8px; }
330
- .reaction-row {
331
- display: grid;
332
- grid-template-columns: 20px 70px 1fr 35px;
333
- align-items: center;
334
- gap: 10px;
335
- font-size: 14px;
336
- color: var(--text-main);
337
- }
338
- .icon-reaction { width: 20px; height: 20px; }
339
- .progress-bar-container { background: #334155; border-radius: 5px; height: 8px; overflow: hidden; }
340
- .progress-bar { background: var(--brand-teal-green); height: 100%; border-radius: 5px; }
341
- .reaction-value { font-weight: 700; text-align: right; color: var(--text-main); }
342
- .secondary-stats { display: flex; gap: 20px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border-color); color: var(--text-secondary); font-size: 13px; align-items: center; }
343
- .icon-secondary { width: 16px; height: 16px; vertical-align: middle; margin-right: 4px; }
344
-
345
- /* Comparison and Insights */
346
- .comparison-container { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
347
- .insights-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border-color); }
348
- .gemini-insights strong, .gemini-insights b { color: var(--brand-teal-green); }
349
- .gemini-insights ul { padding-right: 20px; }
350
- .gemini-insights p, .gemini-insights li { color: var(--text-main); }
351
- .gemini-insights h1, .gemini-insights h2 { color: white; }
352
-
353
-
354
- /* History Tab */
355
- .history-item {
356
- display: flex;
357
- align-items: center;
358
- gap: 12px;
359
- background: #273448;
360
- padding: 10px;
361
- border-radius: 8px;
362
- border-left: 4px solid var(--brand-teal-green);
363
- }
364
- .history-score {
365
- font-size: 20px;
366
- font-weight: 700;
367
- color: var(--brand-teal-green);
368
- background-color: rgba(16, 185, 129, 0.1);
369
- padding: 8px 12px;
370
- border-radius: 6px;
371
- }
372
-
373
- /* Tab Styles */
374
- .gradio-container .tabs > .tab-nav > button.selected {
375
- background: var(--bg-panel) !important;
376
- color: white !important;
377
- border-bottom: 2px solid var(--brand-teal-green) !important;
378
- }
379
- .gradio-container .tabs > .tab-nav > button {
380
- background: var(--bg-main) !important;
381
- color: var(--text-secondary) !important;
382
- border-bottom: 2px solid transparent !important;
383
- }
384
-
385
- @media (max-width: 768px) {
386
- .comparison-container { grid-template-columns: 1fr; }
387
- }
388
  """
389
 
390
- with gr.Blocks(css=CSS, theme=gr.themes.Base()) as demo:
391
- history_state = gr.State([])
 
 
392
 
393
- gr.Markdown(f"""
394
- <div style='text-align:center; font-family:"Cairo", sans-serif; direction:rtl; padding: 20px 0;'>
395
- <img src='{LOGO_BASE64}' alt='MegoFlow Logo' style='width:150px; margin-bottom:10px;'>
396
- <p style='color:var(--text-secondary); font-size:18px;'>حوّل بيانات منشوراتك إلى استراتيجيات نمو قابلة للتنفيذ.</p>
397
- </div>
398
- """)
399
-
400
- with gr.Tabs():
401
- with gr.TabItem("تحليل منشور واحد"):
402
- with gr.Row(equal_height=False):
403
- with gr.Column(scale=1):
404
- with gr.Group(elem_classes="panel"):
405
- gr.Markdown("<div class='panel-title'>📝 نص المنشور</div>")
406
- txt_single = gr.Textbox(lines=10, placeholder="اكتب هنا نص المنشور الذي تريد تحليله...", label=None)
407
- btn_single = gr.Button("حلّل الآن", elem_classes="custom-btn")
408
- with gr.Column(scale=1):
409
- with gr.Group(elem_classes="panel"):
410
- gr.Markdown("<div class='panel-title'>📊 النتائج والتحليل الاستراتيجي</div>")
411
- out_single = gr.HTML(label=None, value="<div class='placeholder'>ستظهر النتائج هنا بعد التحليل.</div>")
412
-
413
- with gr.TabItem("مقارنة A/B"):
414
- with gr.Column():
415
- with gr.Group(elem_classes="panel"):
416
- gr.Markdown("<div class='panel-title'>📝 نصوص المقارنة</div>")
417
- with gr.Row():
418
- txt_a = gr.Textbox(lines=8, placeholder="اكتب نص المنشور (أ) هنا...", label="المنشور (أ)", scale=1)
419
- txt_b = gr.Textbox(lines=8, placeholder="اكتب نص المنشور (ب) هنا...", label="المنشور (ب)", scale=1)
420
- btn_ab = gr.Button("اختبر وقارن", elem_classes="custom-btn")
421
- with gr.Group(elem_classes="panel"):
422
- gr.Markdown("<div class='panel-title'>📊 نتائج المقارنة</div>")
423
- out_ab = gr.HTML(label=None, value="<div class='placeholder'>ستظهر نتائج المقارنة هنا.</div>")
424
-
425
- with gr.TabItem("سجل التحليلات"):
426
- with gr.Group(elem_classes="panel"):
427
- gr.Markdown("<div class='panel-title'>📂 سجل تحليلات الجلسة الحالية</div>")
428
- history_display = gr.HTML(label=None, value="<div class='placeholder'>لا توجد تحليلات في السجل حتى الآن.</div>")
429
-
430
- btn_single.click(analyze_single_post, inputs=[txt_single, history_state], outputs=[out_single, history_display, history_state])
431
- btn_ab.click(analyze_ab_comparison, inputs=[txt_a, txt_b], outputs=out_ab)
 
 
 
 
432
 
433
- if __name__ == "__main__":
434
- if not MODEL_LOADED:
435
- print("="*50 + "\nتحذير: لم يتم تحميل نموذج Actora بنجاح.\n" + f"رسالة الخطأ: {MODEL_ERROR_MESSAGE}" + "\nسيتم تشغيل الواجهة ولكن وظائف التحليل الرقمي لن تعمل.\n" + "="*50)
436
- demo.launch(debug=True)
 
 
437
 
438
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
 
 
 
 
1
+ # ==============================================================================
2
+ # Actora: Social Media Post Performance Predictor
3
+ # ==============================================================================
4
+ # This application provides tools to predict engagement metrics for social media
5
+ # posts and offers analysis using the Gemini API.
6
+ #
7
+ # To run this application:
8
+ # 1. Create a virtual environment:
9
+ # python -m venv venv
10
+ # source venv/bin/activate # On Windows: venv\Scripts\activate
11
+ #
12
+ # 2. Install dependencies:
13
+ # pip install -r requirements.txt
14
+ # (Ensure requirements.txt contains: gradio, python-dotenv, google-generativeai,
15
+ # bleach, torch, transformers, sentencepiece)
16
+ # # TODO: Review requirements.txt for any unused packages.
17
+ #
18
+ # 3. Create a .env file with your API key:
19
+ # GEMINI_API_KEY="your_gemini_api_key_here"
20
+ # USE_MOCK_MODEL="True" # Set to "False" to use the real model
21
+ # # ACTORA_SCALE_FACTOR="10.0" # Optional: a multiplier for the real model's outputs
22
+ #
23
+ # 4. Create an 'assets' directory and place 'logo.svg' inside it.
24
+ #
25
+ # 5. Run the application:
26
+ # python app.py
27
+ # ==============================================================================
28
+
29
  import os
 
 
30
  import logging
 
 
 
31
  import base64
32
+ import re
33
+ import time
34
+ import random
35
+ from typing import Dict
36
 
37
+ from dotenv import load_dotenv
38
+ import bleach
39
+ import gradio as gr
40
+ import google.generativeai as genai
41
 
42
+ # Required for the real model predictor
43
+ import torch
44
+ import torch.nn.functional as F
45
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
 
 
 
46
 
47
+ # --- Configuration ---
48
+ load_dotenv()
49
 
50
+ GEMINI_API_KEY = os.getenv("AIzaSyC5k5j07zeDWxJATTzx6zNABXwXQWvLIqw")
51
+ USE_MOCK_MODEL = os.getenv("USE_MOCK_MODEL", "True").lower() in ("true", "1", "t")
52
+ MODEL_REPO = os.getenv("MODEL_REPO", "amrtweg/Actora")
53
 
54
+ # --- Logging Setup ---
55
+ logging.basicConfig(
56
+ level=logging.INFO,
57
+ format='%(asctime)s - %(levelname)s - [%(funcName)s] - %(message)s'
58
+ )
 
 
 
59
 
60
+ # --- Asset Loading ---
61
+ def load_logo_as_data_url(path: str = "assets/logo.svg") -> str:
62
+ """Reads the logo file, base64 encodes it, and returns a data URL."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  try:
64
+ with open(path, "rb") as f:
65
+ encoded_svg = base64.b64encode(f.read()).decode("utf-8")
66
+ return f"data:image/svg+xml;base64,{encoded_svg}"
67
+ except FileNotFoundError:
68
+ logging.warning(f"Logo not found at '{path}'. Using a placeholder.")
69
+ placeholder_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="120" height="40" viewBox="0 0 120 40"><text x="10" y="28" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#333">Actora</text></svg>'
70
+ encoded_svg = base64.b64encode(placeholder_svg.encode("utf-8")).decode("utf-8")
71
+ return f"data:image/svg+xml;base64,{encoded_svg}"
72
+
73
+ LOGO_DATA_URL = load_logo_as_data_url()
74
+
75
+ # --- Predictor Models ---
76
+
77
+ class MockPredictor:
78
+ """A mock predictor that returns random data for UI testing."""
79
+ def __init__(self, model_path: str, **kwargs):
80
+ self.is_ready = True
81
+ logging.info(f"MockPredictor initialized. Path '{model_path}' is ignored.")
82
+
83
+ def predict(self, text: str) -> Dict[str, int]:
84
+ """Generates random-ish metrics for a given text."""
85
+ if not text or not text.strip():
86
+ return {}
87
+ time.sleep(0.3) # Simulate network/model latency
88
+ results = {
89
+ "liked": random.randint(10, 200),
90
+ "loved": random.randint(5, 50),
91
+ "haha": random.randint(0, 30),
92
+ "wow": random.randint(0, 25),
93
+ "sad": random.randint(0, 10),
94
+ "angry": random.randint(0, 8),
95
+ "comments": random.randint(5, 60),
96
+ "shares": random.randint(2, 40),
97
+ }
98
+ results["interactions"] = sum(v for k, v in results.items() if k not in ["comments", "shares"])
99
+ return results
100
+
101
+ def _normalize_label(s: str) -> str:
102
+ """تبسيط اسم الليبل للمطابقة: lowercase وبدون مسافات/رموز."""
103
+ return re.sub(r"[^a-z]+", "", s.lower())
104
+
105
+ class ActoraPredictor:
106
+ """
107
+ Predictor حقيقي يعتمد على موديل هاجينج فيس من نوع SequenceClassification.
108
+ - بيقرأ id2label من config علشان يعرف ترتيب المخرجات.
109
+ - بيحوّل logits لقيم موجبة باستخدام softplus.
110
+ - لو لقى labels زي liked/loved/... هيخرّجها مباشرة.
111
+ - لو labels ناقصة، بيكملها بـ 0 عادي.
112
+ - بيحسب interactions = مجموع كل المقاييس المتاحة.
113
+ ملاحظات:
114
+ - بعض الموديلات بتطلع logits صغيرة (تشبه احتمالات). لو القيم صغيرة جدًا
115
+ ممكن تزود المقياس بـ ACTORA_SCALE_FACTOR من .env (افتراضي 1.0).
116
+ """
117
+ CANONICAL_KEYS = ["liked", "loved", "haha", "wow", "sad", "angry", "comments", "shares"]
118
+
119
+ def __init__(self, model_path: str, device: str = None, max_length: int = 256):
120
+ self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
121
+ self.max_length = max_length
122
+ self.scale = float(os.getenv("ACTORA_SCALE_FACTOR", "1.0"))
123
+
124
+ logging.info(f"[ActoraPredictor] Loading model: {model_path} on {self.device}")
125
+ try:
126
+ self.tokenizer = AutoTokenizer.from_pretrained(model_path)
127
+ self.model = AutoModelForSequenceClassification.from_pretrained(model_path).to(self.device)
128
+ self.model.eval()
129
+ except Exception as e:
130
+ logging.error(f"Failed to load Hugging Face model '{model_path}'. Error: {e}")
131
+ raise
132
+
133
+ # جهّز خريطة label -> index من config
134
+ id2label = getattr(self.model.config, "id2label", {}) or {}
135
+ if not id2label and hasattr(self.model.config, "label2id"):
136
+ self.label2id = {k.lower(): int(v) for k, v in self.model.config.label2id.items()}
137
+ else:
138
+ self.label2id = {str(v).lower(): int(k) for k, v in id2label.items()}
139
+
140
+ # ابني Mapping من الكانونيكال للـ index باستخدام مطابقة مرنة
141
+ self.idx_map: Dict[str, int] = {}
142
+ flattened = { _normalize_label(lbl): idx for lbl, idx in self.label2id.items() }
143
+ for key in self.CANONICAL_KEYS:
144
+ candidates = [key, key + "s", key + "_count", key + "count"]
145
+ found = None
146
+ for c in candidates:
147
+ norm = _normalize_label(c)
148
+ for flbl, idx in flattened.items():
149
+ if flbl == norm or flbl.endswith(norm) or norm in flbl:
150
+ found = idx
151
+ break
152
+ if found is not None:
153
+ break
154
+ if found is not None:
155
+ self.idx_map[key] = found
156
+ logging.info(f"[ActoraPredictor] Label map resolved: {self.idx_map}")
157
+
158
+ if not self.idx_map:
159
+ logging.warning("[ActoraPredictor] Label map is empty. The model might not have recognizable labels.")
160
+
161
+ self.is_ready = True
162
+
163
+ @torch.inference_mode()
164
+ def predict(self, text: str) -> Dict[str, int]:
165
+ if not self.is_ready:
166
+ raise RuntimeError("ActoraPredictor is not ready.")
167
+ if not text or not text.strip():
168
+ return {}
169
+
170
+ enc = self.tokenizer(
171
+ text, return_tensors="pt", truncation=True, max_length=self.max_length, padding=False
172
+ )
173
+ enc = {k: v.to(self.device) for k, v in enc.items()}
174
+ out = self.model(**enc)
175
+ logits = out.logits.squeeze(0)
176
+ positive = F.softplus(logits).cpu().tolist()
177
+
178
+ results: Dict[str, int] = {}
179
+ for key in self.CANONICAL_KEYS:
180
+ if key in self.idx_map:
181
+ val = positive[self.idx_map[key]] * self.scale
182
+ results[key] = int(round(float(val)))
183
+ else:
184
+ results[key] = 0
185
+
186
+ results["interactions"] = sum(results.get(k, 0) for k in self.CANONICAL_KEYS)
187
+ return results
188
+
189
+ # --- Gemini API Client ---
190
+
191
+ class GeminiClient:
192
+ """Handles communication with the Google Gemini API."""
193
+ def __init__(self, api_key: str):
194
+ self.model = None
195
+ if not api_key:
196
+ logging.warning("GEMINI_API_KEY not found. Gemini-based analysis will be disabled.")
197
+ else:
198
+ try:
199
+ genai.configure(api_key=api_key)
200
+ self.model = genai.GenerativeModel('gemini-pro')
201
+ logging.info("Gemini client initialized successfully.")
202
+ except Exception as e:
203
+ logging.error(f"Failed to configure Gemini client: {e}")
204
+
205
+ def get_analysis(self, post_text: str, metrics: Dict[str, int]) -> str:
206
+ """Generates post analysis using Gemini, with sanitization."""
207
+ if USE_MOCK_MODEL:
208
+ return f"**تحليل وهمي للمنشور:**\n\nهذا النص هو مثال لكيفية ظهور تحليل Gemini. عند تفعيل النموذج الحقيقي، سيتم هنا عرض رؤى واقتراحات حقيقية بناءً على النص والمقاييس المتوقعة: `{metrics}`."
209
+
210
+ if not self.model:
211
+ return "ميزة تحليل Gemini غير مفعلة. يرجى التحقق من مفتاح API."
212
+
213
+ prompt = f"""
214
+ أنت خبير في التسويق عبر وسائل التواصل الاجتماعي. بالنظ�� إلى نص المنشور التالي والمقاييس المتوقعة له، قدم تحليلاً موجزاً ونقاط قابلة للتنفيذ باللغة العربية.
215
+
216
+ التحليل يجب أن يتضمن:
217
+ 1. **نقاط القوة:** ما الذي تم عمله بشكل جيد في المنشور؟
218
+ 2. **فرص للتحسين:** كيف يمكن تحسين المنشور لجذب تفاعل أكبر؟
219
+ 3. **اقتراح:** قدم مثالاً على نسخة محسنة من المنشور.
220
+
221
+ **نص المنشور:**
222
  ---
223
+ {post_text}
224
  ---
225
+
226
+ **المقاييس المتوقعة:**
227
+ - إجمالي التفاعلات: {metrics.get('interactions', 0)}
228
+ - إعجابات (Like): {metrics.get('liked', 0)}
229
+ - قلوب (Love): {metrics.get('loved', 0)}
230
+ - تعليقات: {metrics.get('comments', 0)}
231
+ - مشاركات: {metrics.get('shares', 0)}
232
+
233
+ **التحليل:**
234
  """
235
+ try:
236
+ response = self.model.generate_content(prompt)
237
+ # Sanitize Gemini output to prevent injection attacks
238
+ sanitized_text = bleach.clean(response.text, tags=[], strip=True)
239
+ return sanitized_text
240
+ except Exception as e:
241
+ logging.error(f"Error calling Gemini API: {e}")
242
+ return f"حدث خطأ أثناء التواصل مع Gemini: {e}"
243
+
244
+ # --- Model Initialization ---
245
+ gemini_client = GeminiClient(api_key=GEMINI_API_KEY)
246
+
247
+ # Use a comment to clarify how to switch models. The logic depends on the .env file.
248
+ if USE_MOCK_MODEL:
249
+ logging.info("Using MockPredictor. To use the real model, set USE_MOCK_MODEL='False' in .env file.")
250
+ predictor = MockPredictor(model_path="mock/path")
251
+ else:
252
+ logging.info(f"Using REAL ActoraPredictor with model repo: {MODEL_REPO}.")
253
  try:
254
+ predictor = ActoraPredictor(model_path=MODEL_REPO)
 
 
 
255
  except Exception as e:
256
+ logging.critical(f"Failed to load the real ActoraPredictor model. App cannot start. Error: {e}")
257
+ # In a real app, you might fall back, but here we exit to make the error obvious.
258
+ predictor = MockPredictor(model_path="mock/fallback")
259
+ logging.warning("FALLING BACK to MockPredictor due to critical error.")
260
+
261
+
262
+ # --- Gradio UI ---
263
+
264
+ # Centralized CSS for styling the application.
265
+ APP_CSS = """
266
+ @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;700&display=swap');
267
+ body { font-family: 'Cairo', sans-serif; direction: rtl; }
268
+ .rtl-spacing { margin-right: 8px; margin-left: 0; }
269
+ .card { border: 1px solid #e0e0e0; border-radius: 12px; padding: 20px; margin-top: 16px; box-shadow: 0 4px 8px rgba(0,0,0,0.05); background-color: #ffffff; }
270
+ .card-header { font-size: 1.25rem; font-weight: 700; margin-bottom: 16px; color: #333; border-bottom: 1px solid #eee; padding-bottom: 12px; }
271
+ .metrics-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 12px; }
272
+ .metric-item { text-align: center; padding: 12px; background-color: #f9f9f9; border-radius: 8px; }
273
+ .metric-value { font-size: 1.75rem; font-weight: 700; color: #0056b3; }
274
+ .metric-label { font-size: 0.9rem; color: #666; margin-top: 4px; }
275
+ .centered-container { max-width: 1200px; margin-left: auto; margin-right: auto; }
276
+ .logo-container { text-align: center; padding: 20px 0; }
277
+ .footer { text-align: center; color: #999; font-size: 0.9rem; margin-top: 30px; padding-bottom: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  """
279
 
280
+ def create_metrics_html(metrics: Dict[str, int]) -> str:
281
+ """Generates an HTML block to display engagement metrics attractively."""
282
+ if not metrics:
283
+ return "<div class='card'><p>لم يتم حساب المقاييس بعد. الرجاء إدخال نص والضغط على تحليل.</p></div>"
284
 
285
+ # Corrected to use '.card-header' to match the CSS definition.
286
+ html = f"<div class='card'><div class='card-header'>المقاييس المتوقعة</div><div class='metrics-grid'>"
287
+
288
+ # Ordered list for consistent display
289
+ display_items = [
290
+ ("إجمالي التفاعلات", metrics.get("interactions", 0)), ("أعجبني", metrics.get("liked", 0)),
291
+ ("أحببته", metrics.get("loved", 0)), ("أضحكني", metrics.get("haha", 0)),
292
+ ("أدهشني", metrics.get("wow", 0)), ("أحزنني", metrics.get("sad", 0)),
293
+ ("أغضبني", metrics.get("angry", 0)), ("تعليقات", metrics.get("comments", 0)),
294
+ ("مشاركات", metrics.get("shares", 0)),
295
+ ]
296
+
297
+ for label, value in display_items:
298
+ html += f"<div class='metric-item'><div class='metric-value'>{value:,}</div><div class='metric-label'>{label}</div></div>"
299
+
300
+ html += "</div></div>"
301
+ return html
302
+
303
+ def ui_analyze_single_post(post_text: str):
304
+ """Handler for the single post analysis tab."""
305
+ if not post_text or not post_text.strip():
306
+ return "<p>يرجى إدخال نص صالح في حقل المنشور.</p>", "لا يمكن التحليل بدون نص.", []
307
+
308
+ metrics = predictor.predict(post_text)
309
+ analysis = gemini_client.get_analysis(post_text, metrics)
310
+ metrics_html = create_metrics_html(metrics)
311
+
312
+ new_history_entry = (post_text[:100] + '...' if len(post_text) > 100 else post_text, metrics.get("interactions", 0), time.strftime("%Y-%m-%d %H:%M:%S"))
313
+ return metrics_html, analysis, new_history_entry
314
+
315
+ def ui_analyze_ab_test(post_a: str, post_b: str):
316
+ """Handler for the A/B comparison tab."""
317
+ if not post_a.strip() or not post_b.strip():
318
+ empty_msg = "<div class='card'><p>يرجى إدخال النص في كلا الحقلين للمقارنة.</p></div>"
319
+ return empty_msg, empty_msg
320
+
321
+ metrics_a = predictor.predict(post_a)
322
+ metrics_b = predictor.predict(post_b)
323
+
324
+ html_a = create_metrics_html(metrics_a)
325
+ html_b = create_metrics_html(metrics_b)
326
+
327
+ return html_a, html_b
328
 
329
+ def ui_update_history(current_history, new_entry):
330
+ """Appends a new entry to the history state."""
331
+ if new_entry and new_entry[0]:
332
+ # Prepend to show the latest entry at the top
333
+ return [list(new_entry)] + current_history
334
+ return current_history
335
 
336
 
337
+ with gr.Blocks(css=APP_CSS, theme=gr.themes.Soft(primary_hue="blue")) as demo:
338
+ with gr.Column(elem_classes="centered-container"):
339
+ gr.HTML(f"""
340
+ <div class="logo-container">
341
+ <img src="{LOGO_DATA_URL}" alt="Actora Logo" height="50">
342
+ </div>
343
+ """)
344
+
345
+ history_state = gr.State([])
346
+
347
+ with gr.Tabs() as tabs:
348
+ with gr.TabItem("تحليل منشور", id=0):
349
+ with gr.Row():
350
+ with gr.Column(scale=2):
351
+ post_input = gr.Textbox(lines=15, label="نص المنشور", placeholder="اكتب أو الصق نص المنشور هنا للتحليل...")
352
+ analyze_btn = gr.Button("تحليل التفاعلات", variant="primary")
353
+ with gr.Column(scale=3):
354
+ metrics_output = gr.HTML(label="النتائج")
355
+ analysis_output = gr.Markdown(label="تحليل واقتراحات (بواسطة Gemini)")
356
+
357
+ new_history_entry_trigger = gr.State()
358
+
359
+ with gr.TabItem("مقارنة A/B", id=1):
360
+ with gr.Row(variant="panel"):
361
+ with gr.Column():
362
+ post_a_input = gr.Textbox(lines=10, label="المنشور (أ)", placeholder="أدخل النسخة الأولى من المنشور هنا...")
363
+ metrics_a_output = gr.HTML(label="نتائج المنشور (أ)")
364
+ with gr.Column():
365
+ post_b_input = gr.Textbox(lines=10, label="المنشور (ب)", placeholder="أدخل النسخة الثانية من المنشور هنا...")
366
+ metrics_b_output = gr.HTML(label="نتائج المنشور (ب)")
367
+ compare_btn = gr.Button("مقارنة بين المنشورين", variant="primary")
368
+
369
+ with gr.TabItem("سجل", id=2):
370
+ history_df = gr.DataFrame(
371
+ headers=["النص", "إجمالي التفاعلات", "وقت التحليل"],
372
+ datatype=["str", "number", "str"],
373
+ row_count=10,
374
+ col_count=(3, "fixed"),
375
+ label="سجل التحليلات السابقة",
376
+ interactive=False
377
+ )
378
+
379
+ gr.HTML("<div class='footer'>تطبيق Actora التجريبي &copy; 2025</div>")
380
+
381
+ # --- Event Listeners ---
382
+ analyze_btn.click(
383
+ fn=ui_analyze_single_post,
384
+ inputs=[post_input],
385
+ outputs=[metrics_output, analysis_output, new_history_entry_trigger]
386
+ )
387
+
388
+ compare_btn.click(
389
+ fn=ui_analyze_ab_test,
390
+ inputs=[post_a_input, post_b_input],
391
+ outputs=[metrics_a_output, metrics_b_output]
392
+ )
393
+
394
+ # Chain events to update history table and then update the state itself
395
+ new_history_entry_trigger.change(
396
+ fn=ui_update_history,
397
+ inputs=[history_state, new_history_entry_trigger],
398
+ outputs=[history_df]
399
+ ).then(
400
+ fn=lambda current, new: ui_update_history(current, new),
401
+ inputs=[history_state, new_history_entry_trigger],
402
+ outputs=[history_state]
403
+ )
404
 
405
+ if __name__ == "__main__":
406
+ demo.launch()