|
|
|
|
|
|
|
""" |
|
نظام الإنجازات المحفز لمراحل المشروع |
|
""" |
|
|
|
import os |
|
import sys |
|
import json |
|
import streamlit as st |
|
import pandas as pd |
|
import numpy as np |
|
import time |
|
from datetime import datetime, timedelta |
|
import random |
|
|
|
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))) |
|
|
|
|
|
try: |
|
from database.db_connector import get_connection |
|
except ImportError: |
|
from utils.helpers import get_connection |
|
|
|
from utils.helpers import format_time, get_user_info, load_icons |
|
|
|
|
|
class AchievementSystem: |
|
"""نظام الإنجازات المحفز لمراحل المشروع""" |
|
|
|
def __init__(self, user_id=None): |
|
"""تهيئة نظام الإنجازات المحفز""" |
|
self.user_id = user_id or 1 |
|
self.conn = get_connection() |
|
self.achievements_path = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'achievements') |
|
os.makedirs(self.achievements_path, exist_ok=True) |
|
self.user_data_file = os.path.join(self.achievements_path, f'user_{self.user_id}_achievements.json') |
|
self.icons = load_icons() |
|
|
|
|
|
self.load_user_data() |
|
|
|
|
|
self.define_achievements() |
|
|
|
def load_user_data(self): |
|
"""تحميل بيانات إنجازات المستخدم""" |
|
try: |
|
if os.path.exists(self.user_data_file): |
|
with open(self.user_data_file, 'r', encoding='utf-8') as f: |
|
self.user_data = json.load(f) |
|
else: |
|
|
|
self.user_data = { |
|
'user_id': self.user_id, |
|
'total_points': 0, |
|
'level': 1, |
|
'unlocked_achievements': [], |
|
'in_progress_achievements': [], |
|
'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
} |
|
self.save_user_data() |
|
except Exception as e: |
|
st.error(f"خطأ في تحميل بيانات المستخدم: {e}") |
|
self.user_data = { |
|
'user_id': self.user_id, |
|
'total_points': 0, |
|
'level': 1, |
|
'unlocked_achievements': [], |
|
'in_progress_achievements': [], |
|
'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
} |
|
|
|
def save_user_data(self): |
|
"""حفظ بيانات إنجازات المستخدم""" |
|
try: |
|
with open(self.user_data_file, 'w', encoding='utf-8') as f: |
|
json.dump(self.user_data, f, ensure_ascii=False, indent=2) |
|
except Exception as e: |
|
st.error(f"خطأ في حفظ بيانات المستخدم: {e}") |
|
|
|
def define_achievements(self): |
|
"""تعريف قائمة الإنجازات المتاحة""" |
|
self.achievements = [ |
|
{ |
|
'id': 'first_project', |
|
'name': 'بداية الرحلة', |
|
'description': 'قم بإنشاء مشروعك الأول', |
|
'icon': '🏆', |
|
'points': 100, |
|
'category': 'مشاريع', |
|
'difficulty': 'سهل' |
|
}, |
|
{ |
|
'id': 'five_projects', |
|
'name': 'محترف المشاريع', |
|
'description': 'قم بإنشاء خمسة مشاريع', |
|
'icon': '🏅', |
|
'points': 500, |
|
'category': 'مشاريع', |
|
'difficulty': 'متوسط' |
|
}, |
|
{ |
|
'id': 'ten_projects', |
|
'name': 'خبير المشاريع', |
|
'description': 'قم بإنشاء عشرة مشاريع', |
|
'icon': '🎖️', |
|
'points': 1000, |
|
'category': 'مشاريع', |
|
'difficulty': 'صعب' |
|
}, |
|
{ |
|
'id': 'first_document_analysis', |
|
'name': 'المحلل الأول', |
|
'description': 'قم بتحليل مستند للمرة الأولى', |
|
'icon': '📊', |
|
'points': 150, |
|
'category': 'تحليل', |
|
'difficulty': 'سهل' |
|
}, |
|
{ |
|
'id': 'five_document_analysis', |
|
'name': 'محلل متمرس', |
|
'description': 'قم بتحليل خمسة مستندات', |
|
'icon': '📈', |
|
'points': 600, |
|
'category': 'تحليل', |
|
'difficulty': 'متوسط' |
|
}, |
|
{ |
|
'id': 'complete_boq', |
|
'name': 'خبير جداول الكميات', |
|
'description': 'أكمل تحليل جدول كميات كامل', |
|
'icon': '📋', |
|
'points': 300, |
|
'category': 'تحليل', |
|
'difficulty': 'متوسط' |
|
}, |
|
{ |
|
'id': 'risk_analysis', |
|
'name': 'محلل المخاطر', |
|
'description': 'أكمل تحليل مخاطر متقدم', |
|
'icon': '⚠️', |
|
'points': 400, |
|
'category': 'مخاطر', |
|
'difficulty': 'متوسط' |
|
}, |
|
{ |
|
'id': 'ten_risk_identified', |
|
'name': 'متنبئ المخاطر', |
|
'description': 'تعرف على عشرة مخاطر في المشاريع', |
|
'icon': '🔍', |
|
'points': 700, |
|
'category': 'مخاطر', |
|
'difficulty': 'صعب' |
|
}, |
|
{ |
|
'id': 'first_terms_analysis', |
|
'name': 'محلل الشروط', |
|
'description': 'قم بتحليل بنود الشروط والأحكام', |
|
'icon': '📝', |
|
'points': 250, |
|
'category': 'تحليل', |
|
'difficulty': 'متوسط' |
|
}, |
|
{ |
|
'id': 'quick_analysis', |
|
'name': 'محلل سريع', |
|
'description': 'أكمل تحليل مستند في أقل من 5 دقائق', |
|
'icon': '⚡', |
|
'points': 500, |
|
'category': 'كفاءة', |
|
'difficulty': 'صعب' |
|
}, |
|
{ |
|
'id': 'voice_narration', |
|
'name': 'مترجم صوتي', |
|
'description': 'استخدم ميزة الترجمة الصوتية لأول مرة', |
|
'icon': '🎙️', |
|
'points': 200, |
|
'category': 'ترجمة', |
|
'difficulty': 'سهل' |
|
}, |
|
{ |
|
'id': 'multilingual_expert', |
|
'name': 'خبير متعدد اللغات', |
|
'description': 'استخدم الترجمة الصوتية بخمس لغات مختلفة', |
|
'icon': '🌍', |
|
'points': 800, |
|
'category': 'ترجمة', |
|
'difficulty': 'صعب' |
|
}, |
|
{ |
|
'id': 'first_map', |
|
'name': 'مستكشف الخرائط', |
|
'description': 'استخدم ميزة الخريطة التفاعلية لأول مرة', |
|
'icon': '🗺️', |
|
'points': 200, |
|
'category': 'خرائط', |
|
'difficulty': 'سهل' |
|
}, |
|
{ |
|
'id': 'ai_fine_tuning', |
|
'name': 'مدرب الذكاء', |
|
'description': 'قم بتدريب نموذج ذكاء اصطناعي مخصص', |
|
'icon': '🧠', |
|
'points': 1000, |
|
'category': 'ذكاء اصطناعي', |
|
'difficulty': 'خبير' |
|
}, |
|
{ |
|
'id': 'pricing_master', |
|
'name': 'سيد التسعير', |
|
'description': 'أكمل حساب تكلفة مشروع بالكامل', |
|
'icon': '💰', |
|
'points': 500, |
|
'category': 'تسعير', |
|
'difficulty': 'متوسط' |
|
} |
|
] |
|
|
|
def calculate_level(self, points): |
|
"""حساب مستوى المستخدم بناءً على النقاط""" |
|
|
|
level = 1 + int(points / 1000) |
|
return level |
|
|
|
def unlock_achievement(self, achievement_id): |
|
"""إلغاء قفل إنجاز جديد""" |
|
|
|
achievement = next((a for a in self.achievements if a['id'] == achievement_id), None) |
|
if not achievement: |
|
return False |
|
|
|
|
|
if achievement_id in [a['id'] for a in self.user_data['unlocked_achievements']]: |
|
return False |
|
|
|
|
|
self.user_data['in_progress_achievements'] = [ |
|
a for a in self.user_data['in_progress_achievements'] |
|
if a['id'] != achievement_id |
|
] |
|
|
|
|
|
achievement['unlocked_date'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
self.user_data['unlocked_achievements'].append(achievement) |
|
|
|
|
|
self.user_data['total_points'] += achievement['points'] |
|
self.user_data['level'] = self.calculate_level(self.user_data['total_points']) |
|
|
|
|
|
self.save_user_data() |
|
|
|
return achievement |
|
|
|
def update_achievement_progress(self, achievement_id, progress, total): |
|
"""تحديث تقدم إنجاز معين""" |
|
|
|
achievement = next((a for a in self.achievements if a['id'] == achievement_id), None) |
|
if not achievement: |
|
return False |
|
|
|
|
|
if achievement_id in [a['id'] for a in self.user_data['unlocked_achievements']]: |
|
return False |
|
|
|
|
|
in_progress_achievement = next( |
|
(a for a in self.user_data['in_progress_achievements'] if a['id'] == achievement_id), |
|
None |
|
) |
|
|
|
if in_progress_achievement: |
|
|
|
in_progress_achievement['progress'] = progress |
|
in_progress_achievement['total'] = total |
|
in_progress_achievement['percentage'] = min(100, int((progress / total) * 100)) |
|
in_progress_achievement['last_updated'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
else: |
|
|
|
progress_data = achievement.copy() |
|
progress_data['progress'] = progress |
|
progress_data['total'] = total |
|
progress_data['percentage'] = min(100, int((progress / total) * 100)) |
|
progress_data['start_date'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
progress_data['last_updated'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
self.user_data['in_progress_achievements'].append(progress_data) |
|
|
|
|
|
if progress >= total: |
|
return self.unlock_achievement(achievement_id) |
|
|
|
|
|
self.save_user_data() |
|
|
|
return True |
|
|
|
def check_and_award_achievements(self, action_type, data=None): |
|
"""التحقق من ومنح الإنجازات بناءً على إجراءات المستخدم""" |
|
try: |
|
if action_type == 'create_project': |
|
|
|
projects_count = self._get_projects_count() |
|
|
|
|
|
if projects_count == 1: |
|
self.unlock_achievement('first_project') |
|
elif projects_count == 5: |
|
self.unlock_achievement('five_projects') |
|
elif projects_count == 10: |
|
self.unlock_achievement('ten_projects') |
|
|
|
|
|
self.update_achievement_progress('five_projects', min(projects_count, 5), 5) |
|
self.update_achievement_progress('ten_projects', min(projects_count, 10), 10) |
|
|
|
elif action_type == 'analyze_document': |
|
|
|
analysis_count = self._get_document_analysis_count() |
|
|
|
|
|
if analysis_count == 1: |
|
self.unlock_achievement('first_document_analysis') |
|
elif analysis_count == 5: |
|
self.unlock_achievement('five_document_analysis') |
|
|
|
|
|
self.update_achievement_progress('five_document_analysis', min(analysis_count, 5), 5) |
|
|
|
|
|
if data and 'duration_seconds' in data and data['duration_seconds'] < 300: |
|
self.unlock_achievement('quick_analysis') |
|
|
|
elif action_type == 'analyze_boq': |
|
self.unlock_achievement('complete_boq') |
|
|
|
elif action_type == 'analyze_terms': |
|
self.unlock_achievement('first_terms_analysis') |
|
|
|
elif action_type == 'analyze_risks': |
|
self.unlock_achievement('risk_analysis') |
|
|
|
|
|
if data and 'risks_count' in data: |
|
risks_count = data['risks_count'] |
|
risk_total = self._get_total_risks_identified() |
|
new_total = risk_total + risks_count |
|
|
|
|
|
self.update_achievement_progress('ten_risk_identified', min(new_total, 10), 10) |
|
|
|
if new_total >= 10 and risk_total < 10: |
|
self.unlock_achievement('ten_risk_identified') |
|
|
|
elif action_type == 'use_voice_narration': |
|
self.unlock_achievement('voice_narration') |
|
|
|
|
|
if data and 'language' in data: |
|
languages_used = self._get_languages_used() |
|
if data['language'] not in languages_used: |
|
languages_used.append(data['language']) |
|
self._save_languages_used(languages_used) |
|
|
|
|
|
self.update_achievement_progress('multilingual_expert', len(languages_used), 5) |
|
|
|
if len(languages_used) >= 5: |
|
self.unlock_achievement('multilingual_expert') |
|
|
|
elif action_type == 'use_map': |
|
self.unlock_achievement('first_map') |
|
|
|
elif action_type == 'train_ai_model': |
|
self.unlock_achievement('ai_fine_tuning') |
|
|
|
elif action_type == 'complete_pricing': |
|
self.unlock_achievement('pricing_master') |
|
|
|
except Exception as e: |
|
st.error(f"خطأ في التحقق من الإنجازات: {e}") |
|
|
|
def _get_projects_count(self): |
|
"""الحصول على عدد المشاريع""" |
|
try: |
|
cursor = self.conn.cursor() |
|
cursor.execute("SELECT COUNT(*) FROM documents WHERE user_id = %s AND type = 'project'", (self.user_id,)) |
|
count = cursor.fetchone()[0] |
|
cursor.close() |
|
return count |
|
except Exception: |
|
|
|
projects_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'projects') |
|
if os.path.exists(projects_dir): |
|
return len([f for f in os.listdir(projects_dir) if os.path.isdir(os.path.join(projects_dir, f))]) |
|
return 0 |
|
|
|
def _get_document_analysis_count(self): |
|
"""الحصول على عدد تحليلات المستندات""" |
|
try: |
|
cursor = self.conn.cursor() |
|
cursor.execute("SELECT COUNT(*) FROM document_analysis WHERE document_id IN (SELECT id FROM documents WHERE user_id = %s)", (self.user_id,)) |
|
count = cursor.fetchone()[0] |
|
cursor.close() |
|
return count |
|
except Exception: |
|
|
|
return len(self.user_data['unlocked_achievements']) |
|
|
|
def _get_total_risks_identified(self): |
|
"""الحصول على إجمالي عدد المخاطر المحددة""" |
|
risks_file = os.path.join(self.achievements_path, f'user_{self.user_id}_risks.json') |
|
if os.path.exists(risks_file): |
|
try: |
|
with open(risks_file, 'r', encoding='utf-8') as f: |
|
risks_data = json.load(f) |
|
return risks_data.get('total_risks', 0) |
|
except Exception: |
|
return 0 |
|
return 0 |
|
|
|
def _save_total_risks_identified(self, total): |
|
"""حفظ إجمالي عدد المخاطر المحددة""" |
|
risks_file = os.path.join(self.achievements_path, f'user_{self.user_id}_risks.json') |
|
try: |
|
with open(risks_file, 'w', encoding='utf-8') as f: |
|
json.dump({'total_risks': total}, f, ensure_ascii=False, indent=2) |
|
except Exception: |
|
pass |
|
|
|
def _get_languages_used(self): |
|
"""الحصول على قائمة اللغات المستخدمة""" |
|
languages_file = os.path.join(self.achievements_path, f'user_{self.user_id}_languages.json') |
|
if os.path.exists(languages_file): |
|
try: |
|
with open(languages_file, 'r', encoding='utf-8') as f: |
|
languages_data = json.load(f) |
|
return languages_data.get('languages', []) |
|
except Exception: |
|
return [] |
|
return [] |
|
|
|
def _save_languages_used(self, languages): |
|
"""حفظ قائمة اللغات المستخدمة""" |
|
languages_file = os.path.join(self.achievements_path, f'user_{self.user_id}_languages.json') |
|
try: |
|
with open(languages_file, 'w', encoding='utf-8') as f: |
|
json.dump({'languages': languages}, f, ensure_ascii=False, indent=2) |
|
except Exception: |
|
pass |
|
|
|
def render_achievements_tab(self): |
|
"""عرض علامة تبويب الإنجازات""" |
|
st.markdown("<h3 class='achievement-title'>إنجازاتك</h3>", unsafe_allow_html=True) |
|
|
|
|
|
col1, col2 = st.columns([1, 3]) |
|
with col1: |
|
st.markdown(f"<div class='level-badge'>المستوى {self.user_data['level']}</div>", unsafe_allow_html=True) |
|
with col2: |
|
|
|
next_level_points = (self.user_data['level']) * 1000 |
|
current_level_points = (self.user_data['level'] - 1) * 1000 |
|
progress = (self.user_data['total_points'] - current_level_points) / (next_level_points - current_level_points) |
|
|
|
st.markdown(f"<div class='points-text'>{self.user_data['total_points']} نقطة</div>", unsafe_allow_html=True) |
|
st.progress(progress, text=f"المستوى التالي: {next_level_points} نقطة") |
|
|
|
|
|
st.markdown("<h4 class='achievement-subtitle'>الإنجازات المفتوحة</h4>", unsafe_allow_html=True) |
|
|
|
if not self.user_data['unlocked_achievements']: |
|
st.info("لم تقم بفتح أي إنجازات حتى الآن. أكمل المهام للحصول على الإنجازات!") |
|
else: |
|
|
|
cols = st.columns(3) |
|
for i, achievement in enumerate(self.user_data['unlocked_achievements']): |
|
with cols[i % 3]: |
|
self._render_achievement_card(achievement, is_unlocked=True) |
|
|
|
|
|
st.markdown("<h4 class='achievement-subtitle'>الإنجازات قيد التقدم</h4>", unsafe_allow_html=True) |
|
|
|
if not self.user_data['in_progress_achievements']: |
|
st.info("ليس لديك أي إنجازات قيد التقدم حالياً.") |
|
else: |
|
|
|
for achievement in self.user_data['in_progress_achievements']: |
|
self._render_progress_achievement(achievement) |
|
|
|
|
|
st.markdown("<h4 class='achievement-subtitle'>الإنجازات المتاحة</h4>", unsafe_allow_html=True) |
|
|
|
|
|
unlocked_ids = [a['id'] for a in self.user_data['unlocked_achievements']] |
|
in_progress_ids = [a['id'] for a in self.user_data['in_progress_achievements']] |
|
available_achievements = [a for a in self.achievements if a['id'] not in unlocked_ids and a['id'] not in in_progress_ids] |
|
|
|
if not available_achievements: |
|
st.success("رائع! لقد حققت جميع الإنجازات المتاحة.") |
|
else: |
|
|
|
categories = sorted(set(a['category'] for a in available_achievements)) |
|
for category in categories: |
|
st.markdown(f"<h5 class='achievement-category'>{category}</h5>", unsafe_allow_html=True) |
|
|
|
category_achievements = [a for a in available_achievements if a['category'] == category] |
|
cols = st.columns(3) |
|
for i, achievement in enumerate(category_achievements): |
|
with cols[i % 3]: |
|
self._render_achievement_card(achievement, is_unlocked=False) |
|
|
|
def _render_achievement_card(self, achievement, is_unlocked): |
|
"""عرض بطاقة إنجاز""" |
|
if is_unlocked: |
|
card_class = "achievement-card unlocked" |
|
icon_class = "achievement-icon unlocked" |
|
title_class = "achievement-name unlocked" |
|
points_display = f"{achievement['points']} نقطة" |
|
date_display = f"تم الفتح: {achievement.get('unlocked_date', 'غير معروف')}" |
|
else: |
|
card_class = "achievement-card locked" |
|
icon_class = "achievement-icon locked" |
|
title_class = "achievement-name locked" |
|
points_display = f"{achievement['points']} نقطة" |
|
date_display = f"صعوبة: {achievement['difficulty']}" |
|
|
|
html = f""" |
|
<div class="{card_class}"> |
|
<div class="{icon_class}">{achievement['icon']}</div> |
|
<div class="{title_class}">{achievement['name']}</div> |
|
<div class="achievement-description">{achievement['description']}</div> |
|
<div class="achievement-footer"> |
|
<span class="achievement-points">{points_display}</span> |
|
<span class="achievement-date">{date_display}</span> |
|
</div> |
|
</div> |
|
""" |
|
st.markdown(html, unsafe_allow_html=True) |
|
|
|
def _render_progress_achievement(self, achievement): |
|
"""عرض إنجاز قيد التقدم""" |
|
progress = achievement.get('percentage', 0) |
|
|
|
html = f""" |
|
<div class="progress-achievement"> |
|
<div class="progress-achievement-header"> |
|
<div class="progress-achievement-icon">{achievement['icon']}</div> |
|
<div class="progress-achievement-info"> |
|
<div class="progress-achievement-name">{achievement['name']}</div> |
|
<div class="progress-achievement-description">{achievement['description']}</div> |
|
</div> |
|
<div class="progress-achievement-points">{achievement['points']} نقطة</div> |
|
</div> |
|
</div> |
|
""" |
|
st.markdown(html, unsafe_allow_html=True) |
|
|
|
st.progress(progress / 100, text=f"{progress}% ({achievement.get('progress', 0)}/{achievement.get('total', 1)})") |
|
|
|
def render_achievements_summary(self): |
|
"""عرض ملخص الإنجازات في لوحة التحكم""" |
|
|
|
total_achievements = len(self.achievements) |
|
unlocked_count = len(self.user_data['unlocked_achievements']) |
|
in_progress_count = len(self.user_data['in_progress_achievements']) |
|
|
|
st.markdown(f""" |
|
<div class="achievements-summary"> |
|
<div class="achievements-summary-header"> |
|
<div class="achievements-summary-title">الإنجازات</div> |
|
<div class="achievements-summary-level">المستوى {self.user_data['level']}</div> |
|
</div> |
|
<div class="achievements-summary-progress"> |
|
<div class="achievements-summary-percentage">{int((unlocked_count / total_achievements) * 100)}%</div> |
|
<div class="achievements-summary-counts">{unlocked_count} / {total_achievements}</div> |
|
</div> |
|
<div class="achievements-summary-footer"> |
|
<div class="achievements-summary-stat"> |
|
<div class="achievements-summary-stat-value">{unlocked_count}</div> |
|
<div class="achievements-summary-stat-label">مفتوحة</div> |
|
</div> |
|
<div class="achievements-summary-stat"> |
|
<div class="achievements-summary-stat-value">{in_progress_count}</div> |
|
<div class="achievements-summary-stat-label">قيد التقدم</div> |
|
</div> |
|
<div class="achievements-summary-stat"> |
|
<div class="achievements-summary-stat-value">{self.user_data['total_points']}</div> |
|
<div class="achievements-summary-stat-label">نقطة</div> |
|
</div> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if self.user_data['unlocked_achievements']: |
|
st.markdown("<div class='achievements-recent-title'>آخر الإنجازات</div>", unsafe_allow_html=True) |
|
|
|
recent_achievements = sorted( |
|
self.user_data['unlocked_achievements'], |
|
key=lambda x: x.get('unlocked_date', ''), |
|
reverse=True |
|
)[:3] |
|
|
|
for achievement in recent_achievements: |
|
st.markdown(f""" |
|
<div class="achievement-recent-item"> |
|
<div class="achievement-recent-icon">{achievement['icon']}</div> |
|
<div class="achievement-recent-info"> |
|
<div class="achievement-recent-name">{achievement['name']}</div> |
|
<div class="achievement-recent-date">{achievement.get('unlocked_date', '')}</div> |
|
</div> |
|
<div class="achievement-recent-points">+{achievement['points']}</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
def render(self): |
|
"""عرض واجهة نظام الإنجازات""" |
|
st.markdown("<h2 class='module-title'>نظام الإنجازات المحفز لمراحل المشروع</h2>", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<div class="module-description"> |
|
نظام الإنجازات يحفزك على إكمال المهام وتحقيق أهداف المشروع من خلال مكافآت |
|
وإنجازات قابلة للفتح. اكتسب النقاط وارتقِ بمستواك وافتح إنجازات جديدة كلما تقدمت في استخدام نظام تحليل المناقصات. |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if not self.user_data['unlocked_achievements'] and not self.user_data['in_progress_achievements']: |
|
st.info(""" |
|
👋 مرحباً بك في نظام الإنجازات! |
|
|
|
استكشف الإنجازات المتاحة وابدأ في تحقيقها عن طريق إكمال المهام في أنحاء النظام المختلفة. |
|
كلما حققت المزيد من الإنجازات، حصلت على نقاط أكثر وارتقيت في المستويات. |
|
|
|
ابدأ الآن بإنشاء مشروع جديد أو تحليل مستند! |
|
""") |
|
|
|
|
|
tab1, tab2, tab3 = st.tabs(["الإنجازات", "المستويات والمكافآت", "الإحصائيات"]) |
|
|
|
with tab1: |
|
self.render_achievements_tab() |
|
|
|
with tab2: |
|
st.markdown("<h3 class='achievement-title'>المستويات والمكافآت</h3>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(""" |
|
<div class="levels-info"> |
|
<p>نظام المستويات يعتمد على النقاط التي تكتسبها من إنجاز المهام وفتح الإنجازات:</p> |
|
<ul> |
|
<li>المستوى 1: 0 - 999 نقطة</li> |
|
<li>المستوى 2: 1000 - 1999 نقطة</li> |
|
<li>المستوى 3: 2000 - 2999 نقطة</li> |
|
<li>وهكذا...</li> |
|
</ul> |
|
<p>كلما ارتقيت في المستويات، تفتح مكافآت وميزات جديدة في النظام!</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4 class='achievement-subtitle'>المكافآت المتاحة</h4>", unsafe_allow_html=True) |
|
|
|
rewards = [ |
|
{"level": 2, "name": "قوالب مخصصة", "description": "الوصول إلى قوالب مخصصة للتقارير والتحليلات"}, |
|
{"level": 3, "name": "تنبيهات متقدمة", "description": "إعدادات إشعارات متقدمة للمشاريع والمواعيد النهائية"}, |
|
{"level": 5, "name": "تحليل معزز", "description": "خيارات إضافية لتحليل المستندات والعقود"}, |
|
{"level": 7, "name": "تخصيص متقدم", "description": "خيارات إضافية لتخصيص واجهة النظام والتقارير"}, |
|
{"level": 10, "name": "وضع الخبراء", "description": "وضع متقدم مع ميزات خاصة متاحة فقط للمستخدمين المخضرمين"} |
|
] |
|
|
|
for reward in rewards: |
|
status = "متاح" if self.user_data['level'] >= reward['level'] else "مقفل" |
|
status_class = "available" if self.user_data['level'] >= reward['level'] else "locked" |
|
|
|
st.markdown(f""" |
|
<div class="reward-item"> |
|
<div class="reward-level">المستوى {reward['level']}</div> |
|
<div class="reward-info"> |
|
<div class="reward-name">{reward['name']}</div> |
|
<div class="reward-description">{reward['description']}</div> |
|
</div> |
|
<div class="reward-status {status_class}">{status}</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
with tab3: |
|
st.markdown("<h3 class='achievement-title'>إحصائيات الإنجازات</h3>", unsafe_allow_html=True) |
|
|
|
|
|
categories = {} |
|
for achievement in self.achievements: |
|
category = achievement['category'] |
|
if category not in categories: |
|
categories[category] = {"total": 0, "unlocked": 0} |
|
categories[category]["total"] += 1 |
|
|
|
|
|
for achievement in self.user_data['unlocked_achievements']: |
|
category = achievement['category'] |
|
if category in categories: |
|
categories[category]["unlocked"] += 1 |
|
|
|
|
|
df = pd.DataFrame([ |
|
{ |
|
"الفئة": category, |
|
"المفتوحة": data["unlocked"], |
|
"الإجمالي": data["total"], |
|
"النسبة": round((data["unlocked"] / data["total"]) * 100 if data["total"] > 0 else 0) |
|
} |
|
for category, data in categories.items() |
|
]) |
|
|
|
|
|
st.dataframe( |
|
df, |
|
column_config={ |
|
"النسبة": st.column_config.ProgressColumn( |
|
"نسبة الإنجاز", |
|
format="%d%%", |
|
min_value=0, |
|
max_value=100 |
|
) |
|
}, |
|
hide_index=True |
|
) |
|
|
|
|
|
col1, col2, col3 = st.columns(3) |
|
with col1: |
|
total_points_possible = sum(a['points'] for a in self.achievements) |
|
st.metric( |
|
"إجمالي النقاط المحتملة", |
|
f"{total_points_possible}", |
|
f"{int((self.user_data['total_points'] / total_points_possible) * 100)}%" |
|
) |
|
|
|
with col2: |
|
days_since_first = 0 |
|
if self.user_data['unlocked_achievements']: |
|
first_date = min([ |
|
datetime.strptime(a.get('unlocked_date', datetime.now().strftime('%Y-%m-%d %H:%M:%S')), '%Y-%m-%d %H:%M:%S') |
|
for a in self.user_data['unlocked_achievements'] |
|
]) |
|
days_since_first = (datetime.now() - first_date).days |
|
|
|
st.metric("أيام النشاط", f"{days_since_first}") |
|
|
|
with col3: |
|
if self.user_data['unlocked_achievements']: |
|
achievements_per_day = round(len(self.user_data['unlocked_achievements']) / max(1, days_since_first), 2) |
|
st.metric("معدل الإنجازات اليومي", f"{achievements_per_day}") |
|
else: |
|
st.metric("معدل الإنجازات اليومي", "0") |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.achievement-title { |
|
color: #1E88E5; |
|
font-size: 1.5rem; |
|
margin-bottom: 1rem; |
|
text-align: right; |
|
} |
|
.achievement-subtitle { |
|
color: #424242; |
|
font-size: 1.2rem; |
|
margin: 1.5rem 0 1rem 0; |
|
text-align: right; |
|
} |
|
.achievement-category { |
|
color: #616161; |
|
font-size: 1rem; |
|
margin: 1rem 0 0.5rem 0; |
|
text-align: right; |
|
border-bottom: 1px solid #e0e0e0; |
|
padding-bottom: 0.3rem; |
|
} |
|
.level-badge { |
|
background-color: #1E88E5; |
|
color: white; |
|
padding: 0.5rem 1rem; |
|
border-radius: 1rem; |
|
font-weight: bold; |
|
text-align: center; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
} |
|
.points-text { |
|
font-size: 1.2rem; |
|
font-weight: bold; |
|
color: #424242; |
|
margin-bottom: 0.5rem; |
|
text-align: right; |
|
} |
|
.achievement-card { |
|
border-radius: 10px; |
|
padding: 1rem; |
|
margin-bottom: 1rem; |
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
|
text-align: center; |
|
transition: transform 0.2s; |
|
} |
|
.achievement-card:hover { |
|
transform: translateY(-5px); |
|
} |
|
.achievement-card.unlocked { |
|
background-color: #E3F2FD; |
|
border: 1px solid #BBDEFB; |
|
} |
|
.achievement-card.locked { |
|
background-color: #F5F5F5; |
|
border: 1px solid #E0E0E0; |
|
opacity: 0.7; |
|
} |
|
.achievement-icon { |
|
font-size: 2rem; |
|
margin-bottom: 0.5rem; |
|
} |
|
.achievement-icon.unlocked { |
|
color: #1E88E5; |
|
} |
|
.achievement-icon.locked { |
|
color: #9E9E9E; |
|
} |
|
.achievement-name { |
|
font-weight: bold; |
|
margin-bottom: 0.5rem; |
|
} |
|
.achievement-name.unlocked { |
|
color: #1565C0; |
|
} |
|
.achievement-name.locked { |
|
color: #616161; |
|
} |
|
.achievement-description { |
|
font-size: 0.85rem; |
|
color: #757575; |
|
margin-bottom: 0.7rem; |
|
min-height: 2.5rem; |
|
} |
|
.achievement-footer { |
|
display: flex; |
|
justify-content: space-between; |
|
font-size: 0.8rem; |
|
color: #9E9E9E; |
|
border-top: 1px solid #E0E0E0; |
|
padding-top: 0.5rem; |
|
} |
|
.progress-achievement { |
|
background-color: #F5F5F5; |
|
border-radius: 10px; |
|
padding: 1rem; |
|
margin-bottom: 0.5rem; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
} |
|
.progress-achievement-header { |
|
display: flex; |
|
align-items: center; |
|
margin-bottom: 0.5rem; |
|
} |
|
.progress-achievement-icon { |
|
font-size: 1.5rem; |
|
color: #1E88E5; |
|
margin-left: 1rem; |
|
} |
|
.progress-achievement-info { |
|
flex: 1; |
|
} |
|
.progress-achievement-name { |
|
font-weight: bold; |
|
color: #424242; |
|
} |
|
.progress-achievement-description { |
|
font-size: 0.85rem; |
|
color: #757575; |
|
} |
|
.progress-achievement-points { |
|
color: #1E88E5; |
|
font-weight: bold; |
|
} |
|
.levels-info { |
|
background-color: #F5F5F5; |
|
border-radius: 10px; |
|
padding: 1rem; |
|
margin-bottom: 1.5rem; |
|
text-align: right; |
|
} |
|
.levels-info ul { |
|
list-style-position: inside; |
|
margin: 0.5rem 1rem; |
|
padding: 0; |
|
} |
|
.reward-item { |
|
display: flex; |
|
align-items: center; |
|
background-color: #F5F5F5; |
|
border-radius: 10px; |
|
padding: 1rem; |
|
margin-bottom: 0.5rem; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
} |
|
.reward-level { |
|
background-color: #1E88E5; |
|
color: white; |
|
padding: 0.3rem 0.7rem; |
|
border-radius: 1rem; |
|
font-size: 0.8rem; |
|
font-weight: bold; |
|
margin-left: 1rem; |
|
white-space: nowrap; |
|
} |
|
.reward-info { |
|
flex: 1; |
|
} |
|
.reward-name { |
|
font-weight: bold; |
|
color: #424242; |
|
} |
|
.reward-description { |
|
font-size: 0.85rem; |
|
color: #757575; |
|
} |
|
.reward-status { |
|
font-weight: bold; |
|
padding: 0.3rem 0.7rem; |
|
border-radius: 1rem; |
|
font-size: 0.8rem; |
|
white-space: nowrap; |
|
} |
|
.reward-status.available { |
|
background-color: #C8E6C9; |
|
color: #2E7D32; |
|
} |
|
.reward-status.locked { |
|
background-color: #FFCDD2; |
|
color: #C62828; |
|
} |
|
.achievements-summary { |
|
background-color: #F5F5F5; |
|
border-radius: 10px; |
|
padding: 1rem; |
|
margin-bottom: 1rem; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
} |
|
.achievements-summary-header { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
margin-bottom: 0.5rem; |
|
} |
|
.achievements-summary-title { |
|
font-weight: bold; |
|
color: #424242; |
|
} |
|
.achievements-summary-level { |
|
background-color: #1E88E5; |
|
color: white; |
|
padding: 0.3rem 0.7rem; |
|
border-radius: 1rem; |
|
font-size: 0.8rem; |
|
font-weight: bold; |
|
} |
|
.achievements-summary-progress { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
margin-bottom: 1rem; |
|
} |
|
.achievements-summary-percentage { |
|
font-size: 1.2rem; |
|
font-weight: bold; |
|
color: #1E88E5; |
|
} |
|
.achievements-summary-counts { |
|
color: #757575; |
|
} |
|
.achievements-summary-footer { |
|
display: flex; |
|
justify-content: space-between; |
|
text-align: center; |
|
} |
|
.achievements-summary-stat-value { |
|
font-weight: bold; |
|
color: #424242; |
|
font-size: 1.1rem; |
|
} |
|
.achievements-summary-stat-label { |
|
color: #757575; |
|
font-size: 0.8rem; |
|
} |
|
.achievements-recent-title { |
|
font-weight: bold; |
|
color: #424242; |
|
margin: 1rem 0 0.5rem 0; |
|
} |
|
.achievement-recent-item { |
|
display: flex; |
|
align-items: center; |
|
background-color: #E3F2FD; |
|
border-radius: 10px; |
|
padding: 0.7rem; |
|
margin-bottom: 0.5rem; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
} |
|
.achievement-recent-icon { |
|
font-size: 1.2rem; |
|
color: #1E88E5; |
|
margin-left: 0.7rem; |
|
} |
|
.achievement-recent-info { |
|
flex: 1; |
|
} |
|
.achievement-recent-name { |
|
font-weight: bold; |
|
color: #424242; |
|
font-size: 0.9rem; |
|
} |
|
.achievement-recent-date { |
|
font-size: 0.75rem; |
|
color: #757575; |
|
} |
|
.achievement-recent-points { |
|
color: #1E88E5; |
|
font-weight: bold; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |