EGYADMIN commited on
Commit
5c8d69b
·
verified ·
1 Parent(s): 1c3529d

Upload 110 files

Browse files
app.py CHANGED
@@ -1,1253 +1,974 @@
 
 
 
 
 
 
 
 
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
 
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
+ # تهيئة حالة الجلسة لكل وحدات النظام
16
+ if 'page' not in st.session_state:
17
+ st.session_state.page = 'home'
18
+ if 'analysis_type' not in st.session_state:
19
+ st.session_state.analysis_type = None
20
+ if 'show_document_upload' not in st.session_state:
21
+ st.session_state.show_document_upload = False
22
+ if 'report_type' not in st.session_state:
23
+ st.session_state.report_type = None
24
+ if 'show_report_form' not in st.session_state:
25
+ st.session_state.show_report_form = False
26
+ if 'analysis_result' not in st.session_state:
27
+ st.session_state.analysis_result = None
28
+ if 'current_document' not in st.session_state:
29
+ st.session_state.current_document = None
30
+ if 'current_document_text' not in st.session_state:
31
+ st.session_state.current_document_text = None
32
+ if 'loaded_files' not in st.session_state:
33
+ st.session_state.loaded_files = []
34
+ if 'notifications' not in st.session_state:
35
+ st.session_state.notifications = []
36
 
37
+ # وظيفة لتهيئة حزم NLTK المطلوبة عند بدء التطبيق
38
+ def initialize_nltk_resources():
39
+ """تنزيل وتهيئة موارد NLTK المطلوبة"""
 
40
  try:
41
+ # محاولة تنزيل حزم NLTK الأساسية
42
+ import nltk
43
+
44
+ # قائمة بالحزم المطلوبة
45
+ required_packages = ['punkt', 'stopwords', 'wordnet']
46
+ for package in required_packages:
47
+ try:
48
+ # محاولة استخدام الحزمة أولاً، وإذا فشلت يتم تنزيلها
49
+ nltk.data.find(f'tokenizers/{package}')
50
+ except LookupError:
51
+ print(f"تنزيل حزمة NLTK: {package}")
52
+ nltk.download(package, quiet=True)
53
+
54
+ print("تم تهيئة موارد NLTK بنجاح.")
55
+ except Exception as e:
56
+ print(f"خطأ في تهيئة NLTK: {e}")
57
 
58
+ # تهيئة موارد NLTK عند بدء التطبيق
59
+ initialize_nltk_resources()
 
 
 
 
 
60
 
61
+ # مسار نسبي للملفات الثابتة (للتأكد من العمل في بيئات مختلفة)
62
+ def get_static_path(file_path):
63
+ """مسار ملف ثابت يعمل سواء كان التشغيل من المجلد الرئيسي أو من المجلد الفرعي"""
64
+ # قائمة المسارات المحتملة
65
+ possible_paths = [
66
+ # المسار المباشر (في حالة تشغيل التطبيق من نفس المجلد)
67
+ file_path,
68
+ # المسار النسبي من مجلد التطبيق (tender-analysis-system)
69
+ os.path.join(os.path.dirname(__file__), file_path),
70
+ # المسار النسبي من المجلد الأعلى
71
+ os.path.join(os.path.dirname(os.path.dirname(__file__)), "tender-analysis-system", file_path),
72
  ]
73
 
74
+ # اختبار كل مسار محتمل
75
+ for path in possible_paths:
76
+ if os.path.exists(path):
77
+ return path
 
 
 
 
 
 
 
 
78
 
79
+ # إذا لم يتم العثور على الملف، إعادة المسار الأصلي
80
+ return file_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ # إعداد إعدادات الصفحة
83
+ try:
84
+ st.set_page_config(
85
+ page_title="نظام WAHBi للذكاء الاصطناعي | التعاقدات والمناقصات",
86
+ page_icon="📊",
87
+ layout="wide",
88
+ initial_sidebar_state="expanded"
89
+ )
90
+ except Exception as e:
91
+ print(f"خطأ في إعداد الصفحة: {e}")
92
+ # يحدث هذا غالبًا عند استخدام st.set_page_config أكثر من مرة
93
 
94
+ # استيراد ملفات CSS الجديدة
95
+ try:
96
+ # ملف CSS الرئيسي
97
+ main_css_path = get_static_path("utils/css/main.css")
98
+ if os.path.exists(main_css_path):
99
+ with open(main_css_path, "r", encoding='utf-8') as f:
100
+ main_css = f.read()
101
+ st.markdown(f"<style>{main_css}</style>", unsafe_allow_html=True)
102
+ else:
103
+ print(f"تعذر العثور على ملف CSS الرئيسي: {main_css_path}")
104
+
105
+ # ملف CSS للإصلاحات RTL
106
+ rtl_css_path = get_static_path("utils/css/rtl.css")
107
+ if os.path.exists(rtl_css_path):
108
+ with open(rtl_css_path, "r", encoding='utf-8') as f:
109
+ rtl_css = f.read()
110
+ st.markdown(f"<style>{rtl_css}</style>", unsafe_allow_html=True)
111
+ else:
112
+ print(f"تعذر العثور على ملف CSS للتوجيه RTL: {rtl_css_path}")
113
+
114
+ # ملف CSS للتحسينات المتقدمة
115
+ enhanced_css_path = get_static_path("utils/css/enhanced.css")
116
+ if os.path.exists(enhanced_css_path):
117
+ with open(enhanced_css_path, "r", encoding='utf-8') as f:
118
+ enhanced_css = f.read()
119
+ st.markdown(f"<style>{enhanced_css}</style>", unsafe_allow_html=True)
120
+ else:
121
+ print(f"تعذر العثور على ملف CSS المحسن: {enhanced_css_path}")
122
+ except Exception as e:
123
+ st.warning(f"حدث خطأ أثناء تحميل ملفات CSS: {str(e)}")
124
+ print(f"خطأ في تحميل ملفات CSS: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
+ # إضافة Font Awesome وأي أصول خارجية أخرى
127
+ st.markdown("""
128
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
129
+ """, unsafe_allow_html=True)
 
130
 
131
+ # إضافة CSS المخصص
132
+ st.markdown("""
133
+ <style>
134
+ /* تعديل الاتجاه للدعم العربي */
135
+ .css-18e3th9, .css-1d391kg, .stMarkdown, .stTextArea, .stButton, .stTextInput, .stSelectbox, .stRadio {
136
+ direction: rtl;
137
+ text-align: right;
 
 
 
138
  }
 
 
 
 
 
 
139
 
140
+ /* تحسين مظهر العناوين */
141
+ h1, h2, h3, h4 {
142
+ color: #1E88E5;
143
+ }
144
 
145
+ /* تخصيص عنوان التطبيق */
146
+ .app-title {
147
+ font-size: 2.2rem;
148
+ font-weight: bold;
149
+ text-align: center;
150
+ color: #1E88E5;
151
+ margin-bottom: 1rem;
152
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
153
+ background: linear-gradient(90deg, #1976D2, #64B5F6);
154
+ -webkit-background-clip: text;
155
+ -webkit-text-fill-color: transparent;
156
+ }
157
 
158
+ /* تخصيص الشريط الجانبي */
159
+ .sidebar .sidebar-content {
160
+ background-color: #f8f9fa;
161
+ }
 
 
 
 
 
162
 
163
+ /* تخصيص الأقسام */
164
+ .section-card {
165
+ background-color: #f9f9f9;
166
+ border-radius: 10px;
167
+ padding: 20px;
168
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
169
+ margin-bottom: 20px;
170
+ }
 
171
 
172
+ /* تخصيص الأزرار */
173
+ .stButton>button {
174
+ border-radius: 5px;
175
+ background-color: #1E88E5;
176
+ color: white;
177
+ font-weight: bold;
178
+ border: none;
179
+ padding: 0.5rem 1rem;
180
+ transition: all 0.3s ease;
181
+ }
182
 
183
+ .stButton>button:hover {
184
+ background-color: #1565C0;
185
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
186
+ }
187
 
188
+ /* تخصيص المؤشرات */
189
+ .indicator {
190
+ padding: 1rem;
191
+ border-radius: 10px;
192
+ background-color: #f5f5f5;
193
+ text-align: center;
194
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  }
196
 
197
+ .indicator-value {
198
+ font-size: 2rem;
199
+ font-weight: bold;
200
+ margin-bottom: 0.5rem;
 
 
 
 
 
 
 
201
  }
202
+
203
+ .indicator-label {
204
+ font-size: 1rem;
205
+ color: #666;
 
 
 
206
  }
207
+
208
+ /* تخصيص البطاقات */
209
+ .card {
 
210
  background-color: white;
211
+ border-radius: 10px;
212
+ padding: 15px;
213
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
214
+ margin-bottom: 15px;
215
+ border-right: 4px solid #1E88E5;
216
  }
217
+
218
+ .card-title {
219
+ font-weight: bold;
220
+ color: #1E88E5;
221
+ margin-bottom: 10px;
222
  }
223
+
224
+ .card-metrics {
225
+ display: flex;
226
+ justify-content: space-between;
227
  }
 
 
228
 
229
+ .card-metric {
230
+ text-align: center;
231
+ }
232
 
233
+ .card-metric-value {
234
+ font-weight: bold;
235
+ font-size: 1.5rem;
236
+ }
237
 
238
+ .card-metric-label {
239
+ font-size: 0.8rem;
240
+ color: #666;
241
+ }
242
+ </style>
243
+ """, unsafe_allow_html=True)
244
+
245
+ # استيراد المكونات والوحدات
246
+ from utils.components.sidebar import render_sidebar
247
+ from utils.helpers import create_directory_if_not_exists, get_data_folder
248
+
249
+ # استيراد وحدات التطبيق
250
+ from modules.pricing.pricing_app import PricingApp
251
+ from modules.projects.projects_app import ProjectsApp
252
+ from modules.resources.resources_app import ResourcesApp
253
+ from modules.risk_assessment.risk_assessment_app import RiskAssessmentApp
254
+ from modules.project_tracker.tracker_app import TrackerApp
255
+ from modules.maps.maps_app import MapsApp
256
+ from modules.notifications.notifications_app import NotificationsApp
257
+ from modules.voice_narration.voice_narration_app import VoiceNarrationApp
258
+ from modules.achievements.achievements_app import AchievementsApp
259
+ from modules.ai_finetuning.finetuning_app import FinetuningApp
260
+ from modules.document_comparison.comparison_app import DocumentComparisonApp
261
+
262
+ # إنشاء مجلدات البيانات الضرورية
263
+ create_directory_if_not_exists(get_data_folder())
264
+ create_directory_if_not_exists(os.path.join(get_data_folder(), "projects"))
265
+ create_directory_if_not_exists(os.path.join(get_data_folder(), "documents"))
266
+ create_directory_if_not_exists(os.path.join(get_data_folder(), "analysis"))
267
+
268
+ def main():
269
+ """الدالة الرئيسية للتطبيق"""
270
 
271
+ # تقديم الشريط الجانبي وتلقي الوحدة المختارة
272
+ selected_module = render_sidebar()
273
 
274
+ # إذا كان المستخدم غير مصرح له، قم بإظهار شاشة تسجيل الدخول
275
+ if "is_authenticated" in st.session_state and not st.session_state.is_authenticated:
276
+ render_login_screen()
277
+ return
278
+
279
+ # إظهار الوحدة المختارة
280
+ if selected_module == "الرئيسية":
281
+ render_homepage()
282
+
283
+ elif selected_module == "إدارة المشاريع":
284
+ projects_app = ProjectsApp()
285
+ projects_app.render()
286
+
287
+ elif selected_module == "التسعير المتكاملة":
288
+ pricing_app = PricingApp()
289
+ pricing_app.render()
290
+
291
+ elif selected_module == "الموارد والتكاليف":
292
+ resources_app = ResourcesApp()
293
+ resources_app.render()
294
+
295
+ elif selected_module == "تحليل المستندات":
296
+ # تقديم واجهة تحليل المستندات
297
+ render_document_analysis()
298
+
299
+ elif selected_module == "مقارنة المستندات":
300
+ # تقديم واجهة مقارنة المستندات
301
+ comparison_app = DocumentComparisonApp()
302
+ comparison_app.render()
303
+
304
+ elif selected_module == "تقييم مخاطر العقود":
305
+ risk_app = RiskAssessmentApp()
306
+ risk_app.render()
307
+
308
+ elif selected_module == "التقارير والتحليلات":
309
+ # تقديم واجهة التقارير والتحليلات
310
+ render_reports_and_analytics()
311
+
312
+ elif selected_module == "متتبع حالة المشروع":
313
+ tracker_app = TrackerApp()
314
+ tracker_app.render()
315
+
316
+ elif selected_module == "خريطة المشاريع":
317
+ maps_app = MapsApp()
318
+ maps_app.render()
319
+
320
+ elif selected_module == "نظام الإشعارات":
321
+ notifications_app = NotificationsApp()
322
+ notifications_app.render()
323
+
324
+ elif selected_module == "الترجمة الصوتية":
325
+ voice_app = VoiceNarrationApp()
326
+ voice_app.render()
327
+
328
+ elif selected_module == "نظام الإنجازات":
329
+ achievements_app = AchievementsApp()
330
+ achievements_app.render()
331
+
332
+ elif selected_module == "المساعد الذكي":
333
+ # تقديم واجهة المساعد الذكي
334
+ render_ai_assistant()
335
+
336
+ elif selected_module == "ضبط نماذج الذكاء الاصطناعي":
337
+ finetuning_app = FinetuningApp()
338
+ finetuning_app.render()
339
 
340
+ else:
341
+ st.error("الوحدة المطلوبة غير موجودة")
342
+
343
+
344
+ def render_login_screen():
345
+ """عرض شاشة تسجيل الدخول"""
346
+ st.markdown("<h1 class='app-title'>نظام WAHBi للذكاء الاصطناعي</h1>", unsafe_allow_html=True)
347
+
348
+ st.markdown("""
349
+ <div class="section-card">
350
+ <h2>تسجيل الدخول</h2>
351
+ <p>يرجى إدخال بيانات الاعتماد الخاصة بك للوصول إلى النظام.</p>
352
+ </div>
353
+ """, unsafe_allow_html=True)
354
+
355
+ col1, col2, col3 = st.columns([1, 2, 1])
356
 
357
  with col2:
358
+ username = st.text_input("اسم المستخدم")
359
+ password = st.text_input("كلمة المرور", type="password")
360
+
361
+ if st.button("تسجيل الدخول"):
362
+ # تنفيذ منطق المصادقة
363
+ if username == "admin" and password == "admin": # بيانات اعتماد مؤقتة للتطوير
364
+ st.session_state.is_authenticated = True
365
+ st.session_state.user_info = {
366
+ "id": 1,
367
+ "username": "admin",
368
+ "full_name": "مدير النظام",
369
+ "email": "[email protected]",
370
+ "role": "مدير",
371
+ "department": "الإدارة",
372
+ "last_login": "2023-01-01 09:00:00"
373
+ }
374
+ st.rerun()
375
+ else:
376
+ st.error("اسم المستخدم أو كلمة المرور غير صحيحة")
377
 
378
+ st.markdown("""
379
+ <div style="text-align: center; margin-top: 50px; color: #666;">
380
+ <p>نظام WAHBi للذكاء الاصطناعي © 2025 شركة شبه الجزيرة للمقاولات</p>
381
+ <p>جميع الحقوق محفوظة</p>
382
+ </div>
383
+ """, unsafe_allow_html=True)
384
+
385
+
386
+ def render_homepage():
387
+ """عرض الصفحة الرئيسية للتطبيق"""
388
+ st.markdown("<h1 class='app-title'>نظام WAHBi للذكاء الاصطناعي</h1>", unsafe_allow_html=True)
389
+ st.markdown("<div style='text-align: center; margin-bottom: 20px;'>نظام متكامل لتحليل العقود والمناقصات باستخدام تقنيات الذكاء الاصطناعي المتقدمة</div>", unsafe_allow_html=True)
390
 
391
+ # عرض مؤشرات الأداء الرئيسية
392
+ col1, col2, col3, col4 = st.columns(4)
393
 
394
  with col1:
395
  st.markdown("""
396
+ <div class="indicator">
397
+ <div class="indicator-value" style="color: #1E88E5;">24</div>
398
+ <div class="indicator-label">المناقصات النشطة</div>
 
399
  </div>
400
  """, unsafe_allow_html=True)
401
 
402
  with col2:
403
  st.markdown("""
404
+ <div class="indicator">
405
+ <div class="indicator-value" style="color: #43A047;">8</div>
406
+ <div class="indicator-label">مشاريع قيد التنفيذ</div>
 
407
  </div>
408
  """, unsafe_allow_html=True)
409
 
410
  with col3:
411
  st.markdown("""
412
+ <div class="indicator">
413
+ <div class="indicator-value" style="color: #FB8C00;">12</div>
414
+ <div class="indicator-label">مستندات قيد التحليل</div>
 
415
  </div>
416
  """, unsafe_allow_html=True)
417
 
418
+ with col4:
419
+ st.markdown("""
420
+ <div class="indicator">
421
+ <div class="indicator-value" style="color: #E53935;">5</div>
422
+ <div class="indicator-label">تنبيهات تتطلب الاهتمام</div>
423
+ </div>
424
+ """, unsafe_allow_html=True)
 
 
 
 
425
 
426
+ # عرض المشاريع الأخيرة والوصول السريع
427
+ col1, col2 = st.columns([2, 1])
428
 
429
  with col1:
430
+ st.markdown("### المناقصات الأخيرة")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
+ st.markdown("""
433
+ <div class="card">
434
+ <div class="card-title">إنشاء طريق سريع بمنطقة الرياض</div>
435
+ <div>رقم المناقصة: TR-2025-134</div>
436
+ <div>الجهة المالكة: وزارة النقل</div>
437
+ <div>تاريخ الإغلاق: 15 أبريل 2025</div>
438
+ <div class="card-metrics" style="margin-top: 10px;">
439
+ <div class="card-metric">
440
+ <div class="card-metric-value" style="color: #4CAF50;">85%</div>
441
+ <div class="card-metric-label">نسبة الإنجاز</div>
442
+ </div>
443
+ <div class="card-metric">
444
+ <div class="card-metric-value" style="color: #FFC107;">متوسطة</div>
445
+ <div class="card-metric-label">المخاطر</div>
446
+ </div>
447
+ <div class="card-metric">
448
+ <div class="card-metric-value" style="color: #2196F3;">مرتفعة</div>
449
+ <div class="card-metric-label">الأولوية</div>
450
+ </div>
451
+ </div>
452
+ </div>
453
 
454
+ <div class="card">
455
+ <div class="card-title">تطوير شبكة الصرف الصحي بالمنطقة الشرقية</div>
456
+ <div>رقم المناقصة: WS-2025-089</div>
457
+ <div>الجهة المالكة: وزارة المياه</div>
458
+ <div>تاريخ الإغلاق: 22 أبريل 2025</div>
459
+ <div class="card-metrics" style="margin-top: 10px;">
460
+ <div class="card-metric">
461
+ <div class="card-metric-value" style="color: #4CAF50;">62%</div>
462
+ <div class="card-metric-label">نسبة الإنجاز</div>
463
+ </div>
464
+ <div class="card-metric">
465
+ <div class="card-metric-value" style="color: #F44336;">مرتفعة</div>
466
+ <div class="card-metric-label">المخاطر</div>
467
+ </div>
468
+ <div class="card-metric">
469
+ <div class="card-metric-value" style="color: #2196F3;">مرتفعة</div>
470
+ <div class="card-metric-label">الأولوية</div>
471
+ </div>
472
+ </div>
473
+ </div>
474
 
475
+ <div class="card">
476
+ <div class="card-title">بناء 3 مدارس بمنطقة مكة المكرمة</div>
477
+ <div>رقم المناقصة: ED-2025-112</div>
478
+ <div>الجهة المالكة: وزارة التعليم</div>
479
+ <div>تاريخ الإغلاق: 5 مايو 2025</div>
480
+ <div class="card-metrics" style="margin-top: 10px;">
481
+ <div class="card-metric">
482
+ <div class="card-metric-value" style="color: #4CAF50;">38%</div>
483
+ <div class="card-metric-label">نسبة الإنجاز</div>
484
+ </div>
485
+ <div class="card-metric">
486
+ <div class="card-metric-value" style="color: #4CAF50;">منخفضة</div>
487
+ <div class="card-metric-label">المخاطر</div>
488
+ </div>
489
+ <div class="card-metric">
490
+ <div class="card-metric-value" style="color: #FFC107;">متوسطة</div>
491
+ <div class="card-metric-label">الأولوية</div>
492
+ </div>
493
+ </div>
494
+ </div>
495
+ """, unsafe_allow_html=True)
496
 
497
+ with col2:
498
+ st.markdown("### الوصول السريع")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
 
500
+ st.markdown("""
501
+ <div style="display: grid; gap: 10px;">
502
+ <button style="background-color: #1E88E5; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
503
+ <i class="fas fa-file-alt" style="margin-left: 10px;"></i> تحليل مستند جديد
504
+ </button>
505
+
506
+ <button style="background-color: #43A047; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
507
+ <i class="fas fa-calculator" style="margin-left: 10px;"></i> حساب تكاليف مشروع
508
+ </button>
509
+
510
+ <button style="background-color: #FB8C00; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
511
+ <i class="fas fa-exclamation-triangle" style="margin-left: 10px;"></i> تقييم مخاطر العقد
512
+ </button>
513
+
514
+ <button style="background-color: #8E24AA; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
515
+ <i class="fas fa-map-marker-alt" style="margin-left: 10px;"></i> استعراض خريطة المشاريع
516
+ </button>
517
+
518
+ <button style="background-color: #546E7A; color: white; border: none; border-radius: 5px; padding: 10px; text-align: right; cursor: pointer; font-weight: bold;">
519
+ <i class="fas fa-chart-bar" style="margin-left: 10px;"></i> إنشاء تقارير تحليلية
520
+ </button>
521
+ </div>
522
+ """, unsafe_allow_html=True)
523
 
524
+ st.markdown("### آخر التنبيهات")
 
 
 
 
 
 
 
525
 
526
+ st.markdown("""
527
+ <div style="background-color: #FFEBEE; border-right: 3px solid #E53935; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
528
+ <div style="font-weight: bold; color: #B71C1C;">انتهاء موعد تقديم المناقصة</div>
529
+ <div style="font-size: 0.9rem;">مشروع إنشاء الطريق السريع - متبقي 3 أيام</div>
530
+ </div>
 
 
 
 
 
 
 
 
531
 
532
+ <div style="background-color: #FFF8E1; border-right: 3px solid #FFA000; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
533
+ <div style="font-weight: bold; color: #FF6F00;">تغيير في شروط المناقصة</div>
534
+ <div style="font-size: 0.9rem;">تم تحديث مستندات مشروع شبكة الصرف الصحي</div>
535
+ </div>
 
 
 
 
536
 
537
+ <div style="background-color: #E8F5E9; border-right: 3px solid #43A047; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
538
+ <div style="font-weight: bold; color: #2E7D32;">إكمال تحليل المستند</div>
539
+ <div style="font-size: 0.9rem;">اكتمل تحليل عقد بناء المدارس بنجاح</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  </div>
541
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
543
+ # معلومات حول النظام
544
+ st.markdown("---")
 
 
 
 
 
 
 
 
 
545
 
 
546
  st.markdown("""
547
+ <div class="section-card">
548
+ <h3>حول النظام</h3>
549
+ <p>نظام WAHBi للذكاء الاصطناعي هو نظام متكامل لتحليل العقود والمناقصات وإدارة المشاريع، مصمم خصيصاً لشركات المقاولات والبناء. يستخدم النظام تقنيات الذكاء الاصطناعي المتقدمة لتحليل المستندات واستخراج المعلومات المهمة وتقييم المخاطر ودعم اتخاذ القرار.</p>
 
550
  </div>
551
  """, unsafe_allow_html=True)
552
 
553
+ # معلومات الشركة
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  st.markdown("""
555
+ <div style="text-align: center; margin-top: 30px; color: #666;">
556
+ <p>هذا النظام يعمل لشركة شبه الجزيرة للمقاولات</p>
557
+ <p>جميع الحقوق محفوظة 2025</p>
558
+ </div>
559
  """, unsafe_allow_html=True)
560
 
561
+
562
+ def render_document_analysis():
563
+ """عرض واجهة تحليل المستندات"""
564
+ st.markdown("<h1 class='app-title'>تحليل المستندات</h1>", unsafe_allow_html=True)
565
 
 
566
  st.markdown("""
567
+ <div class="section-card">
568
+ <p>استخدم هذه الوحدة لتحليل مستندات العقود والمناقصات باستخدام تقنيات الذكاء الاصطناعي المتقدمة.
569
+ يمكنك تحميل المستندات بتنسيقات PDF أو Word وسيقوم النظام بتحليلها واستخراج المعلومات المهمة مثل الشروط والتكاليف والمخاطر والتزاماتك كمقاول.</p>
 
570
  </div>
571
  """, unsafe_allow_html=True)
572
 
573
+ # أدوات التحليل
574
+ st.markdown("### أدوات التحليل:", unsafe_allow_html=True)
575
 
576
  col1, col2, col3 = st.columns(3)
577
 
578
  with col1:
579
+ st.markdown("""
580
+ <div class="card">
581
+ <div class="card-title">تحليل العقد الشامل</div>
582
+ <p>تحليل شامل للعقد باستخدام Claude AI لاستخراج جميع البنود والشروط والالتزامات والمواعيد النهائية.</p>
583
+ </div>
584
+ """, unsafe_allow_html=True)
585
+
586
+ if st.button("تحليل جديد", key="btn_complete_analysis"):
587
+ # هنا سيتم استدعاء وحدة تحليل العقد الشامل
588
+ st.session_state.analysis_type = "complete"
589
+ st.session_state.show_document_upload = True
590
+ st.experimental_rerun()
591
 
592
  with col2:
593
+ st.markdown("""
594
+ <div class="card">
595
+ <div class="card-title">تحليل جداول الكميات</div>
596
+ <p>تحليل متخصص لجداول الكميات (BOQ) لاستخراج قوائم المواد والكميات والأسعار والتكاليف الإجمالية.</p>
597
+ </div>
598
+ """, unsafe_allow_html=True)
599
+
600
+ if st.button("تحليل جديد", key="btn_boq_analysis"):
601
+ # هنا سيتم استدعاء وحدة تحل��ل جداول الكميات
602
+ st.session_state.analysis_type = "boq"
603
+ st.session_state.show_document_upload = True
604
+ st.experimental_rerun()
605
 
606
  with col3:
607
+ st.markdown("""
608
+ <div class="card">
609
+ <div class="card-title">تحليل الشروط والأحكام</div>
610
+ <p>تحليل متخصص للشروط والأحكام في العقد لتحديد الشروط الغير عادية أو المقيدة والمخاطر القانونية.</p>
611
+ </div>
612
+ """, unsafe_allow_html=True)
613
+
614
+ if st.button("تحليل جديد", key="btn_terms_analysis"):
615
+ # هنا سيتم استدعاء وحدة تحليل الشروط والأحكام
616
+ st.session_state.analysis_type = "terms"
617
+ st.session_state.show_document_upload = True
618
+ st.experimental_rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619
 
620
+ # التحليلات الأخيرة
621
+ st.markdown("### التحليلات الأخيرة")
 
 
 
 
 
 
 
 
 
622
 
 
623
  st.markdown("""
624
+ <table style="width: 100%; border-collapse: collapse; margin-top: 20px;">
625
+ <thead>
626
+ <tr style="background-color: #f5f5f5;">
627
+ <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">اسم المستند</th>
628
+ <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">نوع التحليل</th>
629
+ <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">تاريخ التحليل</th>
630
+ <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">الحالة</th>
631
+ <th style="padding: 12px; text-align: right; border-bottom: 2px solid #ddd;">الإجراءات</th>
632
+ </tr>
633
+ </thead>
634
+ <tbody>
635
+ <tr>
636
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">عقد إنشاء طريق سريع.pdf</td>
637
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل شامل</td>
638
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-25</td>
639
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #4CAF50; font-weight: bold;">مكتم��</span></td>
640
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
641
+ <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
642
+ <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل التقرير</button>
643
+ </td>
644
+ </tr>
645
+ <tr style="background-color: #f9f9f9;">
646
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">جداول كميات مشروع صرف صحي.xlsx</td>
647
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل جداول الكميات</td>
648
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-23</td>
649
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #4CAF50; font-weight: bold;">مكتمل</span></td>
650
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
651
+ <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
652
+ <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل التقرير</button>
653
+ </td>
654
+ </tr>
655
+ <tr>
656
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">شروط وأحكام عقد بناء مدارس.pdf</td>
657
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل الشروط والأحكام</td>
658
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-20</td>
659
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #4CAF50; font-weight: bold;">مكتمل</span></td>
660
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
661
+ <button style="background-color: #1E88E5; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;">عرض</button>
662
+ <button style="background-color: #78909C; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;">تنزيل التقرير</button>
663
+ </td>
664
+ </tr>
665
+ <tr style="background-color: #f9f9f9;">
666
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">ملحق عقد مشروع كباري.pdf</td>
667
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">تحليل شامل</td>
668
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">2023-03-18</td>
669
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;"><span style="color: #FB8C00; font-weight: bold;">قيد المعالجة</span></td>
670
+ <td style="padding: 12px; text-align: right; border-bottom: 1px solid #ddd;">
671
+ <button style="background-color: #9E9E9E; color: white; border: none; border-radius: 3px; padding: 5px 10px; margin-left: 5px; cursor: pointer;" disabled>عرض</button>
672
+ <button style="background-color: #9E9E9E; color: white; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer;" disabled>تنزيل التقرير</button>
673
+ </td>
674
+ </tr>
675
+ </tbody>
676
+ </table>
677
  """, unsafe_allow_html=True)
678
 
679
+ # إحصائيات التحليل
680
+ st.markdown("### إحصائيات التحليل")
681
 
682
+ col1, col2 = st.columns(2)
 
683
 
684
+ with col1:
685
+ st.markdown("""
686
+ <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; height: 100%;">
687
+ <h4 style="color: #1E88E5; margin-bottom: 15px;">توزيع أنواع المستندات</h4>
688
+ <div style="margin-bottom: 15px;">
689
+ <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
690
+ <span>عقود ومناقصات</span>
691
+ <span>45%</span>
692
+ </div>
693
+ <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
694
+ <div style="height: 100%; width: 45%; background-color: #1E88E5; border-radius: 5px;"></div>
695
+ </div>
696
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
 
698
+ <div style="margin-bottom: 15px;">
699
+ <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
700
+ <span>جداول كميات</span>
701
+ <span>30%</span>
702
+ </div>
703
+ <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
704
+ <div style="height: 100%; width: 30%; background-color: #43A047; border-radius: 5px;"></div>
705
+ </div>
706
+ </div>
707
 
708
+ <div style="margin-bottom: 15px;">
709
+ <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
710
+ <span>شروط وأحكام</span>
711
+ <span>15%</span>
712
  </div>
713
+ <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
714
+ <div style="height: 100%; width: 15%; background-color: #FB8C00; border-radius: 5px;"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
  </div>
716
  </div>
 
717
 
718
+ <div style="margin-bottom: 15px;">
719
+ <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
720
+ <span>مستندات أخرى</span>
721
+ <span>10%</span>
722
+ </div>
723
+ <div style="height: 10px; background-color: #e0e0e0; border-radius: 5px;">
724
+ <div style="height: 100%; width: 10%; background-color: #78909C; border-radius: 5px;"></div>
725
+ </div>
726
+ </div>
727
+ </div>
 
 
 
 
728
  """, unsafe_allow_html=True)
729
+
730
+ with col2:
731
  st.markdown("""
732
+ <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; height: 100%;">
733
+ <h4 style="color: #1E88E5; margin-bottom: 15px;">إحصائيات التحليل الشهرية</h4>
734
+ <div style="display: flex; justify-content: space-between; text-align: center;">
735
+ <div>
736
+ <div style="font-size: 2rem; font-weight: bold; color: #1E88E5;">42</div>
737
+ <div style="color: #666;">مستند تم تحليله</div>
738
+ </div>
739
+ <div>
740
+ <div style="font-size: 2rem; font-weight: bold; color: #43A047;">38</div>
741
+ <div style="color: #666;">تحليل ناجح</div>
742
+ </div>
743
+ <div>
744
+ <div style="font-size: 2rem; font-weight: bold; color: #FB8C00;">4</div>
745
+ <div style="color: #666;">تحليل غير مكتمل</div>
746
+ </div>
747
  </div>
748
+
749
+ <h4 style="color: #1E88E5; margin-top: 20px; margin-bottom: 15px;">متوسط وقت المعالجة</h4>
750
+ <div style="display: flex; align-items: center; margin-bottom: 10px;">
751
+ <div style="width: 150px;">تحليل شامل:</div>
752
+ <div style="flex-grow: 1; height: 8px; background-color: #e0e0e0; border-radius: 5px;">
753
+ <div style="height: 100%; width: 80%; background-color: #1E88E5; border-radius: 5px;"></div>
754
+ </div>
755
+ <div style="width: 50px; text-align: left; padding-left: 10px;">2:30</div>
756
  </div>
757
+
758
+ <div style="display: flex; align-items: center; margin-bottom: 10px;">
759
+ <div style="width: 150px;">جداول الكميات:</div>
760
+ <div style="flex-grow: 1; height: 8px; background-color: #e0e0e0; border-radius: 5px;">
761
+ <div style="height: 100%; width: 50%; background-color: #43A047; border-radius: 5px;"></div>
762
+ </div>
763
+ <div style="width: 50px; text-align: left; padding-left: 10px;">1:45</div>
764
  </div>
765
+
766
+ <div style="display: flex; align-items: center;">
767
+ <div style="width: 150px;">الشروط والأحكام:</div>
768
+ <div style="flex-grow: 1; height: 8px; background-color: #e0e0e0; border-radius: 5px;">
769
+ <div style="height: 100%; width: 60%; background-color: #FB8C00; border-radius: 5px;"></div>
770
+ </div>
771
+ <div style="width: 50px; text-align: left; padding-left: 10px;">2:00</div>
772
  </div>
773
  </div>
774
  """, unsafe_allow_html=True)
775
+
776
+
777
+ def render_reports_and_analytics():
778
+ """عرض واجهة التقارير والتحليلات"""
779
+ st.markdown("<h1 class='app-title'>التقارير والتحليلات</h1>", unsafe_allow_html=True)
780
 
781
+ st.markdown("""
782
+ <div class="section-card">
783
+ <p>استخدم هذه الوحدة لإنشاء تقارير تحليلية متقدمة عن المشاريع والمناقصات والأداء العام.
784
+ يوفر النظام رؤى وتحليلات متعمقة تساعدك على فهم أداء مشاريعك وتحسين عمليات صنع القرار.</p>
785
+ </div>
786
+ """, unsafe_allow_html=True)
787
+
788
+ # أنواع التقارير
789
+ st.markdown("### أنواع التقارير")
790
+
791
+ col1, col2, col3 = st.columns(3)
792
+
793
+ with col1:
794
  st.markdown("""
795
+ <div class="card">
796
+ <div class="card-title">تقارير المشاريع</div>
797
+ <p>تقارير تفصيلية عن حالة المشاريع وتقدمها ومؤشرات الأداء الرئيسية والمشكلات المحتملة.</p>
798
+ </div>
799
  """, unsafe_allow_html=True)
800
 
801
+ if st.button("إنشاء تقرير", key="btn_project_report"):
802
+ # هنا سيتم استدعاء وحدة إنشاء تقارير المشاريع
803
+ st.session_state.report_type = "project"
804
+ st.session_state.show_report_form = True
805
+ st.experimental_rerun()
806
+
807
+ with col2:
808
  st.markdown("""
809
+ <div class="card">
810
+ <div class="card-title">تقارير الأداء المالي</div>
811
+ <p>تحليل مالي للمشاريع يتضمن الإيرادات والتكاليف والأرباح والتدفقات النقدية والانحرافات عن الميزانية.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  </div>
813
  """, unsafe_allow_html=True)
 
 
 
814
 
815
+ if st.button("إنشاء تقرير", key="btn_financial_report"):
816
+ # هنا سيتم استدعاء وحدة إنشاء تقارير الأداء المالي
817
+ st.session_state.report_type = "financial"
818
+ st.session_state.show_report_form = True
819
+ st.experimental_rerun()
820
+
821
+ with col3:
822
  st.markdown("""
823
+ <div class="card">
824
+ <div class="card-title">تقارير المناقصات</div>
825
+ <p>تحليل شامل للمناقصات النشطة والمنتهية ونسب الفوز والمنافسين ومقارنة الأسعار.</p>
826
+ </div>
827
  """, unsafe_allow_html=True)
828
 
829
+ if st.button("إنشاء تقرير", key="btn_tender_report"):
830
+ # هنا سيتم استدعاء وحدة إنشاء تقارير المناقصات
831
+ st.session_state.report_type = "tender"
832
+ st.session_state.show_report_form = True
833
+ st.experimental_rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
 
835
+ # لوحة البيانات
836
+ st.markdown("### لوحة البيانات التنفيذية")
 
 
 
 
 
 
 
 
 
837
 
838
+ col1, col2 = st.columns([2, 1])
 
 
 
 
 
 
 
839
 
840
+ with col1:
841
+ st.markdown("#### أداء المشاريع حسب القطاع")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
 
843
+ # إنشاء بيانات تجريبية للرسم البياني
844
+ sectors = ['البنية التحتية', 'السكني', 'التعليمي', 'الصحي', 'النقل']
845
+ performance = [85, 72, 64, 90, 78]
846
 
847
+ # إنشاء رسم بياني شريطي
848
+ chart_data = pd.DataFrame({'القطاع': sectors, 'الأداء (%)': performance})
849
+ st.bar_chart(chart_data.set_index('القطاع'), use_container_width=True)
850
 
851
+ # عرض بيان توضيحي
852
+ st.caption("مقارنة أداء المشاريع عبر القطاعات المختلفة (نسبة الإنجاز)")
853
 
854
+ with col2:
855
+ st.markdown("#### المؤشرات الرئيسية")
 
 
 
 
 
 
 
 
 
 
 
 
 
856
 
857
+ # نسبة المشاريع المتأخرة
858
+ st.markdown("##### نسبة المشاريع المتأخرة")
859
+ delayed_projects = 15
860
+ st.progress(delayed_projects / 100)
861
+ st.markdown(f"<p style='text-align: center; color: #F44336; font-weight: bold; margin-top: -10px;'>{delayed_projects}%</p>", unsafe_allow_html=True)
862
 
863
+ # متوسط هامش الربح
864
+ st.markdown("##### متوسط هامش الربح")
865
+ profit_margin = 22
866
+ st.progress(profit_margin / 100)
867
+ st.markdown(f"<p style='text-align: center; color: #4CAF50; font-weight: bold; margin-top: -10px;'>{profit_margin}%</p>", unsafe_allow_html=True)
868
 
869
+ # معدل نجاح المناقصات
870
+ st.markdown("##### معدل نجاح المناقصات")
871
+ tender_success = 35
872
+ st.progress(tender_success / 100)
873
+ st.markdown(f"<p style='text-align: center; color: #2196F3; font-weight: bold; margin-top: -10px;'>{tender_success}%</p>", unsafe_allow_html=True)
 
 
874
 
875
+ # تقارير الأداء
876
+ st.markdown("### تقارير الأداء الأخيرة")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
 
878
+ # التقرير الأول
879
+ with st.container():
880
+ col1, col2 = st.columns([3, 1])
881
 
882
+ with col1:
883
+ st.markdown("#### التقرير الشهري لمشاريع الربع الأول 2025")
884
+ st.markdown("تقرير شامل يوضح أداء جميع المشاريع النشطة خلال الربع الأول من عام 2025، بما في ذلك تحليل التكاليف والجدول الزمني والمخاطر.")
885
+ st.markdown("**تاريخ الإنشاء:** 15 مارس 2025")
886
 
887
+ with col2:
888
+ st.markdown("<br>", unsafe_allow_html=True) # إضافة مسافة
889
+ col2_1, col2_2 = st.columns(2)
890
+ with col2_1:
891
+ if st.button("عرض", key="view_report1"):
892
+ st.session_state.view_report = "quarterly_q1_2025"
893
+ st.session_state.show_report_viewer = True
894
+ with col2_2:
895
+ if st.button("تنزيل", key="download_report1"):
896
+ st.info("جاري تحميل التقرير...")
897
+
898
+ st.markdown("---")
899
+
900
+ # التقرير الثاني
901
+ with st.container():
902
+ col1, col2 = st.columns([3, 1])
903
 
904
+ with col1:
905
+ st.markdown("#### تحليل أداء المناقصات 2024-2025")
906
+ st.markdown("تحليل مقارن لنتائج المناقصات بين عامي 2024 و 2025، يوضح التحسن في معدلات النجاح وتحليل أسباب الخسارة وفرص التحسين.")
907
+ st.markdown("**تاريخ الإنشاء:** 28 فبراير 2025")
908
 
909
+ with col2:
910
+ st.markdown("<br>", unsafe_allow_html=True) # إضافة مسافة
911
+ col2_1, col2_2 = st.columns(2)
912
+ with col2_1:
913
+ if st.button("عرض", key="view_report2"):
914
+ st.session_state.view_report = "tenders_analysis_2024_2025"
915
+ st.session_state.show_report_viewer = True
916
+ with col2_2:
917
+ if st.button("تنزيل", key="download_report2"):
918
+ st.info("جاري تحميل التقرير...")
919
+
920
+ st.markdown("---")
921
+
922
+ # التقرير الثالث
923
+ with st.container():
924
+ col1, col2 = st.columns([3, 1])
925
 
926
+ with col1:
927
+ st.markdown("#### تقرير المخاطر المالية للمشاريع الجارية")
928
+ st.markdown("تقرير تفصيلي حول المخاطر المالية للمشاريع الجارية، بما في ذلك تحليل التدفقات النقدية والمستحقات المتأخرة والمطالبات المحتملة.")
929
+ st.markdown("**تاريخ الإنشاء:** 10 فبراير 2025")
930
 
931
+ with col2:
932
+ st.markdown("<br>", unsafe_allow_html=True) # إضافة مسافة
933
+ col2_1, col2_2 = st.columns(2)
934
+ with col2_1:
935
+ if st.button("عرض", key="view_report3"):
936
+ st.session_state.view_report = "financial_risks_2025"
937
+ st.session_state.show_report_viewer = True
938
+ with col2_2:
939
+ if st.button("تنزيل", key="download_report3"):
940
+ st.info("جاري تحميل التقرير...")
 
 
 
 
 
 
 
 
 
 
 
 
941
 
 
 
942
 
943
+ def render_ai_assistant():
944
+ """عرض واجهة المساعد الذكي باستخدام المكون الجديد"""
 
 
 
 
 
 
 
 
 
 
 
 
945
  try:
946
+ from modules.ai_assistant.assistant_app import AssistantApp
947
+
948
+ # عرض العنوان والوصف
949
+ st.markdown("<h1 class='app-title'>المساعد الذكي</h1>", unsafe_allow_html=True)
950
+
951
+ st.markdown("""
952
+ <div class="section-card">
953
+ <p>المساعد الذكي هو واجهة تفاعلية مدعومة بتقنيات الذكاء الاصطناعي لمساعدتك في جميع أنشطة إدارة المشاريع والعقود.
954
+ يمكنك طرح أسئلة بلغتك الطبيعية والحصول على إجابات فورية، أو طلب مساعدة في مهام محددة مثل تحليل بنود العقد أو تقدير التكاليف.</p>
 
 
 
955
  </div>
956
+ """, unsafe_allow_html=True)
957
+
958
+ # استدعاء المساعد الذكي الجديد
959
+ ai_assistant = AssistantApp()
960
+ ai_assistant.render()
961
+
962
+ except Exception as e:
963
+ st.error(f"حدث خطأ في تحميل المساعد الذكي: {str(e)}")
964
+ st.markdown("""
965
+ <div class="error-card">
966
+ <h3>😞 عذراً، واجهنا مشكلة في تحميل المساعد الذكي</h3>
967
+ <p>يرجى المحاولة مرة أخرى لاحقاً أو التواصل مع فريق الدعم الفني إذا استمرت المشكلة.</p>
968
+ </div>
969
+ """, unsafe_allow_html=True)
 
 
 
 
 
970
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
971
 
972
+ # تشغيل التطبيق عند تنفيذ الملف مباشرة
973
  if __name__ == "__main__":
974
+ main()
database/db_connector.py CHANGED
@@ -36,6 +36,26 @@ def get_connection():
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()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
modules/ai_assistant/ai_assistant.py CHANGED
@@ -43,10 +43,9 @@ class AIAssistant:
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,7 +164,7 @@ class AIAssistant:
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,10 +174,7 @@ class AIAssistant:
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,10 +184,8 @@ class AIAssistant:
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,3 +198,576 @@ class AIAssistant:
204
  except Exception as e:
205
  logging.error(f"خطأ في الاتصال بواجهة API الخلفية: {e}")
206
  return {"error": f"خطأ في الاتصال بواجهة API الخلفية: {str(e)}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
  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
  presence_penalty=0
175
  )
176
 
177
+ return response
 
 
 
178
  except Exception as e:
179
  logging.error(f"خطأ في استدعاء OpenAI API: {e}")
180
  return {
 
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
  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()
modules/document_comparison/document_comparator.py CHANGED
@@ -38,8 +38,37 @@ 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
  # إعداد مقيم ROUGE لمقارنة النصوص
42
  self.rouge_scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  def _preprocess_text(self, text):
45
  """معالجة النص قبل التحليل"""
 
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
  """معالجة النص قبل التحليل"""
modules/pricing/construction_calculator.py CHANGED
@@ -19,6 +19,18 @@ def render_construction_calculator():
19
  """
20
  عرض حاسبة تكاليف البناء المتكاملة
21
  """
 
 
 
 
 
 
 
 
 
 
 
 
22
  st.markdown("<h2 class='module-title'>حاسبة تكاليف البناء المتكاملة</h2>", unsafe_allow_html=True)
23
 
24
  # معلومات المشروع
@@ -639,6 +651,30 @@ def render_final_report(project_name, project_location, project_area, project_ty
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,23 +717,39 @@ def render_final_report(project_name, project_location, project_area, project_ty
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
 
 
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
  """
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
  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
 
modules/resources/resources_app.py CHANGED
@@ -461,17 +461,21 @@ class ResourcesApp:
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
  """عرض تبويب المواد"""
@@ -488,7 +492,8 @@ class ResourcesApp:
488
  category_filter = st.multiselect(
489
  "تصفية حسب الفئة",
490
  options=list(set(material['category'] for material in st.session_state.materials)),
491
- default=[]
 
492
  )
493
 
494
  # تطبيق البحث والتصفية
@@ -614,7 +619,8 @@ class ResourcesApp:
614
  category_filter = st.multiselect(
615
  "تصفية حسب الفئة",
616
  options=list(set(labor['category'] for labor in st.session_state.labor)),
617
- default=[]
 
618
  )
619
 
620
  # تطبيق البحث والتصفية
@@ -740,7 +746,8 @@ class ResourcesApp:
740
  category_filter = st.multiselect(
741
  "تصفية حسب الفئة",
742
  options=list(set(equipment['category'] for equipment in st.session_state.equipment)),
743
- default=[]
 
744
  )
745
 
746
  # تطبيق البحث والتصفية
@@ -868,14 +875,16 @@ class ResourcesApp:
868
  category_filter = st.multiselect(
869
  "تصفية حسب الفئة",
870
  options=list(set(subcontractor['category'] for subcontractor in st.session_state.subcontractors)),
871
- default=[]
 
872
  )
873
 
874
  with col3:
875
  city_filter = st.multiselect(
876
  "تصفية حسب المدينة",
877
  options=list(set(subcontractor['city'] for subcontractor in st.session_state.subcontractors)),
878
- default=[]
 
879
  )
880
 
881
  # تطبيق البحث والتصفية
@@ -1038,7 +1047,8 @@ class ResourcesApp:
1038
  selected_materials = st.multiselect(
1039
  "اختر المواد للتحليل",
1040
  options=material_options,
1041
- default=material_options[:3] if len(material_options) >= 3 else material_options
 
1042
  )
1043
 
1044
  if not selected_materials:
@@ -1049,15 +1059,30 @@ class ResourcesApp:
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,32 +1091,37 @@ class ResourcesApp:
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,17 +1289,21 @@ class ResourcesApp:
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,7 +1312,7 @@ class ResourcesApp:
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,8 +1320,8 @@ class ResourcesApp:
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,25 +1330,25 @@ class ResourcesApp:
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,9 +1383,14 @@ class ResourcesApp:
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
 
 
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
  """عرض تبويب المواد"""
 
492
  category_filter = st.multiselect(
493
  "تصفية حسب الفئة",
494
  options=list(set(material['category'] for material in st.session_state.materials)),
495
+ default=[],
496
+ key="material_category_filter_tab"
497
  )
498
 
499
  # تطبيق البحث والتصفية
 
619
  category_filter = st.multiselect(
620
  "تصفية حسب الفئة",
621
  options=list(set(labor['category'] for labor in st.session_state.labor)),
622
+ default=[],
623
+ key="labor_category_filter_tab"
624
  )
625
 
626
  # تطبيق البحث والتصفية
 
746
  category_filter = st.multiselect(
747
  "تصفية حسب الفئة",
748
  options=list(set(equipment['category'] for equipment in st.session_state.equipment)),
749
+ default=[],
750
+ key="equipment_category_filter_tab"
751
  )
752
 
753
  # تطبيق البحث والتصفية
 
875
  category_filter = st.multiselect(
876
  "تصفية حسب الفئة",
877
  options=list(set(subcontractor['category'] for subcontractor in st.session_state.subcontractors)),
878
+ default=[],
879
+ key="subcontractor_category_filter_tab"
880
  )
881
 
882
  with col3:
883
  city_filter = st.multiselect(
884
  "تصفية حسب المدينة",
885
  options=list(set(subcontractor['city'] for subcontractor in st.session_state.subcontractors)),
886
+ default=[],
887
+ key="subcontractor_city_filter_tab"
888
  )
889
 
890
  # تطبيق البحث والتصفية
 
1047
  selected_materials = st.multiselect(
1048
  "اختر المواد للتحليل",
1049
  options=material_options,
1050
+ default=material_options[:3] if len(material_options) >= 3 else material_options,
1051
+ key="price_analysis_materials_tab"
1052
  )
1053
 
1054
  if not selected_materials:
 
1059
  material_ids = {material['name']: material['id'] for material in st.session_state.materials}
1060
  selected_ids = [material_ids[name] for name in selected_materials if name in material_ids]
1061
 
1062
+ # التحقق من وجود بيانات سعرية في session_state.price_history
1063
+ if 'price_history' not in st.session_state or not st.session_state.price_history:
1064
+ st.warning("لا توجد بيانات أسعار متاحة للتحليل.")
1065
+ return
1066
+
1067
  price_history_data = []
1068
  for entry in st.session_state.price_history:
1069
  if entry['material_id'] in selected_ids:
1070
+ # الحصول على اسم المادة من المعرف
1071
  material_name = next((material['name'] for material in st.session_state.materials if material['id'] == entry['material_id']), "")
1072
+
1073
+ # التحقق من وجود المفاتيح المطلوبة
1074
+ if 'date' in entry and 'price' in entry:
1075
+ try:
1076
+ # إضافة البيانات إلى قائمة البيانات مع تحويل التاريخ إلى كائن datetime
1077
+ price_history_data.append({
1078
+ 'material': material_name, # استخدام أسماء إنجليزية للمفاتيح
1079
+ 'date': pd.to_datetime(entry['date']),
1080
+ 'price': float(entry['price']) # التأكد من تحويل السعر إلى رقم
1081
+ })
1082
+ except (ValueError, TypeError) as e:
1083
+ # تسجيل أخطاء تحويل البيانات
1084
+ st.error(f"خطأ في معالجة البيانات: {e}")
1085
+ continue
1086
 
1087
  if not price_history_data:
1088
  st.warning("لا توجد بيانات أسعار متاحة للمواد المختارة.")
 
1091
  # تحويل البيانات إلى DataFrame
1092
  price_history_df = pd.DataFrame(price_history_data)
1093
 
1094
+ # التحقق من وجود بيانات قبل رسم المخطط
1095
+ if len(price_history_df) == 0:
1096
+ st.warning("لا توجد بيانات تاريخية للأسعار متاحة لعرضها")
1097
+ else:
1098
+ # عرض المخطط الخطي للأسعار باستخدام أسماء الأعمدة الإنجليزية
1099
+ fig = px.line(
1100
+ price_history_df,
1101
+ x='date',
1102
+ y='price',
1103
+ color='material',
1104
+ title='تطور أسعار المواد المختارة',
1105
+ labels={'date': 'التاريخ', 'price': 'السعر (ريال)', 'material': 'المادة'}
1106
+ )
1107
+
1108
+ st.plotly_chart(fig, use_container_width=True)
1109
 
1110
  # حساب التغيرات في الأسعار
1111
  materials_price_changes = []
1112
 
1113
  for material_name in selected_materials:
1114
+ # استخدام أسماء الأعمدة الإنجليزية للتصفية والترتيب
1115
+ material_prices = price_history_df[price_history_df['material'] == material_name].sort_values('date')
1116
 
1117
  if len(material_prices) >= 2:
1118
+ first_price = material_prices.iloc[0]['price']
1119
+ last_price = material_prices.iloc[-1]['price']
1120
  price_change = last_price - first_price
1121
  price_change_percent = (price_change / first_price) * 100
1122
 
1123
  # حساب التقلب (الانحراف المعياري)
1124
+ price_volatility = material_prices['price'].std()
1125
 
1126
  materials_price_changes.append({
1127
  'المادة': material_name,
 
1289
  price_history_data = []
1290
  for entry in st.session_state.price_history:
1291
  if entry['material_id'] == material_id:
1292
+ try:
1293
+ price_history_data.append({
1294
+ 'date': pd.to_datetime(entry['date']),
1295
+ 'price': float(entry['price'])
1296
+ })
1297
+ except (ValueError, TypeError) as e:
1298
+ st.error(f"خطأ في معالجة البيانات: {e}")
1299
+ continue
1300
 
1301
  if not price_history_data:
1302
  st.warning("لا توجد بيانات تاريخية كافية للمادة المحددة للقيام بالتوقع.")
1303
  return
1304
 
1305
  # تحويل البيانات إلى DataFrame
1306
+ price_history_df = pd.DataFrame(price_history_data).sort_values('date')
1307
 
1308
  # إجراء التوقع
1309
  # في الواقع، ستستخدم نماذج تعلم آلي مثل ARIMA أو Prophet
 
1312
  # حساب متوسط التغير الشهري
1313
  monthly_changes = []
1314
  for i in range(1, len(price_history_df)):
1315
+ monthly_changes.append(price_history_df.iloc[i]['price'] - price_history_df.iloc[i-1]['price'])
1316
 
1317
  if monthly_changes:
1318
  avg_monthly_change = sum(monthly_changes) / len(monthly_changes)
 
1320
  avg_monthly_change = 0
1321
 
1322
  # إنشاء بيانات التوقع
1323
+ last_date = price_history_df['date'].max()
1324
+ last_price = price_history_df.loc[price_history_df['date'] == last_date, 'price'].values[0]
1325
 
1326
  forecast_dates = pd.date_range(start=last_date + pd.DateOffset(months=1), periods=forecast_period, freq='M')
1327
  forecast_prices = [last_price + (i+1) * avg_monthly_change for i in range(forecast_period)]
 
1330
  forecast_prices = [price + random.uniform(-price*0.05, price*0.05) for price in forecast_prices]
1331
 
1332
  forecast_df = pd.DataFrame({
1333
+ 'date': forecast_dates,
1334
+ 'price': forecast_prices,
1335
+ 'type': ['توقع'] * forecast_period
1336
  })
1337
 
1338
  # دمج البيانات التاريخية والتوقع
1339
  historical_df = price_history_df.copy()
1340
+ historical_df['type'] = ['تاريخي'] * len(historical_df)
1341
 
1342
  combined_df = pd.concat([historical_df, forecast_df], ignore_index=True)
1343
 
1344
  # عرض المخطط
1345
  fig = px.line(
1346
  combined_df,
1347
+ x='date',
1348
+ y='price',
1349
+ color='type',
1350
  title=f'توقع أسعار {selected_material} للـ {forecast_period} أشهر القادمة',
1351
+ labels={'date': 'التاريخ', 'price': 'السعر (ريال)', 'type': 'النوع'},
1352
  color_discrete_map={'تاريخي': 'blue', 'توقع': 'red'}
1353
  )
1354
 
 
1383
  st.markdown("#### جدول توقع الأسعار")
1384
 
1385
  forecast_table = forecast_df.copy()
1386
+ forecast_table['date'] = forecast_table['date'].dt.strftime('%Y-%m')
1387
+ forecast_table['price'] = forecast_table['price'].apply(lambda x: f"{x:,.2f} ريال")
1388
+ # إعادة تسمية الأعمدة إلى العربية لعرض الجدول
1389
+ forecast_table = forecast_table.rename(columns={
1390
+ 'date': 'التاريخ',
1391
+ 'price': 'السعر'
1392
+ })
1393
+ forecast_table = forecast_table.drop(columns=['type'])
1394
 
1395
  st.dataframe(forecast_table, use_container_width=True, hide_index=True)
1396
 
modules/risk_assessment/contract_risk_analyzer.py CHANGED
@@ -1251,30 +1251,35 @@ class ContractRiskAnalyzer:
1251
  chart_data.append({"category": data["name_ar"], "severity": "متوسطة", "count": data["medium"]})
1252
  chart_data.append({"category": data["name_ar"], "severity": "منخفضة", "count": data["low"]})
1253
 
1254
- chart_df = pd.DataFrame(chart_data)
1255
-
1256
- # إنشاء مخطط باستخدام plotly
1257
- fig = px.bar(
1258
- chart_df,
1259
- x="category",
1260
- y="count",
1261
- color="severity",
1262
- title="توزيع المخاطر حسب الفئة والخطورة",
1263
- labels={"category": "فئة المخاطر", "count": "عدد المخاطر", "severity": "مستوى الخطورة"},
1264
- color_discrete_map={"عالية": "#d63031", "متوسطة": "#fdcb6e", "منخفضة": "#00b894"}
1265
- )
1266
-
1267
- # تنسيق المخطط
1268
- fig.update_layout(
1269
- barmode='stack',
1270
- xaxis={'categoryorder': 'total descending'},
1271
- direction='rtl',
1272
- font=dict(family="Arial, sans-serif", size=14),
1273
- height=400,
1274
- margin=dict(l=10, r=10, t=50, b=10)
1275
- )
1276
-
1277
- st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
1278
 
1279
  # عرض مخطط دائري لتوزيع مستويات الخطورة
1280
  col1, col2 = st.columns([1, 1])
@@ -1665,61 +1670,66 @@ class ContractRiskAnalyzer:
1665
  "total": data["contract2"]["total"]
1666
  })
1667
 
1668
- chart_df = pd.DataFrame(chart_data)
1669
-
1670
- # إنشاء مخطط شريطي مقارن
1671
- fig = go.Figure()
1672
-
1673
- for contract in [title1, title2]:
1674
- contract_data = chart_df[chart_df["contract"] == contract]
1675
- fig.add_trace(go.Bar(
1676
- x=contract_data["category"],
1677
- y=contract_data["total"],
1678
- name=contract,
1679
- text=contract_data["total"],
1680
- textposition="auto"
1681
- ))
1682
-
1683
- fig.update_layout(
1684
- title="مقارنة إجمالي المخاطر حسب الفئة",
1685
- xaxis_title="فئة المخاطر",
1686
- yaxis_title="عدد المخاطر",
1687
- barmode='group',
1688
- xaxis={'categoryorder': 'total descending'},
1689
- direction='rtl',
1690
- font=dict(family="Arial, sans-serif", size=14),
1691
- height=500,
1692
- margin=dict(l=10, r=10, t=50, b=10)
1693
- )
1694
-
1695
- st.plotly_chart(fig, use_container_width=True)
1696
-
1697
- # عرض مخطط مقارنة المخاطر العالية
1698
- fig = go.Figure()
1699
-
1700
- for contract in [title1, title2]:
1701
- contract_data = chart_df[chart_df["contract"] == contract]
1702
- fig.add_trace(go.Bar(
1703
- x=contract_data["category"],
1704
- y=contract_data["high"],
1705
- name=contract,
1706
- text=contract_data["high"],
1707
- textposition="auto",
1708
- marker_color="#d63031" if contract == title1 else "#ff7979"
1709
- ))
1710
-
1711
- fig.update_layout(
1712
- title="مقارنة المخاطر العالية حسب الفئة",
1713
- xaxis_title="فئة المخاطر",
1714
- yaxis_title="عدد المخاطر العالية",
1715
- barmode='group',
1716
- xaxis={'categoryorder': 'total descending'},
1717
- direction='rtl',
1718
- font=dict(family="Arial, sans-serif", size=14),
1719
- height=400
1720
- )
1721
-
1722
- st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
1723
 
1724
  # عرض جدول مقارنة المخاطر المشتركة
1725
  st.markdown("<h3>المخاطر المشتركة بين العقدين</h3>", unsafe_allow_html=True)
 
1251
  chart_data.append({"category": data["name_ar"], "severity": "متوسطة", "count": data["medium"]})
1252
  chart_data.append({"category": data["name_ar"], "severity": "منخفضة", "count": data["low"]})
1253
 
1254
+ # التحقق من وجود بيانات قبل إنشاء الرسم البياني
1255
+ if chart_data:
1256
+ chart_df = pd.DataFrame(chart_data)
1257
+
1258
+ # إنشاء مخطط باستخدام plotly
1259
+ fig = px.bar(
1260
+ chart_df,
1261
+ x="category",
1262
+ y="count",
1263
+ color="severity",
1264
+ title="توزيع المخاطر حسب الفئة والخطورة",
1265
+ labels={"category": "فئة المخاطر", "count": "عدد المخاطر", "severity": "مستوى الخطورة"},
1266
+ color_discrete_map={"عالية": "#d63031", "متوسطة": "#fdcb6e", "منخفضة": "#00b894"}
1267
+ )
1268
+
1269
+ # تنسيق المخطط
1270
+ fig.update_layout(
1271
+ barmode='stack',
1272
+ xaxis={'categoryorder': 'total descending'},
1273
+ direction='rtl',
1274
+ font=dict(family="Arial, sans-serif", size=14),
1275
+ height=400,
1276
+ margin=dict(l=10, r=10, t=50, b=10)
1277
+ )
1278
+
1279
+ st.plotly_chart(fig, use_container_width=True)
1280
+ else:
1281
+ # إذا لم تكن هناك بيانات، عرض رسالة بديلة
1282
+ st.info("لم يتم العثور على مخاطر كافية لعرض الرسم البياني")
1283
 
1284
  # عرض مخطط دائري لتوزيع مستويات الخطورة
1285
  col1, col2 = st.columns([1, 1])
 
1670
  "total": data["contract2"]["total"]
1671
  })
1672
 
1673
+ # التحقق من وجود بيانات قبل إنشاء الرسم البياني
1674
+ if chart_data:
1675
+ chart_df = pd.DataFrame(chart_data)
1676
+
1677
+ # إنشاء مخطط شريطي مقارن
1678
+ fig = go.Figure()
1679
+
1680
+ for contract in [title1, title2]:
1681
+ contract_data = chart_df[chart_df["contract"] == contract]
1682
+ fig.add_trace(go.Bar(
1683
+ x=contract_data["category"],
1684
+ y=contract_data["total"],
1685
+ name=contract,
1686
+ text=contract_data["total"],
1687
+ textposition="auto"
1688
+ ))
1689
+
1690
+ fig.update_layout(
1691
+ title="مقارنة إجمالي المخاطر حسب الفئة",
1692
+ xaxis_title="فئة المخاطر",
1693
+ yaxis_title="عدد المخاطر",
1694
+ barmode='group',
1695
+ xaxis={'categoryorder': 'total descending'},
1696
+ direction='rtl',
1697
+ font=dict(family="Arial, sans-serif", size=14),
1698
+ height=500,
1699
+ margin=dict(l=10, r=10, t=50, b=10)
1700
+ )
1701
+
1702
+ st.plotly_chart(fig, use_container_width=True)
1703
+
1704
+ # عرض مخطط مقارنة المخاطر العالية
1705
+ fig = go.Figure()
1706
+
1707
+ for contract in [title1, title2]:
1708
+ contract_data = chart_df[chart_df["contract"] == contract]
1709
+ fig.add_trace(go.Bar(
1710
+ x=contract_data["category"],
1711
+ y=contract_data["high"],
1712
+ name=contract,
1713
+ text=contract_data["high"],
1714
+ textposition="auto",
1715
+ marker_color="#d63031" if contract == title1 else "#ff7979"
1716
+ ))
1717
+
1718
+ fig.update_layout(
1719
+ title="مقارنة المخاطر العالية حسب الفئة",
1720
+ xaxis_title="فئة المخاطر",
1721
+ yaxis_title="عدد المخاطر العالية",
1722
+ barmode='group',
1723
+ xaxis={'categoryorder': 'total descending'},
1724
+ direction='rtl',
1725
+ font=dict(family="Arial, sans-serif", size=14),
1726
+ height=400
1727
+ )
1728
+
1729
+ st.plotly_chart(fig, use_container_width=True)
1730
+ else:
1731
+ # إذا لم تكن هناك بيانات، عرض رسالة بديلة
1732
+ st.info("لم يتم العثور على بيانات كافية للمقارنة وعرض الرسم البياني")
1733
 
1734
  # عرض جدول مقارنة المخاطر المشتركة
1735
  st.markdown("<h3>المخاطر المشتركة بين العقدين</h3>", unsafe_allow_html=True)
static/css/rtl-fixes.css CHANGED
@@ -1,57 +1,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-
 
 
 
 
 
 
 
 
 
 
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
+ }
utils/assets/logo.svg ADDED
utils/components/header.py CHANGED
@@ -7,18 +7,26 @@ from datetime import datetime
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,7 +52,7 @@ def render_header():
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)
 
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
  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)
utils/components/sidebar.py CHANGED
@@ -1,9 +1,10 @@
1
  """
2
- مكون الشريط الجانبي
3
  """
4
 
5
  import streamlit as st
6
  from datetime import datetime
 
7
  import config
8
  from streamlit_option_menu import option_menu
9
 
@@ -16,11 +17,45 @@ def render_sidebar():
16
  اسم الوحدة المحددة
17
  """
18
  with st.sidebar:
19
- st.image("static/images/logo.png", width=150)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- # إنشاء قائمة الخيارات باستخدام مكتبة streamlit_option_menu
22
  selected_module = option_menu(
23
- "نظام العقود والمناقصات",
24
  [
25
  "الرئيسية",
26
  "إدارة المشاريع",
@@ -58,10 +93,40 @@ def render_sidebar():
58
  menu_icon="cast",
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
  )
67
 
 
1
  """
2
+ مكون الشريط الجانبي - تصميم محسن مع دعم كامل للغة العربية
3
  """
4
 
5
  import streamlit as st
6
  from datetime import datetime
7
+ import os
8
  import config
9
  from streamlit_option_menu import option_menu
10
 
 
17
  اسم الوحدة المحددة
18
  """
19
  with st.sidebar:
20
+ # تطبيق الأنماط المحسنة على الشريط الجانبي
21
+ st.markdown("""
22
+ <style>
23
+ .sidebar .sidebar-content {
24
+ background: linear-gradient(135deg, #0B6E4F 0%, #08603a 100%);
25
+ }
26
+ /* المزيد من التنسيقات المخصصة للشريط الجانبي */
27
+ .sidebar .block-container {
28
+ padding-top: 0rem;
29
+ }
30
+ div[data-testid="stSidebarNav"] > div:first-child {
31
+ margin-top: -2rem;
32
+ }
33
+ </style>
34
+ """, unsafe_allow_html=True)
35
+
36
+ # عرض الشعار المحسن
37
+ logo_path = "utils/assets/logo.svg"
38
+ if os.path.exists(logo_path):
39
+ st.image(logo_path, width=180, use_column_width=False, output_format="SVG", clamp=False)
40
+ else:
41
+ # احتياطي إذا لم يتم العثور على الشعار
42
+ st.markdown("""
43
+ <div style="text-align: center; margin-bottom: 2rem;">
44
+ <div style="font-size: 2rem; font-weight: bold; color: white; margin-bottom: 0.5rem;">WAHBI</div>
45
+ <div style="font-size: 1rem; color: rgba(255,255,255,0.8);">نظام العقود الذكي</div>
46
+ </div>
47
+ """, unsafe_allow_html=True)
48
+
49
+ # إضافة العنوان قبل القائمة
50
+ st.markdown("""
51
+ <div class="app-title" style="text-align: center; margin-bottom: 1.5rem; color: white; font-size: 1.3rem; font-weight: bold;">
52
+ نظام تحليل العقود والمناقصات
53
+ </div>
54
+ """, unsafe_allow_html=True)
55
 
56
+ # إنشاء قائمة الخيارات باستخدام مكتبة streamlit_option_menu بتصميم محسن
57
  selected_module = option_menu(
58
+ "", # إزالة العنوان لأننا أضفناه فوق القائمة
59
  [
60
  "الرئيسية",
61
  "إدارة المشاريع",
 
93
  menu_icon="cast",
94
  default_index=0,
95
  styles={
96
+ "container": {
97
+ "padding": "0px",
98
+ "background-color": "transparent",
99
+ "direction": "rtl",
100
+ "border-radius": "10px",
101
+ "margin-bottom": "20px"
102
+ },
103
+ "icon": {
104
+ "color": "#FFB100",
105
+ "font-size": "16px",
106
+ "margin-left": "10px",
107
+ "border-radius": "5px"
108
+ },
109
+ "nav-link": {
110
+ "font-size": "15px",
111
+ "text-align": "right",
112
+ "margin": "0px 0px 5px 0px",
113
+ "padding": "10px 15px",
114
+ "direction": "rtl",
115
+ "justify-content": "flex-end",
116
+ "border-radius": "7px",
117
+ "color": "rgba(255, 255, 255, 0.8)",
118
+ "font-weight": "500",
119
+ "transition": "all 0.3s ease"
120
+ },
121
+ "nav-link-selected": {
122
+ "background-color": "rgba(255, 255, 255, 0.1)",
123
+ "color": "#ffffff",
124
+ "font-weight": "700",
125
+ "border-right": "3px solid #FFB100"
126
+ },
127
+ "separator": {
128
+ "background-color": "rgba(255, 255, 255, 0.1)"
129
+ }
130
  }
131
  )
132
 
utils/css/enhanced.css ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ enhanced.css - نظام تحليل المناقصات والعقود
3
+ تحسينات متقدمة وتأثيرات خاصة للواجهة
4
+ */
5
+
6
+ /* ===== تأثيرات حركية ===== */
7
+
8
+ /* تأثير التلاشي عند إضافة أي عنصر */
9
+ @keyframes fadeIn {
10
+ from {
11
+ opacity: 0;
12
+ transform: translateY(10px);
13
+ }
14
+ to {
15
+ opacity: 1;
16
+ transform: translateY(0);
17
+ }
18
+ }
19
+
20
+ /* تأثير النبض للتنبيهات */
21
+ @keyframes pulse {
22
+ 0% {
23
+ box-shadow: 0 0 0 0 rgba(255, 82, 82, 0.4);
24
+ }
25
+ 70% {
26
+ box-shadow: 0 0 0 10px rgba(255, 82, 82, 0);
27
+ }
28
+ 100% {
29
+ box-shadow: 0 0 0 0 rgba(255, 82, 82, 0);
30
+ }
31
+ }
32
+
33
+ /* تأثير الظهور التدريجي */
34
+ @keyframes slideIn {
35
+ from {
36
+ transform: translateX(30px);
37
+ opacity: 0;
38
+ }
39
+ to {
40
+ transform: translateX(0);
41
+ opacity: 1;
42
+ }
43
+ }
44
+
45
+ /* ===== تطبيق التأثيرات ===== */
46
+
47
+ /* تطبيق تأثير التلاشي على البطاقات الرئيسية */
48
+ .card,
49
+ .section-card,
50
+ .dashboard-card {
51
+ animation: fadeIn 0.5s ease-out;
52
+ }
53
+
54
+ /* تطبيق تأثير النبض على التنبيهات المهمة */
55
+ .alert-critical {
56
+ animation: pulse 2s infinite;
57
+ }
58
+
59
+ /* تطبيق تأثير الظهور التدريجي على القوائم */
60
+ .menu-item {
61
+ animation: slideIn 0.3s ease-out;
62
+ }
63
+
64
+ /* ===== تحسينات متقدمة للمكونات ===== */
65
+
66
+ /* تحسين تفاعلية البطاقات */
67
+ .card {
68
+ transition: all 0.3s ease;
69
+ border-right: 4px solid transparent;
70
+ }
71
+
72
+ .card:hover {
73
+ transform: translateY(-5px);
74
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08);
75
+ border-right: 4px solid var(--primary-color);
76
+ }
77
+
78
+ /* تحسينات العناوين مع تأثيرات التدرجات */
79
+ .special-heading {
80
+ font-size: 2rem;
81
+ font-weight: 700;
82
+ background: linear-gradient(120deg, var(--primary-color), var(--accent-color));
83
+ -webkit-background-clip: text;
84
+ -webkit-text-fill-color: transparent;
85
+ position: relative;
86
+ display: inline-block;
87
+ margin-bottom: 1.5rem;
88
+ }
89
+
90
+ .special-heading::after {
91
+ content: '';
92
+ position: absolute;
93
+ bottom: -10px;
94
+ left: 0;
95
+ width: 60px;
96
+ height: 4px;
97
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
98
+ border-radius: 2px;
99
+ }
100
+
101
+ /* قسم مع خلفية تدرج */
102
+ .gradient-section {
103
+ background: linear-gradient(120deg, rgba(11, 110, 79, 0.05), rgba(87, 84, 255, 0.05));
104
+ border-radius: var(--border-radius-lg);
105
+ padding: var(--spacing-lg);
106
+ margin-bottom: var(--spacing-lg);
107
+ }
108
+
109
+ /* أزرار بتأثيرات متقدمة */
110
+ .enhanced-button {
111
+ border: none;
112
+ padding: 10px 20px;
113
+ border-radius: var(--border-radius-md);
114
+ font-weight: bold;
115
+ transition: all 0.3s ease;
116
+ position: relative;
117
+ overflow: hidden;
118
+ z-index: 1;
119
+ color: var(--text-light);
120
+ background-color: var(--primary-color);
121
+ }
122
+
123
+ .enhanced-button::before {
124
+ content: '';
125
+ position: absolute;
126
+ top: 0;
127
+ left: 0;
128
+ width: 0;
129
+ height: 100%;
130
+ background-color: rgba(255, 255, 255, 0.1);
131
+ transition: width 0.3s ease;
132
+ z-index: -1;
133
+ }
134
+
135
+ .enhanced-button:hover::before {
136
+ width: 100%;
137
+ }
138
+
139
+ .enhanced-button:hover {
140
+ transform: translateY(-2px);
141
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
142
+ }
143
+
144
+ /* ===== مؤشرات وأشرطة التقدم المتقدمة ===== */
145
+
146
+ /* مؤشر تقدم دائري */
147
+ .circle-progress {
148
+ position: relative;
149
+ width: 100px;
150
+ height: 100px;
151
+ border-radius: 50%;
152
+ background: conic-gradient(var(--primary-color) 0%, var(--primary-light) var(--progress, 75%), #f0f0f0 var(--progress, 75%), #f0f0f0 100%);
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ }
157
+
158
+ .circle-progress::before {
159
+ content: '';
160
+ position: absolute;
161
+ width: 70px;
162
+ height: 70px;
163
+ border-radius: 50%;
164
+ background-color: white;
165
+ }
166
+
167
+ .circle-progress-value {
168
+ position: relative;
169
+ z-index: 10;
170
+ font-weight: bold;
171
+ font-size: 1.2rem;
172
+ color: var(--primary-color);
173
+ }
174
+
175
+ /* مؤشر شريطي بتأثيرات */
176
+ .enhanced-progress {
177
+ height: 10px;
178
+ background-color: #f0f0f0;
179
+ border-radius: 5px;
180
+ overflow: hidden;
181
+ margin: 10px 0;
182
+ }
183
+
184
+ .enhanced-progress-bar {
185
+ height: 100%;
186
+ background: linear-gradient(90deg, var(--primary-color), var(--primary-light));
187
+ border-radius: 5px;
188
+ transition: width 0.5s ease;
189
+ position: relative;
190
+ overflow: hidden;
191
+ }
192
+
193
+ .enhanced-progress-bar::after {
194
+ content: '';
195
+ position: absolute;
196
+ top: 0;
197
+ left: 0;
198
+ right: 0;
199
+ bottom: 0;
200
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
201
+ animation: shimmer 1.5s infinite;
202
+ }
203
+
204
+ @keyframes shimmer {
205
+ 0% {
206
+ transform: translateX(-100%);
207
+ }
208
+ 100% {
209
+ transform: translateX(100%);
210
+ }
211
+ }
212
+
213
+ /* ===== بطاقات المؤشرات الإحصائية المطورة ===== */
214
+
215
+ .stat-card {
216
+ border-radius: var(--border-radius-md);
217
+ background-color: white;
218
+ padding: 1.5rem;
219
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
220
+ display: flex;
221
+ align-items: center;
222
+ transition: all 0.3s ease;
223
+ position: relative;
224
+ overflow: hidden;
225
+ }
226
+
227
+ .stat-card:hover {
228
+ transform: translateY(-5px);
229
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
230
+ }
231
+
232
+ .stat-card::before {
233
+ content: '';
234
+ position: absolute;
235
+ top: 0;
236
+ left: 0;
237
+ width: 6px;
238
+ height: 100%;
239
+ background-color: var(--primary-color);
240
+ }
241
+
242
+ .stat-card-icon {
243
+ width: 50px;
244
+ height: 50px;
245
+ display: flex;
246
+ align-items: center;
247
+ justify-content: center;
248
+ border-radius: 12px;
249
+ background-color: rgba(11, 110, 79, 0.1);
250
+ color: var(--primary-color);
251
+ margin-right: 1rem;
252
+ }
253
+
254
+ .stat-card-content {
255
+ flex-grow: 1;
256
+ }
257
+
258
+ .stat-card-value {
259
+ font-size: 2rem;
260
+ font-weight: bold;
261
+ color: var(--text-primary);
262
+ line-height: 1;
263
+ margin-bottom: 0.3rem;
264
+ }
265
+
266
+ .stat-card-label {
267
+ color: var(--text-muted);
268
+ font-size: 0.9rem;
269
+ }
270
+
271
+ .stat-card-trend {
272
+ display: flex;
273
+ align-items: center;
274
+ font-size: 0.8rem;
275
+ margin-top: 0.5rem;
276
+ }
277
+
278
+ .stat-card-trend.up {
279
+ color: var(--success-color);
280
+ }
281
+
282
+ .stat-card-trend.down {
283
+ color: var(--error-color);
284
+ }
285
+
286
+ /* ===== أشكال الخلفيات المحسنة ===== */
287
+
288
+ .dots-background {
289
+ background-color: white;
290
+ background-image: radial-gradient(rgba(11, 110, 79, 0.1) 1px, transparent 1px);
291
+ background-size: 20px 20px;
292
+ background-position: 0 0;
293
+ }
294
+
295
+ .geometric-pattern {
296
+ position: relative;
297
+ overflow: hidden;
298
+ z-index: 1;
299
+ }
300
+
301
+ .geometric-pattern::before {
302
+ content: "";
303
+ position: absolute;
304
+ top: -50%;
305
+ left: -50%;
306
+ width: 200%;
307
+ height: 200%;
308
+ background: repeating-linear-gradient(
309
+ 45deg,
310
+ rgba(11, 110, 79, 0.03),
311
+ rgba(11, 110, 79, 0.03) 10px,
312
+ rgba(11, 110, 79, 0.05) 10px,
313
+ rgba(11, 110, 79, 0.05) 20px
314
+ );
315
+ transform: rotate(-45deg);
316
+ z-index: -1;
317
+ }
318
+
319
+ /* ===== تأثيرات تكميلية ===== */
320
+
321
+ /* تأثير الومضة على الأزرار عند النقر */
322
+ .button-flash {
323
+ position: relative;
324
+ overflow: hidden;
325
+ }
326
+
327
+ .button-flash::after {
328
+ content: '';
329
+ position: absolute;
330
+ top: 50%;
331
+ left: 50%;
332
+ width: 5px;
333
+ height: 5px;
334
+ background: rgba(255, 255, 255, 0.5);
335
+ opacity: 0;
336
+ border-radius: 100%;
337
+ transform: scale(1, 1) translate(-50%, -50%);
338
+ transform-origin: 50% 50%;
339
+ }
340
+
341
+ .button-flash:active::after {
342
+ animation: flash 0.6s ease-out;
343
+ }
344
+
345
+ @keyframes flash {
346
+ 0% {
347
+ opacity: 1;
348
+ transform: scale(0, 0) translate(-50%, -50%);
349
+ }
350
+ 100% {
351
+ opacity: 0;
352
+ transform: scale(50, 50) translate(-50%, -50%);
353
+ }
354
+ }
355
+
356
+ /* ===== تحسينات المسافات والهوامش لسهولة القراءة ===== */
357
+
358
+ /* إضافة مسافات أكبر بين الفقرات */
359
+ p {
360
+ margin-bottom: 1.2rem;
361
+ line-height: 1.6;
362
+ }
363
+
364
+ /* تحسين النقاط والقوائم */
365
+ li {
366
+ margin-bottom: 0.5rem;
367
+ }
368
+
369
+ /* ===== تعديلات خاصة للهواتف المحمولة ===== */
370
+
371
+ @media (max-width: 768px) {
372
+ .dashboard-grid {
373
+ grid-template-columns: 1fr;
374
+ }
375
+
376
+ .stat-card {
377
+ padding: 1rem;
378
+ }
379
+
380
+ .stat-card-icon {
381
+ width: 40px;
382
+ height: 40px;
383
+ }
384
+
385
+ .stat-card-value {
386
+ font-size: 1.5rem;
387
+ }
388
+
389
+ h1 {
390
+ font-size: 1.8rem;
391
+ }
392
+
393
+ h2 {
394
+ font-size: 1.5rem;
395
+ }
396
+
397
+ .indicator-value {
398
+ font-size: 1.5rem;
399
+ }
400
+ }
utils/css/main.css ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ main.css - نظام تحليل المناقصات والعقود
3
+ الأنماط الرئيسية للتطبيق
4
+ */
5
+
6
+ /* ===== المتغيرات العامة ===== */
7
+ :root {
8
+ /* الألوان الأساسية */
9
+ --primary-color: #0B6E4F;
10
+ --primary-light: #08A45C;
11
+ --primary-dark: #074D37;
12
+ --secondary-color: #FFB100;
13
+ --secondary-light: #FFCA4D;
14
+ --secondary-dark: #D99000;
15
+ --accent-color: #5754FF;
16
+ --accent-light: #6F6CFF;
17
+ --accent-dark: #4240DD;
18
+
19
+ /* ألوان محايدة */
20
+ --background-color: #F8F9FA;
21
+ --surface-color: #FFFFFF;
22
+ --card-color: #FAFAFA;
23
+ --border-color: #E0E0E0;
24
+
25
+ /* ألوان النص */
26
+ --text-primary: #222222;
27
+ --text-secondary: #555555;
28
+ --text-muted: #888888;
29
+ --text-light: #FFFFFF;
30
+
31
+ /* ألوان مؤشرات الحالة */
32
+ --success-color: #43A047;
33
+ --warning-color: #FB8C00;
34
+ --error-color: #E53935;
35
+ --info-color: #1E88E5;
36
+
37
+ /* القياسات */
38
+ --border-radius-sm: 4px;
39
+ --border-radius-md: 8px;
40
+ --border-radius-lg: 16px;
41
+ --spacing-xs: 4px;
42
+ --spacing-sm: 8px;
43
+ --spacing-md: 16px;
44
+ --spacing-lg: 24px;
45
+ --spacing-xl: 32px;
46
+
47
+ /* الظلال */
48
+ --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.05);
49
+ --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.1);
50
+ --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.1);
51
+
52
+ /* الخطوط */
53
+ --font-family: 'Cairo', 'Tajawal', 'Readex Pro', sans-serif;
54
+ }
55
+
56
+ /* ===== إعدادات عامة ===== */
57
+ html, body {
58
+ font-family: var(--font-family);
59
+ color: var(--text-primary);
60
+ background-color: var(--background-color);
61
+ }
62
+
63
+ /* ===== العناوين ===== */
64
+ h1, h2, h3, h4, h5, h6 {
65
+ color: var(--primary-color);
66
+ font-weight: 700;
67
+ margin-bottom: var(--spacing-md);
68
+ }
69
+
70
+ h1 {
71
+ font-size: 2.2rem;
72
+ background: linear-gradient(90deg, var(--primary-color), var(--primary-light));
73
+ -webkit-background-clip: text;
74
+ -webkit-text-fill-color: transparent;
75
+ display: inline-block;
76
+ margin-bottom: var(--spacing-lg);
77
+ }
78
+
79
+ h2 {
80
+ font-size: 1.8rem;
81
+ position: relative;
82
+ padding-bottom: var(--spacing-sm);
83
+ }
84
+
85
+ h2::after {
86
+ content: '';
87
+ position: absolute;
88
+ bottom: 0;
89
+ right: 0;
90
+ width: 60px;
91
+ height: 3px;
92
+ background: var(--secondary-color);
93
+ border-radius: var(--border-radius-sm);
94
+ }
95
+
96
+ h3 {
97
+ font-size: 1.5rem;
98
+ color: var(--primary-dark);
99
+ }
100
+
101
+ h4 {
102
+ font-size: 1.2rem;
103
+ color: var(--text-primary);
104
+ }
105
+
106
+ /* ===== عنوان التطبيق ===== */
107
+ .app-title {
108
+ font-size: 2.2rem;
109
+ font-weight: bold;
110
+ text-align: center;
111
+ color: var(--primary-color);
112
+ margin-bottom: var(--spacing-lg);
113
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
114
+ background: linear-gradient(90deg, var(--primary-color), var(--primary-light));
115
+ -webkit-background-clip: text;
116
+ -webkit-text-fill-color: transparent;
117
+ }
118
+
119
+ /* ===== الأقسام والبطاقات ===== */
120
+ .section-card {
121
+ background-color: var(--surface-color);
122
+ border-radius: var(--border-radius-lg);
123
+ padding: var(--spacing-lg);
124
+ box-shadow: var(--shadow-md);
125
+ margin-bottom: var(--spacing-lg);
126
+ border-right: 4px solid var(--primary-color);
127
+ }
128
+
129
+ .card {
130
+ background-color: var(--surface-color);
131
+ border-radius: var(--border-radius-md);
132
+ padding: var(--spacing-md);
133
+ box-shadow: var(--shadow-sm);
134
+ margin-bottom: var(--spacing-md);
135
+ border-right: 3px solid var(--primary-color);
136
+ transition: all 0.3s ease;
137
+ }
138
+
139
+ .card:hover {
140
+ box-shadow: var(--shadow-md);
141
+ transform: translateY(-2px);
142
+ }
143
+
144
+ .card-title {
145
+ font-weight: bold;
146
+ color: var(--primary-color);
147
+ margin-bottom: var(--spacing-sm);
148
+ font-size: 1.2rem;
149
+ }
150
+
151
+ .card-metrics {
152
+ display: flex;
153
+ justify-content: space-between;
154
+ margin-top: var(--spacing-sm);
155
+ }
156
+
157
+ .card-metric {
158
+ text-align: center;
159
+ }
160
+
161
+ .card-metric-value {
162
+ font-weight: bold;
163
+ font-size: 1.5rem;
164
+ }
165
+
166
+ .card-metric-label {
167
+ font-size: 0.8rem;
168
+ color: var(--text-muted);
169
+ }
170
+
171
+ /* ===== المؤشرات ===== */
172
+ .indicator {
173
+ padding: var(--spacing-md);
174
+ border-radius: var(--border-radius-md);
175
+ background-color: var(--surface-color);
176
+ text-align: center;
177
+ box-shadow: var(--shadow-sm);
178
+ border-top: 3px solid var(--primary-color);
179
+ height: 100%;
180
+ }
181
+
182
+ .indicator-value {
183
+ font-size: 2rem;
184
+ font-weight: bold;
185
+ margin-bottom: var(--spacing-xs);
186
+ }
187
+
188
+ .indicator-label {
189
+ font-size: 1rem;
190
+ color: var(--text-muted);
191
+ }
192
+
193
+ /* ===== الأزرار ===== */
194
+ .stButton > button {
195
+ border-radius: var(--border-radius-md);
196
+ background-color: var(--primary-color);
197
+ color: var(--text-light);
198
+ font-weight: bold;
199
+ border: none;
200
+ padding: 0.5rem 1rem;
201
+ transition: all 0.3s ease;
202
+ }
203
+
204
+ .stButton > button:hover {
205
+ background-color: var(--primary-dark);
206
+ box-shadow: var(--shadow-sm);
207
+ }
208
+
209
+ /* زر ثانوي */
210
+ .stButton.secondary > button {
211
+ background-color: var(--secondary-color);
212
+ color: var(--text-primary);
213
+ }
214
+
215
+ .stButton.secondary > button:hover {
216
+ background-color: var(--secondary-dark);
217
+ }
218
+
219
+ /* زر محايد */
220
+ .stButton.neutral > button {
221
+ background-color: var(--text-muted);
222
+ color: var(--text-light);
223
+ }
224
+
225
+ .stButton.neutral > button:hover {
226
+ background-color: var(--text-secondary);
227
+ }
228
+
229
+ /* زر خطر */
230
+ .stButton.danger > button {
231
+ background-color: var(--error-color);
232
+ }
233
+
234
+ .stButton.danger > button:hover {
235
+ background-color: #C62828;
236
+ }
237
+
238
+ /* ===== النماذج ===== */
239
+ .stTextInput > div > div > input,
240
+ .stTextArea > div > div > textarea {
241
+ border-radius: var(--border-radius-md);
242
+ border: 1px solid var(--border-color);
243
+ padding: var(--spacing-sm);
244
+ transition: all 0.3s ease;
245
+ }
246
+
247
+ .stTextInput > div > div > input:focus,
248
+ .stTextArea > div > div > textarea:focus {
249
+ border-color: var(--primary-color);
250
+ box-shadow: 0 0 0 2px rgba(11, 110, 79, 0.2);
251
+ }
252
+
253
+ .stSelectbox > div > div > div {
254
+ border-radius: var(--border-radius-md);
255
+ border: 1px solid var(--border-color);
256
+ }
257
+
258
+ /* ===== الفواصل ===== */
259
+ hr {
260
+ border: none;
261
+ height: 1px;
262
+ background-color: var(--border-color);
263
+ margin: var(--spacing-lg) 0;
264
+ }
265
+
266
+ /* ===== الرسوم البيانية ===== */
267
+ .stPlot {
268
+ background-color: var(--surface-color);
269
+ border-radius: var(--border-radius-md);
270
+ padding: var(--spacing-md);
271
+ box-shadow: var(--shadow-sm);
272
+ }
273
+
274
+ /* ===== التنبيهات ===== */
275
+ .stAlert {
276
+ border-radius: var(--border-radius-md);
277
+ box-shadow: var(--shadow-sm);
278
+ }
279
+
280
+ /* ===== الصور ===== */
281
+ .stImage > img {
282
+ border-radius: var(--border-radius-md);
283
+ box-shadow: var(--shadow-sm);
284
+ }
285
+
286
+ /* ===== تعديلات خاصة بالواجهة ===== */
287
+ .main-content {
288
+ padding: var(--spacing-md);
289
+ }
290
+
291
+ /* ===== أنماط القائمة ===== */
292
+ .menu-container {
293
+ background-color: var(--surface-color);
294
+ border-radius: var(--border-radius-md);
295
+ box-shadow: var(--shadow-sm);
296
+ padding: var(--spacing-sm);
297
+ margin-bottom: var(--spacing-md);
298
+ }
299
+
300
+ .menu-item {
301
+ padding: var(--spacing-sm);
302
+ border-radius: var(--border-radius-sm);
303
+ cursor: pointer;
304
+ transition: all 0.3s ease;
305
+ }
306
+
307
+ .menu-item:hover {
308
+ background-color: rgba(11, 110, 79, 0.1);
309
+ }
310
+
311
+ .menu-item.active {
312
+ background-color: var(--primary-color);
313
+ color: var(--text-light);
314
+ }
315
+
316
+ /* ===== لوحات المعلومات ===== */
317
+ .dashboard-grid {
318
+ display: grid;
319
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
320
+ gap: var(--spacing-md);
321
+ margin-bottom: var(--spacing-lg);
322
+ }
323
+
324
+ .dashboard-card {
325
+ background-color: var(--surface-color);
326
+ border-radius: var(--border-radius-md);
327
+ padding: var(--spacing-md);
328
+ box-shadow: var(--shadow-sm);
329
+ display: flex;
330
+ flex-direction: column;
331
+ height: 100%;
332
+ }
333
+
334
+ .dashboard-card-header {
335
+ display: flex;
336
+ justify-content: space-between;
337
+ align-items: center;
338
+ margin-bottom: var(--spacing-sm);
339
+ padding-bottom: var(--spacing-sm);
340
+ border-bottom: 1px solid var(--border-color);
341
+ }
342
+
343
+ .dashboard-card-title {
344
+ font-weight: bold;
345
+ color: var(--primary-color);
346
+ }
347
+
348
+ .dashboard-card-icon {
349
+ color: var(--accent-color);
350
+ background-color: rgba(87, 84, 255, 0.1);
351
+ width: 40px;
352
+ height: 40px;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ border-radius: 50%;
357
+ }
358
+
359
+ .dashboard-card-content {
360
+ flex-grow: 1;
361
+ }
362
+
363
+ .dashboard-card-footer {
364
+ margin-top: var(--spacing-sm);
365
+ padding-top: var(--spacing-sm);
366
+ border-top: 1px solid var(--border-color);
367
+ display: flex;
368
+ justify-content: space-between;
369
+ align-items: center;
370
+ }
371
+
372
+ /* ===== المحتوى المشروط ===== */
373
+ .conditional-section {
374
+ transition: all 0.3s ease;
375
+ animation: fadeIn 0.3s ease-in-out;
376
+ }
377
+
378
+ @keyframes fadeIn {
379
+ from {
380
+ opacity: 0;
381
+ transform: translateY(10px);
382
+ }
383
+ to {
384
+ opacity: 1;
385
+ transform: translateY(0);
386
+ }
387
+ }
388
+
389
+ /* ===== نمط تذييل الصفحة ===== */
390
+ .footer {
391
+ text-align: center;
392
+ padding: var(--spacing-lg) 0;
393
+ color: var(--text-muted);
394
+ font-size: 0.9rem;
395
+ margin-top: var(--spacing-xl);
396
+ }
397
+
398
+ .footer a {
399
+ color: var(--primary-color);
400
+ text-decoration: none;
401
+ }
402
+
403
+ .footer a:hover {
404
+ text-decoration: underline;
405
+ }
utils/css/rtl.css ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ rtl.css - نظام تحليل المناقصات والعقود
3
+ تخصيصات الواجهة للغة العربية ودعم RTL
4
+ */
5
+
6
+ /* ===== تعديلات عامة لتنسيق RTL ===== */
7
+
8
+ /* توجيه النص العام والمكونات */
9
+ .css-18e3th9,
10
+ .css-1d391kg,
11
+ .stMarkdown,
12
+ .stTextArea,
13
+ .stButton,
14
+ .stTextInput,
15
+ .stSelectbox,
16
+ .stRadio,
17
+ .stCheckbox,
18
+ .stTabs,
19
+ .stWidgetLabel,
20
+ .stPlotlyChart,
21
+ .stDataFrame,
22
+ .stTable {
23
+ direction: rtl;
24
+ text-align: right;
25
+ }
26
+
27
+ /* توجيه صحيح للرسوم البيانية */
28
+ .plotly-graph-div,
29
+ .stPlot {
30
+ direction: ltr;
31
+ }
32
+
33
+ /* تعديل على عناصر القوائم */
34
+ .stSelectbox > div > div > div {
35
+ text-align: right;
36
+ }
37
+
38
+ /* إصلاح اتجاه المدخلات الرقمية */
39
+ input[type="number"] {
40
+ direction: ltr;
41
+ text-align: right;
42
+ }
43
+
44
+ /* تعديل نص الأزرار للتوجيه الصحيح */
45
+ .stButton > button {
46
+ direction: rtl;
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ }
51
+
52
+ /* تعديل الشاشة الرئيسية */
53
+ .main .block-container {
54
+ direction: rtl;
55
+ text-align: right;
56
+ }
57
+
58
+ /* ===== تعديلات أيقونات وعلامات خاصة ===== */
59
+
60
+ /* عكس اتجاه أيقونات السهم والأيقونات الموجهة الأخرى */
61
+ .fa-arrow-left,
62
+ .fa-chevron-left {
63
+ transform: rotate(180deg);
64
+ }
65
+
66
+ .fa-arrow-right,
67
+ .fa-chevron-right {
68
+ transform: rotate(180deg);
69
+ }
70
+
71
+ /* تعديل اتجاه علامات التبويب */
72
+ .stTabs [role="tablist"] {
73
+ flex-direction: row-reverse;
74
+ }
75
+
76
+ /* تعديل على مؤشرات التحميل */
77
+ .stProgress > div > div > div {
78
+ flex-direction: row-reverse;
79
+ }
80
+
81
+ /* ===== تعديلات خاصة للجداول والبيانات ===== */
82
+
83
+ /* إصلاح اتجاه الجداول */
84
+ .stTable {
85
+ direction: rtl;
86
+ }
87
+
88
+ .stTable th {
89
+ text-align: right;
90
+ }
91
+
92
+ /* رؤوس الجداول */
93
+ .stTable thead {
94
+ text-align: right;
95
+ }
96
+
97
+ /* صفوف البيانات */
98
+ .stTable tbody {
99
+ text-align: right;
100
+ }
101
+
102
+ /* تعديل على مكون التاريخ */
103
+ .stDateInput > div {
104
+ flex-direction: row-reverse;
105
+ }
106
+
107
+ /* ===== تحسينات إضافية ===== */
108
+
109
+ /* تعديل مكونات التفاعل */
110
+ .stWidgetLabel p {
111
+ text-align: right;
112
+ }
113
+
114
+ /* تعديل على الهوامش - تبديل الهوامش اليمنى واليسرى */
115
+ .stButton,
116
+ .stTextInput,
117
+ .stNumberInput,
118
+ .stSelectbox,
119
+ .stTextArea {
120
+ margin-right: 0;
121
+ margin-left: auto;
122
+ }
123
+
124
+ /* تعديل على الحدود - تبديل الحدود اليمنى واليسرى */
125
+ .stMarkdown blockquote {
126
+ border-right: 4px solid var(--primary-color);
127
+ border-left: none;
128
+ padding-right: 20px;
129
+ padding-left: 0;
130
+ }
131
+
132
+ /* إضافة دعم أفضل للخطوط العربية */
133
+ body {
134
+ font-family: 'Cairo', 'Tajawal', 'Readex Pro', -apple-system, BlinkMacSystemFont, sans-serif;
135
+ }
136
+
137
+ /* تعديل صناديق الإدخال */
138
+ textarea, input {
139
+ font-family: 'Cairo', 'Tajawal', 'Readex Pro', -apple-system, BlinkMacSystemFont, sans-serif;
140
+ }
141
+
142
+ /* تعديل القوائم ذات التعداد النقطي للاتجاه RTL */
143
+ ul, ol {
144
+ padding-right: 20px;
145
+ padding-left: 0;
146
+ }
147
+
148
+ /* ===== تصحيحات محددة للمكونات ===== */
149
+
150
+ /* تصحيح التنسيق للأعمدة */
151
+ div.row-widget.stRadio > div {
152
+ flex-direction: row-reverse;
153
+ }
154
+
155
+ /* تصحيح مربعات الاختيار */
156
+ .stCheckbox > div {
157
+ flex-direction: row-reverse;
158
+ }
159
+
160
+ /* تحسين مظهر وتجربة المستخدم العربية */
161
+ .stExpander > div > div > div {
162
+ text-align: right;
163
+ }
164
+
165
+ .stFileUploader > div {
166
+ direction: rtl;
167
+ text-align: right;
168
+ }
169
+
170
+ /* تصحيح ظهور القوائم المنسدلة لتلائم RTL */
171
+ .stSelectbox > div[data-baseweb="select"] > div {
172
+ direction: rtl;
173
+ }
174
+
175
+ /* تصحيح اتجاه النص في التنبيهات */
176
+ .stAlert > div {
177
+ flex-direction: row-reverse;
178
+ text-align: right;
179
+ }
180
+
181
+ /* تصحيح اتجاه صناديق النص القابلة للتحرير */
182
+ .css-1hynsf2 .stTextArea label,
183
+ .css-1hynsf2 .stTextInput label {
184
+ text-align: right;
185
+ }
186
+
187
+ /* تصحيح مربعات الاختيار المتعددة */
188
+ div.row-widget.stMultiselect > div {
189
+ direction: rtl;
190
+ }
191
+
192
+ /* تصحيح محاذاة محاذاة المحتوى وترتيب الأيقونات */
193
+ .stAlert > div > div:first-child {
194
+ margin-left: 10px;
195
+ margin-right: 0;
196
+ }
197
+
198
+ /* تصحيحات عامة للتأكد من عدم انعكاس أي شيء بشكل غير صحيح */
199
+ .stMarkdown img {
200
+ direction: ltr;
201
+ }
202
+
203
+ /* ===== تصحيحات لحالات خاصة ===== */
204
+
205
+ /* تصحيح اتجاه مكونات التاريخ والوقت */
206
+ .stDateInput, .stTimeInput {
207
+ direction: rtl;
208
+ }
209
+
210
+ /* تصحيح محدد اللون */
211
+ .stColorPicker > div {
212
+ direction: ltr;
213
+ }
214
+
215
+ /* تصحيح مكون التصفية بالنوع */
216
+ .stType > div {
217
+ direction: rtl;
218
+ }
219
+
220
+ /* تصحيح مكون الشريط الجان��ي */
221
+ .css-1v3fvcr {
222
+ direction: rtl;
223
+ }
utils/helpers/__init__.py CHANGED
@@ -6,6 +6,8 @@
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,6 +25,33 @@ from .utils import (
23
  extract_numbers_from_text
24
  )
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  __all__ = [
27
  'create_directory_if_not_exists',
28
  'get_data_folder',
@@ -36,5 +65,6 @@ __all__ = [
36
  'styled_button',
37
  'filter_dataframe',
38
  'get_file_extension',
39
- 'extract_numbers_from_text'
 
40
  ]
 
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
  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
  'styled_button',
66
  'filter_dataframe',
67
  'get_file_extension',
68
+ 'extract_numbers_from_text',
69
+ 'get_connection'
70
  ]
utils/helpers/utils.py CHANGED
@@ -199,23 +199,47 @@ def format_currency(amount, currency="ريال", decimal_places=2):
199
  formatted = format_number(amount, decimal_places)
200
  return f"{formatted} {currency}"
201
 
202
- def styled_button(label, icon=None, is_link=False, key=None, help=None, on_click=None):
203
- """زر مخصص بأسلوب موحد"""
204
- button_html = f"""
205
- <div class="styled-button">
206
- {icon + ' ' if icon else ''}{label}
207
- </div>
 
 
 
 
 
 
 
208
  """
209
-
210
  if is_link:
 
 
 
 
 
 
 
 
 
 
 
 
211
  return st.markdown(button_html, unsafe_allow_html=True)
212
  else:
213
- return st.button(
214
- f"{icon + ' ' if icon else ''}{label}",
215
- key=key,
216
- help=help,
217
- on_click=on_click
218
- )
 
 
 
 
 
 
219
 
220
  def filter_dataframe(df, column, value):
221
  """ترشيح إطار البيانات"""
 
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
  """ترشيح إطار البيانات"""