EGYADMIN commited on
Commit
73c51c6
·
verified ·
1 Parent(s): 53bbfe2

Upload 111 files

Browse files
app.py CHANGED
@@ -1,923 +1,1253 @@
1
- #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- نظام واهبي للذكاء الاصطناعي لتحليل العقود والمناقصات
6
- تطبيق Streamlit الرئيسي الذي يجمع جميع الوحدات والمكونات
7
- """
8
-
9
  import os
10
  import sys
 
 
11
  import streamlit as st
12
- import pandas as pd
13
- import numpy as np
14
 
15
- # وظيفة لتهيئة حزم NLTK المطلوبة عند بدء التطبيق
16
- def initialize_nltk_resources():
17
- """تنزيل وتهيئة موارد NLTK المطلوبة"""
 
 
 
 
18
  try:
19
- # محاولة تنزيل حزم NLTK الأساسية
20
- import nltk
21
-
22
- # قائمة بالحزم المطلوبة
23
- required_packages = ['punkt', 'stopwords', 'wordnet']
24
- for package in required_packages:
25
- try:
26
- # محاولة استخدام الحزمة أولاً، وإذا فشلت يتم تنزيلها
27
- nltk.data.find(f'tokenizers/{package}')
28
- except LookupError:
29
- print(f"تنزيل حزمة NLTK: {package}")
30
- nltk.download(package, quiet=True)
31
-
32
- print("تم تهيئة موارد NLTK بنجاح.")
33
- except Exception as e:
34
- print(f"خطأ في تهيئة NLTK: {e}")
35
 
36
- # تهيئة موارد NLTK عند بدء التطبيق
37
- initialize_nltk_resources()
 
 
 
 
 
38
 
39
- # مسار نسبي للملفات الثابتة (للتأكد من العمل في بيئات مختلفة)
40
- def get_static_path(file_path):
41
- """مسار ملف ثابت يعمل سواء كان التشغيل من المجلد الرئيسي أو من المجلد الفرعي"""
42
- # قائمة المسارات المحتملة
43
- possible_paths = [
44
- # المسار المباشر (في حالة تشغيل التطبيق من نفس المجلد)
45
- file_path,
46
- # المسار النسبي من مجلد التطبيق (tender-analysis-system)
47
- os.path.join(os.path.dirname(__file__), file_path),
48
- # المسار النسبي من المجلد الأعلى
49
- os.path.join(os.path.dirname(os.path.dirname(__file__)), "tender-analysis-system", file_path),
50
  ]
51
 
52
- # اختبار كل مسار محتمل
53
- for path in possible_paths:
54
- if os.path.exists(path):
55
- return path
 
 
 
 
 
 
 
 
56
 
57
- # إذا لم يتم العثور على الملف، إعادة المسار الأصلي
58
- return file_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- # إعداد إعدادات الصفحة
61
- try:
62
- st.set_page_config(
63
- page_title="نظام WAHBi للذكاء الاصطناعي | التعاقدات والمناقصات",
64
- page_icon="📊",
65
- layout="wide",
66
- initial_sidebar_state="expanded"
67
- )
68
- except Exception as e:
69
- print(f"خطأ في إعداد الصفحة: {e}")
70
- # يحدث هذا غالبًا عند استخدام st.set_page_config أكثر من مرة
71
 
72
- # استيراد ملف CSS للإصلاحات RTL
73
- try:
74
- rtl_css_path = get_static_path("static/css/rtl-fixes.css")
75
- with open(rtl_css_path, "r", encoding='utf-8') as f:
76
- rtl_css = f.read()
77
- st.markdown(f"<style>{rtl_css}</style>", unsafe_allow_html=True)
78
- except FileNotFoundError:
79
- st.warning("تعذر العثور على ملف تصحيحات RTL، سيتم استخدام النمط الافتراضي")
80
- print(f"تعذر العثور على ملف: {rtl_css_path}")
81
- except Exception as e:
82
- st.warning(f"حدث خطأ أثناء تحميل تصحيحات RTL: {str(e)}")
83
- print(f"خطأ في تحميل ملف CSS: {str(e)}")
84
-
85
- # استيراد ملف CSS المحسن
86
- try:
87
- enhanced_css_path = get_static_path("static/css/enhanced-styles.css")
88
- with open(enhanced_css_path, "r", encoding='utf-8') as f:
89
- enhanced_css = f.read()
90
- st.markdown(f"<style>{enhanced_css}</style>", unsafe_allow_html=True)
91
- except FileNotFoundError:
92
- st.warning("تعذر العثور على ملف التنسيقات المحسنة، سيتم استخدام النمط الافتراضي")
93
- print(f"تعذر العثور على ملف: {enhanced_css_path}")
94
- except Exception as e:
95
- st.warning(f"حدث خطأ أثناء تحميل التنسيقات المحسنة: {str(e)}")
96
- print(f"خطأ في تحميل ملف CSS: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- # إضافة Font Awesome وأي أصول خارجية أخرى
99
- st.markdown("""
100
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
101
- """, unsafe_allow_html=True)
 
102
 
103
- # إضافة CSS المخصص
104
- st.markdown("""
105
- <style>
106
- /* تعديل الاتجاه للدعم العربي */
107
- .css-18e3th9, .css-1d391kg, .stMarkdown, .stTextArea, .stButton, .stTextInput, .stSelectbox, .stRadio {
108
- direction: rtl;
109
- text-align: right;
 
 
 
110
  }
 
 
 
 
 
 
111
 
112
- /* تحسين مظهر العناوين */
113
- h1, h2, h3, h4 {
114
- color: #1E88E5;
115
- }
116
 
117
- /* تخصيص عنوان التطبيق */
118
- .app-title {
119
- font-size: 2.2rem;
120
- font-weight: bold;
121
- text-align: center;
122
- color: #1E88E5;
123
- margin-bottom: 1rem;
124
- text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
125
- background: linear-gradient(90deg, #1976D2, #64B5F6);
126
- -webkit-background-clip: text;
127
- -webkit-text-fill-color: transparent;
128
- }
129
 
130
- /* تخصيص الشريط الجانبي */
131
- .sidebar .sidebar-content {
132
- background-color: #f8f9fa;
133
- }
 
 
 
 
 
134
 
135
- /* تخصيص الأقسام */
136
- .section-card {
137
- background-color: #f9f9f9;
138
- border-radius: 10px;
139
- padding: 20px;
140
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
141
- margin-bottom: 20px;
142
- }
 
143
 
144
- /* تخصيص الأزرار */
145
- .stButton>button {
146
- border-radius: 5px;
147
- background-color: #1E88E5;
148
- color: white;
149
- font-weight: bold;
150
- border: none;
151
- padding: 0.5rem 1rem;
152
- transition: all 0.3s ease;
153
- }
154
 
155
- .stButton>button:hover {
156
- background-color: #1565C0;
157
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
158
- }
159
 
160
- /* تخصيص المؤشرات */
161
- .indicator {
162
- padding: 1rem;
163
- border-radius: 10px;
164
- background-color: #f5f5f5;
165
- text-align: center;
166
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
168
 
169
- .indicator-value {
170
- font-size: 2rem;
171
- font-weight: bold;
172
- margin-bottom: 0.5rem;
 
 
 
 
 
 
 
173
  }
174
-
175
- .indicator-label {
176
- font-size: 1rem;
177
- color: #666;
 
 
 
178
  }
179
-
180
- /* تخصيص البطاقات */
181
- .card {
 
182
  background-color: white;
183
- border-radius: 10px;
184
- padding: 15px;
185
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
186
- margin-bottom: 15px;
187
- border-right: 4px solid #1E88E5;
188
- }
189
-
190
- .card-title {
191
- font-weight: bold;
192
- color: #1E88E5;
193
- margin-bottom: 10px;
194
  }
195
-
196
- .card-metrics {
197
- display: flex;
198
- justify-content: space-between;
199
  }
200
-
201
- .card-metric {
202
- text-align: center;
203
  }
 
 
204
 
205
- .card-metric-value {
206
- font-weight: bold;
207
- font-size: 1.5rem;
208
- }
209
 
210
- .card-metric-label {
211
- font-size: 0.8rem;
212
- color: #666;
213
- }
214
- </style>
215
- """, unsafe_allow_html=True)
216
-
217
- # استيراد المكونات والوحدات
218
- from utils.components.sidebar import render_sidebar
219
- from utils.helpers import create_directory_if_not_exists, get_data_folder
220
-
221
- # استيراد وحدات التطبيق
222
- from modules.pricing.pricing_app import PricingApp
223
- from modules.projects.projects_app import ProjectsApp
224
- from modules.resources.resources_app import ResourcesApp
225
- from modules.risk_assessment.risk_assessment_app import RiskAssessmentApp
226
- from modules.project_tracker.tracker_app import TrackerApp
227
- from modules.maps.maps_app import MapsApp
228
- from modules.notifications.notifications_app import NotificationsApp
229
- from modules.voice_narration.voice_narration_app import VoiceNarrationApp
230
- from modules.achievements.achievements_app import AchievementsApp
231
- from modules.ai_finetuning.finetuning_app import FinetuningApp
232
- from modules.document_comparison.comparison_app import DocumentComparisonApp
233
-
234
- # إنشاء مجلدات البيانات الضرورية
235
- create_directory_if_not_exists(get_data_folder())
236
- create_directory_if_not_exists(os.path.join(get_data_folder(), "projects"))
237
- create_directory_if_not_exists(os.path.join(get_data_folder(), "documents"))
238
- create_directory_if_not_exists(os.path.join(get_data_folder(), "analysis"))
239
-
240
- def main():
241
- """الدالة الرئيسية للتطبيق"""
242
 
243
- # تقديم الشريط الجانبي وتلقي الوحدة المختارة
244
- selected_module = render_sidebar()
245
 
246
- # إذا كان المستخدم غير مصرح له، قم بإظهار شاشة تسجيل الدخول
247
- if "is_authenticated" in st.session_state and not st.session_state.is_authenticated:
248
- render_login_screen()
249
- return
250
 
251
- # إظهار الوحدة المختارة
252
- if selected_module == "الرئيسية":
253
- render_homepage()
254
-
255
- elif selected_module == "إدارة المشاريع":
256
- projects_app = ProjectsApp()
257
- projects_app.render()
258
-
259
- elif selected_module == "التسعير المتكاملة":
260
- pricing_app = PricingApp()
261
- pricing_app.render()
262
-
263
- elif selected_module == "الموارد والتكاليف":
264
- resources_app = ResourcesApp()
265
- resources_app.render()
266
-
267
- elif selected_module == "تحليل المستندات":
268
- # تقديم واجهة تحليل المستندات
269
- render_document_analysis()
270
-
271
- elif selected_module == "مقارنة المستندات":
272
- # تقديم واجهة مقارنة المستندات
273
- comparison_app = DocumentComparisonApp()
274
- comparison_app.render()
275
-
276
- elif selected_module == "تقييم مخاطر العقود":
277
- risk_app = RiskAssessmentApp()
278
- risk_app.render()
279
-
280
- elif selected_module == "التقارير والتحليلات":
281
- # تقديم واجهة التقارير والتحليلات
282
- render_reports_and_analytics()
283
-
284
- elif selected_module == "متتبع حالة المشروع":
285
- tracker_app = TrackerApp()
286
- tracker_app.render()
287
-
288
- elif selected_module == "خريطة المشاريع":
289
- maps_app = MapsApp()
290
- maps_app.render()
291
-
292
- elif selected_module == "نظام الإشعارات":
293
- notifications_app = NotificationsApp()
294
- notifications_app.render()
295
-
296
- elif selected_module == "الترجمة الصوتية":
297
- voice_app = VoiceNarrationApp()
298
- voice_app.render()
299
-
300
- elif selected_module == "نظام الإنجازات":
301
- achievements_app = AchievementsApp()
302
- achievements_app.render()
303
-
304
- elif selected_module == "المساعد الذكي":
305
- # تقديم واجهة المساعد الذكي
306
- render_ai_assistant()
307
-
308
- elif selected_module == "ضبط نماذج الذكاء الاصطناعي":
309
- finetuning_app = FinetuningApp()
310
- finetuning_app.render()
311
 
312
- else:
313
- st.error("الوحدة المطلوبة غير موجودة")
314
-
315
-
316
- def render_login_screen():
317
- """عرض شاشة تسجيل الدخول"""
318
- st.markdown("<h1 class='app-title'>نظام WAHBi للذكاء الاصطناعي</h1>", unsafe_allow_html=True)
319
-
320
- st.markdown("""
321
- <div class="section-card">
322
- <h2>تسجيل الدخول</h2>
323
- <p>يرجى إدخال بيانات الاعتماد الخاصة بك للوصول إلى النظام.</p>
324
- </div>
325
- """, unsafe_allow_html=True)
326
-
327
- col1, col2, col3 = st.columns([1, 2, 1])
328
 
329
  with col2:
330
- username = st.text_input("اسم المستخدم")
331
- password = st.text_input("كلمة المرور", type="password")
332
-
333
- if st.button("تسجيل الدخول"):
334
- # تنفيذ منطق المصادقة
335
- if username == "admin" and password == "admin": # بيانات اعتماد مؤقتة للتطوير
336
- st.session_state.is_authenticated = True
337
- st.session_state.user_info = {
338
- "id": 1,
339
- "username": "admin",
340
- "full_name": "مدير النظام",
341
- "email": "[email protected]",
342
- "role": "مدير",
343
- "department": "الإدارة",
344
- "last_login": "2023-01-01 09:00:00"
345
- }
346
- st.rerun()
347
- else:
348
- st.error("اسم المستخدم أو كلمة المرور غير صحيحة")
349
 
350
- st.markdown("""
351
- <div style="text-align: center; margin-top: 50px; color: #666;">
352
- <p>نظام WAHBi للذكاء الاصطناعي © 2025 شركة شبه الجزيرة للمقاولات</p>
353
- <p>جميع الحقوق محفوظة</p>
354
- </div>
355
- """, unsafe_allow_html=True)
356
-
357
-
358
- def render_homepage():
359
- """عرض الصفحة الرئيسية للتطبيق"""
360
- st.markdown("<h1 class='app-title'>نظام WAHBi للذكاء الاصطناعي</h1>", unsafe_allow_html=True)
361
- st.markdown("<div style='text-align: center; margin-bottom: 20px;'>نظام متكامل لتحليل العقود والمناقصات باستخدام تقنيات الذكاء الاصطناعي المتقدمة</div>", unsafe_allow_html=True)
362
 
363
- # عرض مؤشرات الأداء الرئيسية
364
- col1, col2, col3, col4 = st.columns(4)
365
 
366
  with col1:
367
  st.markdown("""
368
- <div class="indicator">
369
- <div class="indicator-value" style="color: #1E88E5;">24</div>
370
- <div class="indicator-label">المناقصات النشطة</div>
 
371
  </div>
372
  """, unsafe_allow_html=True)
373
 
374
  with col2:
375
  st.markdown("""
376
- <div class="indicator">
377
- <div class="indicator-value" style="color: #43A047;">8</div>
378
- <div class="indicator-label">مشاريع قيد التنفيذ</div>
 
379
  </div>
380
  """, unsafe_allow_html=True)
381
 
382
  with col3:
383
  st.markdown("""
384
- <div class="indicator">
385
- <div class="indicator-value" style="color: #FB8C00;">12</div>
386
- <div class="indicator-label">مستندات قيد التحليل</div>
 
387
  </div>
388
  """, unsafe_allow_html=True)
389
 
390
- with col4:
391
- st.markdown("""
392
- <div class="indicator">
393
- <div class="indicator-value" style="color: #E53935;">5</div>
394
- <div class="indicator-label">تنبيهات تتطلب الاهتمام</div>
395
- </div>
396
- """, unsafe_allow_html=True)
 
 
 
 
397
 
398
- # عرض المشاريع الأخيرة والوصول السريع
399
- col1, col2 = st.columns([2, 1])
400
 
401
  with col1:
402
- st.markdown("### المناقصات الأخيرة")
403
-
404
- st.markdown("""
405
- <div class="card">
406
- <div class="card-title">إنشاء طريق سريع بمنطقة الرياض</div>
407
- <div>رقم المناقصة: TR-2025-134</div>
408
- <div>الجهة المالكة: وزارة النقل</div>
409
- <div>تاريخ الإغلاق: 15 أبريل 2025</div>
410
- <div class="card-metrics" style="margin-top: 10px;">
411
- <div class="card-metric">
412
- <div class="card-metric-value" style="color: #4CAF50;">85%</div>
413
- <div class="card-metric-label">نسبة الإنجاز</div>
414
- </div>
415
- <div class="card-metric">
416
- <div class="card-metric-value" style="color: #FFC107;">متوسطة</div>
417
- <div class="card-metric-label">المخاطر</div>
418
- </div>
419
- <div class="card-metric">
420
- <div class="card-metric-value" style="color: #2196F3;">مرتفعة</div>
421
- <div class="card-metric-label">الأولوية</div>
422
- </div>
423
- </div>
424
- </div>
425
-
426
- <div class="card">
427
- <div class="card-title">تطوير شبكة الصرف الصحي بالمنطقة الشرقية</div>
428
- <div>رقم المناقصة: WS-2025-089</div>
429
- <div>الجهة المالكة: وزارة المياه</div>
430
- <div>تاريخ الإغلاق: 22 أبريل 2025</div>
431
- <div class="card-metrics" style="margin-top: 10px;">
432
- <div class="card-metric">
433
- <div class="card-metric-value" style="color: #4CAF50;">62%</div>
434
- <div class="card-metric-label">نسبة الإنجاز</div>
435
- </div>
436
- <div class="card-metric">
437
- <div class="card-metric-value" style="color: #F44336;">مرتفعة</div>
438
- <div class="card-metric-label">المخاطر</div>
439
- </div>
440
- <div class="card-metric">
441
- <div class="card-metric-value" style="color: #2196F3;">مرت��عة</div>
442
- <div class="card-metric-label">الأولوية</div>
443
- </div>
444
- </div>
445
- </div>
446
-
447
- <div class="card">
448
- <div class="card-title">بناء 3 مدارس بمنطقة مكة المكرمة</div>
449
- <div>رقم المناقصة: ED-2025-112</div>
450
- <div>الجهة المالكة: وزارة التعليم</div>
451
- <div>تاريخ الإغلاق: 5 مايو 2025</div>
452
- <div class="card-metrics" style="margin-top: 10px;">
453
- <div class="card-metric">
454
- <div class="card-metric-value" style="color: #4CAF50;">38%</div>
455
- <div class="card-metric-label">نسبة الإنجاز</div>
456
- </div>
457
- <div class="card-metric">
458
- <div class="card-metric-value" style="color: #4CAF50;">منخفضة</div>
459
- <div class="card-metric-label">المخاطر</div>
460
- </div>
461
- <div class="card-metric">
462
- <div class="card-metric-value" style="color: #FFC107;">متوسطة</div>
463
- <div class="card-metric-label">الأولوية</div>
464
- </div>
465
- </div>
466
- </div>
467
- """, unsafe_allow_html=True)
468
 
469
  with col2:
470
- st.markdown("### الوصول السريع")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
 
472
- st.markdown("""
473
- <div style="display: grid; gap: 10px;">
474
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
475
- <i class="fas fa-file-alt" style="margin-left: 10px;"></i> تحليل مستند جديد
476
- </button>
477
-
478
- <button style="background-color: #43A047; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
479
- <i class="fas fa-calculator" style="margin-left: 10px;"></i> حساب تكاليف مشروع
480
- </button>
481
-
482
- <button style="background-color: #FB8C00; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
483
- <i class="fas fa-exclamation-triangle" style="margin-left: 10px;"></i> تقييم مخاطر العقد
484
- </button>
485
-
486
- <button style="background-color: #8E24AA; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
487
- <i class="fas fa-map-marker-alt" style="margin-left: 10px;"></i> استعراض خر��طة المشاريع
488
- </button>
489
-
490
- <button style="background-color: #546E7A; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
491
- <i class="fas fa-chart-bar" style="margin-left: 10px;"></i> إنشاء تقارير تحليلية
492
- </button>
493
- </div>
494
- """, unsafe_allow_html=True)
495
 
496
- st.markdown("### آخر التنبيهات")
 
 
 
 
 
 
 
497
 
498
- st.markdown("""
499
- <div style="background-color: #FFEBEE; border-right: 3px solid #E53935; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
500
- <div style="font-weight: bold; color: #B71C1C;">انتهاء موعد تقديم المناقصة</div>
501
- <div style="font-size: 0.9rem;">مشروع إنشاء الطريق السريع - متبقي 3 أيام</div>
502
- </div>
 
 
 
 
 
 
 
 
503
 
504
- <div style="background-color: #FFF8E1; border-right: 3px solid #FFA000; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
505
- <div style="font-weight: bold; color: #FF6F00;">تغيير في شروط المناقصة</div>
506
- <div style="font-size: 0.9rem;">تم تحديث مستندات مشروع شبكة الصرف الصحي</div>
507
- </div>
 
 
 
 
508
 
509
- <div style="background-color: #E8F5E9; border-right: 3px solid #43A047; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
510
- <div style="font-weight: bold; color: #2E7D32;">إكمال تحليل المستند</div>
511
- <div style="font-size: 0.9rem;">اكتمل تحليل عقد بناء المدارس بنجاح</div>
512
- </div>
513
- """, unsafe_allow_html=True)
514
 
515
- # معلومات حول النظام
516
- st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
 
518
- st.markdown("""
519
- <div class="section-card">
520
- <h3>حول النظام</h3>
521
- <p>نظام WAHBi للذكاء الاصطناعي هو نظام متكامل لتحليل العقود والمناقصات وإدارة المشاريع، مصمم خصيصاً لشركات المقاولات والبناء. يستخدم النظام تقنيات الذكاء الاصطناعي المتقدمة لتحليل المستندات واستخراج المعلومات المهمة وتقييم المخاطر ودعم اتخاذ القرار.</p>
522
- </div>
523
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
 
525
- # معلومات الشركة
526
  st.markdown("""
527
- <div style="text-align: center; margin-top: 30px; color: #666;">
528
- <p>هذا النظام يعمل لشركة شبه الجزيرة للمقاولات</p>
529
- <p>جميع الحقوق محفوظة 2025</p>
530
- </div>
531
  """, unsafe_allow_html=True)
532
 
533
-
534
- def render_document_analysis():
535
- """عرض واجهة تحليل المستندات"""
536
- st.markdown("<h1 class='app-title'>تحليل المستندات</h1>", unsafe_allow_html=True)
537
 
 
538
  st.markdown("""
539
- <div class="section-card">
540
- <p>استخدم هذه الوحدة لتحليل مستندات العقود والمناقصات باستخدام تقنيات الذكاء الاصطناعي المتقدمة.
541
- يمكنك تحميل المستندات بتنسيقات PDF أو Word وسيقوم النظام بتحليلها واستخراج المعلومات المهمة مثل الشروط والتكاليف والمخاطر والتزاماتك كمقاول.</p>
 
542
  </div>
543
  """, unsafe_allow_html=True)
544
 
545
- # أدوات التحليل
546
- st.markdown("### أدوات التحليل")
547
 
548
- col1, col2, col3 = st.columns(3)
 
549
 
550
  with col1:
551
- st.markdown("""
552
- <div class="card">
553
- <div class="card-title">تحليل العقد الشامل</div>
554
- <p>تحليل شامل للعقد باستخدام Claude AI لاستخراج جميع البنود والشروط والالتزامات والمواعيد النهائية.</p>
555
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 8px 12px; cursor: pointer; width: 100%;">
556
- تحليل جديد
557
- </button>
558
- </div>
559
- """, unsafe_allow_html=True)
560
 
561
  with col2:
562
- st.markdown("""
563
- <div class="card">
564
- <div class="card-title">تحليل جداول الكميات</div>
565
- <p>تحليل متخصص لجداول الكميات (BOQ) لاستخراج قوائم المواد والكميات والأسعار والتكاليف الإجمالية.</p>
566
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 8px 12px; cursor: pointer; width: 100%;">
567
- تحليل جديد
568
- </button>
569
- </div>
570
- """, unsafe_allow_html=True)
571
 
572
- with col3:
573
- st.markdown("""
574
- <div class="card">
575
- <div class="card-title">تحليل الشروط والأحكام</div>
576
- <p>تحليل متخصص للشروط والأحكام في العقد لتحديد الشروط الغير عادية أو المقيدة والمخاطر القانونية.</p>
577
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 8px 12px; cursor: pointer; width: 100%;">
578
- تحليل جديد
579
- </button>
580
- </div>
581
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
- # التحليلات الأخيرة
584
- st.markdown("### التحليلات الأخيرة")
 
 
 
 
 
 
 
 
 
585
 
 
586
  st.markdown("""
587
- <table style="width: 100%; border-collapse: collapse; margin-top: 20px;">
588
- <thead>
589
- <tr style="background-color: #f5f5f5;">
590
- <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">اسم المستند</th>
591
- <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">نوع التحليل</th>
592
- <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">تاريخ التحليل</th>
593
- <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">الحالة</th>
594
- <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">الإجراءات</th>
595
- </tr>
596
- </thead>
597
- <tbody>
598
- <tr>
599
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">عقد إنشاء طريق سريع.pdf</td>
600
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل شامل</td>
601
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-25</td>
602
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #4CAF50; font-weight: bold;">مكتمل</span></td>
603
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
604
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
605
- <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل التقرير</button>
606
- </td>
607
- </tr>
608
- <tr style="background-color: #f9f9f9;">
609
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">جداول كميات مشروع صرف صحي.xlsx</td>
610
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل جداول الكميات</td>
611
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-23</td>
612
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #4CAF50; font-weight: bold;">مكتمل</span></td>
613
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
614
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
615
- <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل التقرير</button>
616
- </td>
617
- </tr>
618
- <tr>
619
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">شروط وأحكام عقد بناء مدارس.pdf</td>
620
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل الشروط والأحكام</td>
621
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-20</td>
622
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #4CAF50; font-weight: bold;">مكتمل</span></td>
623
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
624
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
625
- <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل التقرير</button>
626
- </td>
627
- </tr>
628
- <tr style="background-color: #f9f9f9;">
629
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">ملحق عقد مشروع كباري.pdf</td>
630
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل شامل</td>
631
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-18</td>
632
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #FB8C00; font-weight: bold;">قيد المعالجة</span></td>
633
- <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
634
- <button style="background-color: #9E9E9E; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;" disabled>عرض</button>
635
- <button style="background-color: #9E9E9E; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;" disabled>تنزيل التقرير</button>
636
- </td>
637
- </tr>
638
- </tbody>
639
- </table>
640
  """, unsafe_allow_html=True)
641
 
642
- # إحصائيات التحليل
643
- st.markdown("### إحصائيات التحليل")
644
 
 
645
  col1, col2 = st.columns(2)
646
 
647
  with col1:
648
- st.markdown("""
649
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; height: 100%;">
650
- <h4 style="color: #1E88E5; margin-bottom: 15px;">توزيع أنواع المستندات</h4>
651
- <div style="margin-bottom: 15px;">
652
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
653
- <span>عقود ومناقصات</span>
654
- <span>45%</span>
655
- </div>
656
- <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
657
- <div style="height: 100%; width: 45%; background-color: #1E88E5; border-radius: 5px;"></div>
658
- </div>
659
- </div>
660
-
661
- <div style="margin-bottom: 15px;">
662
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
663
- <span>جداول كميات</span>
664
- <span>30%</span>
665
- </div>
666
- <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
667
- <div style="height: 100%; width: 30%; background-color: #43A047; border-radius: 5px;"></div>
668
- </div>
669
- </div>
670
-
671
- <div style="margin-bottom: 15px;">
672
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
673
- <span>شروط وأحكام</span>
674
- <span>15%</span>
675
- </div>
676
- <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
677
- <div style="height: 100%; width: 15%; background-color: #FB8C00; border-radius: 5px;"></div>
678
- </div>
679
- </div>
680
-
681
- <div style="margin-bottom: 15px;">
682
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
683
- <span>مستندات أخرى</span>
684
- <span>10%</span>
685
- </div>
686
- <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
687
- <div style="height: 100%; width: 10%; background-color: #78909C; border-radius: 5px;"></div>
688
- </div>
689
- </div>
690
- </div>
691
- """, unsafe_allow_html=True)
692
 
693
  with col2:
694
- st.markdown("""
695
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; height: 100%;">
696
- <h4 style="color: #1E88E5; margin-bottom: 15px;">إحصائيات التحليل الشهرية</h4>
697
- <div style="display: flex; justify-content: space-between; text-align: center;">
698
- <div>
699
- <div style="font-size: 2rem; font-weight: bold; color: #1E88E5;">42</div>
700
- <div style="color: #666;">مستند تم تحليله</div>
701
- </div>
702
- <div>
703
- <div style="font-size: 2rem; font-weight: bold; color: #43A047;">38</div>
704
- <div style="color: #666;">تحليل ناجح</div>
705
- </div>
706
- <div>
707
- <div style="font-size: 2rem; font-weight: bold; color: #FB8C00;">4</div>
708
- <div style="color: #666;">تحليل غير مكتمل</div>
709
- </div>
710
- </div>
 
 
 
 
 
711
 
712
- <h4 style="color: #1E88E5; margin-top: 20px; margin-bottom: 15px;">متوسط وقت المعالجة</h4>
713
- <div style="display: flex; align-items: center; margin-bottom: 10px;">
714
- <div style="width: 150px;">تحليل شامل:</div>
715
- <div style="flex-grow: 1; height: 8px; background-color: #e0e0e0; border-radius: 5px;">
716
- <div style="height: 100%; width: 80%; background-color: #1E88E5; border-radius: 5px;"></div>
717
- </div>
718
- <div style="width: 50px; text-align: left; padding-left: 10px;">2:30</div>
719
- </div>
720
 
721
- <div style="display: flex; align-items: center; margin-bottom: 10px;">
722
- <div style="width: 150px;">جداول الكميات:</div>
723
- <div style="flex-grow: 1; height: 8px; background-color: #e0e0e0; border-radius: 5px;">
724
- <div style="height: 100%; width: 50%; background-color: #43A047; border-radius: 5px;"></div>
725
- </div>
726
- <div style="width: 50px; text-align: left; padding-left: 10px;">1:45</div>
727
- </div>
728
 
729
- <div style="display: flex; align-items: center;">
730
- <div style="width: 150px;">الشروط والأحكام:</div>
731
- <div style="flex-grow: 1; height: 8px; background-color: #e0e0e0; border-radius: 5px;">
732
- <div style="height: 100%; width: 60%; background-color: #FB8C00; border-radius: 5px;"></div>
733
- </div>
734
- <div style="width: 50px; text-align: left; padding-left: 10px;">2:00</div>
735
- </div>
736
- </div>
737
- """, unsafe_allow_html=True)
738
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
 
740
- def render_reports_and_analytics():
741
- """عرض واجهة التقارير والتحليلات"""
742
- st.markdown("<h1 class='app-title'>التقارير والتحليلات</h1>", unsafe_allow_html=True)
 
743
 
 
744
  st.markdown("""
745
- <div class="section-card">
746
- <p>استخدم هذه الوحدة لإنشاء تقارير تحليلية متقدمة عن المشاريع والمناقصات والأداء العام.
747
- يوفر النظام رؤى وتحليلات متعمقة تساعدك على فهم أداء مشاريعك وتحسين عمليات صنع القرار.</p>
 
748
  </div>
749
  """, unsafe_allow_html=True)
750
 
751
- # أنواع التقارير
752
- st.markdown("### أنواع التقارير")
753
 
754
  col1, col2, col3 = st.columns(3)
755
 
756
  with col1:
757
- st.markdown("""
758
- <div class="card">
759
- <div class="card-title">تقارير المشاريع</div>
760
- <p>تقارير تفصيلية عن حالة المشاريع وتقدمها ومؤشرات الأداء الرئيسية والمشكلات المحتملة.</p>
761
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 8px 12px; cursor: pointer; width: 100%;">
762
- إنشاء تقرير
763
- </button>
764
- </div>
765
- """, unsafe_allow_html=True)
766
 
767
  with col2:
768
- st.markdown("""
769
- <div class="card">
770
- <div class="card-title">تقارير الأداء المالي</div>
771
- <p>تحليل مالي للمشاريع يتضمن الإيرادات والتكاليف والأرباح والتدفقات النقدية والانحرافات عن الميزانية.</p>
772
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 8px 12px; cursor: pointer; width: 100%;">
773
- إنشاء تقرير
774
- </button>
775
- </div>
776
- """, unsafe_allow_html=True)
777
 
778
  with col3:
779
- st.markdown("""
780
- <div class="card">
781
- <div class="card-title">تقارير المناقصات</div>
782
- <p>تحليل شامل للمناقصات النشطة والمنتهية ونسب الفوز والمنافسين ومقارنة الأسعار.</p>
783
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 8px 12px; cursor: pointer; width: 100%;">
784
- إنشاء تقرير
785
- </button>
786
- </div>
787
- """, unsafe_allow_html=True)
788
 
789
- # لوحة البيانات
790
- st.markdown("### لوحة البيانات التنفيذية")
791
 
792
- col1, col2 = st.columns([2, 1])
 
793
 
794
- with col1:
795
- st.markdown("""
796
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px;">
797
- <h4 style="color: #1E88E5; margin-bottom: 15px;">أداء المشاريع حسب القطاع</h4>
798
- <div style="height: 250px; background-color: #fff; border-radius: 5px; display: flex; align-items: center; justify-content: center;">
799
- <img src="https://via.placeholder.com/600x200?text=مخطط+أداء+المشاريع" style="max-width: 100%; max-height: 100%;">
800
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
801
  </div>
802
- """, unsafe_allow_html=True)
 
803
 
804
- with col2:
805
- st.markdown("""
806
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px;">
807
- <h4 style="color: #1E88E5; margin-bottom: 15px;">المؤشرات الرئيسية</h4>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808
 
809
- <div style="margin-bottom: 15px;">
810
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
811
- <span>نسبة المشاريع المتأخرة</span>
812
- <span style="color: #F44336; font-weight: bold;">15%</span>
813
- </div>
814
- <div style="height: 8px; background-color: #e0e0e0; border-radius: 5px;">
815
- <div style="height: 100%; width: 15%; background-color: #F44336; border-radius: 5px;"></div>
816
- </div>
817
- </div>
818
 
819
- <div style="margin-bottom: 15px;">
820
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
821
- <span>متوسط هامش الربح</span>
822
- <span style="color: #4CAF50; font-weight: bold;">22%</span>
823
  </div>
824
- <div style="height: 8px; background-color: #e0e0e0; border-radius: 5px;">
825
- <div style="height: 100%; width: 22%; background-color: #4CAF50; border-radius: 5px;"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
826
  </div>
827
  </div>
 
828
 
829
- <div style="margin-bottom: 15px;">
830
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
831
- <span>معدل نجاح المناقصات</span>
832
- <span style="color: #2196F3; font-weight: bold;">35%</span>
833
- </div>
834
- <div style="height: 8px; background-color: #e0e0e0; border-radius: 5px;">
835
- <div style="height: 100%; width: 35%; background-color: #2196F3; border-radius: 5px;"></div>
836
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  </div>
838
  </div>
839
- """, unsafe_allow_html=True)
840
-
841
- # تقارير الأداء
842
- st.markdown("### تقارير الأداء الأخيرة")
843
-
844
- st.markdown("""
845
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; margin-bottom: 20px;">
846
- <h4 style="color: #1E88E5; margin-bottom: 15px;">التقرير الشهري لمشاريع الربع الأول 2025</h4>
847
- <p>تقرير شامل يوضح أداء جميع المشاريع النشطة خلال الربع الأول من عام 2025، بما في ذلك تحليل التكاليف والجدول الزمني والمخاطر.</p>
848
- <div style="display: flex; justify-content: space-between; margin-top: 10px;">
849
- <div>
850
- <span style="color: #666;">تاريخ الإنشاء: </span>
851
- <span>15 مارس 2025</span>
852
  </div>
853
- <div>
854
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
855
- <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل</button>
856
  </div>
857
  </div>
858
- </div>
859
 
860
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; margin-bottom: 20px;">
861
- <h4 style="color: #1E88E5; margin-bottom: 15px;">تحليل أداء المناقصات 2024-2025</h4>
862
- <p>تحليل مقارن لنتائج المناقصات بين عامي 2024 و 2025، يوضح التحسن في معدلات النجاح وتحليل أسباب الخسارة وفرص التحسين.</p>
863
- <div style="display: flex; justify-content: space-between; margin-top: 10px;">
864
- <div>
865
- <span style="color: #666;">تاريخ الإنشاء: </span>
866
- <span>28 فبراير 2025</span>
 
 
 
 
 
 
867
  </div>
868
- <div>
869
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
870
- <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل</button>
871
  </div>
872
  </div>
873
- </div>
874
-
875
- <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px;">
876
- <h4 style="color: #1E88E5; margin-bottom: 15px;">تقرير المخاطر المالية للمشاريع الجارية</h4>
877
- <p>تقرير تفصيلي حول المخاطر المالية للمشاريع الجارية، بما في ذلك تحليل التدفقات النقدية والمستحقات المتأخرة والمطالبات المحتملة.</p>
878
- <div style="display: flex; justify-content: space-between; margin-top: 10px;">
879
- <div>
880
- <span style="color: #666;">تاريخ الإنشاء: </span>
881
- <span>10 فبراير 2025</span>
882
  </div>
883
- <div>
884
- <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
885
- <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل</button>
886
  </div>
887
  </div>
888
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889
  """, unsafe_allow_html=True)
890
 
891
-
892
- def render_ai_assistant():
893
- """عرض واجهة المساعد الذكي باستخدام المكون الجديد"""
894
- try:
895
- from modules.ai_assistant.assistant_app import AssistantApp
 
 
 
 
 
 
 
 
 
 
 
 
 
 
896
 
897
- # عرض العنوان والوصف
898
- st.markdown("<h1 class='app-title'>المساعد الذكي</h1>", unsafe_allow_html=True)
 
 
 
 
 
899
 
900
- st.markdown("""
901
- <div class="section-card">
902
- <p>المساعد الذكي هو واجهة تفاعلية مدعومة بتقنيات الذكاء الاصطناعي لمساعدتك في جميع أنشطة إدارة المشاريع والعقود.
903
- يمكنك طرح أسئلة بلغتك الطبيعية والحصول على إجابات فورية، أو طلب مساعدة في مهام محددة مثل تحليل بنود العقد أو تقدير التكاليف.</p>
904
- </div>
905
- """, unsafe_allow_html=True)
 
906
 
907
- # استدعاء المساعد الذكي الجديد
908
- ai_assistant = AssistantApp()
909
- ai_assistant.render()
910
 
911
- except Exception as e:
912
- st.error(f"حدث خطأ في تحميل المساعد الذكي: {str(e)}")
913
- st.markdown("""
914
- <div class="error-card">
915
- <h3>😞 عذراً، واجهنا مشكلة في تحميل المساعد الذكي</h3>
916
- <p>يرجى المحاولة مرة أخرى لاحقاً أو التواصل مع فريق الدعم الفني إذا استمرت المشكلة.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  </div>
918
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
920
 
921
- # تشغيل التطبيق عند تنفيذ الملف مباشرة
922
  if __name__ == "__main__":
923
- main()
 
 
 
 
 
 
 
 
 
1
  import os
2
  import sys
3
+ from datetime import datetime
4
+ import locale
5
  import streamlit as st
6
+ from PIL import Image, ImageDraw, ImageFont
7
+ import io
8
 
9
+ # إضافة مسار المشروع إلى مسار النظام
10
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
11
+
12
+ # تعيين اللغة العربية للتاريخ
13
+ try:
14
+ locale.setlocale(locale.LC_TIME, 'ar_SA.UTF-8')
15
+ except:
16
  try:
17
+ locale.setlocale(locale.LC_TIME, 'ar_SA')
18
+ except:
19
+ pass # استخدام اللغة الافتراضية إذا لم تكن العربية متاحة
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ # تكوين الصفحة
22
+ st.set_page_config(
23
+ page_title="نظام تحليل المناقصات",
24
+ page_icon="📊",
25
+ layout="wide",
26
+ initial_sidebar_state="expanded"
27
+ )
28
 
29
+ # إنشاء مجلدات الصور إذا لم تكن موجودة
30
+ def ensure_directories():
31
+ directories = [
32
+ os.path.join(os.path.dirname(__file__), "static"),
33
+ os.path.join(os.path.dirname(__file__), "static/css"),
34
+ os.path.join(os.path.dirname(__file__), "static/js"),
35
+ os.path.join(os.path.dirname(__file__), "static/images"),
 
 
 
 
36
  ]
37
 
38
+ for directory in directories:
39
+ if not os.path.exists(directory):
40
+ try:
41
+ os.makedirs(directory, exist_ok=True)
42
+ st.sidebar.success(f"تم إنشاء المجلد: {directory}")
43
+ except Exception as e:
44
+ st.sidebar.error(f"خطأ في إنشاء المجلد {directory}: {str(e)}")
45
+
46
+ # إنشاء ملف الشعار إذا لم يكن موجوداً
47
+ def create_default_logo():
48
+ images_dir = os.path.join(os.path.dirname(__file__), "static/images")
49
+ logo_path = os.path.join(images_dir, "logo.png")
50
 
51
+ if not os.path.exists(logo_path):
52
+ try:
53
+ # إنشاء صورة بسيطة للشعار
54
+ img = Image.new('RGBA', (200, 200), color=(255, 255, 255, 0))
55
+ d = ImageDraw.Draw(img)
56
+
57
+ # رسم دائرة زرقاء
58
+ d.ellipse((40, 40, 160, 160), fill=(37, 99, 235))
59
+
60
+ # إضافة حرف "م" باللون الأبيض
61
+ try:
62
+ # محاولة استخدام خط مناسب للعربية إذا كان متاحاً
63
+ font = ImageFont.truetype("Arial", 80)
64
+ except:
65
+ # استخدام الخط الافتراضي إذا لم يكن متاحاً
66
+ font = ImageFont.load_default()
67
+
68
+ d.text((100, 100), "م", fill=(255, 255, 255), font=font, anchor="mm")
69
+
70
+ # حفظ الصورة
71
+ img.save(logo_path)
72
+ st.sidebar.success(f"تم إنشاء شعار افتراضي: {logo_path}")
73
+ except Exception as e:
74
+ st.sidebar.error(f"خطأ في إنشاء الشعار الافتراضي: {str(e)}")
75
 
76
+ # تضمين ملفات CSS
77
+ def load_css():
78
+ css_file = os.path.join(os.path.dirname(__file__), "static/css/unified_design_system.css")
79
+ if os.path.exists(css_file):
80
+ with open(css_file, "r", encoding="utf-8") as f:
81
+ st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
82
+ else:
83
+ st.warning(f"ملف CSS غير موجود: {css_file}")
84
+ # إنشاء ملف CSS افتراضي
85
+ create_default_css()
 
86
 
87
+ # إنشاء ملف CSS افتراضي إذا لم يكن موجوداً
88
+ def create_default_css():
89
+ css_dir = os.path.join(os.path.dirname(__file__), "static/css")
90
+ css_file = os.path.join(css_dir, "unified_design_system.css")
91
+
92
+ if not os.path.exists(css_file):
93
+ try:
94
+ # ملف CSS أساسي
95
+ basic_css = """
96
+ /* نظام تصميم موحد أساسي */
97
+ @import url('https://fonts.googleapis.com/css2?family=Almarai:wght@300;400;700;800&display=swap');
98
+
99
+ :root {
100
+ --primary-color: #2563eb;
101
+ --primary-light: #dbeafe;
102
+ --primary-dark: #1e40af;
103
+ --text-dark: #1e293b;
104
+ --text-medium: #475569;
105
+ --background-light: #f8fafc;
106
+ --background-white: #ffffff;
107
+ --border-color: #e2e8f0;
108
+ --success-color: #22c55e;
109
+ --warning-color: #f59e0b;
110
+ --danger-color: #ef4444;
111
+ --info-color: #3b82f6;
112
+ }
113
+
114
+ body {
115
+ font-family: 'Almarai', sans-serif;
116
+ color: var(--text-dark);
117
+ background-color: var(--background-light);
118
+ direction: rtl;
119
+ text-align: right;
120
+ }
121
+
122
+ .stApp {
123
+ direction: rtl;
124
+ }
125
+ """
126
+
127
+ with open(css_file, "w", encoding="utf-8") as f:
128
+ f.write(basic_css)
129
+
130
+ st.sidebar.success(f"تم إنشاء ملف CSS افتراضي: {css_file}")
131
+ except Exception as e:
132
+ st.sidebar.error(f"خطأ في إنشاء ملف CSS الافتراضي: {str(e)}")
133
 
134
+ # إضافة Font Awesome
135
+ def load_font_awesome():
136
+ st.markdown("""
137
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
138
+ """, unsafe_allow_html=True)
139
 
140
+ # دالة للحصول على أيقونة مناسبة لكل عنصر قائمة
141
+ def get_icon(menu_item):
142
+ icons = {
143
+ "الرئيسية": "home",
144
+ "المناقصات": "file-contract",
145
+ "التحليلات": "chart-line",
146
+ "التقارير": "file-alt",
147
+ "المساعد الذكي": "robot",
148
+ "استخراج المستندات": "file-import",
149
+ "الإعدادات": "cog"
150
  }
151
+ return icons.get(menu_item, "circle")
152
+
153
+ # دالة عرض لوحة التحكم
154
+ def show_dashboard():
155
+ # ترويسة الصفحة
156
+ st.markdown("<h1 class='main-title'>لوحة التحكم</h1>", unsafe_allow_html=True)
157
 
158
+ # إحصائيات لوحة التحكم
159
+ col1, col2, col3, col4 = st.columns(4)
 
 
160
 
161
+ with col1:
162
+ st.markdown("""
163
+ <div class="stat-card">
164
+ <div class="stat-icon"><i class="fas fa-file-contract"></i></div>
165
+ <div class="stat-value">156</div>
166
+ <div class="stat-label">إجمالي المناقصات</div>
167
+ <div class="stat-change positive"><i class="fas fa-arrow-up"></i> 12% منذ الشهر الماضي</div>
168
+ </div>
169
+ """, unsafe_allow_html=True)
 
 
 
170
 
171
+ with col2:
172
+ st.markdown("""
173
+ <div class="stat-card">
174
+ <div class="stat-icon"><i class="fas fa-check-circle"></i></div>
175
+ <div class="stat-value">89</div>
176
+ <div class="stat-label">مناقصات مكتملة</div>
177
+ <div class="stat-change positive"><i class="fas fa-arrow-up"></i> 8% منذ الشهر الماضي</div>
178
+ </div>
179
+ """, unsafe_allow_html=True)
180
 
181
+ with col3:
182
+ st.markdown("""
183
+ <div class="stat-card">
184
+ <div class="stat-icon"><i class="fas fa-spinner"></i></div>
185
+ <div class="stat-value">42</div>
186
+ <div class="stat-label">قيد التحليل</div>
187
+ <div class="stat-change negative"><i class="fas fa-arrow-down"></i> 5% منذ الشهر الماضي</div>
188
+ </div>
189
+ """, unsafe_allow_html=True)
190
 
191
+ with col4:
192
+ st.markdown("""
193
+ <div class="stat-card">
194
+ <div class="stat-icon"><i class="fas fa-money-bill-wave"></i></div>
195
+ <div class="stat-value">25.4M</div>
196
+ <div class="stat-label">إجمالي القيمة (ريال)</div>
197
+ <div class="stat-change positive"><i class="fas fa-arrow-up"></i> 15% منذ الشهر الماضي</div>
198
+ </div>
199
+ """, unsafe_allow_html=True)
 
200
 
201
+ # المناقصات الأخيرة
202
+ st.markdown("<h2 class='module-title'>المناقصات الأخيرة</h2>", unsafe_allow_html=True)
 
 
203
 
204
+ # بيانات المناقصات
205
+ data = {
206
+ "رقم": [1, 2, 3, 4, 5],
207
+ "اسم المناقصة": [
208
+ "مشروع تطوير البنية التحتية",
209
+ "توريد معدات طبية",
210
+ "بناء مدرسة ثانوية",
211
+ "صيانة طرق",
212
+ "تطوير نظام إلكتروني"
213
+ ],
214
+ "الجهة": [
215
+ "وزارة النقل",
216
+ "وزارة الصحة",
217
+ "وزارة التعليم",
218
+ "وزارة النقل",
219
+ "وزارة الداخلية"
220
+ ],
221
+ "التاريخ": [
222
+ "2025/03/15",
223
+ "2025/03/20",
224
+ "2025/03/25",
225
+ "2025/03/28",
226
+ "2025/04/01"
227
+ ],
228
+ "الحالة": [
229
+ "مكتملة",
230
+ "قيد التحليل",
231
+ "جديدة",
232
+ "مكتملة",
233
+ "جديدة"
234
+ ]
235
  }
236
 
237
+ # تطبيق أنماط CSS على الجدول
238
+ st.markdown("""
239
+ <style>
240
+ .dataframe {
241
+ width: 100%;
242
+ border-collapse: separate;
243
+ border-spacing: 0;
244
+ margin-bottom: 1.5rem;
245
+ border-radius: var(--border-radius-lg, 0.75rem);
246
+ overflow: hidden;
247
+ box-shadow: var(--shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
248
  }
249
+ .dataframe th {
250
+ background-color: var(--primary-color, #2563eb);
251
+ color: white;
252
+ text-align: right;
253
+ padding: 0.75rem 1rem;
254
+ font-weight: 600;
255
+ border: none;
256
  }
257
+ .dataframe td {
258
+ padding: 0.75rem 1rem;
259
+ border-bottom: 1px solid var(--border-color, #e2e8f0);
260
+ text-align: right;
261
  background-color: white;
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
+ .dataframe tr:nth-child(even) td {
264
+ background-color: rgba(248, 249, 250, 0.7);
 
 
265
  }
266
+ .dataframe tr:hover td {
267
+ background-color: var(--primary-light, #dbeafe);
 
268
  }
269
+ </style>
270
+ """, unsafe_allow_html=True)
271
 
272
+ # عرض الجدول
273
+ st.dataframe(data)
 
 
274
 
275
+ # زر عرض الكل
276
+ col1, col2, col3 = st.columns([2, 2, 1])
277
+ with col3:
278
+ st.button("عرض كل المناقصات", type="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
+ # تحليلات المناقصات
281
+ st.markdown("<h2 class='module-title'>تحليلات المناقصات</h2>", unsafe_allow_html=True)
282
 
283
+ col1, col2 = st.columns(2)
 
 
 
284
 
285
+ with col1:
286
+ st.markdown("""
287
+ <div class="card">
288
+ <div class="card-header">
289
+ <h3 class="card-title">توزيع المناقصات حسب الجهة</h3>
290
+ </div>
291
+ </div>
292
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
+ # استخدام مخطط Streamlit
295
+ entity_data = {
296
+ "وزارة النقل": 30,
297
+ "وزارة الصحة": 25,
298
+ "وزارة التعليم": 20,
299
+ "وزارة الإسكان": 15,
300
+ "أخرى": 10
301
+ }
302
+ st.pie(entity_data)
 
 
 
 
 
 
 
303
 
304
  with col2:
305
+ st.markdown("""
306
+ <div class="card">
307
+ <div class="card-header">
308
+ <h3 class="card-title">توزيع المناقصات حسب الحالة</h3>
309
+ </div>
310
+ </div>
311
+ """, unsafe_allow_html=True)
312
+
313
+ # استخدام مخطط Streamlit
314
+ status_data = {
315
+ "مكتملة": 45,
316
+ "قيد التحليل": 30,
317
+ "جديدة": 20,
318
+ "ملغاة": 5
319
+ }
320
+ st.pie(status_data)
 
 
 
321
 
322
+ # الابتكارات والميزات
323
+ st.markdown("<h2 class='module-title'>الابتكارات والميزات</h2>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
324
 
325
+ col1, col2, col3 = st.columns(3)
 
326
 
327
  with col1:
328
  st.markdown("""
329
+ <div class="innovation-card">
330
+ <div class="innovation-icon"><i class="fas fa-robot"></i></div>
331
+ <h3>المساعد الذكي</h3>
332
+ <p>استخدم الذكاء الاصطناعي لتحليل المناقصات واستخراج المعلومات المهمة بدقة عالية.</p>
333
  </div>
334
  """, unsafe_allow_html=True)
335
 
336
  with col2:
337
  st.markdown("""
338
+ <div class="innovation-card">
339
+ <div class="innovation-icon"><i class="fas fa-file-import"></i></div>
340
+ <h3>استخراج المستندات</h3>
341
+ <p>استخرج البيانات من مستندات المناقصات بتنسيقات مختلفة (PDF، Word، Excel) بشكل آلي.</p>
342
  </div>
343
  """, unsafe_allow_html=True)
344
 
345
  with col3:
346
  st.markdown("""
347
+ <div class="innovation-card">
348
+ <div class="innovation-icon"><i class="fas fa-chart-line"></i></div>
349
+ <h3>تحليلات متقدمة</h3>
350
+ <p>احصل على تحليلات متقدمة ورؤى قيمة حول المناقصات باستخدام تقنيات تحليل البيانات.</p>
351
  </div>
352
  """, unsafe_allow_html=True)
353
 
354
+ # تذييل الصفحة
355
+ st.markdown("""
356
+ <footer class="footer">
357
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
358
+ </footer>
359
+ """, unsafe_allow_html=True)
360
+
361
+ # دالة عرض صفحة المناقصات
362
+ def show_tenders():
363
+ # ترويسة الصفحة
364
+ st.markdown("<h1 class='main-title'>المناقصات</h1>", unsafe_allow_html=True)
365
 
366
+ # أزرار الإجراءات
367
+ col1, col2, col3 = st.columns([1, 1, 2])
368
 
369
  with col1:
370
+ st.button("إضافة مناقصة جديدة", type="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
 
372
  with col2:
373
+ st.button("استيراد من ملف", type="secondary")
374
+
375
+ with col3:
376
+ st.text_input("بحث في المناقصات", placeholder="ادخل كلمات البحث هنا...")
377
+
378
+ # تبويبات المناقصات
379
+ tabs = st.tabs(["جميع المناقصات", "المناقصات الجديدة", "قيد التحليل", "مكتملة", "ملغاة"])
380
+
381
+ with tabs[0]:
382
+ # بيانات المناقصات
383
+ data = {
384
+ "رقم": [1, 2, 3, 4, 5, 6, 7, 8],
385
+ "اسم المناقصة": [
386
+ "مشروع تطوير البنية التحتية",
387
+ "توريد معدات طبية",
388
+ "بناء مدرسة ثانوية",
389
+ "صيانة طرق",
390
+ "تطوير نظام إلكتروني",
391
+ "توريد أجهزة حاسب",
392
+ "إنشاء مركز بيانات",
393
+ "تطوير شبكة اتصالات"
394
+ ],
395
+ "الجهة": [
396
+ "وزارة النقل",
397
+ "وزارة الصحة",
398
+ "وزارة التعليم",
399
+ "وزارة النقل",
400
+ "وزارة الداخلية",
401
+ "وزارة التعليم",
402
+ "وزارة الاتصالات",
403
+ "وزارة الاتصالات"
404
+ ],
405
+ "التاريخ": [
406
+ "2025/03/15",
407
+ "2025/03/20",
408
+ "2025/03/25",
409
+ "2025/03/28",
410
+ "2025/04/01",
411
+ "2025/04/05",
412
+ "2025/04/10",
413
+ "2025/04/15"
414
+ ],
415
+ "القيمة (مليون ريال)": [
416
+ 15.5,
417
+ 8.2,
418
+ 12.0,
419
+ 5.7,
420
+ 3.2,
421
+ 2.5,
422
+ 20.0,
423
+ 18.5
424
+ ],
425
+ "الحالة": [
426
+ "مكتملة",
427
+ "قيد التحليل",
428
+ "جديدة",
429
+ "مكتملة",
430
+ "جديدة",
431
+ "قيد التحليل",
432
+ "جديدة",
433
+ "ملغاة"
434
+ ]
435
+ }
436
 
437
+ # عرض الجدول
438
+ st.dataframe(data)
439
+
440
+ with tabs[1]:
441
+ # تصفية البيانات للمناقصات الجديدة فقط
442
+ new_tenders = {
443
+ "رقم": [],
444
+ "اسم المناقصة": [],
445
+ "الجهة": [],
446
+ "التاريخ": [],
447
+ "القيمة (مليون ريال)": [],
448
+ "الحالة": []
449
+ }
 
 
 
 
 
 
 
 
 
 
450
 
451
+ for i, status in enumerate(data["الحالة"]):
452
+ if status == "جديدة":
453
+ new_tenders["رقم"].append(data["رقم"][i])
454
+ new_tenders["اسم المناقصة"].append(data["اسم المناقصة"][i])
455
+ new_tenders["الجهة"].append(data["الجهة"][i])
456
+ new_tenders["التاريخ"].append(data["التاريخ"][i])
457
+ new_tenders["القيمة (مليون ريال)"].append(data["القيمة (مليون ريال)"][i])
458
+ new_tenders["الحالة"].append(data["الحالة"][i])
459
 
460
+ # عرض الجدول
461
+ st.dataframe(new_tenders)
462
+
463
+ with tabs[2]:
464
+ # تصفية البيانات للمناقصات قيد التحليل فقط
465
+ in_progress_tenders = {
466
+ "رقم": [],
467
+ "اسم المناقصة": [],
468
+ "الجهة": [],
469
+ "التاريخ": [],
470
+ "القيمة (مليون ريال)": [],
471
+ "الحالة": []
472
+ }
473
 
474
+ for i, status in enumerate(data["الحالة"]):
475
+ if status == "قيد التحليل":
476
+ in_progress_tenders["رقم"].append(data["رقم"][i])
477
+ in_progress_tenders["اسم المناقصة"].append(data["اسم المناقصة"][i])
478
+ in_progress_tenders["الجهة"].append(data["الجهة"][i])
479
+ in_progress_tenders["التاريخ"].append(data["التاريخ"][i])
480
+ in_progress_tenders["القيمة (مليون ريال)"].append(data["القيمة (مليون ريال)"][i])
481
+ in_progress_tenders["الحالة"].append(data["الحالة"][i])
482
 
483
+ # عرض الجدول
484
+ st.dataframe(in_progress_tenders)
 
 
 
485
 
486
+ with tabs[3]:
487
+ # تصفية البيانات للمناقصات المكتملة فقط
488
+ completed_tenders = {
489
+ "رقم": [],
490
+ "اسم المناقصة": [],
491
+ "الجهة": [],
492
+ "التاريخ": [],
493
+ "القيمة (مليون ريال)": [],
494
+ "الحالة": []
495
+ }
496
+
497
+ for i, status in enumerate(data["الحالة"]):
498
+ if status == "مكتملة":
499
+ completed_tenders["رقم"].append(data["رقم"][i])
500
+ completed_tenders["اسم المناقصة"].append(data["اسم المناقصة"][i])
501
+ completed_tenders["الجهة"].append(data["الجهة"][i])
502
+ completed_tenders["التاريخ"].append(data["التاريخ"][i])
503
+ completed_tenders["القيمة (مليون ريال)"].append(data["القيمة (مليون ريال)"][i])
504
+ completed_tenders["الحالة"].append(data["الحالة"][i])
505
+
506
+ # عرض الجدول
507
+ st.dataframe(completed_tenders)
508
 
509
+ with tabs[4]:
510
+ # تصفية البيانات للمناقصات الملغاة فقط
511
+ cancelled_tenders = {
512
+ "رقم": [],
513
+ "اسم المناقصة": [],
514
+ "الجهة": [],
515
+ "التاريخ": [],
516
+ "القيمة (مليون ريال)": [],
517
+ "الحالة": []
518
+ }
519
+
520
+ for i, status in enumerate(data["الحالة"]):
521
+ if status == "ملغاة":
522
+ cancelled_tenders["رقم"].append(data["رقم"][i])
523
+ cancelled_tenders["اسم المناقصة"].append(data["اسم المناقصة"][i])
524
+ cancelled_tenders["الجهة"].append(data["الجهة"][i])
525
+ cancelled_tenders["التاريخ"].append(data["التاريخ"][i])
526
+ cancelled_tenders["القيمة (مليون ريال)"].append(data["القيمة (مليون ريال)"][i])
527
+ cancelled_tenders["الحالة"].append(data["الحالة"][i])
528
+
529
+ # عرض الجدول
530
+ st.dataframe(cancelled_tenders)
531
 
532
+ # تذييل الصفحة
533
  st.markdown("""
534
+ <footer class="footer">
535
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
536
+ </footer>
 
537
  """, unsafe_allow_html=True)
538
 
539
+ # دالة عرض صفحة المساعد الذكي
540
+ def show_ai_assistant():
541
+ # ترويسة الصفحة
542
+ st.markdown("<h1 class='main-title'>المساعد الذكي</h1>", unsafe_allow_html=True)
543
 
544
+ # وصف المساعد الذكي
545
  st.markdown("""
546
+ <div class="card mb-4">
547
+ <div class="card-body">
548
+ <p>المساعد الذكي يساعدك في تحليل المناقصات واستخراج المعلومات المهمة منها باستخدام تقنيات الذكاء الاصطناعي المتقدمة.</p>
549
+ </div>
550
  </div>
551
  """, unsafe_allow_html=True)
552
 
553
+ # واجهة المساعد الذكي
554
+ st.markdown("<h2 class='module-title'>تحليل المناقصة</h2>", unsafe_allow_html=True)
555
 
556
+ # خيارات التحليل
557
+ col1, col2 = st.columns(2)
558
 
559
  with col1:
560
+ analysis_type = st.selectbox(
561
+ "نوع التحليل",
562
+ ["تحليل شامل", "استخراج الشروط", "تحليل الأسعار", "تقييم المخاطر"]
563
+ )
 
 
 
 
 
564
 
565
  with col2:
566
+ model = st.selectbox(
567
+ "نموذج الذكاء الاصطناعي",
568
+ ["GPT-4o", "Claude 3 Opus", "Claude 3 Sonnet"]
569
+ )
 
 
 
 
 
570
 
571
+ # تحميل ملف المناقصة
572
+ uploaded_file = st.file_uploader("تحميل ملف المناقصة", type=["pdf", "docx", "txt"])
573
+
574
+ # أو إدخال نص المناقصة
575
+ st.markdown("<p>أو</p>", unsafe_allow_html=True)
576
+ tender_text = st.text_area("نص المناقصة", height=200)
577
+
578
+ # زر التحليل
579
+ if st.button("تحليل المناقصة", type="primary"):
580
+ if uploaded_file is not None or tender_text:
581
+ # عرض شريط التقدم
582
+ progress_bar = st.progress(0)
583
+ for i in range(100):
584
+ # تحديث شريط التقدم
585
+ progress_bar.progress(i + 1)
586
+ if i < 30:
587
+ st.caption("جاري قراءة المناقصة...")
588
+ elif i < 60:
589
+ st.caption("جاري تحليل المحتوى...")
590
+ elif i < 90:
591
+ st.caption("جاري إعداد التقرير...")
592
+ else:
593
+ st.caption("اكتمل التحليل!")
594
+
595
+ # عرض نتائج التحليل
596
+ st.success("تم تحليل المناقصة بنجاح!")
597
+
598
+ st.markdown("""
599
+ <div class="card">
600
+ <div class="card-header">
601
+ <h3 class="card-title">نتائج التحليل</h3>
602
+ </div>
603
+ <div class="card-body">
604
+ <h4>ملخص المناقصة</h4>
605
+ <p>هذه مناقصة لمشروع تطوير البنية التحتية في مدينة الرياض. تشمل المناقصة أعمال إنشاء طرق وجسور وأنفاق.</p>
606
+
607
+ <h4>الشروط الرئيسية</h4>
608
+ <ul>
609
+ <li>مدة التنفيذ: 24 شهراً</li>
610
+ <li>قيمة الضمان: 5% من قيمة العقد</li>
611
+ <li>خبرة سابقة: 10 سنوات في مشاريع مماثلة</li>
612
+ <li>عدد المهندسين المطلوبين: 15 مهندساً</li>
613
+ </ul>
614
+
615
+ <h4>المخاطر المحتملة</h4>
616
+ <ul>
617
+ <li>مخاطر عالية: تأخر التسليم بسبب ضيق الجدول الزمني</li>
618
+ <li>مخاطر متوسطة: ارتفاع تكاليف المواد</li>
619
+ <li>مخاطر منخفضة: مشاكل في التصاريح</li>
620
+ </ul>
621
+ </div>
622
+ </div>
623
+ """, unsafe_allow_html=True)
624
+ else:
625
+ st.error("يرجى تحميل ملف المناقصة أو إدخال نص المناقصة")
626
 
627
+ # تذييل الصفحة
628
+ st.markdown("""
629
+ <footer class="footer">
630
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
631
+ </footer>
632
+ """, unsafe_allow_html=True)
633
+
634
+ # دالة عرض صفحة استخراج المستندات
635
+ def show_document_extraction():
636
+ # ترويسة الصفحة
637
+ st.markdown("<h1 class='main-title'>استخراج المستندات</h1>", unsafe_allow_html=True)
638
 
639
+ # وصف استخراج المستندات
640
  st.markdown("""
641
+ <div class="card mb-4">
642
+ <div class="card-body">
643
+ <p>استخرج البيانات من مستندات المناقصات بتنسيقات مختلفة (PDF، Word، Excel) بشكل آلي.</p>
644
+ </div>
645
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  """, unsafe_allow_html=True)
647
 
648
+ # واجهة استخراج المستندات
649
+ st.markdown("<h2 class='module-title'>استخراج البيانات</h2>", unsafe_allow_html=True)
650
 
651
+ # خيارات الاستخراج
652
  col1, col2 = st.columns(2)
653
 
654
  with col1:
655
+ extraction_type = st.selectbox(
656
+ "نوع الاستخراج",
657
+ ["استخراج جميع البيانات", "استخراج الجداول فقط", "استخراج النصوص فقط"]
658
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
 
660
  with col2:
661
+ output_format = st.selectbox(
662
+ "تنسيق الإخراج",
663
+ ["Excel", "CSV", "JSON", "Text"]
664
+ )
665
+
666
+ # تحميل ملف المستند
667
+ uploaded_files = st.file_uploader("تحميل المستندات", type=["pdf", "docx", "xlsx", "xls"], accept_multiple_files=True)
668
+
669
+ # زر الاستخراج
670
+ if st.button("استخراج البيانات", type="primary"):
671
+ if uploaded_files:
672
+ # عرض شريط التقدم
673
+ progress_bar = st.progress(0)
674
+ for i in range(100):
675
+ # تحديث شريط التقدم
676
+ progress_bar.progress(i + 1)
677
+ if i < 40:
678
+ st.caption("جاري قراءة المستندات...")
679
+ elif i < 80:
680
+ st.caption("جاري استخراج البيانات...")
681
+ else:
682
+ st.caption("جاري إعداد النتائج...")
683
 
684
+ # عرض نتائج الاستخراج
685
+ st.success(f"تم استخراج البيانات من {len(uploaded_files)} مستند بنجاح!")
 
 
 
 
 
 
686
 
687
+ # عرض البيانات المستخرجة
688
+ st.markdown("<h3>البيانات المستخرجة</h3>", unsafe_allow_html=True)
 
 
 
 
 
689
 
690
+ # بيانات عينة
691
+ data = {
692
+ "رقم المناقصة": ["T-2025-001", "T-2025-002", "T-2025-003"],
693
+ "اسم المناقصة": [
694
+ "مشروع تطوير البنية التحتية",
695
+ "توريد معدات طبية",
696
+ "بناء مدرسة ثانوية"
697
+ ],
698
+ "الجهة": [
699
+ "وزارة النقل",
700
+ "وزارة الصحة",
701
+ "وزارة التعليم"
702
+ ],
703
+ "تاريخ الإعلان": [
704
+ "2025/03/01",
705
+ "2025/03/05",
706
+ "2025/03/10"
707
+ ],
708
+ "تاريخ الإغلاق": [
709
+ "2025/04/01",
710
+ "2025/04/05",
711
+ "2025/04/10"
712
+ ],
713
+ "القيمة التقديرية": [
714
+ "15,000,000 ريال",
715
+ "8,200,000 ريال",
716
+ "12,000,000 ريال"
717
+ ]
718
+ }
719
+
720
+ st.dataframe(data)
721
+
722
+ # زر تنزيل النتائج
723
+ st.download_button(
724
+ label="تنزيل النتائج",
725
+ data="بيانات عينة للتنزيل",
726
+ file_name=f"extracted_data.{output_format.lower()}",
727
+ mime="text/plain"
728
+ )
729
+ else:
730
+ st.error("يرجى تحميل مستند واحد على الأقل")
731
+
732
+ # تذييل الصفحة
733
+ st.markdown("""
734
+ <footer class="footer">
735
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
736
+ </footer>
737
+ """, unsafe_allow_html=True)
738
 
739
+ # دالة عرض صفحة التحليلات
740
+ def show_analysis():
741
+ # ترويسة الصفحة
742
+ st.markdown("<h1 class='main-title'>التحليلات</h1>", unsafe_allow_html=True)
743
 
744
+ # وصف التحليلات
745
  st.markdown("""
746
+ <div class="card mb-4">
747
+ <div class="card-body">
748
+ <p>احصل على تحليلات متقدمة ورؤى قيمة حول المناقصات باستخدام تقنيات تحليل البيانات.</p>
749
+ </div>
750
  </div>
751
  """, unsafe_allow_html=True)
752
 
753
+ # فلاتر التحليلات
754
+ st.markdown("<h2 class='module-title'>فلاتر التحليلات</h2>", unsafe_allow_html=True)
755
 
756
  col1, col2, col3 = st.columns(3)
757
 
758
  with col1:
759
+ period = st.selectbox(
760
+ "الفترة الزمنية",
761
+ ["الربع الحالي", "السنة الحالية", "العام الماضي", "آخر 3 سنوات", "الكل"]
762
+ )
 
 
 
 
 
763
 
764
  with col2:
765
+ entity = st.multiselect(
766
+ "الجهة",
767
+ ["وزارة النقل", "وزارة الصحة", "وزارة التعليم", "وزارة الإسكان", "وزارة الداخلية", "وزارة الاتصالات"],
768
+ default=["وزارة النقل", "وزارة الصحة", "وزارة التعليم"]
769
+ )
 
 
 
 
770
 
771
  with col3:
772
+ status = st.multiselect(
773
+ "الحالة",
774
+ ["مكتملة", "قيد التحليل", "جديدة", "ملغاة"],
775
+ default=["مكتملة", "قيد التحليل", "جديدة"]
776
+ )
 
 
 
 
777
 
778
+ # زر تطبيق الفلاتر
779
+ st.button("تطبيق الفلاتر", type="primary")
780
 
781
+ # عرض التحليلات
782
+ st.markdown("<h2 class='module-title'>تحليلات المناقصات</h2>", unsafe_allow_html=True)
783
 
784
+ # تحليلات الاتجاهات
785
+ st.markdown("<h3>اتجاهات المناقصات</h3>", unsafe_allow_html=True)
786
+
787
+ # بيانات عينة للرسم البياني
788
+ chart_data = {
789
+ "الشهر": ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو"],
790
+ "عدد المناقصات": [12, 19, 15, 21, 25, 18]
791
+ }
792
+
793
+ # عرض الرسم البياني
794
+ st.line_chart(chart_data, x="الشهر", y="عدد المناقصات")
795
+
796
+ # توزيع المناقصات حسب الجهة
797
+ st.markdown("<h3>توزيع المناقصات حسب الجهة</h3>", unsafe_allow_html=True)
798
+
799
+ # بيانات عينة للرسم البياني
800
+ entity_data = {
801
+ "وزارة النقل": 30,
802
+ "وزارة الصحة": 25,
803
+ "وزارة التعليم": 20,
804
+ "وزارة الإسكان": 15,
805
+ "وزارة الداخلية": 7,
806
+ "وزارة الاتصالات": 3
807
+ }
808
+
809
+ # عرض الرسم البياني
810
+ st.bar_chart(entity_data)
811
+
812
+ # توزيع المناقصات حسب القيمة
813
+ st.markdown("<h3>توزيع المناقصات حسب القيمة</h3>", unsafe_allow_html=True)
814
+
815
+ # بيانات عينة للرسم البياني
816
+ value_data = {
817
+ "أقل من 1 مليون": 15,
818
+ "1-5 مليون": 30,
819
+ "5-10 مليون": 25,
820
+ "10-50 مليون": 20,
821
+ "أكثر من 50 مليون": 10
822
+ }
823
+
824
+ # عرض الرسم البياني
825
+ st.bar_chart(value_data)
826
+
827
+ # تذيي�� الصفحة
828
+ st.markdown("""
829
+ <footer class="footer">
830
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
831
+ </footer>
832
+ """, unsafe_allow_html=True)
833
+
834
+ # دالة عرض صفحة التقارير
835
+ def show_reports():
836
+ # ترويسة الصفحة
837
+ st.markdown("<h1 class='main-title'>التقارير</h1>", unsafe_allow_html=True)
838
+
839
+ # وصف التقارير
840
+ st.markdown("""
841
+ <div class="card mb-4">
842
+ <div class="card-body">
843
+ <p>إنشاء وعرض تقارير مفصلة عن المناقصات والتحليلات.</p>
844
  </div>
845
+ </div>
846
+ """, unsafe_allow_html=True)
847
 
848
+ # أنواع التقارير
849
+ st.markdown("<h2 class='module-title'>أنواع التقارير</h2>", unsafe_allow_html=True)
850
+
851
+ # تبويبات التقارير
852
+ tabs = st.tabs(["التقارير الدورية", "تقارير الأداء", "تقارير المقارنة", "تقارير مخصصة"])
853
+
854
+ with tabs[0]:
855
+ st.markdown("<h3>التقارير الدورية</h3>", unsafe_allow_html=True)
856
+
857
+ col1, col2 = st.columns(2)
858
+
859
+ with col1:
860
+ report_period = st.selectbox(
861
+ "الفترة",
862
+ ["يومي", "أسبوعي", "شهري", "ربع سنوي", "سنوي"]
863
+ )
864
+
865
+ with col2:
866
+ report_type = st.selectbox(
867
+ "نوع التقرير",
868
+ ["تقرير ملخص", "تقرير مفصل", "تقرير إحصائي"]
869
+ )
870
+
871
+ # زر إنشاء التقرير
872
+ if st.button("إنشاء التقرير", type="primary"):
873
+ # عرض شريط التقدم
874
+ progress_bar = st.progress(0)
875
+ for i in range(100):
876
+ # تحديث شريط التقدم
877
+ progress_bar.progress(i + 1)
878
+ if i < 50:
879
+ st.caption("جاري إعداد التقرير...")
880
+ else:
881
+ st.caption("جاري تنسيق التقرير...")
882
 
883
+ # عرض التقرير
884
+ st.success("تم إنشاء التقرير بنجاح!")
 
 
 
 
 
 
 
885
 
886
+ st.markdown("""
887
+ <div class="card">
888
+ <div class="card-header">
889
+ <h3 class="card-title">تقرير شهري - مارس 2025</h3>
890
  </div>
891
+ <div class="card-body">
892
+ <h4>ملخص التقرير</h4>
893
+ <p>هذا تقرير شهري يوضح أداء المناقصات خلال شهر مارس 2025.</p>
894
+
895
+ <h4>إحصائيات المناقصات</h4>
896
+ <ul>
897
+ <li>إجمالي المناقصات: 45</li>
898
+ <li>المناقصات الجديدة: 15</li>
899
+ <li>المناقصات المكتملة: 20</li>
900
+ <li>المناقصات قيد التحليل: 8</li>
901
+ <li>المناقصات الملغاة: 2</li>
902
+ </ul>
903
+
904
+ <h4>توزيع المناقصات حسب الجهة</h4>
905
+ <ul>
906
+ <li>وزارة النقل: 12</li>
907
+ <li>وزارة الصحة: 10</li>
908
+ <li>وزارة التعليم: 8</li>
909
+ <li>وزارة الإسكان: 7</li>
910
+ <li>أخرى: 8</li>
911
+ </ul>
912
  </div>
913
  </div>
914
+ """, unsafe_allow_html=True)
915
 
916
+ # زر تنزيل التقرير
917
+ st.download_button(
918
+ label="تنزيل التقرير (PDF)",
919
+ data="بيانات عينة للتنزيل",
920
+ file_name="monthly_report_march_2025.pdf",
921
+ mime="application/pdf"
922
+ )
923
+
924
+ with tabs[1]:
925
+ st.markdown("<h3>تقارير الأداء</h3>", unsafe_allow_html=True)
926
+
927
+ # محتوى تقارير الأداء
928
+ st.markdown("""
929
+ <p>تقارير الأداء توفر معلومات عن أداء المناقصات والمشاريع.</p>
930
+ """, unsafe_allow_html=True)
931
+
932
+ # أمثلة على تقارير الأداء
933
+ st.markdown("""
934
+ <div class="card mb-3">
935
+ <div class="card-header">
936
+ <h4 class="card-title">تقرير أداء المناقصات - الربع الأول 2025</h4>
937
+ </div>
938
+ <div class="card-body">
939
+ <p>تقرير يوضح أداء المناقصات خلال الربع الأول من عام 2025.</p>
940
+ <button class="btn btn-primary btn-sm">عرض التقرير</button>
941
  </div>
942
  </div>
943
+
944
+ <div class="card mb-3">
945
+ <div class="card-header">
946
+ <h4 class="card-title">تقرير أداء المشاريع - 2024</h4>
 
 
 
 
 
 
 
 
 
947
  </div>
948
+ <div class="card-body">
949
+ <p>تقرير يوضح أداء المشاريع خلال عام 2024.</p>
950
+ <button class="btn btn-primary btn-sm">عرض التقرير</button>
951
  </div>
952
  </div>
953
+ """, unsafe_allow_html=True)
954
 
955
+ with tabs[2]:
956
+ st.markdown("<h3>تقارير المقارنة</h3>", unsafe_allow_html=True)
957
+
958
+ # محتوى تقارير المقارنة
959
+ st.markdown("""
960
+ <p>تقارير المقارنة توفر مقارنة بين المناقصات والمشاريع المختلفة.</p>
961
+ """, unsafe_allow_html=True)
962
+
963
+ # أمثلة على تقارير المقارنة
964
+ st.markdown("""
965
+ <div class="card mb-3">
966
+ <div class="card-header">
967
+ <h4 class="card-title">تقرير مقارنة المناقصات حسب الجهة</h4>
968
  </div>
969
+ <div class="card-body">
970
+ <p>تقرير يقارن بين المناقصات المختلفة حسب الجهة.</p>
971
+ <button class="btn btn-primary btn-sm">عرض التقرير</button>
972
  </div>
973
  </div>
974
+
975
+ <div class="card mb-3">
976
+ <div class="card-header">
977
+ <h4 class="card-title">تقرير مقارنة المناقصات حسب القيمة</h4>
 
 
 
 
 
978
  </div>
979
+ <div class="card-body">
980
+ <p>تقرير يقارن بين المناقصات المختلفة حسب القيمة.</p>
981
+ <button class="btn btn-primary btn-sm">عرض التقرير</button>
982
  </div>
983
  </div>
984
+ """, unsafe_allow_html=True)
985
+
986
+ with tabs[3]:
987
+ st.markdown("<h3>تقارير مخصصة</h3>", unsafe_allow_html=True)
988
+
989
+ # محتوى التقارير المخصصة
990
+ st.markdown("""
991
+ <p>إنشاء تقارير مخصصة حسب احتياجاتك.</p>
992
+ """, unsafe_allow_html=True)
993
+
994
+ # نموذج إنشاء تقرير مخصص
995
+ st.markdown("<h4>إنشاء تقرير مخصص</h4>", unsafe_allow_html=True)
996
+
997
+ col1, col2 = st.columns(2)
998
+
999
+ with col1:
1000
+ custom_report_name = st.text_input("اسم التقرير", "تقرير مخصص")
1001
+ custom_report_period = st.selectbox(
1002
+ "الفترة الزمنية",
1003
+ ["الربع الحالي", "السنة الحالية", "العام الماضي", "آخر 3 سنوات", "فترة مخصصة"]
1004
+ )
1005
+
1006
+ if custom_report_period == "فترة مخصصة":
1007
+ start_date = st.date_input("تاريخ البداية")
1008
+ end_date = st.date_input("تاريخ النهاية")
1009
+
1010
+ with col2:
1011
+ custom_report_entities = st.multiselect(
1012
+ "الجهات",
1013
+ ["وزارة النقل", "وزارة الصحة", "وزارة التعليم", "وزارة الإسكان", "وزارة الداخلية", "وزارة الاتصالات"],
1014
+ default=["وزارة النقل", "وزارة الصحة", "وزارة التعليم"]
1015
+ )
1016
+
1017
+ custom_report_sections = st.multiselect(
1018
+ "أقسام التقرير",
1019
+ ["ملخص تنفيذي", "إحصائيات المناقصات", "توزيع المناقصات حسب الجهة", "توزيع المناقصات حسب القيمة", "توزيع المناقصات حسب الحالة", "رسوم بيانية", "جداول تفصيلية"],
1020
+ default=["ملخص تنفيذي", "إحصائيات المناقصات", "رسوم بيانية"]
1021
+ )
1022
+
1023
+ # زر إنشاء التقرير المخصص
1024
+ if st.button("إنشاء التقرير المخصص", type="primary"):
1025
+ st.success("تم إنشاء التقرير المخصص بنجاح!")
1026
+
1027
+ # زر تنزيل التقرير المخصص
1028
+ st.download_button(
1029
+ label="تنزيل التقرير المخصص (PDF)",
1030
+ data="بيانات عينة للتنزيل",
1031
+ file_name="custom_report.pdf",
1032
+ mime="application/pdf"
1033
+ )
1034
+
1035
+ # تذييل الصفحة
1036
+ st.markdown("""
1037
+ <footer class="footer">
1038
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
1039
+ </footer>
1040
  """, unsafe_allow_html=True)
1041
 
1042
+ # دالة عرض صفحة الإعدادات
1043
+ def show_settings():
1044
+ # ترويسة الصفحة
1045
+ st.markdown("<h1 class='main-title'>الإعدادات</h1>", unsafe_allow_html=True)
1046
+
1047
+ # وصف الإعدادات
1048
+ st.markdown("""
1049
+ <div class="card mb-4">
1050
+ <div class="card-body">
1051
+ <p>تخصيص إعدادات النظام حسب احتياجاتك.</p>
1052
+ </div>
1053
+ </div>
1054
+ """, unsafe_allow_html=True)
1055
+
1056
+ # تبويبات الإعدادات
1057
+ tabs = st.tabs(["إعدادات عامة", "إعدادات المستخدم", "إعدادات الذكاء الاصطناعي", "إعدادات الواجهة"])
1058
+
1059
+ with tabs[0]:
1060
+ st.markdown("<h3>إعدادات عامة</h3>", unsafe_allow_html=True)
1061
 
1062
+ # إعدادات اللغة
1063
+ st.markdown("<h4>إعدادات اللغة</h4>", unsafe_allow_html=True)
1064
+ language = st.selectbox(
1065
+ "اللغة",
1066
+ ["العربية", "English"],
1067
+ index=0
1068
+ )
1069
 
1070
+ # إعدادات المنطقة الزمنية
1071
+ st.markdown("<h4>إعدادات المنطقة الزمنية</h4>", unsafe_allow_html=True)
1072
+ timezone = st.selectbox(
1073
+ "المنطقة الزمنية",
1074
+ ["توقيت الرياض (GMT+3)", "توقيت جرينتش (GMT)", "توقيت نيويورك (GMT-5)"],
1075
+ index=0
1076
+ )
1077
 
1078
+ # إعدادات التنبيهات
1079
+ st.markdown("<h4>إعدادات التنبيهات</h4>", unsafe_allow_html=True)
 
1080
 
1081
+ email_notifications = st.checkbox("تنبيهات البريد الإلكتروني", value=True)
1082
+ browser_notifications = st.checkbox("تنبيهات المتصفح", value=True)
1083
+
1084
+ if email_notifications:
1085
+ email = st.text_input("البريد الإلكتروني للتنبيهات")
1086
+
1087
+ with tabs[1]:
1088
+ st.markdown("<h3>إعدادات المستخدم</h3>", unsafe_allow_html=True)
1089
+
1090
+ # معلومات المستخدم
1091
+ st.markdown("<h4>معلومات المستخدم</h4>", unsafe_allow_html=True)
1092
+
1093
+ col1, col2 = st.columns(2)
1094
+
1095
+ with col1:
1096
+ username = st.text_input("اسم المستخدم", "admin")
1097
+ email = st.text_input("البريد الإلكتروني", "[email protected]")
1098
+
1099
+ with col2:
1100
+ full_name = st.text_input("الاسم الكامل", "مدير النظام")
1101
+ phone = st.text_input("رقم الهاتف", "+966 5XXXXXXXX")
1102
+
1103
+ # تغيير كلمة المرور
1104
+ st.markdown("<h4>تغيير كلمة المرور</h4>", unsafe_allow_html=True)
1105
+
1106
+ current_password = st.text_input("كلمة المرور الحالية", type="password")
1107
+ new_password = st.text_input("كلمة المرور الجديدة", type="password")
1108
+ confirm_password = st.text_input("تأكيد كلمة المرور الجديدة", type="password")
1109
+
1110
+ if st.button("تغيير كلمة المرور"):
1111
+ if not current_password or not new_password or not confirm_password:
1112
+ st.error("يرجى ملء جميع الحقول")
1113
+ elif new_password != confirm_password:
1114
+ st.error("كلمة المرور الجديدة وتأكيدها غير متطابقين")
1115
+ else:
1116
+ st.success("تم تغيير كلمة المرور بنجاح")
1117
+
1118
+ with tabs[2]:
1119
+ st.markdown("<h3>إعدادات الذكاء الاصطناعي</h3>", unsafe_allow_html=True)
1120
+
1121
+ # نماذج الذكاء الاصطناعي
1122
+ st.markdown("<h4>نماذج الذكاء الاصطناعي</h4>", unsafe_allow_html=True)
1123
+
1124
+ default_model = st.selectbox(
1125
+ "النموذج الافتراضي",
1126
+ ["GPT-4o", "Claude 3 Opus", "Claude 3 Sonnet"]
1127
+ )
1128
+
1129
+ # إعدادات API
1130
+ st.markdown("<h4>إعدادات API</h4>", unsafe_allow_html=True)
1131
+
1132
+ openai_api_key = st.text_input("مفتاح API لـ OpenAI", type="password")
1133
+ anthropic_api_key = st.text_input("مفتاح API لـ Anthropic", type="password")
1134
+
1135
+ # إعدادات متقدمة
1136
+ st.markdown("<h4>إعدادات متقدمة</h4>", unsafe_allow_html=True)
1137
+
1138
+ temperature = st.slider("درجة الإبداعية (Temperature)", 0.0, 1.0, 0.7)
1139
+ max_tokens = st.slider("الحد الأقصى للرموز (Max Tokens)", 100, 4000, 2000)
1140
+
1141
+ with tabs[3]:
1142
+ st.markdown("<h3>إعدادات الواجهة</h3>", unsafe_allow_html=True)
1143
+
1144
+ # السمة
1145
+ st.markdown("<h4>السمة</h4>", unsafe_allow_html=True)
1146
+
1147
+ theme = st.selectbox(
1148
+ "السمة",
1149
+ ["فاتح", "داكن", "تلقائي (حسب إعدادات النظام)"]
1150
+ )
1151
+
1152
+ # الألوان
1153
+ st.markdown("<h4>الألوان</h4>", unsafe_allow_html=True)
1154
+
1155
+ primary_color = st.color_picker("اللون الرئيسي", "#2563eb")
1156
+ secondary_color = st.color_picker("اللون الثانوي", "#10b981")
1157
+
1158
+ # حجم الخط
1159
+ st.markdown("<h4>حجم الخط</h4>", unsafe_allow_html=True)
1160
+
1161
+ font_size = st.select_slider(
1162
+ "حجم الخط",
1163
+ options=["صغير", "متوسط", "كبير", "كبير جداً"]
1164
+ )
1165
+
1166
+ # زر حفظ الإعدادات
1167
+ if st.button("حفظ الإعدادات", type="primary"):
1168
+ st.success("تم حفظ الإعدادات بنجاح")
1169
+
1170
+ # تذييل الصفحة
1171
+ st.markdown("""
1172
+ <footer class="footer">
1173
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
1174
+ </footer>
1175
+ """, unsafe_allow_html=True)
1176
+
1177
+ # التأكد من وجود المجلدات والملفات اللازمة
1178
+ ensure_directories()
1179
+ create_default_logo()
1180
+
1181
+ # تحميل CSS
1182
+ load_css()
1183
+
1184
+ # إضافة Font Awesome
1185
+ load_font_awesome()
1186
+
1187
+ # الشريط الجانبي
1188
+ with st.sidebar:
1189
+ # شعار النظام
1190
+ logo_path = os.path.join(os.path.dirname(__file__), "static/images/logo.png")
1191
+ if os.path.exists(logo_path):
1192
+ st.image(logo_path, width=150)
1193
+ else:
1194
+ st.markdown("<h3>نظام تحليل المناقصات</h3>", unsafe_allow_html=True)
1195
+
1196
+ st.markdown("<h2 class='sidebar-title'>نظام تحليل المناقصات</h2>", unsafe_allow_html=True)
1197
+
1198
+ # الحصول على التاريخ الحالي بالعربية
1199
+ now = datetime.now()
1200
+ day = now.strftime("%d")
1201
+ try:
1202
+ month = now.strftime("%B")
1203
+ except:
1204
+ month = now.strftime("%m")
1205
+ year = now.strftime("%Y")
1206
+
1207
+ # عرض التاريخ
1208
+ st.markdown(f"""
1209
+ <div class="date-box">
1210
+ <div class="date-day">{day}</div>
1211
+ <div class="date-info">
1212
+ <div class="date-month">{month}</div>
1213
+ <div class="date-year">{year}</div>
1214
  </div>
1215
+ </div>
1216
+ """, unsafe_allow_html=True)
1217
+
1218
+ st.markdown("<div class='sidebar-divider'></div>", unsafe_allow_html=True)
1219
+
1220
+ # القائمة الرئيسية
1221
+ selected = st.sidebar.radio(
1222
+ "القائمة الرئيسية",
1223
+ ["الرئيسية", "المناقصات", "التحليلات", "التقارير", "المساعد الذكي", "استخراج المستندات", "الإعدادات"],
1224
+ format_func=lambda x: f"<i class='fas fa-{get_icon(x)}'></i> {x}",
1225
+ label_visibility="collapsed"
1226
+ )
1227
+
1228
+ # معلومات الإصدار
1229
+ st.markdown("""
1230
+ <div class="sidebar-footer">
1231
+ <p>إصدار 2.0.0</p>
1232
+ </div>
1233
+ """, unsafe_allow_html=True)
1234
 
1235
+ # عرض الصفحة المناسبة بناءً على الاختيار
1236
+ if selected == "الرئيسية":
1237
+ show_dashboard()
1238
+ elif selected == "المناقصات":
1239
+ show_tenders()
1240
+ elif selected == "التحليلات":
1241
+ show_analysis()
1242
+ elif selected == "التقارير":
1243
+ show_reports()
1244
+ elif selected == "المساعد الذكي":
1245
+ show_ai_assistant()
1246
+ elif selected == "استخراج المستندات":
1247
+ show_document_extraction()
1248
+ elif selected == "الإعدادات":
1249
+ show_settings()
1250
 
1251
+ # تشغيل التطبيق
1252
  if __name__ == "__main__":
1253
+ pass
database/db_connector.py CHANGED
@@ -36,26 +36,6 @@ def get_connection():
36
  except Exception as e:
37
  print(f"خطأ في الاتصال بقاعدة البيانات: {e}")
38
 
39
- # إذا فشل الاتصال، استخدم اتصال قاعدة بيانات SQLite محلية
40
- import os
41
- import sqlite3
42
-
43
- # إنشاء مجلد البيانات إذا لم يكن موجوداً
44
- data_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data')
45
- os.makedirs(data_dir, exist_ok=True)
46
-
47
- # إنشاء اتصال قاعدة بيانات SQLite محلية
48
- db_path = os.path.join(data_dir, 'local_db.sqlite')
49
- conn = sqlite3.connect(db_path)
50
-
51
- # إعادة محاكاة سلوك اتصال PostgreSQL
52
- conn.execute = conn.cursor().execute
53
-
54
- # إضافة وظيفة وهمية للاقتطاع (commit) والإغلاق
55
- original_close = conn.close
56
- def enhanced_close():
57
- conn.commit()
58
- original_close()
59
- conn.close = enhanced_close
60
-
61
- return conn
 
36
  except Exception as e:
37
  print(f"خطأ في الاتصال بقاعدة البيانات: {e}")
38
 
39
+ # إذا فشل الاتصال، استخدم اتصال قاعدة بيانات وهمي
40
+ from utils.helpers import get_connection as get_mock_connection
41
+ return get_mock_connection()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/ai_assistant/ai_assistant.py CHANGED
@@ -43,9 +43,10 @@ class AIAssistant:
43
  # تهيئة مفتاح OpenAI API
44
  self.openai_api_key = os.environ.get("OPENAI_API_KEY")
45
  if self.openai_api_key:
46
- openai.api_key = self.openai_api_key
47
  self.is_api_available = True
48
  else:
 
49
  self.is_api_available = False
50
 
51
  # نموذج OpenAI المستخدم
@@ -164,7 +165,7 @@ class AIAssistant:
164
  if model is None:
165
  model = self.model
166
 
167
- response = openai.ChatCompletion.create(
168
  model=model,
169
  messages=messages,
170
  max_tokens=max_tokens,
@@ -174,7 +175,10 @@ class AIAssistant:
174
  presence_penalty=0
175
  )
176
 
177
- return response
 
 
 
178
  except Exception as e:
179
  logging.error(f"خطأ في استدعاء OpenAI API: {e}")
180
  return {
@@ -184,8 +188,10 @@ class AIAssistant:
184
  def _call_backend_api(self, endpoint, data):
185
  """استدعاء واجهة API الخلفية للنظام"""
186
  try:
 
 
187
  response = requests.post(
188
- f"http://localhost:5000/api/{endpoint}",
189
  json=data,
190
  timeout=60
191
  )
@@ -198,576 +204,3 @@ class AIAssistant:
198
  except Exception as e:
199
  logging.error(f"خطأ في الاتصال بواجهة API الخلفية: {e}")
200
  return {"error": f"خطأ في الاتصال بواجهة API الخلفية: {str(e)}"}
201
-
202
- def _process_user_message(self, user_message, mode=None):
203
- """معالجة رسالة المستخدم والحصول على رد من المساعد الذكي"""
204
- if mode is None:
205
- mode = st.session_state.assistant_mode
206
-
207
- # إنشاء قائمة الرسائل للمحادثة
208
- messages = [
209
- {"role": "system", "content": self.system_prompts[mode]}
210
- ]
211
-
212
- # إضافة سياق المستند إذا كان متاحاً
213
- if st.session_state.document_context:
214
- messages.append({
215
- "role": "system",
216
- "content": f"معلومات سياقية عن المستند: {st.session_state.document_context}"
217
- })
218
-
219
- # إضافة المحادثة السابقة
220
- for msg in st.session_state.assistant_messages:
221
- messages.append({
222
- "role": msg["role"],
223
- "content": msg["content"]
224
- })
225
-
226
- # إضافة رسالة المستخدم الحالية
227
- messages.append({
228
- "role": "user",
229
- "content": user_message
230
- })
231
-
232
- # استدعاء API
233
- response = self._call_openai_api(messages)
234
-
235
- # استخراج الرد
236
- assistant_response = response["choices"][0]["message"]["content"]
237
-
238
- # تحديث سجل المحادثة
239
- st.session_state.assistant_messages.append({"role": "user", "content": user_message})
240
- st.session_state.assistant_messages.append({"role": "assistant", "content": assistant_response})
241
-
242
- return assistant_response
243
-
244
- def _clear_chat(self):
245
- """مسح المحادثة الحالية"""
246
- st.session_state.assistant_messages = []
247
- st.session_state.document_context = None
248
-
249
- def _save_conversation(self):
250
- """حفظ المحادثة الحالية"""
251
- if not st.session_state.assistant_messages:
252
- st.warning("لا توجد محادثة لحفظها.")
253
- return False
254
-
255
- timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
256
- user_info = get_user_info()
257
-
258
- conversation_data = {
259
- "timestamp": timestamp,
260
- "user": user_info["username"],
261
- "mode": st.session_state.assistant_mode,
262
- "messages": st.session_state.assistant_messages,
263
- "document_context": st.session_state.document_context
264
- }
265
-
266
- filename = f"conversation_{user_info['username']}_{timestamp}.json"
267
- file_path = os.path.join(self.conversations_dir, filename)
268
-
269
- try:
270
- with open(file_path, 'w', encoding='utf-8') as f:
271
- json.dump(conversation_data, f, ensure_ascii=False, indent=2)
272
-
273
- return True
274
- except Exception as e:
275
- logging.error(f"خطأ في حفظ المحادثة: {e}")
276
- return False
277
-
278
- def _load_conversation(self, filename):
279
- """تحميل محادثة محفوظة"""
280
- file_path = os.path.join(self.conversations_dir, filename)
281
-
282
- try:
283
- with open(file_path, 'r', encoding='utf-8') as f:
284
- conversation_data = json.load(f)
285
-
286
- st.session_state.assistant_messages = conversation_data["messages"]
287
- st.session_state.assistant_mode = conversation_data["mode"]
288
- st.session_state.document_context = conversation_data.get("document_context")
289
-
290
- return True
291
- except Exception as e:
292
- logging.error(f"خطأ في تحميل المحادثة: {e}")
293
- return False
294
-
295
- def _get_saved_conversations(self):
296
- """الحصول على قائمة المحادثات المحفوظة"""
297
- conversations = []
298
-
299
- try:
300
- for filename in os.listdir(self.conversations_dir):
301
- if filename.endswith(".json") and filename.startswith("conversation_"):
302
- file_path = os.path.join(self.conversations_dir, filename)
303
-
304
- with open(file_path, 'r', encoding='utf-8') as f:
305
- data = json.load(f)
306
-
307
- conversations.append({
308
- "filename": filename,
309
- "timestamp": data.get("timestamp", ""),
310
- "user": data.get("user", ""),
311
- "mode": data.get("mode", "general"),
312
- "message_count": len(data.get("messages", []))
313
- })
314
- except Exception as e:
315
- logging.error(f"خطأ في قراءة المحادثات المحفوظة: {e}")
316
-
317
- # ترتيب المحادثات حسب التاريخ (الأحدث أولاً)
318
- conversations.sort(key=lambda x: x["timestamp"], reverse=True)
319
-
320
- return conversations
321
-
322
- def render_chat_interface(self):
323
- """عرض واجهة المحادثة الرئيسية"""
324
- st.markdown("<h2 class='module-title'>المساعد الذكي</h2>", unsafe_allow_html=True)
325
-
326
- # التحقق من توفر OpenAI API
327
- if not self.is_api_available:
328
- st.warning("⚠️ مفتاح OpenAI API غير متوفر. لن يكون المساعد الذكي قادراً على الرد. يرجى التواصل مع مسؤول النظام.")
329
-
330
- # إضافة CSS
331
- st.markdown("""
332
- <style>
333
- .chat-container {
334
- background-color: #f8f9fa;
335
- border-radius: 10px;
336
- padding: 20px;
337
- margin-bottom: 20px;
338
- max-height: 500px;
339
- overflow-y: auto;
340
- }
341
-
342
- .chat-message {
343
- margin-bottom: 15px;
344
- display: flex;
345
- flex-direction: row;
346
- }
347
-
348
- .user-message {
349
- justify-content: flex-end;
350
- }
351
-
352
- .assistant-message {
353
- justify-content: flex-start;
354
- }
355
-
356
- .message-bubble {
357
- padding: 10px 15px;
358
- border-radius: 15px;
359
- max-width: 75%;
360
- }
361
-
362
- .user-bubble {
363
- background-color: #1E88E5;
364
- color: white;
365
- border-top-left-radius: 15px;
366
- border-top-right-radius: 15px;
367
- border-bottom-left-radius: 15px;
368
- border-bottom-right-radius: 0;
369
- }
370
-
371
- .assistant-bubble {
372
- background-color: #f0f0f0;
373
- color: #333;
374
- border-top-left-radius: 15px;
375
- border-top-right-radius: 15px;
376
- border-bottom-left-radius: 0;
377
- border-bottom-right-radius: 15px;
378
- }
379
-
380
- .message-avatar {
381
- width: 40px;
382
- height: 40px;
383
- border-radius: 50%;
384
- background-color: #ccc;
385
- display: flex;
386
- align-items: center;
387
- justify-content: center;
388
- margin: 0 10px;
389
- font-weight: bold;
390
- color: white;
391
- }
392
-
393
- .user-avatar {
394
- background-color: #78909C;
395
- }
396
-
397
- .assistant-avatar {
398
- background-color: #1E88E5;
399
- }
400
-
401
- .message-content {
402
- white-space: pre-wrap;
403
- }
404
-
405
- .message-time {
406
- font-size: 0.8em;
407
- color: #888;
408
- margin-top: 5px;
409
- text-align: right;
410
- }
411
-
412
- .chat-input {
413
- background-color: #f8f9fa;
414
- border-radius: 10px;
415
- padding: 20px;
416
- }
417
-
418
- .suggestions-container {
419
- display: flex;
420
- flex-wrap: wrap;
421
- gap: 10px;
422
- margin-top: 10px;
423
- }
424
-
425
- .suggestion-chip {
426
- background-color: #e9ecef;
427
- border-radius: 20px;
428
- padding: 5px 15px;
429
- cursor: pointer;
430
- text-align: center;
431
- transition: background-color 0.3s;
432
- }
433
-
434
- .suggestion-chip:hover {
435
- background-color: #dee2e6;
436
- }
437
- </style>
438
- """, unsafe_allow_html=True)
439
-
440
- # عرض أوضاع المساعد
441
- st.markdown("#### اختر وضع المساعد الذكي")
442
-
443
- col1, col2, col3, col4, col5 = st.columns(5)
444
-
445
- with col1:
446
- if st.button("مساعد عام", key="mode_general",
447
- help="مساعد عام للإجابة على الأسئلة المتعلقة بالعقود والمناقصات"):
448
- st.session_state.assistant_mode = "general"
449
- st.rerun()
450
-
451
- with col2:
452
- if st.button("تحليل العقود", key="mode_contract_analysis",
453
- help="متخصص في تحليل العقود وتحديد البنود والشروط والمخاطر"):
454
- st.session_state.assistant_mode = "contract_analysis"
455
- st.rerun()
456
-
457
- with col3:
458
- if st.button("تقدير التكاليف", key="mode_cost_estimation",
459
- help="متخصص في تقدير تكاليف المشاريع والبنود"):
460
- st.session_state.assistant_mode = "cost_estimation"
461
- st.rerun()
462
-
463
- with col4:
464
- if st.button("تقييم المخاطر", key="mode_risk_assessment",
465
- help="متخصص في تحديد وتقييم المخاطر المحتملة في المشاريع والعقود"):
466
- st.session_state.assistant_mode = "risk_assessment"
467
- st.rerun()
468
-
469
- with col5:
470
- if st.button("تخطيط المشاريع", key="mode_project_planning",
471
- help="متخصص في تخطيط وإدارة المشاريع وتحديد المراحل والموارد"):
472
- st.session_state.assistant_mode = "project_planning"
473
- st.rerun()
474
-
475
- st.markdown(f"**الوضع الحالي:** {self.assistant_modes[st.session_state.assistant_mode]}")
476
-
477
- # تحميل سياق من مستند (اختياري)
478
- st.markdown("---")
479
- with st.expander("إضافة سياق من مستند", expanded=False):
480
- context_text = st.text_area(
481
- "نص المستند (اختياري)",
482
- value=st.session_state.document_context if st.session_state.document_context else "",
483
- height=150,
484
- help="أضف نص المستند هنا ليتم استخدامه كسياق للمحادثة"
485
- )
486
-
487
- uploaded_file = st.file_uploader(
488
- "أو قم بتحميل ملف نصي أو PDF",
489
- type=["txt", "pdf"],
490
- help="يمكنك تحميل ملف نصي أو PDF ليتم استخدامه كسياق للمحادثة"
491
- )
492
-
493
- doc_col1, doc_col2 = st.columns(2)
494
-
495
- with doc_col1:
496
- if st.button("إضافة السياق", disabled=not context_text and not uploaded_file):
497
- if uploaded_file:
498
- try:
499
- # قراءة الملف المرفوع
500
- if uploaded_file.name.endswith(".pdf"):
501
- import PyPDF2
502
- reader = PyPDF2.PdfReader(uploaded_file)
503
- context = ""
504
- for page in reader.pages:
505
- context += page.extract_text() + "\n"
506
- else:
507
- context = uploaded_file.read().decode("utf-8")
508
-
509
- st.session_state.document_context = context
510
- st.success("تم إضافة نص المستند كسياق للمحادثة بنجاح.")
511
- except Exception as e:
512
- st.error(f"حدث خطأ أثناء قراءة الملف: {str(e)}")
513
- elif context_text:
514
- st.session_state.document_context = context_text
515
- st.success("تم إضافة نص المستند كسياق للمحادثة بنجاح.")
516
-
517
- with doc_col2:
518
- if st.button("مسح السياق", disabled=not st.session_state.document_context):
519
- st.session_state.document_context = None
520
- st.success("تم مسح سياق المستند بنجاح.")
521
-
522
- # عرض المحادثة
523
- st.markdown("---")
524
- st.markdown("#### المحادثة مع المساعد الذكي")
525
-
526
- # عرض رسائل المحادثة
527
- chat_container = st.container()
528
-
529
- with chat_container:
530
- with st.container():
531
- if not st.session_state.assistant_messages:
532
- st.markdown("""
533
- <div style="text-align: center; padding: 30px; color: #666;">
534
- <p>مرحباً بك في المساعد الذكي!</p>
535
- <p>يمكنك البدء بطرح سؤال أو طلب مساعدة.</p>
536
- </div>
537
- """, unsafe_allow_html=True)
538
- else:
539
- message_html = ""
540
-
541
- for msg in st.session_state.assistant_messages:
542
- if msg["role"] == "user":
543
- message_html += f"""
544
- <div class="chat-message user-message">
545
- <div class="message-bubble user-bubble">
546
- <div class="message-content">{msg["content"]}</div>
547
- </div>
548
- <div class="message-avatar user-avatar">أ</div>
549
- </div>
550
- """
551
- else:
552
- message_html += f"""
553
- <div class="chat-message assistant-message">
554
- <div class="message-avatar assistant-avatar">W</div>
555
- <div class="message-bubble assistant-bubble">
556
- <div class="message-content">{msg["content"]}</div>
557
- </div>
558
- </div>
559
- """
560
-
561
- st.markdown(f"""
562
- <div class="chat-container">
563
- {message_html}
564
- </div>
565
- """, unsafe_allow_html=True)
566
-
567
- # ادخال الرسالة
568
- st.markdown("#### أدخل رسالتك")
569
-
570
- with st.container():
571
- with st.form(key="chat_form"):
572
- user_message = st.text_area("رسالتك", height=100, placeholder="اكتب سؤالك أو طلبك هنا...")
573
-
574
- col1, col2, col3 = st.columns([2, 2, 1])
575
-
576
- with col1:
577
- send_button = st.form_submit_button(
578
- "إرسال",
579
- help="إرسال الرسالة إلى المساعد الذكي"
580
- )
581
-
582
- with col2:
583
- suggested_questions = [
584
- "كيف يمكنني تحليل بنود الدفع في العقد؟",
585
- "ما هي أفضل طريقة لتقدير تكاليف مشروع بناء؟",
586
- "كيف أحدد المخاطر المحتملة في مشروع جديد؟",
587
- "كيف يمكنني إنشاء جدول زمني فعال للمشروع؟",
588
- "ما هي أهم البنود التي يجب الانتباه إليها في عقود المقاولات؟"
589
- ]
590
-
591
- if st.session_state.assistant_mode == "contract_analysis":
592
- suggested_questions = [
593
- "كيف أحدد البنود غير المواتية في العقد؟",
594
- "ما هي العناصر الأساسية التي يجب أن يتضمنها عقد المقاولة؟",
595
- "كيف أتعامل مع بنود الغرامات والتعويضات؟",
596
- "كيف يمكنني التفاوض على تحسين شروط الدفع؟",
597
- "ما هي الفروق الرئيسية بين عقد الثمن الثابت وعقد التكلفة زائد أتعاب؟"
598
- ]
599
- elif st.session_state.assistant_mode == "cost_estimation":
600
- suggested_questions = [
601
- "كيف أقدر تكلفة المواد في مشروع بناء؟",
602
- "ما هي نسبة النفقات العامة المعقولة لمشروع مقاولات؟",
603
- "كيف أحسب تكلفة العمالة بدقة؟",
604
- "ما هي العوامل التي تؤثر على تكلفة المعدات؟",
605
- "كيف أقدر هامش الربح المناسب للمشروع؟"
606
- ]
607
-
608
- selected_question = st.selectbox(
609
- "أو اختر سؤال مقترح",
610
- [""] + suggested_questions,
611
- index=0
612
- )
613
-
614
- with col3:
615
- clear_button = st.form_submit_button(
616
- "مسح المحادثة",
617
- help="مسح جميع الرسائل في المحادثة الحالية"
618
- )
619
-
620
- if send_button and user_message:
621
- # معالجة رسالة المستخدم
622
- with st.spinner("جاري معالجة الرسالة..."):
623
- self._process_user_message(user_message)
624
- st.rerun()
625
-
626
- if send_button and selected_question and not user_message:
627
- # استخدام السؤال المقترح
628
- with st.spinner("جاري معالجة الرسالة..."):
629
- self._process_user_message(selected_question)
630
- st.rerun()
631
-
632
- if clear_button:
633
- self._clear_chat()
634
- st.rerun()
635
-
636
- # زر لحفظ المحادثة
637
- col1, col2, col3 = st.columns([1, 1, 2])
638
-
639
- with col1:
640
- if st.button("حفظ المحادثة", key="save_conversation", disabled=not st.session_state.assistant_messages):
641
- if self._save_conversation():
642
- st.success("تم حفظ المحادثة بنجاح.")
643
- else:
644
- st.error("حدث خطأ أثناء حفظ المحادثة.")
645
-
646
- with col2:
647
- if st.button("تحميل محادثة سابقة", key="show_load_conversation"):
648
- st.session_state.show_conversations = True
649
- st.rerun()
650
-
651
- # عرض المحادثات المحفوظة
652
- if "show_conversations" in st.session_state and st.session_state.show_conversations:
653
- st.markdown("---")
654
- st.markdown("#### المحادثات المحفوظة")
655
-
656
- conversations = self._get_saved_conversations()
657
-
658
- if not conversations:
659
- st.info("لا توجد محادثات محفوظة.")
660
- else:
661
- # عرض المحادثات في جدول
662
- conversation_data = []
663
- for conv in conversations:
664
- timestamp = datetime.strptime(conv["timestamp"], "%Y%m%d%H%M%S") if conv["timestamp"] else ""
665
- formatted_time = timestamp.strftime("%Y-%m-%d %H:%M:%S") if timestamp else ""
666
-
667
- conversation_data.append({
668
- "التاريخ": formatted_time,
669
- "المستخدم": conv["user"],
670
- "وضع المساعد": self.assistant_modes.get(conv["mode"], "غير معروف"),
671
- "عدد الرسائل": conv["message_count"],
672
- "الملف": conv["filename"]
673
- })
674
-
675
- df = pd.DataFrame(conversation_data)
676
- st.dataframe(df, height=300)
677
-
678
- # اختيار محادثة لتحميلها
679
- selected_filename = st.selectbox(
680
- "اختر محادثة لتحميلها",
681
- options=[""] + [conv["filename"] for conv in conversations],
682
- format_func=lambda x: next((f"{c['user']} - {datetime.strptime(c['timestamp'], '%Y%m%d%H%M%S').strftime('%Y-%m-%d %H:%M:%S')}" for c in conversations if c["filename"] == x), x),
683
- index=0
684
- )
685
-
686
- col1, col2 = st.columns(2)
687
-
688
- with col1:
689
- if st.button("تحميل المحادثة المختارة", disabled=not selected_filename):
690
- if self._load_conversation(selected_filename):
691
- st.success("تم تحميل المحادثة بنجاح.")
692
- st.session_state.show_conversations = False
693
- st.rerun()
694
- else:
695
- st.error("حدث خطأ أثناء تحميل المحادثة.")
696
-
697
- with col2:
698
- if st.button("إلغاء", key="cancel_load_conversation"):
699
- st.session_state.show_conversations = False
700
- st.rerun()
701
-
702
- # عرض المعلومات عن وضع المساعد الحالي
703
- st.markdown("---")
704
- st.markdown(f"#### معلومات عن وضع المساعد: {self.assistant_modes[st.session_state.assistant_mode]}")
705
-
706
- if st.session_state.assistant_mode == "general":
707
- st.markdown("""
708
- المساعد العام يمكنه مساعدتك في مجموعة متنوعة من المهام المتعلقة بالعقود والمناقصات وإدارة المشاريع. يمكنه:
709
- - الإجابة على الأسئلة العامة حول العقود والمناقصات
710
- - توجيهك إلى الوحدات المناسبة في النظام
711
- - تقديم معلومات عامة عن إدارة المشاريع وأفضل الممارسات
712
- - المساعدة في فهم المصطلحات والمفاهيم المتعلقة بمجال المقاولات
713
- """)
714
- elif st.session_state.assistant_mode == "contract_analysis":
715
- st.markdown("""
716
- مساعد تحليل العقود متخصص في:
717
- - تحليل بنود العقود وتوضيح معانيها
718
- - تحديد الالتزامات والحقوق لكل طرف
719
- - تسليط الضوء على البنود غير المواتية أو الغامضة
720
- - تقديم توصيات للتفاوض على تحسين شروط العقد
721
- - مقارنة العقد مع أفضل الممارسات في القطاع
722
- """)
723
- elif st.session_state.assistant_mode == "cost_estimation":
724
- st.markdown("""
725
- مساعد تقدير التكاليف متخصص في:
726
- - حساب تكاليف المشاريع بناءً على المتطلبات والمواصفات
727
- - تقدير تكاليف المواد والعمالة والمعدات
728
- - تحليل التكاليف المباشرة وغير المباشرة
729
- - تقديم نصائح لتقليل التكاليف وزيادة الكفاءة
730
- - تحديد العوامل التي قد تؤثر على التكلفة الإجمالية
731
- """)
732
- elif st.session_state.assistant_mode == "risk_assessment":
733
- st.markdown("""
734
- مساعد تقييم المخاطر متخصص في:
735
- - تحديد المخاطر المحتملة في المشاريع والعقود
736
- - تقييم احتمالية وتأثير كل خطر
737
- - اقتراح استراتيجيات للتخفيف من المخاطر
738
- - إنشاء خطط للطوارئ والاستجابة للمخاطر
739
- - تحليل تأثير المخاطر على الجدول الزمني والتكلفة
740
- """)
741
- elif st.session_state.assistant_mode == "project_planning":
742
- st.markdown("""
743
- مساعد تخطيط المشاريع متخصص في:
744
- - تقسيم المشروع إلى مراحل ومهام وأنشطة
745
- - تحديد الموارد اللازمة لكل نشاط
746
- - إنشاء الجداول الزمنية والمسار الحرج
747
- - التخطيط للموارد البشرية والمعدات والمواد
748
- - مراقبة تقدم المشروع وإدارة التغييرات
749
- """)
750
-
751
- # عرض معلومات حقوق الملكية
752
- render_credits()
753
-
754
- def render(self):
755
- """عرض واجهة المساعد الذكي الرئيسية"""
756
- # تحميل CSS المخصص
757
- load_css()
758
-
759
- # عرض واجهة المحادثة
760
- self.render_chat_interface()
761
-
762
-
763
- # تشغيل التطبيق بشكل مستقل عند استدعاء الملف مباشرة
764
- if __name__ == "__main__":
765
- st.set_page_config(
766
- page_title="المساعد الذكي | WAHBi AI",
767
- page_icon="🤖",
768
- layout="wide",
769
- initial_sidebar_state="expanded"
770
- )
771
-
772
- assistant = AIAssistant()
773
- assistant.render()
 
43
  # تهيئة مفتاح OpenAI API
44
  self.openai_api_key = os.environ.get("OPENAI_API_KEY")
45
  if self.openai_api_key:
46
+ self.client = openai.OpenAI(api_key=self.openai_api_key)
47
  self.is_api_available = True
48
  else:
49
+ self.client = None
50
  self.is_api_available = False
51
 
52
  # نموذج OpenAI المستخدم
 
165
  if model is None:
166
  model = self.model
167
 
168
+ response = self.client.chat.completions.create(
169
  model=model,
170
  messages=messages,
171
  max_tokens=max_tokens,
 
175
  presence_penalty=0
176
  )
177
 
178
+ # تحويل الاستجابة إلى تنسيق متوافق مع الكود القديم
179
+ return {
180
+ "choices": [{"message": {"content": response.choices[0].message.content}}]
181
+ }
182
  except Exception as e:
183
  logging.error(f"خطأ في استدعاء OpenAI API: {e}")
184
  return {
 
188
  def _call_backend_api(self, endpoint, data):
189
  """استدعاء واجهة API الخلفية للنظام"""
190
  try:
191
+ # تغيير المنفذ من 5000 إلى 8000 لتجنب التعارض مع الخدمات الأخرى
192
+ api_url = os.environ.get("BACKEND_API_URL", "http://localhost:8000/api")
193
  response = requests.post(
194
+ f"{api_url}/{endpoint}",
195
  json=data,
196
  timeout=60
197
  )
 
204
  except Exception as e:
205
  logging.error(f"خطأ في الاتصال بواجهة API الخلفية: {e}")
206
  return {"error": f"خطأ في الاتصال بواجهة API الخلفية: {str(e)}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/document_comparison/document_comparator.py CHANGED
@@ -38,37 +38,8 @@ class DocumentComparator:
38
  self.comparison_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'document_comparison')
39
  create_directory_if_not_exists(self.comparison_dir)
40
 
41
- # تهيئة NLTK وتنزيل حزمة punkt إذا لم تكن موجودة
42
- self._initialize_nltk()
43
-
44
  # إعداد مقيم ROUGE لمقارنة النصوص
45
  self.rouge_scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=False)
46
-
47
- def _initialize_nltk(self):
48
- """تهيئة مكتبة NLTK وتنزيل الحزم المطلوبة"""
49
- try:
50
- # استيراد nltk
51
- import nltk
52
-
53
- # قائمة بالحزم المطلوبة
54
- required_packages = ['punkt', 'stopwords', 'wordnet']
55
- for package in required_packages:
56
- try:
57
- # محاولة استخدام الحزمة أولاً، وإذا فشلت يتم تنزيلها
58
- nltk.data.find(f'tokenizers/{package}')
59
- except LookupError:
60
- print(f"تنزيل حزمة NLTK: {package}")
61
- nltk.download(package, quiet=True)
62
-
63
- # محاولة استخدام sent_tokenize للتحقق من وجود حزمة punkt
64
- from nltk.tokenize import sent_tokenize
65
- sent_tokenize("This is a test sentence.")
66
- except LookupError:
67
- # تنزيل حزمة punkt تلقائيًا إذا لم تكن موجودة
68
- import nltk
69
- nltk.download('punkt', quiet=True)
70
- # طباعة رسالة تأكيد التنزيل
71
- st.info("تم تنزيل حزمة NLTK punkt بنجاح للاستخدام في مقارنة المستندات.")
72
 
73
  def _preprocess_text(self, text):
74
  """معالجة النص قبل التحليل"""
 
38
  self.comparison_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'document_comparison')
39
  create_directory_if_not_exists(self.comparison_dir)
40
 
 
 
 
41
  # إعداد مقيم ROUGE لمقارنة النصوص
42
  self.rouge_scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  def _preprocess_text(self, text):
45
  """معالجة النص قبل التحليل"""
modules/pricing/construction_calculator.py CHANGED
@@ -19,18 +19,6 @@ def render_construction_calculator():
19
  """
20
  عرض حاسبة تكاليف البناء المتكاملة
21
  """
22
- # التأكد من وجود المتغيرات في حالة الجلسة
23
- if 'materials_cost' not in st.session_state:
24
- st.session_state.materials_cost = 0.0
25
- if 'equipment_cost' not in st.session_state:
26
- st.session_state.equipment_cost = 0.0
27
- if 'labor_cost' not in st.session_state:
28
- st.session_state.labor_cost = 0.0
29
- if 'admin_cost' not in st.session_state:
30
- st.session_state.admin_cost = 0.0
31
- if 'profit_margin' not in st.session_state:
32
- st.session_state.profit_margin = 15.0
33
-
34
  st.markdown("<h2 class='module-title'>حاسبة تكاليف البناء المتكاملة</h2>", unsafe_allow_html=True)
35
 
36
  # معلومات المشروع
@@ -651,30 +639,6 @@ def render_final_report(project_name, project_location, project_area, project_ty
651
  """
652
  st.markdown("<h3>التقرير النهائي لتكاليف المشروع</h3>", unsafe_allow_html=True)
653
 
654
- # التأكد من وجود المتغيرات المطلوبة في حالة الجلسة وضمان أن لديهم قيم صحيحة
655
- required_fields = {
656
- 'materials_cost': 0.0,
657
- 'equipment_cost': 0.0,
658
- 'labor_cost': 0.0,
659
- 'admin_cost': 0.0,
660
- 'profit_margin': 15.0,
661
- 'materials': [],
662
- 'equipment': [],
663
- 'labor': [],
664
- 'admin_expenses': []
665
- }
666
-
667
- # مرور على كافة الحقول المطلوبة للتأكد من وجودها
668
- for field, default_value in required_fields.items():
669
- if field not in st.session_state:
670
- st.session_state[field] = default_value
671
-
672
- # التحقق من أن القيم العددية صالحة (غير None وليست NaN)
673
- if field in ['materials_cost', 'equipment_cost', 'labor_cost', 'admin_cost', 'profit_margin']:
674
- # إذا كانت القيمة None أو NaN، استخدم القيمة الافتراضية
675
- if st.session_state[field] is None or pd.isna(st.session_state[field]):
676
- st.session_state[field] = default_value
677
-
678
  # حساب التكاليف المباشرة والإجمالية
679
  direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost
680
  total_costs = direct_costs + st.session_state.admin_cost
@@ -717,39 +681,23 @@ def render_final_report(project_name, project_location, project_area, project_ty
717
  st.markdown("</div>", unsafe_allow_html=True)
718
 
719
  # عرض التفاصيل بالمتر المربع
720
- if project_area > 0:
721
- per_sqm_cost = total_price / project_area
722
- st.markdown("<div class='card'>", unsafe_allow_html=True)
723
- st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True)
724
- st.markdown(f"<p>تكلفة المتر المربع الإجمالية: <strong>{per_sqm_cost:,.2f} ريال/م²</strong></p>", unsafe_allow_html=True)
725
- st.markdown("</div>", unsafe_allow_html=True)
726
- else:
727
- st.markdown("<div class='card'>", unsafe_allow_html=True)
728
- st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True)
729
- st.markdown("<p>يرجى إدخال مساحة صحيحة للمشروع لحساب تكلفة المتر المربع</p>", unsafe_allow_html=True)
730
- st.markdown("</div>", unsafe_allow_html=True)
731
 
732
  # رسم بياني لتوزيع التكاليف
733
  st.markdown("<h4>توزيع التكاليف</h4>", unsafe_allow_html=True)
734
 
735
- # تجنب القسمة على صفر
736
- if total_price > 0:
737
- cost_distribution = [
738
- {"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": st.session_state.materials_cost / total_price * 100},
739
- {"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": st.session_state.equipment_cost / total_price * 100},
740
- {"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": st.session_state.labor_cost / total_price * 100},
741
- {"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": st.session_state.admin_cost / total_price * 100},
742
- {"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": profit_amount / total_price * 100}
743
- ]
744
- else:
745
- # إذا كان المجموع صفر، اجعل جميع النسب المئوية صفر
746
- cost_distribution = [
747
- {"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": 0},
748
- {"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": 0},
749
- {"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": 0},
750
- {"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": 0},
751
- {"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": 0}
752
- ]
753
 
754
  cost_df = pd.DataFrame(cost_distribution)
755
 
 
19
  """
20
  عرض حاسبة تكاليف البناء المتكاملة
21
  """
 
 
 
 
 
 
 
 
 
 
 
 
22
  st.markdown("<h2 class='module-title'>حاسبة تكاليف البناء المتكاملة</h2>", unsafe_allow_html=True)
23
 
24
  # معلومات المشروع
 
639
  """
640
  st.markdown("<h3>التقرير النهائي لتكاليف المشروع</h3>", unsafe_allow_html=True)
641
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  # حساب التكاليف المباشرة والإجمالية
643
  direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost
644
  total_costs = direct_costs + st.session_state.admin_cost
 
681
  st.markdown("</div>", unsafe_allow_html=True)
682
 
683
  # عرض التفاصيل بالمتر المربع
684
+ per_sqm_cost = total_price / project_area
685
+
686
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
687
+ st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True)
688
+ st.markdown(f"<p>تكلفة المتر المربع الإجمالية: <strong>{per_sqm_cost:,.2f} ريال/م²</strong></p>", unsafe_allow_html=True)
689
+ st.markdown("</div>", unsafe_allow_html=True)
 
 
 
 
 
690
 
691
  # رسم بياني لتوزيع التكاليف
692
  st.markdown("<h4>توزيع التكاليف</h4>", unsafe_allow_html=True)
693
 
694
+ cost_distribution = [
695
+ {"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": st.session_state.materials_cost / total_price * 100},
696
+ {"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": st.session_state.equipment_cost / total_price * 100},
697
+ {"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": st.session_state.labor_cost / total_price * 100},
698
+ {"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": st.session_state.admin_cost / total_price * 100},
699
+ {"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": profit_amount / total_price * 100}
700
+ ]
 
 
 
 
 
 
 
 
 
 
 
701
 
702
  cost_df = pd.DataFrame(cost_distribution)
703
 
modules/resources/resources_app.py CHANGED
@@ -461,21 +461,17 @@ class ResourcesApp:
461
  # تحويل البيانات إلى DataFrame
462
  price_history_df = pd.DataFrame(price_history_data)
463
 
464
- # التحقق من وجود بيانات قبل رسم المخطط
465
- if len(price_history_data) == 0:
466
- st.warning("لا توجد بيانات تاريخية للأسعار متاحة لعرضها")
467
- else:
468
- # رسم المخطط الخطي
469
- fig = px.line(
470
- price_history_df,
471
- x='التاريخ',
472
- y='السعر',
473
- color='المادة',
474
- title='تطور أسعار المواد الرئيسية خلال العام الماضي',
475
- labels={'التاريخ': 'التاريخ', 'السعر': 'السعر (ريال)', 'المادة': 'المادة'}
476
- )
477
- # عرض المخطط فقط إذا تم إنشاؤه
478
- st.plotly_chart(fig, use_container_width=True)
479
 
480
  def _render_materials_tab(self):
481
  """عرض تبويب المواد"""
@@ -1053,30 +1049,15 @@ class ResourcesApp:
1053
  material_ids = {material['name']: material['id'] for material in st.session_state.materials}
1054
  selected_ids = [material_ids[name] for name in selected_materials if name in material_ids]
1055
 
1056
- # التحقق من وجود بيانات سعرية في session_state.price_history
1057
- if 'price_history' not in st.session_state or not st.session_state.price_history:
1058
- st.warning("لا توجد بيانات أسعار متاحة للتحليل.")
1059
- return
1060
-
1061
  price_history_data = []
1062
  for entry in st.session_state.price_history:
1063
  if entry['material_id'] in selected_ids:
1064
- # الحصول على اسم المادة من المعرف
1065
  material_name = next((material['name'] for material in st.session_state.materials if material['id'] == entry['material_id']), "")
1066
-
1067
- # التحقق من وجود المفاتيح المطلوبة
1068
- if 'date' in entry and 'price' in entry:
1069
- try:
1070
- # إضافة البيانات إلى قائمة البيانات مع تحويل التاريخ إلى كائن datetime
1071
- price_history_data.append({
1072
- 'material': material_name, # استخدام أسماء إنجليزية للمفاتيح
1073
- 'date': pd.to_datetime(entry['date']),
1074
- 'price': float(entry['price']) # التأكد من تحويل السعر إلى رقم
1075
- })
1076
- except (ValueError, TypeError) as e:
1077
- # تسجيل أخطاء تحويل البيانات
1078
- st.error(f"خطأ في معالجة البيانات: {e}")
1079
- continue
1080
 
1081
  if not price_history_data:
1082
  st.warning("لا توجد بيانات أسعار متاحة للمواد المختارة.")
@@ -1085,37 +1066,32 @@ class ResourcesApp:
1085
  # تحويل البيانات إلى DataFrame
1086
  price_history_df = pd.DataFrame(price_history_data)
1087
 
1088
- # التحقق من وجود بيانات قبل رسم المخطط
1089
- if len(price_history_df) == 0:
1090
- st.warning("لا توجد بيانات تاريخية للأسعار متاحة لعرضها")
1091
- else:
1092
- # عرض المخطط الخطي للأسعار باستخدام أسماء الأعمدة الإنجليزية
1093
- fig = px.line(
1094
- price_history_df,
1095
- x='date',
1096
- y='price',
1097
- color='material',
1098
- title='تطور أسعار المواد المختارة',
1099
- labels={'date': 'التاريخ', 'price': 'السعر (ريال)', 'material': 'المادة'}
1100
- )
1101
-
1102
- st.plotly_chart(fig, use_container_width=True)
1103
 
1104
  # حساب التغيرات في الأسعار
1105
  materials_price_changes = []
1106
 
1107
  for material_name in selected_materials:
1108
- # استخدام أسماء الأعمدة الإنجليزية للتصفية والترتيب
1109
- material_prices = price_history_df[price_history_df['material'] == material_name].sort_values('date')
1110
 
1111
  if len(material_prices) >= 2:
1112
- first_price = material_prices.iloc[0]['price']
1113
- last_price = material_prices.iloc[-1]['price']
1114
  price_change = last_price - first_price
1115
  price_change_percent = (price_change / first_price) * 100
1116
 
1117
  # حساب التقلب (الانحراف المعياري)
1118
- price_volatility = material_prices['price'].std()
1119
 
1120
  materials_price_changes.append({
1121
  'المادة': material_name,
@@ -1283,21 +1259,17 @@ class ResourcesApp:
1283
  price_history_data = []
1284
  for entry in st.session_state.price_history:
1285
  if entry['material_id'] == material_id:
1286
- try:
1287
- price_history_data.append({
1288
- 'date': pd.to_datetime(entry['date']),
1289
- 'price': float(entry['price'])
1290
- })
1291
- except (ValueError, TypeError) as e:
1292
- st.error(f"خطأ في معالجة البيانات: {e}")
1293
- continue
1294
 
1295
  if not price_history_data:
1296
  st.warning("لا توجد بيانات تاريخية كافية للمادة المحددة للقيام بالتوقع.")
1297
  return
1298
 
1299
  # تحويل البيانات إلى DataFrame
1300
- price_history_df = pd.DataFrame(price_history_data).sort_values('date')
1301
 
1302
  # إجراء التوقع
1303
  # في الواقع، ستستخدم نماذج تعلم آلي مثل ARIMA أو Prophet
@@ -1306,7 +1278,7 @@ class ResourcesApp:
1306
  # حساب متوسط التغير الشهري
1307
  monthly_changes = []
1308
  for i in range(1, len(price_history_df)):
1309
- monthly_changes.append(price_history_df.iloc[i]['price'] - price_history_df.iloc[i-1]['price'])
1310
 
1311
  if monthly_changes:
1312
  avg_monthly_change = sum(monthly_changes) / len(monthly_changes)
@@ -1314,8 +1286,8 @@ class ResourcesApp:
1314
  avg_monthly_change = 0
1315
 
1316
  # إنشاء بيانات التوقع
1317
- last_date = price_history_df['date'].max()
1318
- last_price = price_history_df.loc[price_history_df['date'] == last_date, 'price'].values[0]
1319
 
1320
  forecast_dates = pd.date_range(start=last_date + pd.DateOffset(months=1), periods=forecast_period, freq='M')
1321
  forecast_prices = [last_price + (i+1) * avg_monthly_change for i in range(forecast_period)]
@@ -1324,25 +1296,25 @@ class ResourcesApp:
1324
  forecast_prices = [price + random.uniform(-price*0.05, price*0.05) for price in forecast_prices]
1325
 
1326
  forecast_df = pd.DataFrame({
1327
- 'date': forecast_dates,
1328
- 'price': forecast_prices,
1329
- 'type': ['توقع'] * forecast_period
1330
  })
1331
 
1332
  # دمج البيانات التاريخية والتوقع
1333
  historical_df = price_history_df.copy()
1334
- historical_df['type'] = ['تاريخي'] * len(historical_df)
1335
 
1336
  combined_df = pd.concat([historical_df, forecast_df], ignore_index=True)
1337
 
1338
  # عرض المخطط
1339
  fig = px.line(
1340
  combined_df,
1341
- x='date',
1342
- y='price',
1343
- color='type',
1344
  title=f'توقع أسعار {selected_material} للـ {forecast_period} أشهر القادمة',
1345
- labels={'date': 'التاريخ', 'price': 'السعر (ريال)', 'type': 'النوع'},
1346
  color_discrete_map={'تاريخي': 'blue', 'توقع': 'red'}
1347
  )
1348
 
@@ -1377,14 +1349,9 @@ class ResourcesApp:
1377
  st.markdown("#### جدول توقع الأسعار")
1378
 
1379
  forecast_table = forecast_df.copy()
1380
- forecast_table['date'] = forecast_table['date'].dt.strftime('%Y-%m')
1381
- forecast_table['price'] = forecast_table['price'].apply(lambda x: f"{x:,.2f} ريال")
1382
- # إعادة تسمية الأعمدة إلى العربية لعرض الجدول
1383
- forecast_table = forecast_table.rename(columns={
1384
- 'date': 'التاريخ',
1385
- 'price': 'السعر'
1386
- })
1387
- forecast_table = forecast_table.drop(columns=['type'])
1388
 
1389
  st.dataframe(forecast_table, use_container_width=True, hide_index=True)
1390
 
 
461
  # تحويل البيانات إلى DataFrame
462
  price_history_df = pd.DataFrame(price_history_data)
463
 
464
+ # رسم المخطط الخطي
465
+ fig = px.line(
466
+ price_history_df,
467
+ x='التاريخ',
468
+ y='السعر',
469
+ color='المادة',
470
+ title='تطور أسعار المواد الرئيسية خلال العام الماضي',
471
+ labels={'price': 'السعر (ريال)', 'date': 'التاريخ'}
472
+ )
473
+
474
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
475
 
476
  def _render_materials_tab(self):
477
  """عرض تبويب المواد"""
 
1049
  material_ids = {material['name']: material['id'] for material in st.session_state.materials}
1050
  selected_ids = [material_ids[name] for name in selected_materials if name in material_ids]
1051
 
 
 
 
 
 
1052
  price_history_data = []
1053
  for entry in st.session_state.price_history:
1054
  if entry['material_id'] in selected_ids:
 
1055
  material_name = next((material['name'] for material in st.session_state.materials if material['id'] == entry['material_id']), "")
1056
+ price_history_data.append({
1057
+ 'المادة': material_name,
1058
+ 'التاريخ': pd.to_datetime(entry['date']),
1059
+ 'السعر': entry['price']
1060
+ })
 
 
 
 
 
 
 
 
 
1061
 
1062
  if not price_history_data:
1063
  st.warning("لا توجد بيانات أسعار متاحة للمواد المختارة.")
 
1066
  # تحويل البيانات إلى DataFrame
1067
  price_history_df = pd.DataFrame(price_history_data)
1068
 
1069
+ # عرض المخطط الخطي للأسعار
1070
+ fig = px.line(
1071
+ price_history_df,
1072
+ x='التاريخ',
1073
+ y='السعر',
1074
+ color='المادة',
1075
+ title='تطور أسعار المواد المختارة',
1076
+ labels={'السعر': 'السعر (ريال)'}
1077
+ )
1078
+
1079
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
1080
 
1081
  # حساب التغيرات في الأسعار
1082
  materials_price_changes = []
1083
 
1084
  for material_name in selected_materials:
1085
+ material_prices = price_history_df[price_history_df['المادة'] == material_name].sort_values('التاريخ')
 
1086
 
1087
  if len(material_prices) >= 2:
1088
+ first_price = material_prices.iloc[0]['السعر']
1089
+ last_price = material_prices.iloc[-1]['السعر']
1090
  price_change = last_price - first_price
1091
  price_change_percent = (price_change / first_price) * 100
1092
 
1093
  # حساب التقلب (الانحراف المعياري)
1094
+ price_volatility = material_prices['السعر'].std()
1095
 
1096
  materials_price_changes.append({
1097
  'المادة': material_name,
 
1259
  price_history_data = []
1260
  for entry in st.session_state.price_history:
1261
  if entry['material_id'] == material_id:
1262
+ price_history_data.append({
1263
+ 'التاريخ': pd.to_datetime(entry['date']),
1264
+ 'السعر': entry['price']
1265
+ })
 
 
 
 
1266
 
1267
  if not price_history_data:
1268
  st.warning("لا توجد بيانات تاريخية كافية للمادة المحددة للقيام بالتوقع.")
1269
  return
1270
 
1271
  # تحويل البيانات إلى DataFrame
1272
+ price_history_df = pd.DataFrame(price_history_data).sort_values('التاريخ')
1273
 
1274
  # إجراء التوقع
1275
  # في الواقع، ستستخدم نماذج تعلم آلي مثل ARIMA أو Prophet
 
1278
  # حساب متوسط التغير الشهري
1279
  monthly_changes = []
1280
  for i in range(1, len(price_history_df)):
1281
+ monthly_changes.append(price_history_df.iloc[i]['السعر'] - price_history_df.iloc[i-1]['السعر'])
1282
 
1283
  if monthly_changes:
1284
  avg_monthly_change = sum(monthly_changes) / len(monthly_changes)
 
1286
  avg_monthly_change = 0
1287
 
1288
  # إنشاء بيانات التوقع
1289
+ last_date = price_history_df['التاريخ'].max()
1290
+ last_price = price_history_df.loc[price_history_df['التاريخ'] == last_date, 'السعر'].values[0]
1291
 
1292
  forecast_dates = pd.date_range(start=last_date + pd.DateOffset(months=1), periods=forecast_period, freq='M')
1293
  forecast_prices = [last_price + (i+1) * avg_monthly_change for i in range(forecast_period)]
 
1296
  forecast_prices = [price + random.uniform(-price*0.05, price*0.05) for price in forecast_prices]
1297
 
1298
  forecast_df = pd.DataFrame({
1299
+ 'التاريخ': forecast_dates,
1300
+ 'السعر': forecast_prices,
1301
+ 'النوع': ['توقع'] * forecast_period
1302
  })
1303
 
1304
  # دمج البيانات التاريخية والتوقع
1305
  historical_df = price_history_df.copy()
1306
+ historical_df['النوع'] = ['تاريخي'] * len(historical_df)
1307
 
1308
  combined_df = pd.concat([historical_df, forecast_df], ignore_index=True)
1309
 
1310
  # عرض المخطط
1311
  fig = px.line(
1312
  combined_df,
1313
+ x='التاريخ',
1314
+ y='السعر',
1315
+ color='النوع',
1316
  title=f'توقع أسعار {selected_material} للـ {forecast_period} أشهر القادمة',
1317
+ labels={'السعر': 'السعر (ريال)'},
1318
  color_discrete_map={'تاريخي': 'blue', 'توقع': 'red'}
1319
  )
1320
 
 
1349
  st.markdown("#### جدول توقع الأسعار")
1350
 
1351
  forecast_table = forecast_df.copy()
1352
+ forecast_table['التاريخ'] = forecast_table['التاريخ'].dt.strftime('%Y-%m')
1353
+ forecast_table['السعر'] = forecast_table['السعر'].apply(lambda x: f"{x:,.2f} ريال")
1354
+ forecast_table = forecast_table.drop(columns=['النوع'])
 
 
 
 
 
1355
 
1356
  st.dataframe(forecast_table, use_container_width=True, hide_index=True)
1357
 
static/css/backup/enhanced-styles.css ADDED
@@ -0,0 +1,913 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* أنماط CSS المحسنة للنظام - تم التحديث 2025 */
2
+ @import url('https://fonts.googleapis.com/css2?family=Almarai:wght@300;400;700;800&family=Cairo:wght@200;300;400;500;600;700;800;900&family=Tajawal:wght@200;300;400;500;700;800;900&display=swap');
3
+
4
+ :root {
5
+ --primary-color: #0EA5A5; /* لون رئيسي جديد: فيروزي */
6
+ --primary-light: rgba(14, 165, 165, 0.1);
7
+ --primary-dark: #088585;
8
+ --primary-gradient: linear-gradient(135deg, #0EA5A5, #088585);
9
+ --secondary-color: #FF9A3C; /* لون ثانوي: برتقالي */
10
+ --secondary-light: rgba(255, 154, 60, 0.1);
11
+ --text-dark: #1d2b36;
12
+ --text-medium: #3a4f5f;
13
+ --text-light: #607d94;
14
+ --background-light: #f8f9fa;
15
+ --border-color: #e1e5ea;
16
+ --danger-color: #e3342f;
17
+ --success-color: #38c172;
18
+ --warning-color: #f7b731;
19
+ --info-color: #3490dc;
20
+ --card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
21
+ --header-gradient: linear-gradient(120deg, #0EA5A5, #088585);
22
+ --sidebar-gradient: linear-gradient(180deg, #1d2b36, #2d3a45);
23
+ --border-radius: 10px;
24
+ --transition-speed: 0.3s;
25
+ }
26
+
27
+ /* تعيين اتجاه النص من اليمين إلى اليسار للغة العربية */
28
+ body {
29
+ direction: rtl;
30
+ text-align: right;
31
+ font-family: 'Almarai', 'Tajawal', 'Cairo', sans-serif;
32
+ color: var(--text-dark);
33
+ background-color: #fafafa;
34
+ /* التوافق مع جميع المتصفحات والأجهزة */
35
+ -webkit-font-smoothing: antialiased;
36
+ -moz-osx-font-smoothing: grayscale;
37
+ text-rendering: optimizeLegibility;
38
+ }
39
+
40
+ /* أنماط العناوين */
41
+ h1, h2, h3, h4, h5, h6 {
42
+ font-family: 'Almarai', 'Tajawal', 'Cairo', sans-serif;
43
+ color: var(--text-dark);
44
+ font-weight: 700;
45
+ line-height: 1.4;
46
+ margin-bottom: 0.75rem;
47
+ }
48
+
49
+ /* أنماط العنوان الرئيسي بتدرج لوني */
50
+ .main-title {
51
+ background: var(--primary-gradient);
52
+ -webkit-background-clip: text;
53
+ background-clip: text;
54
+ color: transparent;
55
+ font-size: 2.25rem;
56
+ font-weight: 800;
57
+ text-align: center;
58
+ margin: 1.5rem 0;
59
+ padding: 0.5rem;
60
+ position: relative;
61
+ }
62
+
63
+ .main-title::after {
64
+ content: "";
65
+ position: absolute;
66
+ bottom: -5px;
67
+ left: 30%;
68
+ right: 30%;
69
+ height: 3px;
70
+ background: var(--primary-gradient);
71
+ border-radius: 3px;
72
+ }
73
+
74
+ /* أنماط ترويسة الصفحة محسنة */
75
+ .header-container {
76
+ display: flex;
77
+ flex-direction: row;
78
+ justify-content: space-between;
79
+ align-items: center;
80
+ padding: 1rem 1.5rem;
81
+ background: var(--header-gradient);
82
+ border-radius: var(--border-radius);
83
+ margin-bottom: 1.5rem;
84
+ box-shadow: var(--card-shadow);
85
+ color: white;
86
+ }
87
+
88
+ .header-title {
89
+ margin-right: 1.25rem;
90
+ }
91
+
92
+ .header-title h1 {
93
+ margin: 0;
94
+ font-size: 1.75rem;
95
+ color: white;
96
+ font-weight: 800;
97
+ }
98
+
99
+ .header-title p {
100
+ margin: 0.25rem 0 0 0;
101
+ font-size: 0.9rem;
102
+ color: rgba(255, 255, 255, 0.8);
103
+ font-weight: 400;
104
+ }
105
+
106
+ .header-info {
107
+ display: flex;
108
+ align-items: center;
109
+ }
110
+
111
+ .date-box {
112
+ display: flex;
113
+ background-color: rgba(255, 255, 255, 0.2);
114
+ border: 1px solid rgba(255, 255, 255, 0.3);
115
+ color: white;
116
+ border-radius: 8px;
117
+ padding: 0.5rem 0.75rem;
118
+ margin-left: 1rem;
119
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
120
+ backdrop-filter: blur(5px);
121
+ }
122
+
123
+ .date-day {
124
+ font-size: 1.75rem;
125
+ font-weight: bold;
126
+ margin-left: 0.5rem;
127
+ line-height: 1;
128
+ }
129
+
130
+ .date-info {
131
+ display: flex;
132
+ flex-direction: column;
133
+ font-size: 0.8rem;
134
+ }
135
+
136
+ .date-month {
137
+ font-weight: bold;
138
+ line-height: 1.2;
139
+ }
140
+
141
+ .date-year {
142
+ line-height: 1;
143
+ }
144
+
145
+ /* أنماط قائمة التنقل الجديدة */
146
+ .nav-menu {
147
+ background-color: white;
148
+ border-radius: var(--border-radius);
149
+ padding: 0.5rem;
150
+ box-shadow: var(--card-shadow);
151
+ margin: 1rem 0;
152
+ }
153
+
154
+ .nav-menu ul {
155
+ display: flex;
156
+ list-style: none;
157
+ padding: 0;
158
+ margin: 0;
159
+ justify-content: flex-end;
160
+ flex-wrap: wrap;
161
+ }
162
+
163
+ .nav-menu li {
164
+ margin: 0.25rem;
165
+ }
166
+
167
+ .nav-menu a {
168
+ display: flex;
169
+ align-items: center;
170
+ color: var(--text-medium);
171
+ text-decoration: none;
172
+ padding: 0.5rem 0.75rem;
173
+ border-radius: 6px;
174
+ transition: all var(--transition-speed);
175
+ font-weight: 500;
176
+ border: 1px solid transparent;
177
+ }
178
+
179
+ .nav-menu a:hover {
180
+ background-color: var(--primary-light);
181
+ color: var(--primary-color);
182
+ border-color: var(--primary-color);
183
+ transform: translateY(-2px);
184
+ }
185
+
186
+ .nav-icon {
187
+ margin-left: 0.5rem;
188
+ font-size: 1.25rem;
189
+ }
190
+
191
+ /* أنماط عنوان الوحدة */
192
+ .module-title {
193
+ color: var(--text-dark);
194
+ font-size: 1.75rem;
195
+ margin-bottom: 1.25rem;
196
+ border-right: 4px solid var(--primary-color);
197
+ padding-right: 0.75rem;
198
+ position: relative;
199
+ }
200
+
201
+ /* أنماط بطاقات المعلومات المحسنة */
202
+ .info-card {
203
+ background-color: white;
204
+ border-radius: var(--border-radius);
205
+ padding: 1.5rem;
206
+ margin-bottom: 1.25rem;
207
+ box-shadow: var(--card-shadow);
208
+ border-top: 4px solid var(--primary-color);
209
+ transition: transform var(--transition-speed);
210
+ }
211
+
212
+ .info-card:hover {
213
+ transform: translateY(-5px);
214
+ }
215
+
216
+ .info-card h3 {
217
+ color: var(--text-dark);
218
+ margin-top: 0;
219
+ margin-bottom: 0.75rem;
220
+ font-weight: 700;
221
+ }
222
+
223
+ .info-card p {
224
+ color: var(--text-medium);
225
+ margin: 0;
226
+ line-height: 1.6;
227
+ }
228
+
229
+ /* أنماط الجداول */
230
+ .dataframe {
231
+ width: 100%;
232
+ border-collapse: separate;
233
+ border-spacing: 0;
234
+ margin-bottom: 1.5rem;
235
+ border-radius: var(--border-radius);
236
+ overflow: hidden;
237
+ box-shadow: var(--card-shadow);
238
+ }
239
+
240
+ .dataframe th {
241
+ background-color: var(--primary-color);
242
+ color: white;
243
+ text-align: right;
244
+ padding: 0.75rem 1rem;
245
+ font-weight: 600;
246
+ border: none;
247
+ }
248
+
249
+ .dataframe td {
250
+ padding: 0.75rem 1rem;
251
+ border-bottom: 1px solid var(--border-color);
252
+ text-align: right;
253
+ background-color: white;
254
+ }
255
+
256
+ .dataframe tr:last-child td {
257
+ border-bottom: none;
258
+ }
259
+
260
+ .dataframe tr:nth-child(even) td {
261
+ background-color: rgba(248, 249, 250, 0.7);
262
+ }
263
+
264
+ .dataframe tr:hover td {
265
+ background-color: var(--primary-light);
266
+ }
267
+
268
+ /* أنماط الأزرار الجديدة */
269
+ button, .stButton>button {
270
+ background: var(--primary-gradient);
271
+ color: white;
272
+ border: none;
273
+ border-radius: 6px;
274
+ padding: 0.6rem 1.25rem;
275
+ cursor: pointer;
276
+ transition: all var(--transition-speed);
277
+ font-weight: 600;
278
+ font-size: 0.95rem;
279
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
280
+ display: inline-flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ }
284
+
285
+ button:hover, .stButton>button:hover {
286
+ background: var(--primary-dark);
287
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
288
+ transform: translateY(-2px);
289
+ }
290
+
291
+ button:active, .stButton>button:active {
292
+ transform: translateY(0);
293
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
294
+ }
295
+
296
+ /* أزرار ثانوية */
297
+ .btn-secondary, .stButton.secondary>button {
298
+ background: white;
299
+ color: var(--primary-color);
300
+ border: 1px solid var(--primary-color);
301
+ }
302
+
303
+ .btn-secondary:hover, .stButton.secondary>button:hover {
304
+ background: var(--primary-light);
305
+ }
306
+
307
+ /* أنماط المخططات */
308
+ .plot-container {
309
+ background-color: white;
310
+ border-radius: var(--border-radius);
311
+ padding: 1rem;
312
+ margin: 1.25rem 0;
313
+ box-shadow: var(--card-shadow);
314
+ }
315
+
316
+ /* أنماط بطاقات الابتكارات */
317
+ .innovation-card {
318
+ background-color: white;
319
+ border-radius: var(--border-radius);
320
+ padding: 1.5rem;
321
+ margin-bottom: 1.25rem;
322
+ border-right: 4px solid var(--primary-color);
323
+ box-shadow: var(--card-shadow);
324
+ transition: transform var(--transition-speed);
325
+ }
326
+
327
+ .innovation-card:hover {
328
+ transform: translateY(-5px);
329
+ }
330
+
331
+ .innovation-icon {
332
+ font-size: 2rem;
333
+ margin-bottom: 0.75rem;
334
+ background: var(--primary-gradient);
335
+ -webkit-background-clip: text;
336
+ background-clip: text;
337
+ color: transparent;
338
+ }
339
+
340
+ .innovation-card h3 {
341
+ color: var(--text-dark);
342
+ margin-bottom: 0.75rem;
343
+ font-weight: 700;
344
+ }
345
+
346
+ .innovation-card p {
347
+ color: var(--text-medium);
348
+ font-size: 0.95rem;
349
+ line-height: 1.6;
350
+ }
351
+
352
+ /* أنماط فريق التطوير */
353
+ .team-section {
354
+ display: flex;
355
+ flex-wrap: wrap;
356
+ justify-content: center;
357
+ gap: 1.5rem;
358
+ margin: 2rem 0;
359
+ }
360
+
361
+ .team-member {
362
+ text-align: center;
363
+ margin-bottom: 1.25rem;
364
+ background-color: white;
365
+ border-radius: var(--border-radius);
366
+ padding: 1.5rem;
367
+ box-shadow: var(--card-shadow);
368
+ transition: transform var(--transition-speed);
369
+ width: 230px;
370
+ }
371
+
372
+ .team-member:hover {
373
+ transform: translateY(-5px);
374
+ }
375
+
376
+ .team-member h3 {
377
+ color: var(--text-dark);
378
+ margin-bottom: 0.3rem;
379
+ font-size: 1.1rem;
380
+ font-weight: 700;
381
+ }
382
+
383
+ .team-member h4 {
384
+ color: var(--primary-color);
385
+ margin-top: 0;
386
+ margin-bottom: 0.75rem;
387
+ font-size: 0.9rem;
388
+ }
389
+
390
+ .team-member p {
391
+ color: var(--text-medium);
392
+ font-size: 0.85rem;
393
+ line-height: 1.5;
394
+ }
395
+
396
+ .avatar {
397
+ background: var(--primary-gradient);
398
+ color: white;
399
+ width: 90px;
400
+ height: 90px;
401
+ border-radius: 50%;
402
+ display: flex;
403
+ justify-content: center;
404
+ align-items: center;
405
+ margin: 0 auto 1rem auto;
406
+ font-size: 2rem;
407
+ font-weight: 700;
408
+ box-shadow: 0 5px 15px rgba(14, 165, 165, 0.3);
409
+ }
410
+
411
+ /* أنماط تذييل الصفحة */
412
+ .footer {
413
+ text-align: center;
414
+ color: var(--text-light);
415
+ font-size: 0.85rem;
416
+ margin-top: 2rem;
417
+ margin-bottom: 1rem;
418
+ padding: 1rem;
419
+ border-top: 1px solid var(--border-color);
420
+ }
421
+
422
+ /* أنماط رسائل التنبيه المحسنة */
423
+ .alert {
424
+ padding: 1rem 1.25rem;
425
+ border-radius: var(--border-radius);
426
+ margin-bottom: 1rem;
427
+ position: relative;
428
+ border-right: 4px solid;
429
+ }
430
+
431
+ .alert-icon {
432
+ margin-left: 0.5rem;
433
+ font-size: 1.25rem;
434
+ }
435
+
436
+ .alert-info {
437
+ background-color: rgba(52, 144, 220, 0.1);
438
+ color: var(--info-color);
439
+ border-right-color: var(--info-color);
440
+ }
441
+
442
+ .alert-success {
443
+ background-color: rgba(56, 193, 114, 0.1);
444
+ color: var(--success-color);
445
+ border-right-color: var(--success-color);
446
+ }
447
+
448
+ .alert-warning {
449
+ background-color: rgba(247, 183, 49, 0.1);
450
+ color: var(--warning-color);
451
+ border-right-color: var(--warning-color);
452
+ }
453
+
454
+ .alert-danger {
455
+ background-color: rgba(227, 52, 47, 0.1);
456
+ color: var(--danger-color);
457
+ border-right-color: var(--danger-color);
458
+ }
459
+
460
+ /* أنماط التبويبات المحسنة */
461
+ .stTabs [data-baseweb="tab-list"] {
462
+ gap: 1px;
463
+ background-color: var(--background-light);
464
+ border-radius: var(--border-radius);
465
+ padding: 0.3rem;
466
+ }
467
+
468
+ .stTabs [data-baseweb="tab"] {
469
+ height: 50px;
470
+ white-space: pre-wrap;
471
+ background-color: white;
472
+ border-radius: 6px;
473
+ gap: 1px;
474
+ padding: 0.6rem 1rem;
475
+ font-family: 'Almarai', 'Tajawal', sans-serif;
476
+ font-weight: 500;
477
+ transition: all var(--transition-speed);
478
+ }
479
+
480
+ .stTabs [aria-selected="true"] {
481
+ background: var(--primary-gradient) !important;
482
+ color: white !important;
483
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
484
+ }
485
+
486
+ /* أنماط مخصصة لدعم اللغة العربية في إدخالات النصوص والأرقام */
487
+ input, textarea, .stTextInput>div>div>input, .stNumberInput>div>div>input {
488
+ direction: rtl;
489
+ text-align: right;
490
+ font-family: 'Almarai', 'Tajawal', sans-serif;
491
+ font-size: 0.95rem;
492
+ padding: 0.6rem 0.75rem;
493
+ border-radius: 6px;
494
+ border: 1px solid var(--border-color);
495
+ transition: border-color var(--transition-speed);
496
+ }
497
+
498
+ input:focus, textarea:focus, .stTextInput>div>div>input:focus, .stNumberInput>div>div>input:focus {
499
+ outline: none;
500
+ border-color: var(--primary-color);
501
+ box-shadow: 0 0 0 1px var(--primary-light);
502
+ }
503
+
504
+ /* أنماط قائمة الخيارات */
505
+ .stSelectbox [data-baseweb="select"] {
506
+ direction: rtl;
507
+ text-align: right;
508
+ font-family: 'Almarai', 'Tajawal', sans-serif;
509
+ }
510
+
511
+ /* أنماط للنسخة المحمولة والاستجابة للشاشات */
512
+ @media (max-width: 992px) {
513
+ .main-title {
514
+ font-size: 1.75rem;
515
+ }
516
+
517
+ .header-container {
518
+ padding: 1rem;
519
+ }
520
+
521
+ .header-title h1 {
522
+ font-size: 1.5rem;
523
+ }
524
+ }
525
+
526
+ @media (max-width: 768px) {
527
+ .header-container {
528
+ flex-direction: column;
529
+ align-items: flex-start;
530
+ }
531
+
532
+ .header-info {
533
+ margin-top: 1rem;
534
+ width: 100%;
535
+ justify-content: flex-start;
536
+ }
537
+
538
+ .nav-menu ul {
539
+ flex-wrap: wrap;
540
+ justify-content: flex-start;
541
+ }
542
+
543
+ .nav-menu li {
544
+ margin-bottom: 0.5rem;
545
+ margin-left: 0.5rem;
546
+ width: calc(50% - 1rem);
547
+ }
548
+
549
+ .module-title {
550
+ font-size: 1.5rem;
551
+ }
552
+
553
+ .main-title {
554
+ font-size: 1.4rem;
555
+ }
556
+
557
+ .innovation-card, .info-card {
558
+ margin-bottom: 1rem;
559
+ padding: 1rem;
560
+ }
561
+
562
+ .dataframe {
563
+ display: block;
564
+ overflow-x: auto;
565
+ }
566
+
567
+ .team-member {
568
+ width: 100%;
569
+ }
570
+ }
571
+
572
+ @media (max-width: 480px) {
573
+ .main-title {
574
+ font-size: 1.25rem;
575
+ }
576
+
577
+ .header-title h1 {
578
+ font-size: 1.25rem;
579
+ }
580
+
581
+ .nav-menu li {
582
+ width: 100%;
583
+ margin-left: 0;
584
+ }
585
+ }
586
+
587
+ /* تحسينات خاصة للأيفون والأجهزة ذات الشاشات الصغيرة */
588
+ @media only screen and (max-width: 375px) {
589
+ .nav-menu {
590
+ margin: 0.5rem 0;
591
+ }
592
+
593
+ button, .stButton>button {
594
+ padding: 0.5rem 0.75rem;
595
+ font-size: 0.9rem;
596
+ }
597
+
598
+ .module-title, .info-card h3, .innovation-card h3 {
599
+ font-size: 1.1rem;
600
+ }
601
+ }
602
+
603
+ /* أضف كلاس للأيقونات */
604
+ .icon {
605
+ font-size: 1.1rem;
606
+ margin-left: 0.5rem;
607
+ }
608
+
609
+ /* أنماط للصور والخلفيات */
610
+ .bg-light {
611
+ background-color: var(--background-light);
612
+ }
613
+
614
+ .card {
615
+ background: white;
616
+ border-radius: var(--border-radius);
617
+ box-shadow: var(--card-shadow);
618
+ padding: 1.25rem;
619
+ margin-bottom: 1.25rem;
620
+ }
621
+
622
+ /* قسم معلومات النظام */
623
+ .about-system {
624
+ margin: 2rem 0;
625
+ background: white;
626
+ border-radius: var(--border-radius);
627
+ box-shadow: var(--card-shadow);
628
+ padding: 1.5rem;
629
+ }
630
+
631
+ .about-system h2 {
632
+ color: var(--primary-color);
633
+ margin-bottom: 1rem;
634
+ padding-bottom: 0.5rem;
635
+ border-bottom: 2px solid var(--primary-light);
636
+ }
637
+
638
+ .about-system p {
639
+ line-height: 1.6;
640
+ margin-bottom: 1rem;
641
+ }
642
+
643
+ .about-system ul {
644
+ padding-right: 1.5rem;
645
+ margin-bottom: 1rem;
646
+ }
647
+
648
+ .about-system li {
649
+ margin-bottom: 0.5rem;
650
+ line-height: 1.6;
651
+ }
652
+
653
+ /* أنماط مؤشر التقدم */
654
+ .progress {
655
+ height: 0.5rem;
656
+ overflow: hidden;
657
+ background-color: var(--background-light);
658
+ border-radius: 0.25rem;
659
+ margin: 0.5rem 0 1rem 0;
660
+ }
661
+
662
+ .progress-bar {
663
+ height: 100%;
664
+ border-radius: 0.25rem;
665
+ background: var(--primary-gradient);
666
+ }
667
+
668
+ /* المؤقت للمواعيد النهائية */
669
+ .countdown-timer {
670
+ display: flex;
671
+ justify-content: center;
672
+ gap: 1rem;
673
+ margin: 1.5rem 0;
674
+ }
675
+
676
+ .time-block {
677
+ background: white;
678
+ border-radius: var(--border-radius);
679
+ padding: 0.75rem 1rem;
680
+ text-align: center;
681
+ min-width: 80px;
682
+ box-shadow: var(--card-shadow);
683
+ }
684
+
685
+ .time-value {
686
+ font-size: 1.75rem;
687
+ font-weight: 700;
688
+ color: var(--primary-color);
689
+ line-height: 1;
690
+ }
691
+
692
+ .time-label {
693
+ font-size: 0.8rem;
694
+ color: var(--text-medium);
695
+ margin-top: 0.25rem;
696
+ }
697
+
698
+ /* إعدادات المستخدم */
699
+ .settings-form {
700
+ background: white;
701
+ border-radius: var(--border-radius);
702
+ padding: 1.5rem;
703
+ box-shadow: var(--card-shadow);
704
+ }
705
+
706
+ .settings-group {
707
+ margin-bottom: 1.5rem;
708
+ }
709
+
710
+ .settings-group h3 {
711
+ font-size: 1.2rem;
712
+ margin-bottom: 1rem;
713
+ padding-bottom: 0.5rem;
714
+ border-bottom: 1px solid var(--border-color);
715
+ }
716
+
717
+ .settings-item {
718
+ margin-bottom: 1rem;
719
+ }
720
+
721
+ .settings-item label {
722
+ display: block;
723
+ margin-bottom: 0.5rem;
724
+ font-weight: 500;
725
+ }
726
+
727
+ /* أنماط للتسعير المتقدم */
728
+ .pricing-grid {
729
+ display: grid;
730
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
731
+ gap: 1.25rem;
732
+ margin: 1.5rem 0;
733
+ }
734
+
735
+ .price-card {
736
+ background: white;
737
+ border-radius: var(--border-radius);
738
+ padding: 1.25rem;
739
+ box-shadow: var(--card-shadow);
740
+ display: flex;
741
+ flex-direction: column;
742
+ }
743
+
744
+ .price-header {
745
+ margin-bottom: 1rem;
746
+ padding-bottom: 0.5rem;
747
+ border-bottom: 1px solid var(--border-color);
748
+ }
749
+
750
+ .price-value {
751
+ font-size: 1.5rem;
752
+ font-weight: 700;
753
+ color: var(--primary-color);
754
+ margin: 0.5rem 0;
755
+ }
756
+
757
+ .price-details {
758
+ flex-grow: 1;
759
+ }
760
+
761
+ .price-details ul {
762
+ padding-right: 1.25rem;
763
+ margin-bottom: 1rem;
764
+ }
765
+
766
+ .price-details li {
767
+ margin-bottom: 0.5rem;
768
+ line-height: 1.5;
769
+ }
770
+
771
+ .price-footer {
772
+ margin-top: auto;
773
+ padding-top: 1rem;
774
+ }
775
+
776
+ /* أنماط إضافية للأيقونات والرموز */
777
+ .colored-icon {
778
+ background: var(--primary-gradient);
779
+ -webkit-background-clip: text;
780
+ background-clip: text;
781
+ color: transparent;
782
+ }
783
+
784
+ /* أنماط الشعار */
785
+ .logo {
786
+ display: flex;
787
+ align-items: center;
788
+ }
789
+
790
+ .logo-text {
791
+ font-weight: 800;
792
+ font-size: 1.25rem;
793
+ margin-right: 0.5rem;
794
+ background: var(--primary-gradient);
795
+ -webkit-background-clip: text;
796
+ background-clip: text;
797
+ color: transparent;
798
+ }
799
+
800
+ /* كلاس للخط العريض */
801
+ .bold {
802
+ font-weight: 700;
803
+ }
804
+
805
+ /* تحسين مظهر مخطط مرمايد للنظام */
806
+ .mermaid {
807
+ margin: 1.5rem 0;
808
+ }
809
+
810
+ /* تعديلات على شكل العنصر الجانبي */
811
+ .stSidebar {
812
+ background-color: white;
813
+ box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
814
+ }
815
+
816
+ [data-testid="stSidebarContent"] {
817
+ background: var(--sidebar-gradient);
818
+ }
819
+
820
+ /* تحسين شكل لوحة المعلومات */
821
+ .dashboard-card {
822
+ background: white;
823
+ border-radius: var(--border-radius);
824
+ padding: 1rem;
825
+ box-shadow: var(--card-shadow);
826
+ height: 100%;
827
+ position: relative;
828
+ overflow: hidden;
829
+ }
830
+
831
+ .dashboard-card::before {
832
+ content: "";
833
+ position: absolute;
834
+ top: 0;
835
+ left: 0;
836
+ right: 0;
837
+ height: 4px;
838
+ background: var(--primary-gradient);
839
+ }
840
+
841
+ .dashboard-value {
842
+ font-size: 2rem;
843
+ font-weight: 800;
844
+ color: var(--primary-color);
845
+ margin: 0.5rem 0;
846
+ }
847
+
848
+ .dashboard-title {
849
+ color: var(--text-medium);
850
+ font-size: 0.9rem;
851
+ margin-bottom: 0.5rem;
852
+ font-weight: 600;
853
+ }
854
+
855
+ .dashboard-change {
856
+ font-size: 0.8rem;
857
+ display: flex;
858
+ align-items: center;
859
+ }
860
+
861
+ .change-up {
862
+ color: var(--success-color);
863
+ }
864
+
865
+ .change-down {
866
+ color: var(--danger-color);
867
+ }
868
+
869
+ /* بالنسبة للشركة */
870
+ .company-info {
871
+ text-align: center;
872
+ background: white;
873
+ padding: 1rem;
874
+ border-radius: var(--border-radius);
875
+ margin: 1.5rem 0;
876
+ box-shadow: var(--card-shadow);
877
+ }
878
+
879
+ .company-logo {
880
+ max-width: 150px;
881
+ margin: 0 auto 1rem auto;
882
+ }
883
+
884
+ .company-name {
885
+ font-size: 1.2rem;
886
+ font-weight: 700;
887
+ color: var(--text-dark);
888
+ margin-bottom: 0.5rem;
889
+ }
890
+
891
+ .company-slogan {
892
+ font-size: 0.9rem;
893
+ color: var(--text-medium);
894
+ margin-bottom: 1rem;
895
+ }
896
+
897
+ .company-contact {
898
+ display: flex;
899
+ justify-content: center;
900
+ gap: 1rem;
901
+ margin-top: 1rem;
902
+ }
903
+
904
+ .contact-item {
905
+ display: flex;
906
+ align-items: center;
907
+ font-size: 0.85rem;
908
+ color: var(--text-medium);
909
+ }
910
+
911
+ .contact-icon {
912
+ margin-left: 0.3rem;
913
+ }
static/css/backup/rtl-fixes.css ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* تصحيحات RTL للتوافق مع streamlit */
2
+
3
+ /* تصحيحات عامة */
4
+ .stApp {
5
+ direction: rtl;
6
+ text-align: right;
7
+ }
8
+
9
+ /* تصحيحات القوائم */
10
+ .st-emotion-cache-r421ms.e1f1d6gn0 {
11
+ padding-right: 0;
12
+ padding-left: 1em;
13
+ }
14
+
15
+ /* تصحيحات العناوين */
16
+ h1, h2, h3, h4, h5, h6, .st-ae, .st-af, .st-ag, .st-ah, .st-ai, .st-aj {
17
+ text-align: right;
18
+ }
19
+
20
+ /* تصحيحات الجداول */
21
+ .stDataFrame {
22
+ direction: rtl;
23
+ }
24
+
25
+ .stDataFrame .dataframe {
26
+ text-align: right;
27
+ }
28
+
29
+ /* تصحيحات الأزرار */
30
+ .stButton button {
31
+ margin-right: 0;
32
+ margin-left: 0.5em;
33
+ }
34
+
35
+ /* تصحيحات شريط التمرير */
36
+ .stSlider div[data-baseweb="slider"] {
37
+ direction: ltr;
38
+ }
39
+
40
+ /* تصحيحات الاختيار المتعدد */
41
+ .stMultiSelect [data-baseweb="tag"] {
42
+ margin-right: 0;
43
+ margin-left: 0.5em;
44
+ }
45
+
46
+ /* تصحيحات الأعمدة */
47
+ .row-widget.stRadio > div {
48
+ flex-direction: row-reverse;
49
+ }
50
+
51
+ .row-widget.stCheckbox > div {
52
+ flex-direction: row-reverse;
53
+ }
54
+
55
+ /* تصحيحات مربع النص */
56
+ .stTextInput input {
57
+ text-
static/css/backup/styles.css ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* أنماط CSS للنظام */
2
+
3
+ /* تعيين اتجاه النص من اليمين إلى اليسار للغة العربية */
4
+ body {
5
+ direction: rtl;
6
+ text-align: right;
7
+ font-family: 'Tajawal', 'Cairo', sans-serif;
8
+ }
9
+
10
+ /* أنماط العناوين */
11
+ h1, h2, h3, h4, h5, h6 {
12
+ font-family: 'Tajawal', 'Cairo', sans-serif;
13
+ color: #333;
14
+ }
15
+
16
+ /* أنماط ترويسة الصفحة */
17
+ .header-container {
18
+ display: flex;
19
+ flex-direction: row;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+ padding: 10px 0;
23
+ background-color: #f8f9fa;
24
+ border-radius: 10px;
25
+ margin-bottom: 20px;
26
+ }
27
+
28
+ .header-title {
29
+ margin-right: 20px;
30
+ }
31
+
32
+ .header-title h1 {
33
+ margin: 0;
34
+ font-size: 24px;
35
+ color: #2c3e50;
36
+ }
37
+
38
+ .header-title p {
39
+ margin: 0;
40
+ font-size: 14px;
41
+ color: #7f8c8d;
42
+ }
43
+
44
+ .header-info {
45
+ display: flex;
46
+ align-items: center;
47
+ }
48
+
49
+ .date-box {
50
+ display: flex;
51
+ background-color: #ff9a3c;
52
+ color: white;
53
+ border-radius: 8px;
54
+ padding: 5px 10px;
55
+ margin-left: 15px;
56
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
57
+ }
58
+
59
+ .date-day {
60
+ font-size: 24px;
61
+ font-weight: bold;
62
+ margin-left: 5px;
63
+ line-height: 1;
64
+ }
65
+
66
+ .date-info {
67
+ display: flex;
68
+ flex-direction: column;
69
+ font-size: 12px;
70
+ }
71
+
72
+ .date-month {
73
+ font-weight: bold;
74
+ line-height: 1.2;
75
+ }
76
+
77
+ .date-year {
78
+ line-height: 1;
79
+ }
80
+
81
+ /* أنماط قائمة التنقل */
82
+ .nav-menu {
83
+ margin: 10px 0;
84
+ }
85
+
86
+ .nav-menu ul {
87
+ display: flex;
88
+ list-style: none;
89
+ padding: 0;
90
+ margin: 0;
91
+ justify-content: flex-end;
92
+ }
93
+
94
+ .nav-menu li {
95
+ margin-left: 15px;
96
+ }
97
+
98
+ .nav-menu a {
99
+ display: flex;
100
+ align-items: center;
101
+ color: #2c3e50;
102
+ text-decoration: none;
103
+ padding: 5px 10px;
104
+ border-radius: 5px;
105
+ transition: background-color 0.3s;
106
+ }
107
+
108
+ .nav-menu a:hover {
109
+ background-color: #f0f0f0;
110
+ }
111
+
112
+ .nav-icon {
113
+ margin-left: 5px;
114
+ }
115
+
116
+ /* أنماط عنوان الوحدة */
117
+ .module-title {
118
+ color: #2c3e50;
119
+ font-size: 28px;
120
+ margin-bottom: 20px;
121
+ border-right: 5px solid #ff9a3c;
122
+ padding-right: 10px;
123
+ }
124
+
125
+ .main-title {
126
+ color: #2c3e50;
127
+ font-size: 32px;
128
+ text-align: center;
129
+ margin: 20px 0;
130
+ }
131
+
132
+ /* أنماط بطاقات المعلومات */
133
+ .info-card {
134
+ background-color: #f8f9fa;
135
+ border-radius: 10px;
136
+ padding: 20px;
137
+ margin-bottom: 20px;
138
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
139
+ }
140
+
141
+ .info-card h3 {
142
+ color: #333;
143
+ margin-top: 0;
144
+ margin-bottom: 10px;
145
+ }
146
+
147
+ .info-card p {
148
+ color: #666;
149
+ margin: 0;
150
+ }
151
+
152
+ /* أنماط الجداول */
153
+ .dataframe {
154
+ width: 100%;
155
+ border-collapse: collapse;
156
+ margin-bottom: 20px;
157
+ }
158
+
159
+ .dataframe th {
160
+ background-color: #f0f0f0;
161
+ color: #333;
162
+ text-align: right;
163
+ padding: 8px;
164
+ border: 1px solid #ddd;
165
+ }
166
+
167
+ .dataframe td {
168
+ padding: 8px;
169
+ border: 1px solid #ddd;
170
+ text-align: right;
171
+ }
172
+
173
+ .dataframe tr:nth-child(even) {
174
+ background-color: #f9f9f9;
175
+ }
176
+
177
+ .dataframe tr:hover {
178
+ background-color: #f0f0f0;
179
+ }
180
+
181
+ /* أنماط الأزرار */
182
+ button, .stButton>button {
183
+ background-color: #ff9a3c;
184
+ color: white;
185
+ border: none;
186
+ border-radius: 4px;
187
+ padding: 8px 16px;
188
+ cursor: pointer;
189
+ transition: background-color 0.3s;
190
+ }
191
+
192
+ button:hover, .stButton>button:hover {
193
+ background-color: #e67e22;
194
+ }
195
+
196
+ /* أنماط المخططات */
197
+ .plot-container {
198
+ margin: 20px 0;
199
+ }
200
+
201
+ /* أنماط بطاقات الابتكارات */
202
+ .innovation-card {
203
+ background-color: #f8f9fa;
204
+ border-radius: 10px;
205
+ padding: 15px;
206
+ margin-bottom: 20px;
207
+ border-right: 5px solid #ff9a3c;
208
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
209
+ }
210
+
211
+ .innovation-icon {
212
+ font-size: 24px;
213
+ margin-bottom: 10px;
214
+ }
215
+
216
+ .innovation-card h3 {
217
+ color: #333;
218
+ margin-bottom: 10px;
219
+ }
220
+
221
+ .innovation-card p {
222
+ color: #666;
223
+ font-size: 14px;
224
+ }
225
+
226
+ /* أنماط فريق التطوير */
227
+ .team-member {
228
+ text-align: center;
229
+ margin-bottom: 20px;
230
+ }
231
+
232
+ .team-member h3 {
233
+ color: #333;
234
+ margin-bottom: 5px;
235
+ font-size: 18px;
236
+ }
237
+
238
+ .team-member h4 {
239
+ color: #ff9a3c;
240
+ margin-top: 0;
241
+ margin-bottom: 10px;
242
+ font-size: 14px;
243
+ }
244
+
245
+ .team-member p {
246
+ color: #666;
247
+ font-size: 12px;
248
+ }
249
+
250
+ .avatar {
251
+ background-color: #ff9a3c;
252
+ color: white;
253
+ width: 100px;
254
+ height: 100px;
255
+ border-radius: 50%;
256
+ display: flex;
257
+ justify-content: center;
258
+ align-items: center;
259
+ margin: 0 auto 15px auto;
260
+ font-size: 36px;
261
+ }
262
+
263
+ /* أنماط تذييل الصفحة */
264
+ .footer {
265
+ text-align: center;
266
+ color: #7f8c8d;
267
+ font-size: 12px;
268
+ margin-top: 30px;
269
+ margin-bottom: 10px;
270
+ }
271
+
272
+ /* أنماط رسائل التنبيه */
273
+ .alert {
274
+ padding: 10px 15px;
275
+ border-radius: 4px;
276
+ margin-bottom: 15px;
277
+ }
278
+
279
+ .alert-info {
280
+ background-color: #d1ecf1;
281
+ color: #0c5460;
282
+ border: 1px solid #bee5eb;
283
+ }
284
+
285
+ .alert-success {
286
+ background-color: #d4edda;
287
+ color: #155724;
288
+ border: 1px solid #c3e6cb;
289
+ }
290
+
291
+ .alert-warning {
292
+ background-color: #fff3cd;
293
+ color: #856404;
294
+ border: 1px solid #ffeeba;
295
+ }
296
+
297
+ .alert-danger {
298
+ background-color: #f8d7da;
299
+ color: #721c24;
300
+ border: 1px solid #f5c6cb;
301
+ }
302
+
303
+ /* أنماط الأيقونات */
304
+ .icon {
305
+ font-size: 18px;
306
+ margin-left: 5px;
307
+ }
308
+
309
+ /* أنماط التبويبات */
310
+ .stTabs [data-baseweb="tab-list"] {
311
+ gap: 1px;
312
+ }
313
+
314
+ .stTabs [data-baseweb="tab"] {
315
+ height: 50px;
316
+ white-space: pre-wrap;
317
+ background-color: white;
318
+ border-radius: 4px 4px 0 0;
319
+ gap: 1px;
320
+ padding-top: 10px;
321
+ padding-bottom: 10px;
322
+ }
323
+
324
+ .stTabs [aria-selected="true"] {
325
+ background-color: #ff9a3c !important;
326
+ color: white !important;
327
+ }
328
+
329
+ /* أنماط مخصصة لدعم اللغة العربية في إدخالات النصوص والأرقام */
330
+ input, textarea, .stTextInput>div>div>input, .stNumberInput>div>div>input {
331
+ direction: rtl;
332
+ text-align: right;
333
+ }
334
+
335
+ /* أنماط قائمة الخيارات */
336
+ .stSelectbox [data-baseweb="select"] {
337
+ direction: rtl;
338
+ text-align: right;
339
+ }
340
+
341
+ /* أنماط تحرير البيانات */
342
+ .stDataEditor {
343
+ direction: rtl;
344
+ }
345
+
346
+ /* أنماط للنسخة المحمولة */
347
+ @media (max-width: 768px) {
348
+ .header-container {
349
+ flex-direction: column;
350
+ align-items: flex-start;
351
+ }
352
+
353
+ .header-info {
354
+ margin-top: 10px;
355
+ }
356
+
357
+ .nav-menu ul {
358
+ flex-wrap: wrap;
359
+ }
360
+
361
+ .nav-menu li {
362
+ margin-bottom: 5px;
363
+ margin-left: 10px;
364
+ }
365
+
366
+ .module-title {
367
+ font-size: 24px;
368
+ }
369
+
370
+ .main-title {
371
+ font-size: 24px;
372
+ }
373
+ }
static/css/rtl-fixes.css CHANGED
@@ -1,66 +1,57 @@
1
- /* إصلاحات لدعم اللغة العربية والعرض من اليمين إلى اليسار */
2
-
3
- /* نقل الشريط الجانبي من اليسار إلى اليمين */
4
- [data-testid="stSidebar"] {
5
- right: 0;
6
- left: unset !important;
7
- }
8
-
9
- section[data-testid="stSidebarContent"] {
10
- direction: rtl !important;
11
- text-align: right !important;
12
- }
13
-
14
- /* تعديل الاتجاه للمحتوى الرئيسي */
15
- .main .block-container {
16
- direction: rtl;
17
- text-align: right;
18
- margin-left: 0;
19
- margin-right: 21rem;
20
- max-width: calc(100% - 21rem);
21
- }
22
-
23
- @media (max-width: 768px) {
24
- .main .block-container {
25
- margin-right: 0;
26
- max-width: 100%;
27
- }
28
- }
29
-
30
- /* تصحيح اتجاه النصوص والعناصر */
31
- .streamlit-expanderHeader,
32
- .stRadio > div,
33
- .stCheckbox > div,
34
- .stSelectbox > div,
35
- .stTextInput > div {
36
- direction: rtl;
37
- text-align: right;
38
- }
39
-
40
- /* تعديل قائمة الخيارات في streamlit_option_menu */
41
- .nav-link {
42
- text-align: right !important;
43
- }
44
-
45
- /* تحسين ظهور الجداول */
46
- [data-testid="stTable"] {
47
- direction: rtl;
48
- }
49
-
50
- /* تصحيح أزرار الأرقام والتواريخ */
51
- .stNumberInput [data-baseweb="input"],
52
- .stDateInput [data-baseweb="input"] {
53
- direction: ltr;
54
- }
55
-
56
- /* جعل عناصر الملاحظات والخانات النصية تدعم العربية */
57
- [data-testid="stMarkdown"], textarea {
58
- direction: rtl;
59
- text-align: right;
60
- }
61
-
62
- /* تحسين ظهور قوائم الاختيار */
63
- .stMultiSelect div:first-child,
64
- .stSelectbox div:first-child {
65
- text-align: right;
66
- }
 
1
+ /* تصحيحات RTL للتوافق مع streamlit */
2
+
3
+ /* تصحيحات عامة */
4
+ .stApp {
5
+ direction: rtl;
6
+ text-align: right;
7
+ }
8
+
9
+ /* تصحيحات القوائم */
10
+ .st-emotion-cache-r421ms.e1f1d6gn0 {
11
+ padding-right: 0;
12
+ padding-left: 1em;
13
+ }
14
+
15
+ /* تصحيحات العناوين */
16
+ h1, h2, h3, h4, h5, h6, .st-ae, .st-af, .st-ag, .st-ah, .st-ai, .st-aj {
17
+ text-align: right;
18
+ }
19
+
20
+ /* تصحيحات الجداول */
21
+ .stDataFrame {
22
+ direction: rtl;
23
+ }
24
+
25
+ .stDataFrame .dataframe {
26
+ text-align: right;
27
+ }
28
+
29
+ /* تصحيحات الأزرار */
30
+ .stButton button {
31
+ margin-right: 0;
32
+ margin-left: 0.5em;
33
+ }
34
+
35
+ /* تصحيحات شريط التمرير */
36
+ .stSlider div[data-baseweb="slider"] {
37
+ direction: ltr;
38
+ }
39
+
40
+ /* تصحيحات الاختيار المتعدد */
41
+ .stMultiSelect [data-baseweb="tag"] {
42
+ margin-right: 0;
43
+ margin-left: 0.5em;
44
+ }
45
+
46
+ /* تصحيحات الأعمدة */
47
+ .row-widget.stRadio > div {
48
+ flex-direction: row-reverse;
49
+ }
50
+
51
+ .row-widget.stCheckbox > div {
52
+ flex-direction: row-reverse;
53
+ }
54
+
55
+ /* تصحيحات مربع النص */
56
+ .stTextInput input {
57
+ text-
 
 
 
 
 
 
 
 
 
static/css/unified_design_system.css ADDED
@@ -0,0 +1,488 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* نظام تصميم موحد لنظام تحليل المناقصات */
2
+ @import url('https://fonts.googleapis.com/css2?family=Almarai:wght@300;400;700;800&display=swap');
3
+
4
+ :root {
5
+ /* الألوان الأساسية */
6
+ --primary-color: #2563eb;
7
+ --primary-light: #dbeafe;
8
+ --primary-dark: #1e40af;
9
+ --secondary-color: #10b981;
10
+ --secondary-light: #d1fae5;
11
+ --secondary-dark: #047857;
12
+
13
+ /* ألوان النص */
14
+ --text-dark: #1e293b;
15
+ --text-medium: #475569;
16
+ --text-light: #94a3b8;
17
+ --text-white: #ffffff;
18
+
19
+ /* ألوان الخلفية */
20
+ --background-light: #f8fafc;
21
+ --background-white: #ffffff;
22
+ --background-dark: #0f172a;
23
+
24
+ /* ألوان الحدود */
25
+ --border-color: #e2e8f0;
26
+ --border-color-dark: #cbd5e1;
27
+
28
+ /* ألوان الحالة */
29
+ --success-color: #22c55e;
30
+ --warning-color: #f59e0b;
31
+ --danger-color: #ef4444;
32
+ --info-color: #3b82f6;
33
+
34
+ /* الظلال */
35
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
36
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
37
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
38
+
39
+ /* نصف قطر الحدود */
40
+ --border-radius-sm: 0.25rem;
41
+ --border-radius-md: 0.5rem;
42
+ --border-radius-lg: 0.75rem;
43
+ --border-radius-xl: 1rem;
44
+ --border-radius-full: 9999px;
45
+
46
+ /* المسافات */
47
+ --spacing-xs: 0.25rem;
48
+ --spacing-sm: 0.5rem;
49
+ --spacing-md: 1rem;
50
+ --spacing-lg: 1.5rem;
51
+ --spacing-xl: 2rem;
52
+ --spacing-2xl: 3rem;
53
+
54
+ /* الخط */
55
+ --font-family: 'Almarai', sans-serif;
56
+ --font-size-xs: 0.75rem;
57
+ --font-size-sm: 0.875rem;
58
+ --font-size-md: 1rem;
59
+ --font-size-lg: 1.125rem;
60
+ --font-size-xl: 1.25rem;
61
+ --font-size-2xl: 1.5rem;
62
+ --font-size-3xl: 1.875rem;
63
+ --font-size-4xl: 2.25rem;
64
+
65
+ /* الانتقالات */
66
+ --transition-fast: 150ms;
67
+ --transition-normal: 300ms;
68
+ --transition-slow: 500ms;
69
+ }
70
+
71
+ /* إعدادات عامة */
72
+ body {
73
+ font-family: var(--font-family);
74
+ color: var(--text-dark);
75
+ background-color: var(--background-light);
76
+ direction: rtl;
77
+ text-align: right;
78
+ line-height: 1.5;
79
+ }
80
+
81
+ .stApp {
82
+ direction: rtl;
83
+ }
84
+
85
+ /* تصحيحات RTL لـ Streamlit */
86
+ .css-1544g2n {
87
+ padding-left: 1rem !important;
88
+ padding-right: 1rem !important;
89
+ }
90
+
91
+ .css-1kyxreq {
92
+ justify-content: flex-start !important;
93
+ }
94
+
95
+ /* العناوين */
96
+ h1, h2, h3, h4, h5, h6 {
97
+ font-family: var(--font-family);
98
+ font-weight: 700;
99
+ margin-bottom: var(--spacing-md);
100
+ color: var(--text-dark);
101
+ }
102
+
103
+ .main-title {
104
+ font-size: var(--font-size-3xl);
105
+ font-weight: 800;
106
+ color: var(--primary-dark);
107
+ margin-bottom: var(--spacing-lg);
108
+ padding-bottom: var(--spacing-sm);
109
+ border-bottom: 2px solid var(--primary-light);
110
+ }
111
+
112
+ .module-title {
113
+ font-size: var(--font-size-xl);
114
+ font-weight: 700;
115
+ color: var(--text-dark);
116
+ margin-top: var(--spacing-xl);
117
+ margin-bottom: var(--spacing-md);
118
+ }
119
+
120
+ /* الشريط الجانبي */
121
+ .sidebar-title {
122
+ font-size: var(--font-size-xl);
123
+ font-weight: 700;
124
+ color: var(--primary-color);
125
+ margin-bottom: var(--spacing-md);
126
+ text-align: center;
127
+ }
128
+
129
+ .sidebar-divider {
130
+ height: 1px;
131
+ background-color: var(--border-color);
132
+ margin: var(--spacing-md) 0;
133
+ }
134
+
135
+ .sidebar-footer {
136
+ position: absolute;
137
+ bottom: 0;
138
+ left: 0;
139
+ right: 0;
140
+ padding: var(--spacing-md);
141
+ text-align: center;
142
+ font-size: var(--font-size-xs);
143
+ color: var(--text-light);
144
+ border-top: 1px solid var(--border-color);
145
+ }
146
+
147
+ /* صندوق التاريخ */
148
+ .date-box {
149
+ display: flex;
150
+ align-items: center;
151
+ background-color: var(--primary-light);
152
+ border-radius: var(--border-radius-md);
153
+ padding: var(--spacing-sm);
154
+ margin-bottom: var(--spacing-md);
155
+ }
156
+
157
+ .date-day {
158
+ font-size: var(--font-size-3xl);
159
+ font-weight: 800;
160
+ color: var(--primary-color);
161
+ margin-left: var(--spacing-md);
162
+ line-height: 1;
163
+ }
164
+
165
+ .date-info {
166
+ display: flex;
167
+ flex-direction: column;
168
+ }
169
+
170
+ .date-month {
171
+ font-size: var(--font-size-md);
172
+ font-weight: 700;
173
+ color: var(--primary-dark);
174
+ }
175
+
176
+ .date-year {
177
+ font-size: var(--font-size-sm);
178
+ color: var(--text-medium);
179
+ }
180
+
181
+ /* البطاقات */
182
+ .card {
183
+ background-color: var(--background-white);
184
+ border-radius: var(--border-radius-lg);
185
+ box-shadow: var(--shadow-md);
186
+ margin-bottom: var(--spacing-lg);
187
+ overflow: hidden;
188
+ }
189
+
190
+ .card-header {
191
+ padding: var(--spacing-md);
192
+ border-bottom: 1px solid var(--border-color);
193
+ background-color: var(--background-light);
194
+ }
195
+
196
+ .card-title {
197
+ font-size: var(--font-size-lg);
198
+ font-weight: 700;
199
+ color: var(--text-dark);
200
+ margin: 0;
201
+ }
202
+
203
+ .card-body {
204
+ padding: var(--spacing-lg);
205
+ }
206
+
207
+ /* بطاقات الإحصائيات */
208
+ .stat-card {
209
+ background-color: var(--background-white);
210
+ border-radius: var(--border-radius-lg);
211
+ box-shadow: var(--shadow-md);
212
+ padding: var(--spacing-lg);
213
+ margin-bottom: var(--spacing-lg);
214
+ text-align: center;
215
+ transition: transform var(--transition-normal), box-shadow var(--transition-normal);
216
+ }
217
+
218
+ .stat-card:hover {
219
+ transform: translateY(-5px);
220
+ box-shadow: var(--shadow-lg);
221
+ }
222
+
223
+ .stat-icon {
224
+ font-size: var(--font-size-3xl);
225
+ color: var(--primary-color);
226
+ margin-bottom: var(--spacing-md);
227
+ }
228
+
229
+ .stat-value {
230
+ font-size: var(--font-size-3xl);
231
+ font-weight: 800;
232
+ color: var(--text-dark);
233
+ margin-bottom: var(--spacing-xs);
234
+ }
235
+
236
+ .stat-label {
237
+ font-size: var(--font-size-md);
238
+ color: var(--text-medium);
239
+ margin-bottom: var(--spacing-sm);
240
+ }
241
+
242
+ .stat-change {
243
+ font-size: var(--font-size-sm);
244
+ font-weight: 600;
245
+ padding: var(--spacing-xs) var(--spacing-sm);
246
+ border-radius: var(--border-radius-full);
247
+ display: inline-block;
248
+ }
249
+
250
+ .stat-change.positive {
251
+ color: var(--success-color);
252
+ background-color: rgba(34, 197, 94, 0.1);
253
+ }
254
+
255
+ .stat-change.negative {
256
+ color: var(--danger-color);
257
+ background-color: rgba(239, 68, 68, 0.1);
258
+ }
259
+
260
+ /* بطاقات الابتكارات */
261
+ .innovation-card {
262
+ background-color: var(--background-white);
263
+ border-radius: var(--border-radius-lg);
264
+ box-shadow: var(--shadow-md);
265
+ padding: var(--spacing-lg);
266
+ margin-bottom: var(--spacing-lg);
267
+ text-align: center;
268
+ transition: transform var(--transition-normal), box-shadow var(--transition-normal);
269
+ height: 100%;
270
+ }
271
+
272
+ .innovation-card:hover {
273
+ transform: translateY(-5px);
274
+ box-shadow: var(--shadow-lg);
275
+ }
276
+
277
+ .innovation-icon {
278
+ font-size: var(--font-size-3xl);
279
+ color: var(--primary-color);
280
+ margin-bottom: var(--spacing-md);
281
+ background-color: var(--primary-light);
282
+ width: 80px;
283
+ height: 80px;
284
+ border-radius: var(--border-radius-full);
285
+ display: flex;
286
+ align-items: center;
287
+ justify-content: center;
288
+ margin-left: auto;
289
+ margin-right: auto;
290
+ }
291
+
292
+ .innovation-card h3 {
293
+ font-size: var(--font-size-xl);
294
+ font-weight: 700;
295
+ color: var(--text-dark);
296
+ margin-bottom: var(--spacing-sm);
297
+ }
298
+
299
+ .innovation-card p {
300
+ font-size: var(--font-size-md);
301
+ color: var(--text-medium);
302
+ }
303
+
304
+ /* الأزرار */
305
+ .stButton > button {
306
+ font-family: var(--font-family) !important;
307
+ font-weight: 600 !important;
308
+ border-radius: var(--border-radius-md) !important;
309
+ transition: all var(--transition-fast) !important;
310
+ }
311
+
312
+ .stButton > button:hover {
313
+ transform: translateY(-2px);
314
+ }
315
+
316
+ .stButton > button[data-baseweb="button"] {
317
+ background-color: var(--primary-color) !important;
318
+ }
319
+
320
+ /* حقول الإدخال */
321
+ .stTextInput > div > div > input {
322
+ font-family: var(--font-family) !important;
323
+ border-radius: var(--border-radius-md) !important;
324
+ }
325
+
326
+ .stTextArea > div > div > textarea {
327
+ font-family: var(--font-family) !important;
328
+ border-radius: var(--border-radius-md) !important;
329
+ }
330
+
331
+ /* القوائم المنسدلة */
332
+ .stSelectbox > div > div > div {
333
+ font-family: var(--font-family) !important;
334
+ border-radius: var(--border-radius-md) !important;
335
+ }
336
+
337
+ /* تبويبات */
338
+ .stTabs {
339
+ background-color: var(--background-white);
340
+ border-radius: var(--border-radius-lg);
341
+ box-shadow: var(--shadow-md);
342
+ padding: var(--spacing-md);
343
+ margin-bottom: var(--spacing-lg);
344
+ }
345
+
346
+ /* تذييل الصفحة */
347
+ .footer {
348
+ margin-top: var(--spacing-2xl);
349
+ padding-top: var(--spacing-lg);
350
+ border-top: 1px solid var(--border-color);
351
+ text-align: center;
352
+ color: var(--text-medium);
353
+ font-size: var(--font-size-sm);
354
+ }
355
+
356
+ /* تنبيهات */
357
+ .stAlert {
358
+ border-radius: var(--border-radius-md) !important;
359
+ }
360
+
361
+ /* تحسينات للجداول */
362
+ .dataframe {
363
+ width: 100%;
364
+ border-collapse: separate;
365
+ border-spacing: 0;
366
+ margin-bottom: var(--spacing-lg);
367
+ border-radius: var(--border-radius-lg);
368
+ overflow: hidden;
369
+ box-shadow: var(--shadow-md);
370
+ }
371
+
372
+ .dataframe th {
373
+ background-color: var(--primary-color);
374
+ color: var(--text-white);
375
+ text-align: right;
376
+ padding: var(--spacing-md);
377
+ font-weight: 600;
378
+ border: none;
379
+ }
380
+
381
+ .dataframe td {
382
+ padding: var(--spacing-md);
383
+ border-bottom: 1px solid var(--border-color);
384
+ text-align: right;
385
+ background-color: var(--background-white);
386
+ }
387
+
388
+ .dataframe tr:nth-child(even) td {
389
+ background-color: rgba(248, 249, 250, 0.7);
390
+ }
391
+
392
+ .dataframe tr:hover td {
393
+ background-color: var(--primary-light);
394
+ }
395
+
396
+ /* تحسينات للرسوم البيانية */
397
+ .stPlotlyChart {
398
+ background-color: var(--background-white);
399
+ border-radius: var(--border-radius-lg);
400
+ box-shadow: var(--shadow-md);
401
+ padding: var(--spacing-md);
402
+ margin-bottom: var(--spacing-lg);
403
+ }
404
+
405
+ /* تحسينات للتنقل */
406
+ .stRadio > div {
407
+ display: flex;
408
+ flex-direction: column;
409
+ }
410
+
411
+ .stRadio > div > label {
412
+ font-family: var(--font-family) !important;
413
+ margin-bottom: var(--spacing-sm);
414
+ }
415
+
416
+ /* تحسينات للشاشات الصغيرة */
417
+ @media (max-width: 768px) {
418
+ .main-title {
419
+ font-size: var(--font-size-2xl);
420
+ }
421
+
422
+ .module-title {
423
+ font-size: var(--font-size-lg);
424
+ }
425
+
426
+ .stat-card {
427
+ padding: var(--spacing-md);
428
+ }
429
+
430
+ .stat-value {
431
+ font-size: var(--font-size-2xl);
432
+ }
433
+
434
+ .innovation-card {
435
+ padding: var(--spacing-md);
436
+ }
437
+
438
+ .innovation-icon {
439
+ width: 60px;
440
+ height: 60px;
441
+ font-size: var(--font-size-2xl);
442
+ }
443
+
444
+ .card-body {
445
+ padding: var(--spacing-md);
446
+ }
447
+ }
448
+
449
+ /* تحسينات للشاشات الكبيرة */
450
+ @media (min-width: 1200px) {
451
+ .main-title {
452
+ font-size: var(--font-size-4xl);
453
+ }
454
+
455
+ .module-title {
456
+ font-size: var(--font-size-2xl);
457
+ }
458
+
459
+ .stat-value {
460
+ font-size: var(--font-size-4xl);
461
+ }
462
+
463
+ .innovation-icon {
464
+ width: 100px;
465
+ height: 100px;
466
+ font-size: var(--font-size-4xl);
467
+ }
468
+ }
469
+
470
+ /* تحسينات للطباعة */
471
+ @media print {
472
+ .stSidebar {
473
+ display: none !important;
474
+ }
475
+
476
+ .main-title {
477
+ font-size: var(--font-size-2xl);
478
+ }
479
+
480
+ .card, .stat-card, .innovation-card {
481
+ box-shadow: none;
482
+ border: 1px solid var(--border-color);
483
+ }
484
+
485
+ .footer {
486
+ margin-top: var(--spacing-lg);
487
+ }
488
+ }
static/js/enhanced_main.js ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ملف JavaScript المحسن لنظام تحليل المناقصات
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ // تهيئة المكونات التفاعلية
4
+ initializeComponents();
5
+
6
+ // إضافة استجابة للأحداث
7
+ setupEventListeners();
8
+
9
+ // تحسين تجربة المستخدم
10
+ enhanceUserExperience();
11
+ });
12
+
13
+ // تهيئة المكونات التفاعلية
14
+ function initializeComponents() {
15
+ // تهيئة القوائم المنسدلة
16
+ const dropdowns = document.querySelectorAll('.dropdown');
17
+ if (dropdowns) {
18
+ dropdowns.forEach(dropdown => {
19
+ dropdown.addEventListener('click', function() {
20
+ this.classList.toggle('active');
21
+ });
22
+ });
23
+ }
24
+
25
+ // تهيئة التبويبات
26
+ const tabButtons = document.querySelectorAll('.tab-button');
27
+ if (tabButtons) {
28
+ tabButtons.forEach(button => {
29
+ button.addEventListener('click', function() {
30
+ const tabId = this.getAttribute('data-tab');
31
+
32
+ // إزالة الفئة النشطة من جميع الأزرار
33
+ document.querySelectorAll('.tab-button').forEach(btn => {
34
+ btn.classList.remove('active');
35
+ });
36
+
37
+ // إزالة الفئة النشطة من جميع المحتويات
38
+ document.querySelectorAll('.tab-content').forEach(content => {
39
+ content.classList.remove('active');
40
+ });
41
+
42
+ // إضافة الفئة النشطة للزر والمحتوى المحدد
43
+ this.classList.add('active');
44
+ document.getElementById(tabId).classList.add('active');
45
+ });
46
+ });
47
+ }
48
+
49
+ // تهيئة الرسائل المنبثقة
50
+ const closeButtons = document.querySelectorAll('.alert .close');
51
+ if (closeButtons) {
52
+ closeButtons.forEach(button => {
53
+ button.addEventListener('click', function() {
54
+ this.parentElement.style.display = 'none';
55
+ });
56
+ });
57
+ }
58
+ }
59
+
60
+ // إعداد مستمعي الأحداث
61
+ function setupEventListeners() {
62
+ // مستمع لتغيير السمة
63
+ const themeToggle = document.getElementById('theme-toggle');
64
+ if (themeToggle) {
65
+ themeToggle.addEventListener('change', function() {
66
+ if (this.checked) {
67
+ document.body.classList.add('dark-theme');
68
+ localStorage.setItem('theme', 'dark');
69
+ } else {
70
+ document.body.classList.remove('dark-theme');
71
+ localStorage.setItem('theme', 'light');
72
+ }
73
+ });
74
+ }
75
+
76
+ // مستمع لتغيير حجم الخط
77
+ const fontSizeControls = document.querySelectorAll('.font-size-control');
78
+ if (fontSizeControls) {
79
+ fontSizeControls.forEach(control => {
80
+ control.addEventListener('click', function() {
81
+ const size = this.getAttribute('data-size');
82
+ document.body.classList.remove('font-small', 'font-medium', 'font-large', 'font-xlarge');
83
+ document.body.classList.add(`font-${size}`);
84
+ localStorage.setItem('fontSize', size);
85
+ });
86
+ });
87
+ }
88
+
89
+ // مستمع لأحداث التمرير
90
+ window.addEventListener('scroll', function() {
91
+ const header = document.querySelector('.main-header');
92
+ if (header) {
93
+ if (window.scrollY > 50) {
94
+ header.classList.add('scrolled');
95
+ } else {
96
+ header.classList.remove('scrolled');
97
+ }
98
+ }
99
+ });
100
+ }
101
+
102
+ // تحسين تجربة المستخدم
103
+ function enhanceUserExperience() {
104
+ // استعادة إعدادات المستخدم من التخزين المحلي
105
+ restoreUserSettings();
106
+
107
+ // تحسين أوقات التحميل
108
+ lazyLoadImages();
109
+
110
+ // تحسين التنقل
111
+ enhanceNavigation();
112
+ }
113
+
114
+ // استعادة إعدادات المستخدم
115
+ function restoreUserSettings() {
116
+ // استعادة السمة
117
+ const savedTheme = localStorage.getItem('theme');
118
+ if (savedTheme === 'dark') {
119
+ document.body.classList.add('dark-theme');
120
+ const themeToggle = document.getElementById('theme-toggle');
121
+ if (themeToggle) {
122
+ themeToggle.checked = true;
123
+ }
124
+ }
125
+
126
+ // استعادة حجم الخط
127
+ const savedFontSize = localStorage.getItem('fontSize');
128
+ if (savedFontSize) {
129
+ document.body.classList.add(`font-${savedFontSize}`);
130
+ }
131
+ }
132
+
133
+ // تحميل الصور بكسل
134
+ function lazyLoadImages() {
135
+ const lazyImages = document.querySelectorAll('img.lazy');
136
+ if (lazyImages) {
137
+ if ('IntersectionObserver' in window) {
138
+ const imageObserver = new IntersectionObserver(function(entries, observer) {
139
+ entries.forEach(function(entry) {
140
+ if (entry.isIntersecting) {
141
+ const image = entry.target;
142
+ image.src = image.dataset.src;
143
+ image.classList.remove('lazy');
144
+ imageObserver.unobserve(image);
145
+ }
146
+ });
147
+ });
148
+
149
+ lazyImages.forEach(function(image) {
150
+ imageObserver.observe(image);
151
+ });
152
+ } else {
153
+ // Fallback for browsers that don't support IntersectionObserver
154
+ lazyImages.forEach(function(image) {
155
+ image.src = image.dataset.src;
156
+ image.classList.remove('lazy');
157
+ });
158
+ }
159
+ }
160
+ }
161
+
162
+ // تحسين التنقل
163
+ function enhanceNavigation() {
164
+ // إضافة تأثيرات انتقالية للروابط
165
+ const navLinks = document.querySelectorAll('.nav-link');
166
+ if (navLinks) {
167
+ navLinks.forEach(link => {
168
+ link.addEventListener('click', function(e) {
169
+ const href = this.getAttribute('href');
170
+
171
+ // إذا كان الرابط داخلياً
172
+ if (href.startsWith('#')) {
173
+ e.preventDefault();
174
+ const targetId = href.substring(1);
175
+ const targetElement = document.getElementById(targetId);
176
+
177
+ if (targetElement) {
178
+ window.scrollTo({
179
+ top: targetElement.offsetTop - 100,
180
+ behavior: 'smooth'
181
+ });
182
+ }
183
+ }
184
+ });
185
+ });
186
+ }
187
+
188
+ // تمييز الرابط النشط
189
+ highlightActiveLink();
190
+ }
191
+
192
+ // تمييز الرابط النشط
193
+ function highlightActiveLink() {
194
+ const currentPath = window.location.pathname;
195
+ const navLinks = document.querySelectorAll('.nav-link');
196
+
197
+ if (navLinks) {
198
+ navLinks.forEach(link => {
199
+ const href = link.getAttribute('href');
200
+ if (href === currentPath || (currentPath === '/' && href === '/index.html')) {
201
+ link.classList.add('active');
202
+ }
203
+ });
204
+ }
205
+ }
206
+
207
+ // دالة لعرض رسائل النجاح
208
+ function showSuccessMessage(message, duration = 3000) {
209
+ showMessage(message, 'success', duration);
210
+ }
211
+
212
+ // دالة لعرض رسائل الخطأ
213
+ function showErrorMessage(message, duration = 3000) {
214
+ showMessage(message, 'error', duration);
215
+ }
216
+
217
+ // دالة لعرض رسائل التحذير
218
+ function showWarningMessage(message, duration = 3000) {
219
+ showMessage(message, 'warning', duration);
220
+ }
221
+
222
+ // دالة لعرض رسائل المعلومات
223
+ function showInfoMessage(message, duration = 3000) {
224
+ showMessage(message, 'info', duration);
225
+ }
226
+
227
+ // دالة عامة لعرض الرسائل
228
+ function showMessage(message, type, duration) {
229
+ // إنشاء عنصر الرسالة
230
+ const messageElement = document.createElement('div');
231
+ messageElement.className = `alert alert-${type}`;
232
+ messageElement.innerHTML = `
233
+ <span class="message">${message}</span>
234
+ <button class="close">&times;</button>
235
+ `;
236
+
237
+ // إضافة العنصر إلى الصفحة
238
+ const alertContainer = document.querySelector('.alert-container');
239
+ if (alertContainer) {
240
+ alertContainer.appendChild(messageElement);
241
+ } else {
242
+ const container = document.createElement('div');
243
+ container.className = 'alert-container';
244
+ container.appendChild(messageElement);
245
+ document.body.appendChild(container);
246
+ }
247
+
248
+ // إضافة مستمع لزر الإغلاق
249
+ const closeButton = messageElement.querySelector('.close');
250
+ if (closeButton) {
251
+ closeButton.addEventListener('click', function() {
252
+ messageElement.remove();
253
+ });
254
+ }
255
+
256
+ // إزالة الرسالة تلقائياً بعد المدة المحددة
257
+ if (duration > 0) {
258
+ setTimeout(function() {
259
+ messageElement.classList.add('fade-out');
260
+ setTimeout(function() {
261
+ messageElement.remove();
262
+ }, 300);
263
+ }, duration);
264
+ }
265
+ }
266
+
267
+ // دالة للتحقق من صحة النموذج
268
+ function validateForm(formId) {
269
+ const form = document.getElementById(formId);
270
+ if (!form) return false;
271
+
272
+ let isValid = true;
273
+ const requiredFields = form.querySelectorAll('[required]');
274
+
275
+ requiredFields.forEach(field => {
276
+ if (!field.value.trim()) {
277
+ isValid = false;
278
+ field.classList.add('error');
279
+
280
+ // إضافة رسالة خطأ
281
+ const errorMessage = field.dataset.errorMessage || 'هذا الحقل مطلوب';
282
+ let errorElement = field.nextElementSibling;
283
+
284
+ if (!errorElement || !errorElement.classList.contains('error-message')) {
285
+ errorElement = document.createElement('div');
286
+ errorElement.className = 'error-message';
287
+ field.parentNode.insertBefore(errorElement, field.nextSibling);
288
+ }
289
+
290
+ errorElement.textContent = errorMessage;
291
+ } else {
292
+ field.classList.remove('error');
293
+ const errorElement = field.nextElementSibling;
294
+ if (errorElement && errorElement.classList.contains('error-message')) {
295
+ errorElement.remove();
296
+ }
297
+ }
298
+ });
299
+
300
+ return isValid;
301
+ }
302
+
303
+ // تصدير الدوال للاستخدام العام
304
+ window.app = {
305
+ showSuccessMessage,
306
+ showErrorMessage,
307
+ showWarningMessage,
308
+ showInfoMessage,
309
+ validateForm
310
+ };
templates/base.html ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ar" dir="rtl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>نظام تحليل المناقصات</title>
7
+ <!-- Font Awesome -->
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <!-- نظام التصميم الموحد -->
10
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/unified_design_system.css') }}">
11
+ <!-- تخصيصات إضافية -->
12
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
14
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.28/jspdf.plugin.autotable.min.js"></script>
15
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
16
+ </head>
17
+ <body>
18
+ <div class="app-container">
19
+ <!-- الشريط الجانبي -->
20
+ <aside class="sidebar">
21
+ <div class="sidebar-header">
22
+ <div class="sidebar-logo">
23
+ <img src="{{ url_for('static', filename='images/logo.png') }}" alt="شعار النظام">
24
+ </div>
25
+ <button class="sidebar-toggle">
26
+ <i class="fas fa-bars"></i>
27
+ </button>
28
+ </div>
29
+ <nav class="sidebar-menu">
30
+ <div class="menu-section">
31
+ <div class="menu-section-title">القائمة الرئيسية</div>
32
+ <a href="{{ url_for('index') }}" class="menu-item active">
33
+ <i class="fas fa-home menu-icon"></i>
34
+ <span>الرئيسية</span>
35
+ </a>
36
+ <a href="{{ url_for('tenders') }}" class="menu-item">
37
+ <i class="fas fa-file-contract menu-icon"></i>
38
+ <span>المناقصات</span>
39
+ </a>
40
+ <a href="{{ url_for('analysis') }}" class="menu-item">
41
+ <i class="fas fa-chart-line menu-icon"></i>
42
+ <span>التحليلات</span>
43
+ </a>
44
+ <a href="{{ url_for('reports') }}" class="menu-item">
45
+ <i class="fas fa-file-alt menu-icon"></i>
46
+ <span>التقارير</span>
47
+ </a>
48
+ </div>
49
+ <div class="menu-section">
50
+ <div class="menu-section-title">الأدوات</div>
51
+ <a href="{{ url_for('ai_assistant') }}" class="menu-item">
52
+ <i class="fas fa-robot menu-icon"></i>
53
+ <span>المساعد الذكي</span>
54
+ </a>
55
+ <a href="{{ url_for('document_extraction') }}" class="menu-item">
56
+ <i class="fas fa-file-import menu-icon"></i>
57
+ <span>استخراج المستندات</span>
58
+ </a>
59
+ <a href="{{ url_for('settings') }}" class="menu-item">
60
+ <i class="fas fa-cog menu-icon"></i>
61
+ <span>الإعدادات</span>
62
+ </a>
63
+ </div>
64
+ </nav>
65
+ </aside>
66
+
67
+ <!-- المحتوى الرئيسي -->
68
+ <main class="main-content">
69
+ <!-- ترويسة الصفحة -->
70
+ <header class="header">
71
+ <div class="d-flex align-items-center">
72
+ <button class="mobile-menu-toggle d-lg-none">
73
+ <i class="fas fa-bars"></i>
74
+ </button>
75
+ <h1 class="mb-0 mr-3">نظام تحليل المناقصات</h1>
76
+ </div>
77
+ <div class="d-flex align-items-center">
78
+ <button class="btn btn-sm btn-outline theme-toggle" data-tooltip="تبديل السمة">
79
+ <i class="fas fa-moon"></i>
80
+ </button>
81
+ <div class="dropdown ml-3">
82
+ <button class="btn btn-sm btn-outline">
83
+ <i class="fas fa-user mr-1"></i>
84
+ <span>المستخدم</span>
85
+ </button>
86
+ </div>
87
+ </div>
88
+ </header>
89
+
90
+ <!-- محتوى الصفحة -->
91
+ <div class="content-wrapper">
92
+ <!-- ترويسة المحتوى -->
93
+ <div class="header-container">
94
+ <div class="header-title">
95
+ <h1>لوحة التحكم</h1>
96
+ <p>مرحبًا بك في نظام تحليل المناقصات</p>
97
+ </div>
98
+ <div class="header-info">
99
+ <div class="date-box">
100
+ <div class="date-day">{{ day }}</div>
101
+ <div class="date-info">
102
+ <div class="date-month">{{ month }}</div>
103
+ <div class="date-year">{{ year }}</div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+
109
+ <!-- إحصائيات لوحة التحكم -->
110
+ <div class="dashboard-stats">
111
+ <div class="stat-card">
112
+ <div class="stat-icon"><i class="fas fa-file-contract"></i></div>
113
+ <div class="stat-value">156</div>
114
+ <div class="stat-label">إجمالي المناقصات</div>
115
+ <div class="stat-change positive"><i class="fas fa-arrow-up"></i> 12% منذ الشهر الماضي</div>
116
+ </div>
117
+ <div class="stat-card">
118
+ <div class="stat-icon"><i class="fas fa-check-circle"></i></div>
119
+ <div class="stat-value">89</div>
120
+ <div class="stat-label">مناقصات مكتملة</div>
121
+ <div class="stat-change positive"><i class="fas fa-arrow-up"></i> 8% منذ الشهر الماضي</div>
122
+ </div>
123
+ <div class="stat-card">
124
+ <div class="stat-icon"><i class="fas fa-spinner"></i></div>
125
+ <div class="stat-value">42</div>
126
+ <div class="stat-label">قيد التحليل</div>
127
+ <div class="stat-change negative"><i class="fas fa-arrow-down"></i> 5% منذ الشهر الماضي</div>
128
+ </div>
129
+ <div class="stat-card">
130
+ <div class="stat-icon"><i class="fas fa-money-bill-wave"></i></div>
131
+ <div class="stat-value">25.4M</div>
132
+ <div class="stat-label">إجمالي القيمة (ريال)</div>
133
+ <div class="stat-change positive"><i class="fas fa-arrow-up"></i> 15% منذ الشهر الماضي</div>
134
+ </div>
135
+ </div>
136
+
137
+ <!-- المناقصات الأخيرة -->
138
+ <div class="card mb-4">
139
+ <div class="card-header">
140
+ <h3 class="card-title">المناقصات الأخيرة</h3>
141
+ <button class="btn btn-primary btn-sm">عرض الكل</button>
142
+ </div>
143
+ <div class="card-body">
144
+ <div class="table-container">
145
+ <table class="table table-striped">
146
+ <thead>
147
+ <tr>
148
+ <th>#</th>
149
+ <th>اسم المناقصة</th>
150
+ <th>الجهة</th>
151
+ <th>التاريخ</th>
152
+ <th>الحالة</th>
153
+ <th>الإجراءات</th>
154
+ </tr>
155
+ </thead>
156
+ <tbody>
157
+ <tr>
158
+ <td>1</td>
159
+ <td>مشروع تطوير البنية التحتية</td>
160
+ <td>وزارة النقل</td>
161
+ <td>2025/03/15</td>
162
+ <td><span class="badge badge-success">مكتملة</span></td>
163
+ <td>
164
+ <button class="btn btn-primary btn-sm"><i class="fas fa-eye"></i></button>
165
+ <button class="btn btn-secondary btn-sm"><i class="fas fa-edit"></i></button>
166
+ </td>
167
+ </tr>
168
+ <tr>
169
+ <td>2</td>
170
+ <td>توريد معدات طبية</td>
171
+ <td>وزارة الصحة</td>
172
+ <td>2025/03/20</td>
173
+ <td><span class="badge badge-warning">قيد التحليل</span></td>
174
+ <td>
175
+ <button class="btn btn-primary btn-sm"><i class="fas fa-eye"></i></button>
176
+ <button class="btn btn-secondary btn-sm"><i class="fas fa-edit"></i></button>
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <td>3</td>
181
+ <td>بناء مدرسة ثانوية</td>
182
+ <td>وزارة التعليم</td>
183
+ <td>2025/03/25</td>
184
+ <td><span class="badge badge-info">جديدة</span></td>
185
+ <td>
186
+ <button class="btn btn-primary btn-sm"><i class="fas fa-eye"></i></button>
187
+ <button class="btn btn-secondary btn-sm"><i class="fas fa-edit"></i></button>
188
+ </td>
189
+ </tr>
190
+ </tbody>
191
+ </table>
192
+ </div>
193
+ </div>
194
+ </div>
195
+
196
+ <!-- تحليلات المناقصات -->
197
+ <div class="row">
198
+ <div class="col-md-6">
199
+ <div class="card mb-4">
200
+ <div class="card-header">
201
+ <h3 class="card-title">توزيع المناقصات حسب الجهة</h3>
202
+ </div>
203
+ <div class="card-body">
204
+ <canvas id="entityChart" height="300"></canvas>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ <div class="col-md-6">
209
+ <div class="card mb-4">
210
+ <div class="card-header">
211
+ <h3 class="card-title">توزيع المناقصات حسب الحالة</h3>
212
+ </div>
213
+ <div class="card-body">
214
+ <canvas id="statusChart" height="300"></canvas>
215
+ </div>
216
+ </div>
217
+ </div>
218
+ </div>
219
+
220
+ <!-- الابتكارات والميزات -->
221
+ <h2 class="module-title">الابتكارات والميزات</h2>
222
+ <div class="row">
223
+ <div class="col-md-4">
224
+ <div class="innovation-card">
225
+ <div class="innovation-icon"><i class="fas fa-robot"></i></div>
226
+ <h3>المساعد الذكي</h3>
227
+ <p>استخدم الذكاء الاصطناعي لتحليل المناقصات واستخراج المعلومات المهمة بدقة عالية.</p>
228
+ </div>
229
+ </div>
230
+ <div class="col-md-4">
231
+ <div class="innovation-card">
232
+ <div class="innovation-icon"><i class="fas fa-file-import"></i></div>
233
+ <h3>استخراج المستندات</h3>
234
+ <p>استخرج البيانات من مستندات المناقصات بتنسيقات مختلفة (PDF، Word، Excel) بشكل آلي.</p>
235
+ </div>
236
+ </div>
237
+ <div class="col-md-4">
238
+ <div class="innovation-card">
239
+ <div class="innovation-icon"><i class="fas fa-chart-line"></i></div>
240
+ <h3>تحليلات متقدمة</h3>
241
+ <p>احصل على تحليلات متقدمة ورؤى قيمة حول المناقصات باستخدام تقنيات تحليل البيانات.</p>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </div>
246
+
247
+ <!-- تذييل الصفحة -->
248
+ <footer class="footer">
249
+ <p>جميع الحقوق محفوظة &copy; 2025 نظام تحليل المناقصات</p>
250
+ </footer>
251
+ </main>
252
+ </div>
253
+
254
+ <!-- سكريبت JavaScript المحسن -->
255
+ <script src="{{ url_for('static', filename='js/enhanced_main.js') }}"></script>
256
+ <script>
257
+ // بيانات الرسوم البيانية
258
+ document.addEventListener('DOMContentLoaded', function() {
259
+ // رسم بياني لتوزيع المناقصات حسب الجهة
260
+ const entityCtx = document.getElementById('entityChart').getContext('2d');
261
+ new Chart(entityCtx, {
262
+ type: 'pie',
263
+ data: {
264
+ labels: ['وزارة النقل', 'وزارة الصحة', 'وزارة التعليم', 'وزارة الإسكان', 'أخرى'],
265
+ datasets: [{
266
+ data: [30, 25, 20, 15, 10],
267
+ backgroundColor: [
268
+ '#2563eb', // primary-color
269
+ '#10b981', // secondary-color
270
+ '#f59e0b', // accent-color
271
+ '#3b82f6', // info-color
272
+ '#94a3b8' // text-light
273
+ ],
274
+ borderWidth: 1
275
+ }]
276
+ },
277
+ options: {
278
+ responsive: true,
279
+ plugins: {
280
+ legend: {
281
+ position: 'right',
282
+ rtl: true
283
+ }
284
+ }
285
+ }
286
+ });
287
+
288
+ // رسم بياني لتوزيع المناقصات حسب الحالة
289
+ const statusCtx = document.getElementById('statusChart').getContext('2d');
290
+ new Chart(statusCtx, {
291
+ type: 'doughnut',
292
+ data: {
293
+ labels: ['مكتملة', 'قيد التحليل', 'جديدة', 'ملغاة'],
294
+ datasets: [{
295
+ data: [45, 30, 20, 5],
296
+ backgroundColor: [
297
+ '#22c55e', // success-color
298
+ '#f59e0b', // warning-color
299
+ '#3b82f6', // info-color
300
+ '#ef4444' // danger-color
301
+ ],
302
+ borderWidth: 1
303
+ }]
304
+ },
305
+ options: {
306
+ responsive: true,
307
+ plugins: {
308
+ legend: {
309
+ position: 'right',
310
+ rtl: true
311
+ }
312
+ }
313
+ }
314
+ });
315
+ });
316
+ </script>
317
+ </body>
318
+ </html>
utils/components/header.py CHANGED
@@ -7,26 +7,18 @@ from datetime import datetime
7
  import config
8
 
9
 
10
- def render_header(page_title=None):
11
  """
12
  عرض ترويسة الصفحة المحسنة
13
-
14
- الوسيطات:
15
- page_title: عنوان الصفحة المعروضة (اختياري)
16
  """
17
  # إنشاء مكون الترويسة باستخدام HTML
18
- title_display = "نظام تحليل العقود والمناقصات"
19
- # إذا تم تمرير عنوان للصفحة، قم بإضافته للعنوان الرئيسي
20
- if page_title:
21
- title_display = f"نظام تحليل العقود والمناقصات: {page_title}"
22
-
23
  header_html = """
24
  <div class="header-container">
25
  <div class="header-title">
26
  <div class="logo">
27
  <span class="logo-text">WAHBi AI</span>
28
  </div>
29
- <h1>{title}</h1>
30
  <p>الحلول الشاملة للتسعير والتحليل بالذكاء الاصطناعي - شركة شبه الجزيرة للمقاولات</p>
31
  </div>
32
  <div class="header-info">
@@ -52,7 +44,7 @@ def render_header(page_title=None):
52
  year = today.year
53
 
54
  # استبدال القيم في قالب HTML
55
- header_html = header_html.format(title=title_display, day=day, month=month, year=year)
56
 
57
  # عرض الترويسة
58
  st.markdown(header_html, unsafe_allow_html=True)
 
7
  import config
8
 
9
 
10
+ def render_header():
11
  """
12
  عرض ترويسة الصفحة المحسنة
 
 
 
13
  """
14
  # إنشاء مكون الترويسة باستخدام HTML
 
 
 
 
 
15
  header_html = """
16
  <div class="header-container">
17
  <div class="header-title">
18
  <div class="logo">
19
  <span class="logo-text">WAHBi AI</span>
20
  </div>
21
+ <h1>نظام تحليل العقود والمناقصات</h1>
22
  <p>الحلول الشاملة للتسعير والتحليل بالذكاء الاصطناعي - شركة شبه الجزيرة للمقاولات</p>
23
  </div>
24
  <div class="header-info">
 
44
  year = today.year
45
 
46
  # استبدال القيم في قالب HTML
47
+ header_html = header_html.format(day=day, month=month, year=year)
48
 
49
  # عرض الترويسة
50
  st.markdown(header_html, unsafe_allow_html=True)
utils/components/sidebar.py CHANGED
@@ -59,14 +59,8 @@ def render_sidebar():
59
  default_index=0,
60
  styles={
61
  "container": {"padding": "5px", "background-color": "#f0f2f6", "direction": "rtl"},
62
- "icon": {"color": "orange", "font-size": "18px", "margin-left": "10px"},
63
- "nav-link": {
64
- "font-size": "14px",
65
- "text-align": "right",
66
- "margin": "0px",
67
- "direction": "rtl",
68
- "justify-content": "flex-end"
69
- },
70
  "nav-link-selected": {"background-color": "#ff9a3c"},
71
  }
72
  )
 
59
  default_index=0,
60
  styles={
61
  "container": {"padding": "5px", "background-color": "#f0f2f6", "direction": "rtl"},
62
+ "icon": {"color": "orange", "font-size": "18px"},
63
+ "nav-link": {"font-size": "14px", "text-align": "right", "margin": "0px"},
 
 
 
 
 
 
64
  "nav-link-selected": {"background-color": "#ff9a3c"},
65
  }
66
  )
utils/helpers.py CHANGED
@@ -2,281 +2,331 @@
2
  # -*- coding: utf-8 -*-
3
 
4
  """
5
- وحدة المساعدات العامة
6
- توفر هذه الوحدة مجموعة من الدوال المساعدة المستخدمة في مختلف أجزاء النظام
7
  """
8
 
9
  import os
10
- import datetime
 
 
 
11
  import json
12
  import re
13
- import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  def create_directory_if_not_exists(directory_path):
16
- """إنشاء مجلد إذا لم يكن موجوداً بالفعل"""
17
- if not os.path.exists(directory_path):
18
- os.makedirs(directory_path)
 
 
19
  return True
20
- return False
 
 
 
21
 
22
  def get_data_folder():
23
- """الحصول على مسار مجلد البيانات"""
24
- data_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'data'))
 
25
  create_directory_if_not_exists(data_folder)
26
  return data_folder
27
 
28
- def format_time(timestamp=None):
29
- """تنسيق الوقت بصيغة قابلة للقراءة"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  if timestamp is None:
31
- timestamp = datetime.datetime.now()
32
- return timestamp.strftime("%Y-%m-%d %H:%M:%S")
 
 
 
 
33
 
34
  def get_user_info():
35
  """الحصول على معلومات المستخدم الحالي"""
36
- # في الوقت الحالي، نستخدم معلومات مستخدم افتراضية
37
- # يمكن تعديل هذه الدالة لاحقاً للتكامل مع نظام المصادقة
 
 
 
38
  return {
39
  "id": 1,
40
  "username": "admin",
41
- "name": "مدير النظام",
42
- "role": "admin"
 
 
 
43
  }
44
 
45
- def load_css():
46
- """تحميل أنماط CSS المخصصة"""
47
- st.markdown("""
48
- <style>
49
- .module-title {
50
- color: #1E88E5;
51
- text-align: center;
52
- margin-bottom: 20px;
53
- font-weight: bold;
54
- }
55
-
56
- .section-title {
57
- color: #1565C0;
58
- margin-top: 20px;
59
- margin-bottom: 10px;
60
- font-weight: bold;
61
- }
62
-
63
- .info-box {
64
- background-color: #E3F2FD;
65
- padding: 15px;
66
- border-radius: 5px;
67
- margin-bottom: 15px;
68
- }
69
-
70
- .warning-box {
71
- background-color: #FFF8E1;
72
- padding: 15px;
73
- border-radius: 5px;
74
- margin-bottom: 15px;
75
- }
76
-
77
- .error-box {
78
- background-color: #FFEBEE;
79
- padding: 15px;
80
- border-radius: 5px;
81
- margin-bottom: 15px;
82
- }
83
 
84
- .success-box {
85
- background-color: #E8F5E9;
86
- padding: 15px;
87
- border-radius: 5px;
88
- margin-bottom: 15px;
89
- }
 
 
90
 
91
- .centered {
92
- text-align: center;
93
- }
 
94
 
95
- .footer {
96
- text-align: center;
97
- margin-top: 30px;
98
- color: #78909C;
99
- font-size: 0.8em;
100
- }
 
 
 
 
 
101
 
102
- /* تحسين تصميم الجداول */
103
- table {
104
- width: 100%;
105
- }
 
 
 
 
 
 
 
 
106
 
107
- thead th {
108
- background-color: #1976D2 !important;
109
- color: white !important;
110
- }
111
 
112
- tbody tr:nth-child(even) {
113
- background-color: #f2f2f2;
114
- }
115
 
116
- tbody tr:hover {
117
- background-color: #E3F2FD;
118
- }
119
 
120
- /* تحسين ظهور الفورم */
121
- input, select, textarea {
122
- border-radius: 5px !important;
123
- border: 1px solid #ddd !important;
124
- padding: 10px !important;
125
- }
126
 
127
- .stButton>button {
128
- border-radius: 5px !important;
129
- }
130
 
131
- /* زيادة حجم الخط للتوافق مع اللغة العربية */
132
- html, body, [class*="css"] {
133
- font-family: 'Tajawal', sans-serif;
134
- }
135
- </style>
136
 
137
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Tajawal:wght@400;700&display=swap">
138
- """, unsafe_allow_html=True)
139
-
140
- def render_credits():
141
- """عرض المعلوم��ت عن حقوق الملكية وإصدار النظام"""
142
- st.markdown("""
143
- <div class="footer">
144
- <p>هذا النظام يعمل لصالح شركة شبه الجزيرة للمقاولات، جميع الحقوق محفوظة 2025</p>
145
- <p>نظام WAHBi AI - الإصدار 2.0</p>
146
- </div>
147
- """, unsafe_allow_html=True)
148
 
149
- def load_icons():
150
- """تحميل الأيقونات المستخدمة في النظام"""
151
- icons = {
152
- "project": "🏗️",
153
- "document": "📄",
154
- "analysis": "🔍",
155
- "warning": "⚠️",
156
- "success": "✅",
157
- "error": "❌",
158
- "info": "ℹ️",
159
- "settings": "⚙️",
160
- "user": "👤",
161
- "money": "💰",
162
- "time": "⏱️",
163
- "location": "📍",
164
- "notification": "🔔",
165
- "edit": "✏️",
166
- "delete": "🗑️",
167
- "upload": "📤",
168
- "download": "📥",
169
- "save": "💾",
170
- "cancel": "❌",
171
- "add": "➕",
172
- "calendar": "📅",
173
- "chat": "💬",
174
- "search": "🔎",
175
- "star": "⭐",
176
- "trophy": "🏆",
177
- "medal": "🥇",
178
- "chart": "📊",
179
- "map": "🗺️",
180
- "building": "🏢",
181
- "road": "🛣️",
182
- "bridge": "🌉",
183
- }
184
- return icons
185
 
186
- def format_number(number, decimal_places=2):
187
- """تنسيق الأرقام بطريقة أنيقة"""
188
- if isinstance(number, (int, float)):
189
- if decimal_places == 0:
190
- return "{:,.0f}".format(number)
191
- else:
192
- return "{:,.{dp}f}".format(number, dp=decimal_places)
193
- return str(number)
194
 
195
- def format_currency(amount, currency="ريال", decimal_places=2):
196
- """تنسيق المبالغ المالية"""
197
- if amount is None:
198
- return "غير محدد"
199
- formatted = format_number(amount, decimal_places)
200
- return f"{formatted} {currency}"
201
 
202
- def styled_button(label, key=None, type="primary", on_click=None, args=None, full_width=False, icon=None, is_link=False, help=None):
203
- """
204
- إنشاء زر بتنسيق معين
205
- :param label: نص الزر
206
- :param key: مفتاح الزر الفريد
207
- :param type: نوع التنسيق ('primary', 'secondary', 'success', 'warning', 'danger', 'info', 'glass', 'flat')
208
- :param on_click: الدالة التي سيتم تنفيذها عند النقر
209
- :param args: معاملات الدالة
210
- :param full_width: هل يأخذ الزر العرض كاملاً
211
- :param icon: أيقونة لعرضها قبل النص (emoji أو HTML)
212
- :param is_link: إذا كان الزر رابطاً بدلاً من زر عادي
213
- :param help: نص المساعدة للزر
214
- :return: زر مُنسّق
215
- """
216
- if is_link:
217
- btn_class = f"{type}-btn"
218
- if icon:
219
- btn_class += " action-btn"
220
- label_with_icon = f"{icon} {label}"
221
- else:
222
- label_with_icon = label
223
-
224
- button_html = f"""
225
- <div class="{btn_class}">
226
- {label_with_icon}
227
- </div>
228
- """
229
- return st.markdown(button_html, unsafe_allow_html=True)
230
- else:
231
- with st.container():
232
- btn_class = f"{type}-btn"
233
- if icon:
234
- btn_class += " action-btn"
235
- label_with_icon = f"{icon} {label}"
236
- else:
237
- label_with_icon = label
238
-
239
- st.markdown(f'<div class="{btn_class}">', unsafe_allow_html=True)
240
- clicked = st.button(label_with_icon, key=key, on_click=on_click, args=args, use_container_width=full_width, help=help)
241
- st.markdown('</div>', unsafe_allow_html=True)
242
- return clicked
243
-
244
- def filter_dataframe(df, column, value):
245
- """ترشيح إطار البيانات"""
246
- if value == "الكل":
247
- return df
248
- return df[df[column] == value]
249
-
250
- def get_file_extension(filename):
251
- """استخراج امتداد الملف"""
252
- if not filename:
253
  return ""
254
- return os.path.splitext(filename)[-1].lower()
 
 
 
255
 
256
- def extract_numbers_from_text(text):
257
- """استخراج الأرقام من النص
 
258
 
259
- Args:
260
- text (str): النص المراد استخراج الأرقام منه
261
-
262
- Returns:
263
- list: قائمة بالأرقام المستخرجة
264
- """
265
- if not text:
266
- return []
267
 
268
- # نمط للعثور على الأرقام (صحيحة أو عشرية) في النص
269
- pattern = r'[-+]?\d*\.\d+|\d+'
270
 
271
- # استخراج جميع الأرقام من النص
272
- numbers = re.findall(pattern, text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
- # تحويل النصوص المستخرجة إلى أرقام (صحيحة أو عشرية)
275
- converted_numbers = []
276
- for num in numbers:
277
- if '.' in num:
278
- converted_numbers.append(float(num))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  else:
280
- converted_numbers.append(int(num))
281
-
282
- return converted_numbers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  # -*- coding: utf-8 -*-
3
 
4
  """
5
+ وحدة المساعدة المركزية للنظام
6
+ تحتوي على دوال مساعدة مشتركة تستخدم في جميع أنحاء التطبيق
7
  """
8
 
9
  import os
10
+ import sys
11
+ import streamlit as st
12
+ import pandas as pd
13
+ import numpy as np
14
  import json
15
  import re
16
+ import time
17
+ import tempfile
18
+ from datetime import datetime, timedelta
19
+ import random
20
+ import secrets
21
+ import shutil
22
+ import base64
23
+ import logging
24
+ from pathlib import Path
25
+
26
+ # تكوين التسجيلات
27
+ logging.basicConfig(
28
+ level=logging.INFO,
29
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
30
+ )
31
+ logger = logging.getLogger("wahbi-ai")
32
+
33
 
34
  def create_directory_if_not_exists(directory_path):
35
+ """إنشاء مسار إذا لم يكن موجوداً"""
36
+ try:
37
+ if not os.path.exists(directory_path):
38
+ os.makedirs(directory_path)
39
+ logger.info(f"تم إنشاء المجلد: {directory_path}")
40
  return True
41
+ except Exception as e:
42
+ logger.error(f"خطأ في إنشاء المجلد {directory_path}: {e}")
43
+ return False
44
+
45
 
46
  def get_data_folder():
47
+ """الحصول على مسار مجلد البيانات الرئيسي"""
48
+ # مسار بيانات النظام الرئيسي
49
+ data_folder = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data")
50
  create_directory_if_not_exists(data_folder)
51
  return data_folder
52
 
53
+
54
+ def load_config():
55
+ """تحميل إعدادات التكوين من ملف config.json"""
56
+ config_path = os.path.join(get_data_folder(), "config.json")
57
+
58
+ # إذا لم يكن ملف التكوين موجوداً، قم بإنشاء ملف افتراضي
59
+ if not os.path.exists(config_path):
60
+ default_config = {
61
+ "system": {
62
+ "version": "1.0.0",
63
+ "release_date": "2025-03-30",
64
+ "company_name": "شركة شبه الجزيرة للمقاولات",
65
+ "company_logo": "",
66
+ "language": "ar",
67
+ "theme": "light",
68
+ "debug_mode": False
69
+ },
70
+ "ai_models": {
71
+ "default_model": "claude-3-7-sonnet-20250219",
72
+ "openai_model": "gpt-4o",
73
+ "huggingface_model": "mistralai/Mistral-7B-Instruct-v0.2"
74
+ },
75
+ "notifications": {
76
+ "enable_email": False,
77
+ "enable_browser": True,
78
+ "check_interval": 60
79
+ }
80
+ }
81
+
82
+ with open(config_path, 'w', encoding='utf-8') as f:
83
+ json.dump(default_config, f, ensure_ascii=False, indent=2)
84
+
85
+ return default_config
86
+
87
+ # تحميل ملف التكوين الموجود
88
+ try:
89
+ with open(config_path, 'r', encoding='utf-8') as f:
90
+ return json.load(f)
91
+ except Exception as e:
92
+ logger.error(f"خطأ في تحميل ملف التكوين: {e}")
93
+ return {}
94
+
95
+
96
+ def save_config(config):
97
+ """حفظ إعدادات التكوين إلى ملف config.json"""
98
+ config_path = os.path.join(get_data_folder(), "config.json")
99
+
100
+ try:
101
+ with open(config_path, 'w', encoding='utf-8') as f:
102
+ json.dump(config, f, ensure_ascii=False, indent=2)
103
+ return True
104
+ except Exception as e:
105
+ logger.error(f"خطأ في حفظ ملف التكوين: {e}")
106
+ return False
107
+
108
+
109
+ def format_time(timestamp=None, format_str="%Y-%m-%d %H:%M:%S"):
110
+ """تنسيق الوقت إلى صيغة معينة"""
111
  if timestamp is None:
112
+ timestamp = datetime.now()
113
+ elif isinstance(timestamp, (int, float)):
114
+ timestamp = datetime.fromtimestamp(timestamp)
115
+
116
+ return timestamp.strftime(format_str)
117
+
118
 
119
  def get_user_info():
120
  """الحصول على معلومات المستخدم الحالي"""
121
+ # في التطبيق الفعلي، يمكن استرداد معلومات المستخدم من قاعدة البيانات أو من حالة الجلسة
122
+ if "user_info" in st.session_state:
123
+ return st.session_state.user_info
124
+
125
+ # معلومات افتراضية للتطوير
126
  return {
127
  "id": 1,
128
  "username": "admin",
129
+ "full_name": "مدير النظام",
130
+ "email": "admin@example.com",
131
+ "role": "مدير",
132
+ "department": "الإدارة",
133
+ "last_login": format_time()
134
  }
135
 
136
+
137
+ def get_current_project():
138
+ """الحصول على معلومات المشروع الحالي"""
139
+ if "current_project" in st.session_state:
140
+ return st.session_state.current_project
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
+ # في حالة عدم وجود مشروع محدد
143
+ return None
144
+
145
+
146
+ def load_icons():
147
+ """تحميل الأيقونات المستخدمة في النظام"""
148
+ icons_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets", "icons")
149
+ icons = {}
150
 
151
+ # التأكد من وجود مجلد الأيقونات
152
+ if not os.path.exists(icons_path):
153
+ create_directory_if_not_exists(icons_path)
154
+ return icons
155
 
156
+ # تحميل جميع الأيقونات
157
+ for icon_file in os.listdir(icons_path):
158
+ if icon_file.endswith(('.png', '.svg', '.jpg')):
159
+ icon_name = os.path.splitext(icon_file)[0]
160
+ icon_path = os.path.join(icons_path, icon_file)
161
+
162
+ try:
163
+ with open(icon_path, "rb") as f:
164
+ icons[icon_name] = base64.b64encode(f.read()).decode()
165
+ except Exception as e:
166
+ logger.error(f"خطأ في تحميل الأيقونة {icon_name}: {e}")
167
 
168
+ return icons
169
+
170
+
171
+ def get_random_id(length=8):
172
+ """إنشاء معرف عشوائي بطول محدد"""
173
+ return secrets.token_hex(length // 2)
174
+
175
+
176
+ def compress_text(text, max_length=10000):
177
+ """اختصار النص إلى حد أقصى محدد مع الحفاظ على المعنى"""
178
+ if not text or len(text) <= max_length:
179
+ return text
180
 
181
+ # تقسيم النص إلى جمل
182
+ sentences = re.split(r'(?<=[.!?])\s+', text)
 
 
183
 
184
+ # حساب متوسط طول الجملة
185
+ avg_sentence_length = len(text) / len(sentences)
 
186
 
187
+ # حساب عدد الجمل التي يمكن تضمينها
188
+ num_sentences_to_keep = int(max_length / avg_sentence_length)
 
189
 
190
+ # الاحتفاظ بالجمل الأولى والأخيرة للحفاظ على السياق
191
+ keep_first = num_sentences_to_keep // 2
192
+ keep_last = num_sentences_to_keep - keep_first
 
 
 
193
 
194
+ # دمج الجمل المختارة
195
+ compressed_text = ' '.join(sentences[:keep_first] + sentences[-keep_last:])
 
196
 
197
+ # إضافة إشارة إلى أن النص تم اختصاره
198
+ if len(compressed_text) < len(text):
199
+ compressed_text += " [...المزيد من النص تم اختصاره...]"
 
 
200
 
201
+ return compressed_text
 
 
 
 
 
 
 
 
 
 
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
+ def str_to_bool(text):
205
+ """تحويل النص إلى قيمة منطقية"""
206
+ return text.lower() in ('yes', 'true', 'y', 't', '1', 'نعم', 'صحيح')
 
 
 
 
 
207
 
 
 
 
 
 
 
208
 
209
+ def handle_arabic_text(text):
210
+ """معالجة النص العربي للعرض بشكل صحيح"""
211
+ if not text:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  return ""
213
+
214
+ # إضافة علامة RTL لضمان عرض النص العربي بشكل صحيح
215
+ return f"<div dir='rtl'>{text}</div>"
216
+
217
 
218
+ def render_credits():
219
+ """عرض معلومات النظام وحقوق الملكية"""
220
+ st.markdown("---")
221
 
222
+ config = load_config()
223
+ system_info = config.get("system", {})
 
 
 
 
 
 
224
 
225
+ col1, col2, col3 = st.columns([1, 2, 1])
 
226
 
227
+ with col2:
228
+ st.markdown(
229
+ f"""
230
+ <div style="text-align: center; color: #666;">
231
+ <p>{system_info.get('company_name', 'شركة شبه الجزيرة للمقاولات')}</p>
232
+ <p>الإصدار: {system_info.get('version', '1.0.0')}</p>
233
+ <p>© جميع الحقوق محفوظة 2025</p>
234
+ </div>
235
+ """,
236
+ unsafe_allow_html=True
237
+ )
238
+
239
+
240
+ # دالة للحصول على اتصال قاعدة البيانات
241
+ def get_connection():
242
+ """
243
+ دالة للحصول على اتصال بقاعدة البيانات
244
 
245
+ ملاحظة: ينبغي أن تكون مستبدلة بالدالة من db_connector.py في البيئة الإنتاجية
246
+ """
247
+ try:
248
+ # استيراد المستوى الفعلي للاتصال بقاعدة البيانات
249
+ from database.db_connector import get_connection as get_db_connection
250
+ return get_db_connection()
251
+ except ImportError:
252
+ # إذا كان الاتصال بقاعدة البيانات غير متاح
253
+ logger.warning("لم يتم العثور على وحدة اتصال قاعدة البيانات. استخدام مخزن بيانات مؤقت.")
254
+ # إرجاع None للإشارة إلى عدم وجود اتصال
255
+ return None
256
+
257
+
258
+ def load_css(file_name=None):
259
+ """تحميل ملف CSS مخصص"""
260
+ try:
261
+ if file_name:
262
+ css_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets", "css", file_name)
263
+
264
+ if os.path.exists(css_file):
265
+ with open(css_file, "r", encoding="utf-8") as f:
266
+ css_content = f.read()
267
+ else:
268
+ logger.warning(f"ملف CSS غير موجود: {css_file}")
269
+ return
270
  else:
271
+ # CSS افتراضي
272
+ css_content = """
273
+ .sidebar .sidebar-content {
274
+ direction: rtl;
275
+ text-align: right;
276
+ }
277
+ div[data-testid="stForm"] {
278
+ border: 1px solid #ddd;
279
+ padding: 10px;
280
+ border-radius: 10px;
281
+ }
282
+ .module-title {
283
+ color: #1E88E5;
284
+ text-align: center;
285
+ font-size: 1.8rem;
286
+ margin-bottom: 1rem;
287
+ }
288
+ .instructions {
289
+ background-color: #f8f9fa;
290
+ border-right: 3px solid #4CAF50;
291
+ padding: 10px;
292
+ margin-bottom: 15px;
293
+ }
294
+ .results-container {
295
+ background-color: #f5f5f5;
296
+ padding: 15px;
297
+ border-radius: 5px;
298
+ margin-top: 20px;
299
+ }
300
+ .risk-high {
301
+ color: #d32f2f;
302
+ font-weight: bold;
303
+ }
304
+ .risk-medium {
305
+ color: #f57c00;
306
+ font-weight: bold;
307
+ }
308
+ .risk-low {
309
+ color: #388e3c;
310
+ font-weight: bold;
311
+ }
312
+ .form-container {
313
+ background-color: #f9f9f9;
314
+ padding: 20px;
315
+ border-radius: 10px;
316
+ margin-bottom: 20px;
317
+ }
318
+ .section-header {
319
+ color: #2196F3;
320
+ font-size: 1.2rem;
321
+ font-weight: bold;
322
+ margin-top: 20px;
323
+ margin-bottom: 10px;
324
+ border-bottom: 1px solid #eee;
325
+ padding-bottom: 5px;
326
+ }
327
+ """
328
+
329
+ st.markdown(f"<style>{css_content}</style>", unsafe_allow_html=True)
330
+
331
+ except Exception as e:
332
+ logger.error(f"خطأ في تحميل ملف CSS: {e}")
utils/helpers/__init__.py CHANGED
@@ -6,8 +6,6 @@
6
  import streamlit as st
7
  import pandas as pd
8
  import numpy as np
9
- import os
10
- import sqlite3
11
 
12
  from .utils import (
13
  create_directory_if_not_exists,
@@ -25,33 +23,6 @@ from .utils import (
25
  extract_numbers_from_text
26
  )
27
 
28
- def get_connection():
29
- """
30
- إنشاء اتصال وهمي لقاعدة البيانات للاستخدام عند عدم توفر اتصال PostgreSQL
31
-
32
- الإرجاع:
33
- اتصال وهمي لقاعدة البيانات
34
- """
35
- # إنشاء مجلد البيانات إذا لم يكن موجوداً
36
- data_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'data')
37
- os.makedirs(data_dir, exist_ok=True)
38
-
39
- # إنشاء اتصال قاعدة بيانات SQLite محلية
40
- db_path = os.path.join(data_dir, 'local_db.sqlite')
41
- conn = sqlite3.connect(db_path)
42
-
43
- # إعادة محاكاة سلوك اتصال PostgreSQL
44
- conn.execute = conn.cursor().execute
45
-
46
- # إضافة وظيفة وهمية للاقتطاع (commit) والإغلاق
47
- original_close = conn.close
48
- def enhanced_close():
49
- conn.commit()
50
- original_close()
51
- conn.close = enhanced_close
52
-
53
- return conn
54
-
55
  __all__ = [
56
  'create_directory_if_not_exists',
57
  'get_data_folder',
@@ -65,6 +36,5 @@ __all__ = [
65
  'styled_button',
66
  'filter_dataframe',
67
  'get_file_extension',
68
- 'extract_numbers_from_text',
69
- 'get_connection'
70
  ]
 
6
  import streamlit as st
7
  import pandas as pd
8
  import numpy as np
 
 
9
 
10
  from .utils import (
11
  create_directory_if_not_exists,
 
23
  extract_numbers_from_text
24
  )
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  __all__ = [
27
  'create_directory_if_not_exists',
28
  'get_data_folder',
 
36
  'styled_button',
37
  'filter_dataframe',
38
  'get_file_extension',
39
+ 'extract_numbers_from_text'
 
40
  ]