Feedbacks / app.py
EngReem85's picture
Update app.py
4d05878 verified
import gradio as gr
import sqlite3
import hashlib
import html
import re
from datetime import datetime, timedelta
DB_NAME = "testimonials.db"
# ==================================================
# Database
# ==================================================
def get_connection():
return sqlite3.connect(DB_NAME, check_same_thread=False)
def init_db():
conn = get_connection()
c = conn.cursor()
c.execute("""
CREATE TABLE IF NOT EXISTS testimonials (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
role TEXT,
content TEXT NOT NULL,
sentiment TEXT,
rating INTEGER DEFAULT 5,
timestamp TEXT NOT NULL,
likes INTEGER DEFAULT 0,
is_highlighted INTEGER DEFAULT 0,
ip_hash TEXT
)
""")
conn.commit()
conn.close()
# ==================================================
# Utilities
# ==================================================
def clean_text(text):
if not text:
return ""
text = text.strip()
text = re.sub(r"\s+", " ", text)
return text
def contains_bad_words(text):
bad_words = [
"يلعن",
"تبن",
"سافل",
"كلب",
"خنزير",
"عاهرة"
]
normalized = re.sub(r"\s+", "", text.lower())
return any(word in normalized for word in bad_words)
def escape_safe(text):
return html.escape(text)
def get_ip_hash(request: gr.Request):
try:
client_ip = request.client.host
return hashlib.sha256(
client_ip.encode()
).hexdigest()[:16]
except:
return "unknown"
def detect_sentiment(text):
positive_words = [
"رائع",
"ممتاز",
"جميل",
"احترافي",
"مفيد",
"مذهل"
]
negative_words = [
"سيء",
"ضعيف",
"فاشل",
"سيئة"
]
text = text.lower()
positive_score = sum(
word in text for word in positive_words
)
negative_score = sum(
word in text for word in negative_words
)
if positive_score > negative_score:
return "إيجابي 😊"
elif negative_score > positive_score:
return "سلبي 😕"
return "محايد 😐"
def format_time(timestamp):
try:
dt = datetime.fromisoformat(timestamp)
now = datetime.now()
diff = now - dt
if diff.days > 7:
return dt.strftime("%Y/%m/%d")
elif diff.days > 0:
return f"منذ {diff.days} يوم"
elif diff.seconds >= 3600:
hours = diff.seconds // 3600
return f"منذ {hours} ساعة"
elif diff.seconds >= 60:
minutes = diff.seconds // 60
return f"منذ {minutes} دقيقة"
return "الآن"
except:
return "غير معروف"
# ==================================================
# Add Testimonial
# ==================================================
def add_testimonial(
name,
role,
content,
rating,
request: gr.Request
):
content = clean_text(content)
if not content:
return (
"❌ الرجاء كتابة رأيك",
"",
"",
update_display()
)
if len(content) > 800:
return (
"❌ الحد الأقصى 800 حرف",
"",
"",
update_display()
)
if contains_bad_words(content):
return (
"❌ يوجد كلمات غير مناسبة",
"",
"",
update_display()
)
ip_hash = get_ip_hash(request)
conn = get_connection()
c = conn.cursor()
# Anti spam
one_hour_ago = (
datetime.now() - timedelta(hours=1)
).isoformat()
c.execute("""
SELECT COUNT(*)
FROM testimonials
WHERE ip_hash = ?
AND timestamp >= ?
""", (ip_hash, one_hour_ago))
count = c.fetchone()[0]
if count >= 3:
conn.close()
return (
"❌ وصلت الحد المسموح حالياً",
"",
"",
update_display()
)
name = clean_text(name)
role = clean_text(role)
if not name:
name = "مجهول"
sentiment = detect_sentiment(content)
c.execute("""
INSERT INTO testimonials (
name,
role,
content,
sentiment,
rating,
timestamp,
likes,
is_highlighted,
ip_hash
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
name,
role,
content,
sentiment,
int(rating),
datetime.now().isoformat(),
0,
0,
ip_hash
))
conn.commit()
conn.close()
return (
"✅ تم نشر رأيك بنجاح",
"",
"",
update_display()
)
# ==================================================
# Fetch Testimonials
# ==================================================
def get_testimonials(
filter_type="all",
sort_by="newest"
):
conn = get_connection()
c = conn.cursor()
query = """
SELECT
id,
name,
role,
content,
sentiment,
rating,
timestamp,
likes,
is_highlighted
FROM testimonials
"""
conditions = []
if filter_type == "highlight":
conditions.append(
"is_highlighted = 1"
)
elif filter_type == "liked":
conditions.append(
"likes > 0"
)
if conditions:
query += " WHERE " + " AND ".join(conditions)
if sort_by == "likes":
query += """
ORDER BY likes DESC,
timestamp DESC
"""
else:
query += """
ORDER BY timestamp DESC
"""
c.execute(query)
rows = c.fetchall()
conn.close()
return rows
# ==================================================
# Render Card
# ==================================================
def render_card(row):
(
testimonial_id,
name,
role,
content,
sentiment,
rating,
timestamp,
likes,
is_highlighted
) = row
name = escape_safe(name)
role = escape_safe(role)
content = escape_safe(content)
stars = "⭐" * int(rating)
highlight_style = ""
if is_highlighted:
highlight_style = """
border-right:5px solid #f59e0b;
background:#fffbeb;
"""
role_html = ""
if role:
role_html = f"""
<span style="
background:#eef2ff;
color:#4338ca;
padding:0.25rem 0.7rem;
border-radius:999px;
font-size:0.75rem;
">
{role}
</span>
"""
return f"""
<div style="
background:white;
border-radius:24px;
padding:1.5rem;
margin-bottom:1rem;
border:1px solid #e2e8f0;
box-shadow:0 4px 12px rgba(0,0,0,0.05);
{highlight_style}
">
<div style="
display:flex;
justify-content:space-between;
align-items:center;
flex-wrap:wrap;
gap:1rem;
">
<div>
<div style="
font-size:1rem;
font-weight:700;
color:#0f172a;
">
{name}
</div>
<div style="
display:flex;
gap:0.5rem;
margin-top:0.5rem;
align-items:center;
flex-wrap:wrap;
">
{role_html}
<span style="
color:#64748b;
font-size:0.8rem;
">
{sentiment}
</span>
</div>
</div>
<div style="
color:#94a3b8;
font-size:0.75rem;
">
{format_time(timestamp)}
</div>
</div>
<div style="
margin-top:1rem;
color:#1e293b;
line-height:1.8;
">
{content}
</div>
<div style="
margin-top:1rem;
display:flex;
justify-content:space-between;
align-items:center;
">
<div>
{stars}
</div>
<div style="
color:#64748b;
font-size:0.9rem;
">
👍 {likes}
</div>
</div>
</div>
"""
# ==================================================
# Update Display
# ==================================================
def update_display(
filter_type="all",
sort_by="newest"
):
rows = get_testimonials(
filter_type,
sort_by
)
if not rows:
return """
<div style="
background:white;
border-radius:24px;
padding:2rem;
text-align:center;
border:1px dashed #cbd5e1;
color:#64748b;
">
✨ لا توجد آراء حالياً
</div>
"""
average_rating = round(
sum(row[5] for row in rows)
/ len(rows),
1
)
html_output = f"""
<div style="
margin-bottom:1rem;
background:#e0e7ff;
padding:0.8rem 1rem;
border-radius:999px;
display:inline-block;
font-weight:600;
color:#3730a3;
">
📊 عدد الآراء: {len(rows)}
|
⭐ متوسط التقييم: {average_rating}
</div>
"""
for row in rows:
html_output += render_card(row)
return html_output
# ==================================================
# Interface
# ==================================================
def create_interface():
with gr.Blocks(
title="جدار الشفافية",
theme=gr.themes.Soft()
) as demo:
gr.HTML("""
<div style="
text-align:center;
margin-bottom:2rem;
">
<h1 style="
font-size:2.7rem;
font-weight:800;
margin-bottom:0.5rem;
background:
linear-gradient(
135deg,
#1e293b,
#4338ca
);
-webkit-background-clip:text;
color:transparent;
">
جدار الشفافية
</h1>
<p style="
color:#475569;
font-size:1rem;
">
منصة آراء مباشرة وشفافة
</p>
</div>
""")
with gr.Row():
# Left
with gr.Column(scale=1):
gr.Markdown(
"## 📝 أضف رأيك"
)
name_input = gr.Textbox(
label="الاسم",
placeholder="اختياري"
)
role_input = gr.Textbox(
label="الدور",
placeholder="مثال: مهندس"
)
rating_input = gr.Slider(
minimum=1,
maximum=5,
value=5,
step=1,
label="التقييم"
)
content_input = gr.Textbox(
label="الرأي",
placeholder="شارك رأيك هنا...",
lines=5
)
submit_btn = gr.Button(
"📢 نشر الرأي",
variant="primary"
)
status_output = gr.Markdown()
# Right
with gr.Column(scale=2):
gr.Markdown(
"## 💬 آراء المستخدمين"
)
with gr.Row():
filter_dropdown = gr.Dropdown(
choices=[
("الكل", "all"),
("المؤثرة", "highlight"),
("الأكثر إعجاباً", "liked")
],
value="all",
label="فلترة"
)
sort_dropdown = gr.Dropdown(
choices=[
("الأحدث", "newest"),
("الأكثر إعجاباً", "likes")
],
value="newest",
label="ترتيب"
)
refresh_btn = gr.Button(
"🔄 تحديث",
variant="secondary"
)
testimonials_display = gr.HTML()
# Submit
submit_btn.click(
fn=add_testimonial,
inputs=[
name_input,
role_input,
content_input,
rating_input
],
outputs=[
status_output,
name_input,
role_input,
testimonials_display
]
)
# Refresh button
refresh_btn.click(
fn=update_display,
inputs=[
filter_dropdown,
sort_dropdown
],
outputs=testimonials_display
)
# Filter change
filter_dropdown.change(
fn=update_display,
inputs=[
filter_dropdown,
sort_dropdown
],
outputs=testimonials_display
)
# Sort change
sort_dropdown.change(
fn=update_display,
inputs=[
filter_dropdown,
sort_dropdown
],
outputs=testimonials_display
)
# Initial load
demo.load(
fn=update_display,
inputs=[
filter_dropdown,
sort_dropdown
],
outputs=testimonials_display
)
return demo
# ==================================================
# Run App
# ==================================================
if __name__ == "__main__":
init_db()
demo = create_interface()
demo.launch()