EngReem85 commited on
Commit
977eefa
·
verified ·
1 Parent(s): fb1a3ea

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +282 -0
app.py ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sqlite3
3
+ import datetime
4
+ import hashlib
5
+ from datetime import datetime, timedelta
6
+ import json
7
+
8
+ # إنشاء قاعدة البيانات
9
+ def init_db():
10
+ conn = sqlite3.connect('testimonials.db')
11
+ c = conn.cursor()
12
+ c.execute('''CREATE TABLE IF NOT EXISTS testimonials
13
+ (id INTEGER PRIMARY KEY AUTOINCREMENT,
14
+ name TEXT,
15
+ role TEXT,
16
+ content TEXT NOT NULL,
17
+ timestamp TEXT NOT NULL,
18
+ likes INTEGER DEFAULT 0,
19
+ is_highlighted INTEGER DEFAULT 0,
20
+ ip_hash TEXT)''')
21
+ conn.commit()
22
+ conn.close()
23
+
24
+ # إضافة رأي جديد
25
+ def add_testimonial(name, role, content, request):
26
+ if not content or content.strip() == "":
27
+ return "❌ الرجاء كتابة رأيك", None, None, None
28
+
29
+ if len(content) > 800:
30
+ return "❌ الرأي طويل جداً (الحد الأقصى 800 حرف)", None, None, None
31
+
32
+ # فلتر كلمات بسيط
33
+ bad_words = ["يلعن", "تبن", "سافل", "كلب", "خنزير", "عاهرة"]
34
+ if any(word in content.lower() for word in bad_words):
35
+ return "❌ نرجو الابتعاد عن الكلمات غير اللائقة", None, None, None
36
+
37
+ # تسجيل عنوان IP بشكل مشفر (للحماية)
38
+ ip_hash = None
39
+ if request:
40
+ client_ip = request.client.host
41
+ ip_hash = hashlib.sha256(client_ip.encode()).hexdigest()[:16]
42
+
43
+ conn = sqlite3.connect('testimonials.db')
44
+ c = conn.cursor()
45
+
46
+ name_final = name.strip() if name and name.strip() != "" else "مجهول"
47
+ role_final = role.strip() if role else ""
48
+ timestamp = datetime.now().isoformat()
49
+
50
+ c.execute("INSERT INTO testimonials (name, role, content, timestamp, likes, is_highlighted, ip_hash) VALUES (?, ?, ?, ?, ?, ?, ?)",
51
+ (name_final, role_final, content.strip(), timestamp, 0, 0, ip_hash))
52
+ conn.commit()
53
+ conn.close()
54
+
55
+ return "✅ تم نشر رأيك بنجاح!", "", "", get_all_testimonials()
56
+
57
+ # جلب كل الآراء
58
+ def get_all_testimonials(filter_type="all", sort_by="newest"):
59
+ conn = sqlite3.connect('testimonials.db')
60
+ c = conn.cursor()
61
+
62
+ # بناء الاستعلام حسب الفلتر
63
+ query = "SELECT id, name, role, content, timestamp, likes, is_highlighted FROM testimonials"
64
+ conditions = []
65
+ params = []
66
+
67
+ if filter_type == "highlight":
68
+ conditions.append("is_highlighted = 1")
69
+ elif filter_type == "liked":
70
+ conditions.append("likes > 0")
71
+
72
+ if conditions:
73
+ query += " WHERE " + " AND ".join(conditions)
74
+
75
+ # الترتيب
76
+ if sort_by == "likes":
77
+ query += " ORDER BY likes DESC, timestamp DESC"
78
+ else:
79
+ query += " ORDER BY timestamp DESC"
80
+
81
+ c.execute(query, params)
82
+ rows = c.fetchall()
83
+ conn.close()
84
+
85
+ if not rows:
86
+ return [("", "✨ لا توجد آراء بعد", "", "", "", "", "")]
87
+
88
+ # تحويل البيانات لعرضها بشكل جميل
89
+ testimonials_list = []
90
+ for row in rows:
91
+ id, name, role, content, timestamp, likes, is_highlighted = row
92
+ # تحويل التوقيت
93
+ try:
94
+ dt = datetime.fromisoformat(timestamp)
95
+ now = datetime.now()
96
+ diff = now - dt
97
+
98
+ if diff.days > 7:
99
+ time_ago = dt.strftime("%Y/%m/%d")
100
+ elif diff.days > 0:
101
+ time_ago = f"منذ {diff.days} يوم"
102
+ elif diff.seconds > 3600:
103
+ hours = diff.seconds // 3600
104
+ time_ago = f"منذ {hours} ساعة"
105
+ elif diff.seconds > 60:
106
+ minutes = diff.seconds // 60
107
+ time_ago = f"منذ {minutes} دقيقة"
108
+ else:
109
+ time_ago = "الآن"
110
+ except:
111
+ time_ago = "تاريخ غير معروف"
112
+
113
+ # إضافة علامة مؤثر
114
+ highlight_badge = "⭐ مؤثر" if is_highlighted else ""
115
+
116
+ # تنسيق البطاقة
117
+ role_text = f"<span style='font-size:0.75rem; color:#64748b; background:#f1f5f9; padding:0.2rem 0.6rem; border-radius:40px; margin-right:0.5rem;'>{role}</span>" if role else ""
118
+
119
+ card = f"""
120
+ <div style='background: white; border-radius: 24px; padding: 1.5rem; margin-bottom: 1rem; box-shadow: 0 4px 12px rgba(0,0,0,0.05); border: 1px solid #e2e8f0; {("border-right: 5px solid #f59e0b; background: #fffbeb;" if is_highlighted else "")}'>
121
+ <div style='display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.75rem;'>
122
+ <div>
123
+ <span style='font-weight: 700; font-size: 1rem; color: #0f172a;'>{name}</span>
124
+ {role_text}
125
+ </div>
126
+ <div style='font-size: 0.7rem; color: #94a3b8;'>
127
+ <i class='far fa-calendar-alt'></i> {time_ago}
128
+ </div>
129
+ </div>
130
+ <div style='font-size: 0.98rem; line-height: 1.6; color: #1e293b; margin: 0.8rem 0 1rem 0;'>
131
+ {content}
132
+ </div>
133
+ <div style='display: flex; gap: 1rem; margin-top: 0.5rem;'>
134
+ <button onclick="likeTestimonial({id})" style='background: none; border: none; display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: #64748b; cursor: pointer; padding: 0.3rem 0.8rem; border-radius: 40px;'>
135
+ <i class='far fa-heart'></i> <span id='like-count-{id}'>{likes}</span>
136
+ </button>
137
+ <button onclick="highlightTestimonial({id})" style='background: none; border: none; display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: #64748b; cursor: pointer; padding: 0.3rem 0.8rem; border-radius: 40px;'>
138
+ <i class='{"fas fa-star" if is_highlighted else "far fa-star"}' style='{"color:#f59e0b" if is_highlighted else ""}'></i> {"إزالة التميز" if is_highlighted else "تمييز كمؤثر"}
139
+ </button>
140
+ </div>
141
+ </div>
142
+ """
143
+ testimonials_list.append(card)
144
+
145
+ return testimonials_list
146
+
147
+ # إعجاب
148
+ def like_testimonial(testimonial_id):
149
+ conn = sqlite3.connect('testimonials.db')
150
+ c = conn.cursor()
151
+ c.execute("UPDATE testimonials SET likes = likes + 1 WHERE id = ?", (testimonial_id,))
152
+ conn.commit()
153
+ conn.close()
154
+ return get_all_testimonials()
155
+
156
+ # تمييز رأي
157
+ def highlight_testimonial(testimonial_id):
158
+ conn = sqlite3.connect('testimonials.db')
159
+ c = conn.cursor()
160
+ c.execute("SELECT is_highlighted FROM testimonials WHERE id = ?", (testimonial_id,))
161
+ current = c.fetchone()
162
+ new_value = 0 if current and current[0] == 1 else 1
163
+ c.execute("UPDATE testimonials SET is_highlighted = ? WHERE id = ?", (new_value, testimonial_id))
164
+ conn.commit()
165
+ conn.close()
166
+ return get_all_testimonials()
167
+
168
+ # دالة التحديث الرئيسية
169
+ def update_display(filter_type, sort_by):
170
+ testimonials_list = get_all_testimonials(filter_type, sort_by)
171
+ count = len(testimonials_list)
172
+ stats_text = f"📊 {count} {('رأي' if count == 1 else 'رأي')}"
173
+
174
+ html_content = f"""
175
+ <div style='margin-bottom: 1rem; padding: 0.5rem 1rem; background: #e0e7ff; border-radius: 40px; display: inline-block;'>
176
+ {stats_text}
177
+ </div>
178
+ """
179
+
180
+ for card in testimonials_list:
181
+ html_content += card
182
+
183
+ return html_content
184
+
185
+ # واجهة Gradio
186
+ def create_interface():
187
+ with gr.Blocks(title="جدار الشفافية - آراء حقيقية بلا تحرير", theme=gr.themes.Soft()) as demo:
188
+ gr.HTML("""
189
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
190
+ <div style="text-align: center; margin-bottom: 2rem;">
191
+ <div style="display: inline-block; background: linear-gradient(135deg, #e0e7ff, #c7d2fe); padding: 0.4rem 1.2rem; border-radius: 60px; font-size: 0.85rem; font-weight: 600; color: #1e3a8a; margin-bottom: 1rem;">
192
+ <i class="fas fa-microphone-alt"></i> شفافية كاملة · آراء حقيقية
193
+ </div>
194
+ <h1 style="font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #0f172a, #1e293b); background-clip: text; -webkit-background-clip: text; color: transparent; margin-bottom: 0.75rem;">
195
+ جدار الشفافية
196
+ </h1>
197
+ <p style="font-size: 1.1rem; color: #334155; max-width: 620px; margin: 0 auto;">
198
+ لا تحرير · لا موافقة مسبقة · آراء فورية من المشاركين الحقيقيين
199
+ </p>
200
+ <div style="display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin-top: 1rem; background: #dcfce7; width: fit-content; margin-left: auto; margin-right: auto; padding: 0.5rem 1.2rem; border-radius: 60px; color: #166534;">
201
+ <i class="fas fa-check-circle"></i> <span>مباشر وشفاف — كل رأي يُنشر فوراً للجميع</span>
202
+ </div>
203
+ </div>
204
+ """)
205
+
206
+ with gr.Row():
207
+ with gr.Column(scale=1):
208
+ gr.Markdown("### 📝 شارك رأيك")
209
+ name_input = gr.Textbox(label="الاسم (اختياري)", placeholder="مثال: أحمد محمد", max_lines=1)
210
+ role_input = gr.Textbox(label="الدور (اختياري)", placeholder="مثال: متدرب، مهندس", max_lines=1)
211
+ content_input = gr.Textbox(label="رأيك *", placeholder="شارك تجربتك الحقيقية — كيف كان الأثر؟", lines=4, max_lines=6)
212
+ submit_btn = gr.Button("📢 انشر رأيك الآن", variant="primary")
213
+ status_msg = gr.Markdown("")
214
+
215
+ with gr.Column(scale=2):
216
+ gr.Markdown("### 💬 آراء المشاركين")
217
+ with gr.Row():
218
+ filter_dropdown = gr.Dropdown(
219
+ choices=["الكل", "الأكثر تأثيراً", "الأكثر إعجاباً"],
220
+ value="الكل",
221
+ label="تصفية حسب"
222
+ )
223
+ sort_dropdown = gr.Dropdown(
224
+ choices=["الأحدث أولاً", "الأكثر إعجاباً"],
225
+ value="الأحدث أولاً",
226
+ label="ترتيب حسب"
227
+ )
228
+
229
+ refresh_btn = gr.Button("🔄 تحديث", variant="secondary", size="sm")
230
+ testimonials_display = gr.HTML(label="")
231
+
232
+ # تحويل القيم الإنجليزية
233
+ def map_filters(filter_val, sort_val):
234
+ filter_map = {"الكل": "all", "الأكثر تأثيراً": "highlight", "الأكثر إعجاباً": "liked"}
235
+ sort_map = {"الأحدث أولاً": "newest", "الأكثر إعجاباً": "likes"}
236
+ return filter_map.get(filter_val, "all"), sort_map.get(sort_val, "newest")
237
+
238
+ # تحديث العرض
239
+ def update_view(filter_val, sort_val):
240
+ f, s = map_filters(filter_val, sort_val)
241
+ return update_display(f, s)
242
+
243
+ # إضافة رأي جديد
244
+ def submit_testimonial(name, role, content, filter_val, sort_val, request):
245
+ result = add_testimonial(name, role, content, request)
246
+ if result[0].startswith("✅"):
247
+ f, s = map_filters(filter_val, sort_val)
248
+ return result[0], "", "", update_display(f, s)
249
+ return result[0], "", "", gr.update()
250
+
251
+ # ربط الأحداث
252
+ submit_btn.click(
253
+ submit_testimonial,
254
+ inputs=[name_input, role_input, content_input, filter_dropdown, sort_dropdown],
255
+ outputs=[status_msg, name_input, role_input, testimonials_display]
256
+ )
257
+
258
+ refresh_btn.click(
259
+ update_view,
260
+ inputs=[filter_dropdown, sort_dropdown],
261
+ outputs=[testimonials_display]
262
+ )
263
+
264
+ filter_dropdown.change(update_view, inputs=[filter_dropdown, sort_dropdown], outputs=[testimonials_display])
265
+ sort_dropdown.change(update_view, inputs=[filter_dropdown, sort_dropdown], outputs=[testimonials_display])
266
+
267
+ # تحميل أولي
268
+ demo.load(update_view, inputs=[filter_dropdown, sort_dropdown], outputs=[testimonials_display])
269
+
270
+ gr.HTML("""
271
+ <footer style="margin-top: 3rem; text-align: center; font-size: 0.8rem; color: #64748b; border-top: 1px solid #e2e8f0; padding-top: 2rem;">
272
+ <i class="fas fa-code"></i> منصة شفافة مبنية على الثقة · كل رأي يُنشر كما هو · تخزين مركزي للجميع
273
+ </footer>
274
+ """)
275
+
276
+ return demo
277
+
278
+ # تشغيل التطبيق
279
+ if __name__ == "__main__":
280
+ init_db()
281
+ demo = create_interface()
282
+ demo.launch()