EGYADMIN commited on
Commit
6b8a792
·
verified ·
1 Parent(s): b6a9964

Update modules/data_analysis/data_analysis_app.py

Browse files
modules/data_analysis/data_analysis_app.py CHANGED
@@ -1,525 +1,497 @@
1
- """
2
- وحدة تكامل البيانات مع الذكاء الاصطناعي
3
-
4
- هذا الملف يحتوي على الفئات والدوال اللازمة لتكامل وحدة تحليل البيانات مع وحدة الذكاء الاصطناعي.
5
- """
6
-
7
  import pandas as pd
8
  import numpy as np
9
  import matplotlib.pyplot as plt
10
  import plotly.express as px
11
  import plotly.graph_objects as go
 
12
  from datetime import datetime
13
- import json
14
  import os
15
  import sys
16
  from pathlib import Path
17
 
18
- # إضافة المسار للوصول إلى وحدة تحليل البيانات
19
  current_dir = os.path.dirname(os.path.abspath(__file__))
20
  parent_dir = os.path.dirname(os.path.dirname(current_dir))
21
  if parent_dir not in sys.path:
22
  sys.path.append(parent_dir)
23
 
24
- # محاولة استيراد وحدة تحليل البيانات
25
- try:
26
- from modules.data_analysis.data_analysis_app import DataAnalysisApp
27
- except ImportError:
28
- # تعريف فئة بديلة في حالة فشل الاستيراد
29
- class DataAnalysisApp:
30
- def __init__(self):
31
- pass
32
-
33
- def run(self):
34
- pass
35
-
36
- class DataAIIntegration:
37
- """فئة تكامل البيانات مع الذكاء الاصطناعي"""
38
 
39
  def __init__(self):
40
- """تهيئة فئة تكامل البيانات مع الذكاء الاصطناعي"""
41
- self.data_analysis_app = DataAnalysisApp()
 
42
 
43
- def analyze_tender_data(self, tender_data):
44
- """
45
- تحليل بيانات المناقصة باستخدام الذكاء الاصطناعي
46
-
47
- المعلمات:
48
- tender_data (dict): بيانات المناقصة
49
-
50
- العوائد:
51
- dict: نتائج التحليل
52
- """
53
- # تحويل البيانات إلى DataFrame
54
- if isinstance(tender_data, dict):
55
- df = pd.DataFrame([tender_data])
56
- elif isinstance(tender_data, list):
57
- df = pd.DataFrame(tender_data)
58
- else:
59
- df = tender_data
60
-
61
- # تحليل البيانات
62
- results = {
63
- 'summary': self._generate_summary(df),
64
- 'recommendations': self._generate_recommendations(df),
65
- 'risk_analysis': self._analyze_risks(df),
66
- 'cost_analysis': self._analyze_costs(df),
67
- 'competitive_analysis': self._analyze_competition(df)
68
- }
69
 
70
- return results
71
-
72
- def analyze_historical_data(self, project_type=None, location=None, time_period=None):
73
- """
74
- تحليل البيانات التاريخية للمناقصات
75
-
76
- المعلمات:
77
- project_type (str): نوع المشروع (اختياري)
78
- location (str): الموقع (اختياري)
79
- time_period (str): الفترة الزمنية (اختياري)
80
-
81
- العوائد:
82
- dict: نتائج التحليل
83
- """
84
- # الحصول على البيانات التاريخية (محاكاة)
85
- historical_data = self._get_historical_data()
86
-
87
- # تطبيق التصفية إذا تم تحديدها
88
- filtered_data = historical_data.copy()
89
-
90
- if project_type:
91
- filtered_data = filtered_data[filtered_data['نوع المشروع'] == project_type]
92
-
93
- if location:
94
- filtered_data = filtered_data[filtered_data['الموقع'] == location]
95
-
96
- if time_period:
97
- # تنفيذ تصفية الفترة الزمنية (محاكاة)
98
- pass
99
-
100
- # تحليل البيانات
101
- results = {
102
- 'win_rate': self._calculate_win_rate(filtered_data),
103
- 'avg_profit_margin': self._calculate_avg_profit_margin(filtered_data),
104
- 'price_trends': self._analyze_price_trends(filtered_data),
105
- 'success_factors': self._identify_success_factors(filtered_data),
106
- 'visualizations': self._generate_visualizations(filtered_data)
107
- }
108
 
109
- return results
110
-
111
- def predict_tender_success(self, tender_data):
112
- """
113
- التن��ؤ بفرص نجاح المناقصة
114
-
115
- المعلمات:
116
- tender_data (dict): بيانات المناقصة
117
-
118
- العوائد:
119
- dict: نتائج التنبؤ
120
- """
121
- # تحويل البيانات إلى DataFrame
122
- if isinstance(tender_data, dict):
123
- df = pd.DataFrame([tender_data])
124
- elif isinstance(tender_data, list):
125
- df = pd.DataFrame(tender_data)
126
- else:
127
- df = tender_data
128
-
129
- # تنفيذ التنبؤ (محاكاة)
130
- success_probability = np.random.uniform(0, 100)
131
-
132
- # تحديد العوامل المؤثرة (محاكاة)
133
- factors = [
134
- {'name': 'السعر التنافسي', 'impact': np.random.uniform(0, 1), 'direction': 'إيجابي' if np.random.random() > 0.5 else 'سلبي'},
135
- {'name': 'الخبرة السابقة', 'impact': np.random.uniform(0, 1), 'direction': 'إيجابي' if np.random.random() > 0.5 else 'سلبي'},
136
- {'name': 'الجودة الفنية', 'impact': np.random.uniform(0, 1), 'direction': 'إيجابي' if np.random.random() > 0.5 else 'سلبي'},
137
- {'name': 'المدة الزمنية', 'impact': np.random.uniform(0, 1), 'direction': 'إيجابي' if np.random.random() > 0.5 else 'سلبي'},
138
- {'name': 'المنافسة', 'impact': np.random.uniform(0, 1), 'direction': 'إيجابي' if np.random.random() > 0.5 else 'سلبي'}
139
- ]
140
-
141
- # ترتيب العوامل حسب التأثير
142
- factors = sorted(factors, key=lambda x: x['impact'], reverse=True)
143
-
144
- # إعداد النتائج
145
- results = {
146
- 'success_probability': success_probability,
147
- 'confidence': np.random.uniform(70, 95),
148
- 'factors': factors,
149
- 'recommendations': self._generate_success_recommendations(factors)
150
- }
151
 
152
- return results
153
-
154
- def optimize_pricing(self, tender_data, competitors_data=None):
155
- """
156
- تحسين التسعير للمناقصة
157
-
158
- المعلمات:
159
- tender_data (dict): بيانات المناقصة
160
- competitors_data (list): بيانات المنافسين (اختياري)
161
-
162
- العوائد:
163
- dict: نتائج التحسين
164
- """
165
- # تحويل البيانات إلى DataFrame
166
- if isinstance(tender_data, dict):
167
- df = pd.DataFrame([tender_data])
168
- elif isinstance(tender_data, list):
169
- df = pd.DataFrame(tender_data)
170
- else:
171
- df = tender_data
172
-
173
- # تحليل بيانات المنافسين إذا كانت متوفرة
174
- if competitors_data:
175
- competitors_df = pd.DataFrame(competitors_data)
176
- else:
177
- # استخدام بيانات افتراضية للمنافسين
178
- competitors_df = self._get_competitors_data()
179
-
180
- # تنفيذ تحسين التسعير (محاكاة)
181
- base_price = float(df['الميزانية التقديرية'].iloc[0]) if 'الميزانية التقديرية' in df.columns else 10000000
182
-
183
- # حساب نطاق السعر المقترح
184
- min_price = base_price * 0.85
185
- optimal_price = base_price * 0.92
186
- max_price = base_price * 0.98
187
-
188
- # تحليل حساسية السعر
189
- price_sensitivity = []
190
- for price_factor in np.linspace(0.8, 1.1, 7):
191
- price = base_price * price_factor
192
- win_probability = max(0, min(100, 100 - (price_factor - 0.9) * 200))
193
- profit = price - (base_price * 0.75)
194
- expected_value = win_probability / 100 * profit
195
-
196
- price_sensitivity.append({
197
- 'price_factor': price_factor,
198
- 'price': price,
199
- 'win_probability': win_probability,
200
- 'profit': profit,
201
- 'expected_value': expected_value
202
- })
203
 
204
- # إعداد النتائج
205
- results = {
206
- 'min_price': min_price,
207
- 'optimal_price': optimal_price,
208
- 'max_price': max_price,
209
- 'price_sensitivity': price_sensitivity,
210
- 'market_position': self._analyze_market_position(optimal_price, competitors_df),
211
- 'recommendations': self._generate_pricing_recommendations(optimal_price, price_sensitivity)
212
- }
213
 
214
- return results
215
-
216
- def analyze_dwg_files(self, file_path):
217
- """
218
- تحليل ملفات DWG باستخدام الذكاء الاصطناعي
219
-
220
- المعلمات:
221
- file_path (str): مسار ملف DWG
222
-
223
- العوائد:
224
- dict: نتائج التحليل
225
- """
226
- # محاكاة تحليل ملف DWG
227
- results = {
228
- 'file_name': os.path.basename(file_path),
229
- 'file_size': f"{np.random.randint(1, 10)} MB",
230
- 'elements_count': np.random.randint(100, 1000),
231
- 'layers_count': np.random.randint(5, 20),
232
- 'dimensions': {
233
- 'width': f"{np.random.randint(10, 100)} م",
234
- 'height': f"{np.random.randint(10, 100)} م",
235
- 'area': f"{np.random.randint(100, 10000)} م²"
236
- },
237
- 'elements': {
238
- 'walls': np.random.randint(10, 100),
239
- 'doors': np.random.randint(5, 50),
240
- 'windows': np.random.randint(5, 50),
241
- 'columns': np.random.randint(5, 50),
242
- 'stairs': np.random.randint(1, 10)
243
- },
244
- 'materials': [
245
- {'name': 'خرسانة', 'volume': f"{np.random.randint(10, 1000)} م³"},
246
- {'name': 'حديد', 'weight': f"{np.random.randint(1, 100)} طن"},
247
- {'name': 'طابوق', 'count': f"{np.random.randint(1000, 10000)} قطعة"},
248
- {'name': 'زجاج', 'area': f"{np.random.randint(10, 1000)} م²"},
249
- {'name': 'خشب', 'volume': f"{np.random.randint(1, 50)} م³"}
250
- ],
251
- 'cost_estimate': {
252
- 'materials': np.random.randint(100000, 1000000),
253
- 'labor': np.random.randint(50000, 500000),
254
- 'equipment': np.random.randint(10000, 100000),
255
- 'total': np.random.randint(200000, 2000000)
256
- },
257
- 'recommendations': [
258
- 'يمكن تقليل تكلفة المواد باستخدام بدائل أقل تكلفة',
259
- 'يمكن تحسين كفاءة استخدام المساحة',
260
- 'يمكن تقليل عدد الأعمدة لتوفير التكلفة',
261
- 'يمكن تحسين تصميم السلالم لزيادة السلامة',
262
- 'يمكن تحسين توزيع النوافذ لزيادة الإضاءة الطبيعية'
263
- ]
264
- }
265
 
266
- return results
267
-
268
- def integrate_with_ai_assistant(self, ai_assistant):
269
- """
270
- تكامل وحدة تحليل البيانات مع وحدة الذكاء الاصطناعي
271
-
272
- المعلمات:
273
- ai_assistant: كائن وحدة الذكاء الاصطناعي
274
-
275
- العوائد:
276
- bool: نجاح التكامل
277
- """
278
- try:
279
- # إضافة وظائف تحليل البيانات إلى وحدة الذكاء الاصطناعي
280
- ai_assistant.data_integration = self
281
-
282
- # إضافة دوال التحليل إلى وحدة الذكاء الاصطناعي
283
- ai_assistant.analyze_tender_data = self.analyze_tender_data
284
- ai_assistant.analyze_historical_data = self.analyze_historical_data
285
- ai_assistant.predict_tender_success = self.predict_tender_success
286
- ai_assistant.optimize_pricing = self.optimize_pricing
287
- ai_assistant.analyze_dwg_files = self.analyze_dwg_files
288
-
289
- return True
290
- except Exception as e:
291
- print(f"خطأ في تكامل وحدة تحليل البيانات مع وحدة الذكاء الاصطناعي: {str(e)}")
292
- return False
293
 
294
- # دوال مساعدة داخلية
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
 
296
- def _get_historical_data(self):
297
- """الحصول على البيانات التاريخية"""
298
- # محاكاة البيانات التاريخية
299
- np.random.seed(42)
300
-
301
- n_tenders = 50
302
- tender_ids = [f"T-{2021 + i//20}-{i%20 + 1:03d}" for i in range(n_tenders)]
303
- tender_types = np.random.choice(["مبنى إداري", "مبنى سكني", "مدرسة", "مستشفى", "طرق", "جسور", "بنية تحتية"], n_tenders)
304
- tender_locations = np.random.choice(["الرياض", "جدة", "الدمام", "مكة", "المدينة", "أبها", "تبوك"], n_tenders)
305
- tender_areas = np.random.randint(1000, 10000, n_tenders)
306
- tender_durations = np.random.randint(6, 36, n_tenders)
307
- tender_budgets = np.random.randint(1000000, 50000000, n_tenders)
308
- tender_costs = np.array([budget * np.random.uniform(0.8, 1.1) for budget in tender_budgets])
309
- tender_profits = tender_budgets - tender_costs
310
- tender_profit_margins = tender_profits / tender_budgets * 100
311
- tender_statuses = np.random.choice(["فائز", "خاسر", "قيد التنفيذ", "منجز"], n_tenders)
312
- tender_dates = [f"202{1 + i//20}-{np.random.randint(1, 13):02d}-{np.random.randint(1, 29):02d}" for i in range(n_tenders)]
313
-
314
- # إنشاء DataFrame للمناقصات السابقة
315
- tenders_data = {
316
- "رقم المناقصة": tender_ids,
317
- "نوع المشروع": tender_types,
318
- "الموقع": tender_locations,
319
- "المساحة (م2)": tender_areas,
320
- "المدة (شهر)": tender_durations,
321
- "الميزانية (ريال)": tender_budgets,
322
- "التكلفة (ريال)": tender_costs,
323
- "الربح (ريال)": tender_profits,
324
- "هامش الربح (%)": tender_profit_margins,
325
- "الحالة": tender_statuses,
326
- "تاريخ التقديم": tender_dates
327
- }
328
 
329
- return pd.DataFrame(tenders_data)
330
-
331
- def _get_competitors_data(self):
332
- """الحصول على بيانات المنافسين"""
333
- # محاكاة بيانات المنافسين
334
- n_competitors = 10
335
- competitor_ids = [f"C-{i+1:02d}" for i in range(n_competitors)]
336
- competitor_names = [
337
- "شركة الإنشاءات المتطورة", "شركة البناء الحديث", "شركة التطوير العمراني", "شركة الإعمار الدولية",
338
- "شركة البنية التحتية المتكاملة", "شركة المقاولات العامة", "شركة التشييد والبناء", "شركة الهندسة والإنشاءات",
339
- "شركة المشاريع الكبرى", "شركة التطوير العقاري"
340
- ]
341
- competitor_specialties = np.random.choice(["مباني", "طرق", "جسور", "بنية تحتية", "متعددة"], n_competitors)
342
- competitor_sizes = np.random.choice(["صغيرة", "متوسطة", "كبيرة"], n_competitors)
343
- competitor_market_shares = np.random.uniform(1, 15, n_competitors)
344
- competitor_win_rates = np.random.uniform(10, 60, n_competitors)
345
- competitor_avg_margins = np.random.uniform(5, 20, n_competitors)
346
-
347
- # إنشاء DataFrame للمنافسين
348
- competitors_data = {
349
- "رمز المنافس": competitor_ids,
350
- "اسم المنافس": competitor_names,
351
- "التخصص": competitor_specialties,
352
- "الحجم": competitor_sizes,
353
- "حصة السوق (%)": competitor_market_shares,
354
- "معدل الفوز (%)": competitor_win_rates,
355
- "متوسط هامش الربح (%)": competitor_avg_margins
356
- }
357
 
358
- return pd.DataFrame(competitors_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
- def _generate_summary(self, df):
361
- """توليد ملخص للبيانات"""
362
- # محاكاة توليد ملخص
363
- return "تحليل البيانات يشير إلى أن هذه المناقصة تتعلق بمشروع إنشائي متوسط الحجم. تتضمن المناقصة متطلبات فنية متوسطة المستوى وشروط تعاقدية معيارية. بناءً على البيانات التاريخية، هناك فرصة جيدة للفوز بهذه المناقصة إذا تم تقديم عرض تنافسي مع التركيز على الجوانب الفنية والجودة."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
- def _generate_recommendations(self, df):
366
- """توليد توصيات بناءً على البيانات"""
367
- # محاكاة توليد توصيات
368
- return [
369
- "تقديم عرض سعر تنافسي يقل بنسبة 5-10% عن الميزانية التقديرية",
370
- "التركيز على الخبرات السابقة في مشاريع مماثلة",
371
- "تقديم حلول مبتكرة لتقليل مدة التنفيذ",
372
- "تعزيز الجوانب الفنية في العرض",
373
- "تقديم خطة تنفيذ مفصلة مع جدول زمني واضح"
374
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
- def _analyze_risks(self, df):
377
- """تحليل المخاطر"""
378
- # محاكاة تحليل المخاطر
379
- return [
380
- {"risk": "ارتفاع أسعار المواد", "probability": "متوسطة", "impact": "عالي", "mitigation": "تثبيت أسعار المواد الرئيسية مع الموردين"},
381
- {"risk": "تأخر التنفيذ", "probability": "متوسطة", "impact": "عالي", "mitigation": "وضع خطة تنفيذ مفصلة مع هوامش زمنية"},
382
- {"risk": "نقص العمالة الماهرة", "probability": "منخفضة", "impact": "متوسط", "mitigation": "التعاقد المسبق مع مقاولي الباطن"},
383
- {"risk": "تغيير نطاق العمل", "probability": "متوسطة", "impact": "عالي", "mitigation": "توثيق نطاق العمل بدقة وتحديد إجراءات التغيير"},
384
- {"risk": "مشاكل في التربة", "probability": "منخفضة", "impact": "عالي", "mitigation": "إجراء فحوصات شاملة للتربة قبل البدء"}
385
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
- def _analyze_costs(self, df):
388
- """تحليل التكاليف (دالة داخلية)"""
389
- # محاكاة تحليل التكاليف
390
- total_budget = float(df['الميزانية التقديرية'].iloc[0]) if 'الميزانية التقديرية' in df.columns else 10000000
391
-
392
- # توزيع التكاليف
393
- materials_cost = total_budget * 0.6
394
- labor_cost = total_budget * 0.25
395
- equipment_cost = total_budget * 0.15
396
-
397
- # إنشاء بيانات التوزيع للرسم البياني
398
- cost_data = {
399
- "الفئة": ["المواد", "العمالة", "المعدات"],
400
- "التكلفة": [materials_cost, labor_cost, equipment_cost]
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  }
402
 
403
  # إنشاء DataFrame
404
- cost_df = pd.DataFrame(cost_data)
405
-
406
- return {
407
- 'total_budget': total_budget,
408
- 'materials_cost': materials_cost,
409
- 'labor_cost': labor_cost,
410
- 'equipment_cost': equipment_cost,
411
- 'cost_data': cost_data,
412
- 'cost_df': cost_df
413
- }
414
-
415
- def _analyze_competition(self, df):
416
- """تحليل المنافسة"""
417
- # محاكاة تحليل المنافسة
418
- competitors = self._get_competitors_data()
419
- top_competitors = competitors.nlargest(5, "معدل الفوز (%)").to_dict('records')
420
-
421
- return {
422
- 'total_competitors': len(competitors),
423
- 'top_competitors': top_competitors,
424
- 'market_position': "متوسط",
425
- 'competitive_advantage': "الخبرة الفنية والتسعير التنافسي",
426
- 'threats': ["منافسون جدد في السوق", "تسعير أقل من المنافسين الرئيسيين"]
427
- }
428
-
429
- def _calculate_win_rate(self, df):
430
- """حساب معدل الفوز"""
431
- # محاكاة حساب ��عدل الفوز
432
- if 'الحالة' in df.columns:
433
- won_tenders = df[df['الحالة'] == 'فائز']
434
- win_rate = len(won_tenders) / len(df) * 100 if len(df) > 0 else 0
435
- return win_rate
436
- return 45.5 # قيمة افتراضية
437
-
438
- def _calculate_avg_profit_margin(self, df):
439
- """حساب متوسط هامش الربح"""
440
- # محاكاة حساب متوسط هامش الربح
441
- if 'هامش الربح (%)' in df.columns:
442
- return df['هامش الربح (%)'].mean()
443
- return 12.3 # قيمة افتراضية
444
-
445
- def _analyze_price_trends(self, df):
446
- """تحليل اتجاهات الأسعار"""
447
- # محاكاة تحليل اتجاهات الأسعار
448
- return {
449
- 'trend': 'تصاعدي',
450
- 'annual_increase': '8.5%',
451
- 'forecast': 'استمرار الزيادة بنسبة 5-7% سنويًا',
452
- 'factors': ['زيادة أسعار المواد', 'نقص العمالة', 'زيادة الطلب']
453
- }
454
-
455
- def _identify_success_factors(self, df):
456
- """تحديد عوامل النجاح"""
457
- # محاكاة تحديد عوامل النجاح
458
- return [
459
- {'factor': 'السعر التنافسي', 'importance': 'عالي'},
460
- {'factor': 'الخبرة السابقة', 'importance': 'عالي'},
461
- {'factor': 'جودة العرض الفني', 'importance': 'متوسط'},
462
- {'factor': 'مدة التنفيذ', 'importance': 'متوسط'},
463
- {'factor': 'سمعة الشركة', 'importance': 'منخفض'}
464
- ]
465
-
466
- def _generate_visualizations(self, df):
467
- """توليد الرسومات البيانية"""
468
- # محاكاة توليد الرسومات البيانية
469
- return {
470
- 'visualization_urls': [
471
- '/static/images/win_rate_chart.png',
472
- '/static/images/profit_margin_chart.png',
473
- '/static/images/project_types_chart.png'
474
- ],
475
- 'visualization_data': {
476
- 'win_rate_by_year': [
477
- {'year': '2021', 'win_rate': 42.5},
478
- {'year': '2022', 'win_rate': 45.0},
479
- {'year': '2023', 'win_rate': 48.2},
480
- {'year': '2024', 'win_rate': 47.8}
481
- ],
482
- 'profit_margin_by_project_type': [
483
- {'type': 'مبنى إداري', 'margin': 12.5},
484
- {'type': 'مبنى سكني', 'margin': 10.8},
485
- {'type': 'مدرسة', 'margin': 15.2},
486
- {'type': 'مستشفى', 'margin': 13.7},
487
- {'type': 'طرق', 'margin': 14.3},
488
- {'type': 'جسور', 'margin': 11.2},
489
- {'type': 'بنية تحتية', 'margin': 12.9}
490
- ]
491
- }
492
- }
493
-
494
- def _generate_success_recommendations(self, factors):
495
- """توليد توصيات لزيادة فرص النجاح"""
496
- # محاكاة توليد توصيات
497
- return [
498
- "تخفيض السعر بنسبة 5-8% للحصول على ميزة تنافسية",
499
- "التركيز على إبراز الخبرات السابقة في مشاريع مماثلة",
500
- "تحسين جودة العرض الفني",
501
- "تقديم جدول زمني مختصر للتنفيذ",
502
- "تقديم ضمانات إضافية للجودة"
503
- ]
504
-
505
- def _analyze_market_position(self, price, competitors_df):
506
- """تحليل الموقف التنافسي في السوق"""
507
- # محاكاة تحليل الموقف التنافسي
508
- return {
509
- 'position': 'متوسط',
510
- 'price_percentile': 45,
511
- 'competitors_lower': 3,
512
- 'competitors_higher': 7,
513
- 'recommendation': 'تخفيض السعر بشكل طفيف للحصول على موقف تنافسي أفضل'
514
- }
515
-
516
- def _generate_pricing_recommendations(self, optimal_price, price_sensitivity):
517
- """توليد توصيات التسعير"""
518
- # محاكاة توليد توصيات التسعير
519
- return [
520
- f"السعر الأمثل هو {optimal_price:,.0f} ريال",
521
- "تجنب تخفيض السعر أكثر من 15% من الميزانية التقديرية",
522
- "التركيز على تقديم قيمة إضافية بدلاً من تخفيض السعر بشكل كبير",
523
- "في حالة المنافسة الشديدة، يمكن تخفيض السعر بنسبة إضافية 3%",
524
- "تقديم خيارات سعرية بديلة مع اختلاف نطاق العمل"
525
- ]
 
1
+ import streamlit as st
 
 
 
 
 
2
  import pandas as pd
3
  import numpy as np
4
  import matplotlib.pyplot as plt
5
  import plotly.express as px
6
  import plotly.graph_objects as go
7
+ import seaborn as sns
8
  from datetime import datetime
 
9
  import os
10
  import sys
11
  from pathlib import Path
12
 
13
+ # إضافة المسار للوصول إلى الوحدات الأخرى
14
  current_dir = os.path.dirname(os.path.abspath(__file__))
15
  parent_dir = os.path.dirname(os.path.dirname(current_dir))
16
  if parent_dir not in sys.path:
17
  sys.path.append(parent_dir)
18
 
19
+ class DataAnalysisApp:
20
+ """تطبيق تحليل البيانات"""
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  def __init__(self):
23
+ """تهيئة تطبيق تحليل البيانات"""
24
+ self.data = None
25
+ self.file_path = None
26
 
27
+ def run(self):
28
+ """تشغيل تطبيق تحليل البيانات"""
29
+ # استيراد مدير التكوين
30
+ from config_manager import ConfigManager
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ # محاولة تعيين تكوين الصفحة (سيتم تجاهلها إذا كان التكوين معينًا بالفعل)
33
+ config_manager = ConfigManager()
34
+ config_manager.set_page_config_if_needed(
35
+ page_title="تحليل البيانات",
36
+ page_icon="📊",
37
+ layout="wide"
38
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # عرض عنوان التطبيق
41
+ st.title("تحليل البيانات")
42
+ st.write("استخدم هذه الأداة لتحليل بيانات المناقصات والمشاريع")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
+ # إنشاء علامات تبويب للتطبيق
45
+ tabs = st.tabs(["تحميل البيانات", "استكشاف البيانات", "تحليل متقدم", "التصور المرئي", "التقارير"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ with tabs[0]:
48
+ self._load_data_tab()
 
 
 
 
 
 
 
49
 
50
+ with tabs[1]:
51
+ self._explore_data_tab()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ with tabs[2]:
54
+ self._advanced_analysis_tab()
55
+
56
+ with tabs[3]:
57
+ self._visualization_tab()
58
+
59
+ with tabs[4]:
60
+ self._reports_tab()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ def _load_data_tab(self):
63
+ """علامة تبويب تحميل البيانات"""
64
+ st.header("تحميل البيانات")
65
+
66
+ # خيارات تحميل البيانات
67
+ data_source = st.radio(
68
+ "اختر مصدر البيانات:",
69
+ ["تحميل ملف", "استيراد من قاعدة البيانات", "استخدام بيانات نموذجية"]
70
+ )
71
+
72
+ if data_source == "تحميل ملف":
73
+ uploaded_file = st.file_uploader("اختر ملف CSV أو Excel", type=["csv", "xlsx", "xls"])
74
+
75
+ if uploaded_file is not None:
76
+ try:
77
+ if uploaded_file.name.endswith('.csv'):
78
+ self.data = pd.read_csv(uploaded_file)
79
+ else:
80
+ self.data = pd.read_excel(uploaded_file)
81
+
82
+ st.success(f"تم تحميل الملف بنجاح! عدد الصفوف: {self.data.shape[0]}, عدد الأعمدة: {self.data.shape[1]}")
83
+ st.write("معاينة البيانات:")
84
+ st.dataframe(self.data.head())
85
+ except Exception as e:
86
+ st.error(f"حدث خطأ أثناء تحميل الملف: {str(e)}")
87
+
88
+ elif data_source == "استيراد من قاعدة البيانات":
89
+ st.info("هذه الميزة قيد التطوير")
90
+
91
+ # محاكاة الاتصال بقاعدة البيانات
92
+ if st.button("اتصال بقاعدة البيانات"):
93
+ with st.spinner("جاري الاتصال بقاعدة البيانات..."):
94
+ # محاكاة تأخير الاتصال
95
+ import time
96
+ time.sleep(2)
97
+
98
+ # إنشاء بيانات نموذجية
99
+ self.data = self._create_sample_data()
100
+
101
+ st.success("تم الاتصال بقاعدة البيانات بنجاح!")
102
+ st.write("معاينة البيانات:")
103
+ st.dataframe(self.data.head())
104
+
105
+ elif data_source == "استخدام بيانات نموذجية":
106
+ if st.button("تحميل بيانات نموذجية"):
107
+ self.data = self._create_sample_data()
108
+ st.success("تم تحميل البيانات النموذجية بنجاح!")
109
+ st.write("معاينة البيانات:")
110
+ st.dataframe(self.data.head())
111
 
112
+ def _explore_data_tab(self):
113
+ """علامة تبويب استكشاف البيانات"""
114
+ st.header("استكشاف البيانات")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ if self.data is None:
117
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
118
+ return
119
+
120
+ # عرض معلومات عامة عن البيانات
121
+ st.subheader("معلومات عامة")
122
+ col1, col2 = st.columns(2)
123
+
124
+ with col1:
125
+ st.write(f"عدد الصفوف: {self.data.shape[0]}")
126
+ st.write(f"عدد الأعمدة: {self.data.shape[1]}")
127
+ st.write(f"القيم المفقودة: {self.data.isna().sum().sum()}")
128
+
129
+ with col2:
130
+ st.write(f"أنواع البيانات:")
131
+ st.write(self.data.dtypes)
132
+
133
+ # عرض إحصاءات وصفية
134
+ st.subheader("إحصاءات وصفية")
135
+ st.dataframe(self.data.describe())
 
 
 
 
 
 
 
 
136
 
137
+ # عرض معلومات عن الأعمدة
138
+ st.subheader("معلومات الأعمدة")
139
+
140
+ selected_column = st.selectbox("اختر عمودًا لتحليله:", self.data.columns)
141
+
142
+ if selected_column:
143
+ col1, col2 = st.columns(2)
144
+
145
+ with col1:
146
+ st.write(f"نوع البيانات: {self.data[selected_column].dtype}")
147
+ st.write(f"القيم الفريدة: {self.data[selected_column].nunique()}")
148
+ st.write(f"القيم المفقودة: {self.data[selected_column].isna().sum()}")
149
+
150
+ with col2:
151
+ if pd.api.types.is_numeric_dtype(self.data[selected_column]):
152
+ st.write(f"الحد الأدنى: {self.data[selected_column].min()}")
153
+ st.write(f"الحد الأقصى: {self.data[selected_column].max()}")
154
+ st.write(f"المتوسط: {self.data[selected_column].mean()}")
155
+ st.write(f"الوسيط: {self.data[selected_column].median()}")
156
+ else:
157
+ st.write("القيم الأكثر تكرارًا:")
158
+ st.write(self.data[selected_column].value_counts().head())
159
+
160
+ # عرض رسم بياني للعمود المحدد
161
+ st.subheader(f"رسم بياني لـ {selected_column}")
162
+
163
+ if pd.api.types.is_numeric_dtype(self.data[selected_column]):
164
+ fig = px.histogram(self.data, x=selected_column, title=f"توزيع {selected_column}")
165
+ st.plotly_chart(fig, use_container_width=True)
166
+ else:
167
+ fig = px.bar(self.data[selected_column].value_counts().reset_index(),
168
+ x='index', y=selected_column, title=f"توزيع {selected_column}")
169
+ fig.update_layout(xaxis_title="القيمة", yaxis_title="العدد")
170
+ st.plotly_chart(fig, use_container_width=True)
171
 
172
+ def _advanced_analysis_tab(self):
173
+ """علامة تبويب التحليل المتقدم"""
174
+ st.header("تحليل متقدم")
175
+
176
+ if self.data is None:
177
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
178
+ return
179
+
180
+ # أنواع التحليل المتقدم
181
+ analysis_type = st.selectbox(
182
+ "اختر نوع التحليل:",
183
+ ["تحليل الارتباط", "تحليل الاتجاهات", "تحليل المجموعات", "تحليل التباين"]
184
+ )
185
+
186
+ if analysis_type == "تحليل الارتباط":
187
+ st.subheader("تحليل الارتباط")
188
+
189
+ # اختيار الأعمدة الرقمية فقط
190
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
191
+
192
+ if len(numeric_columns) < 2:
193
+ st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإجراء تحليل الارتباط")
194
+ return
195
+
196
+ # حساب مصفوفة الارتباط
197
+ correlation_matrix = self.data[numeric_columns].corr()
198
+
199
+ # عرض مصفوفة الارتباط
200
+ st.write("مصفوفة الارتباط:")
201
+ st.dataframe(correlation_matrix)
202
+
203
+ # رسم خريطة حرارية للارتباط
204
+ st.write("خريطة حرارية للارتباط:")
205
+ fig = px.imshow(correlation_matrix, text_auto=True, aspect="auto",
206
+ title="خريطة حرارية لمصفوفة الارتباط")
207
+ st.plotly_chart(fig, use_container_width=True)
208
+
209
+ # تحليل الارتباط بين عمودين محددين
210
+ st.subheader("تحليل الارتباط بين عمودين محددين")
211
+
212
+ col1 = st.selectbox("اختر العمود الأول:", numeric_columns, key="corr_col1")
213
+ col2 = st.selectbox("اختر العمود الثاني:", numeric_columns, key="corr_col2")
214
+
215
+ if col1 != col2:
216
+ # حساب معامل الارتباط
217
+ correlation = self.data[col1].corr(self.data[col2])
218
+
219
+ st.write(f"معامل الارتباط بين {col1} و {col2}: {correlation:.4f}")
220
+
221
+ # رسم مخطط التشتت
222
+ fig = px.scatter(self.data, x=col1, y=col2, title=f"مخطط التشتت: {col1} مقابل {col2}")
223
+ fig.update_layout(xaxis_title=col1, yaxis_title=col2)
224
+ st.plotly_chart(fig, use_container_width=True)
225
+ else:
226
+ st.warning("الرجاء اختيار عمودين مختلفين")
227
+
228
+ elif analysis_type == "تحليل الاتجاهات":
229
+ st.subheader("تحليل الاتجاهات")
230
+ st.info("هذه الميزة قيد التطوير")
231
+
232
+ elif analysis_type == "تحليل المجموعات":
233
+ st.subheader("تحليل المجموعات")
234
+ st.info("هذه الميزة قيد التطوير")
235
+
236
+ elif analysis_type == "تحليل التباين":
237
+ st.subheader("تحليل التباين")
238
+ st.info("هذه الميزة قيد التطوير")
239
 
240
+ def _visualization_tab(self):
241
+ """علامة تبويب التصور المرئي"""
242
+ st.header("التصور المرئي")
243
+
244
+ if self.data is None:
245
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
246
+ return
247
+
248
+ # أنواع الرسوم البيانية
249
+ chart_type = st.selectbox(
250
+ "اختر نوع الرسم البياني:",
251
+ ["مخطط شريطي", "مخطط خطي", "مخطط دائري", "مخطط تشتت", "مخطط صندوقي", "مخطط حراري"]
252
+ )
253
+
254
+ # اختيار الأعمدة حسب نوع الرسم البياني
255
+ if chart_type == "مخطط شريطي":
256
+ st.subheader("مخطط شريطي")
257
+
258
+ x_column = st.selectbox("اختر عمود المحور الأفقي (x):", self.data.columns, key="bar_x")
259
+ y_column = st.selectbox("اختر عمود المحور الرأسي (y):",
260
+ self.data.select_dtypes(include=['number']).columns.tolist(),
261
+ key="bar_y")
262
+
263
+ # خيارات إضافية
264
+ color_column = st.selectbox("اختر عمود اللون (اختياري):",
265
+ ["لا يوجد"] + self.data.columns.tolist(),
266
+ key="bar_color")
267
+
268
+ # إنشاء الرسم البياني
269
+ if color_column == "لا يوجد":
270
+ fig = px.bar(self.data, x=x_column, y=y_column, title=f"{y_column} حسب {x_column}")
271
+ else:
272
+ fig = px.bar(self.data, x=x_column, y=y_column, color=color_column,
273
+ title=f"{y_column} حسب {x_column} (مصنف حسب {color_column})")
274
+
275
+ fig.update_layout(xaxis_title=x_column, yaxis_title=y_column)
276
+ st.plotly_chart(fig, use_container_width=True)
277
+
278
+ elif chart_type == "مخطط خطي":
279
+ st.subheader("مخطط خطي")
280
+
281
+ x_column = st.selectbox("اختر عمود المحور الأفقي (x):", self.data.columns, key="line_x")
282
+ y_columns = st.multiselect("اختر أعمدة المحور الرأسي (y):",
283
+ self.data.select_dtypes(include=['number']).columns.tolist(),
284
+ key="line_y")
285
+
286
+ if y_columns:
287
+ # إنشاء الرسم البياني
288
+ fig = go.Figure()
289
+
290
+ for y_column in y_columns:
291
+ fig.add_trace(go.Scatter(x=self.data[x_column], y=self.data[y_column],
292
+ mode='lines+markers', name=y_column))
293
+
294
+ fig.update_layout(title=f"مخطط خطي", xaxis_title=x_column, yaxis_title="القيمة")
295
+ st.plotly_chart(fig, use_container_width=True)
296
+ else:
297
+ st.warning("الرجاء اختيار عمود واحد على الأقل للمحور الرأسي")
298
+
299
+ elif chart_type == "مخطط دائري":
300
+ st.subheader("مخطط دائري")
301
+
302
+ column = st.selectbox("اختر العمود:", self.data.columns, key="pie_column")
303
+
304
+ # إنشاء الرسم البياني
305
+ fig = px.pie(self.data, names=column, title=f"توزيع {column}")
306
+ st.plotly_chart(fig, use_container_width=True)
307
+
308
+ elif chart_type == "مخطط تشتت":
309
+ st.subheader("مخطط تشتت")
310
+
311
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
312
+
313
+ if len(numeric_columns) < 2:
314
+ st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإنشاء مخطط تشتت")
315
+ return
316
+
317
+ x_column = st.selectbox("اختر عمود المحور الأفقي (x):", numeric_columns, key="scatter_x")
318
+ y_column = st.selectbox("اختر عمود المحور الرأسي (y):", numeric_columns, key="scatter_y")
319
+
320
+ # خيارات إضافية
321
+ color_column = st.selectbox("اختر عمود اللون (اختياري):",
322
+ ["لا يوجد"] + self.data.columns.tolist(),
323
+ key="scatter_color")
324
+
325
+ size_column = st.selectbox("اختر عمود الحجم (اختياري):",
326
+ ["لا يوجد"] + numeric_columns,
327
+ key="scatter_size")
328
+
329
+ # إنشاء الرسم البياني
330
+ if color_column == "لا يوجد" and size_column == "لا يوجد":
331
+ fig = px.scatter(self.data, x=x_column, y=y_column,
332
+ title=f"{y_column} مقابل {x_column}")
333
+ elif color_column != "لا يوجد" and size_column == "لا يوجد":
334
+ fig = px.scatter(self.data, x=x_column, y=y_column, color=color_column,
335
+ title=f"{y_column} مقابل {x_column} (مصنف حسب {color_column})")
336
+ elif color_column == "لا يوجد" and size_column != "لا يوجد":
337
+ fig = px.scatter(self.data, x=x_column, y=y_column, size=size_column,
338
+ title=f"{y_column} مقابل {x_column} (الحجم حسب {size_column})")
339
+ else:
340
+ fig = px.scatter(self.data, x=x_column, y=y_column, color=color_column, size=size_column,
341
+ title=f"{y_column} مقابل {x_column} (مصنف حسب {color_column}, الحجم حسب {size_column})")
342
+
343
+ fig.update_layout(xaxis_title=x_column, yaxis_title=y_column)
344
+ st.plotly_chart(fig, use_container_width=True)
345
+
346
+ elif chart_type == "مخطط صندوقي":
347
+ st.subheader("مخطط صندوقي")
348
+
349
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
350
+
351
+ if not numeric_columns:
352
+ st.warning("يجب أن يكون هناك عمود رقمي واحد على الأقل لإنشاء مخطط صندوقي")
353
+ return
354
+
355
+ y_column = st.selectbox("اختر عمود القيمة:", numeric_columns, key="box_y")
356
+
357
+ # خيارات إضافية
358
+ x_column = st.selectbox("اختر عمود التصنيف (اختياري):",
359
+ ["لا يوجد"] + self.data.columns.tolist(),
360
+ key="box_x")
361
+
362
+ # إنشاء الرسم البياني
363
+ if x_column == "لا يوجد":
364
+ fig = px.box(self.data, y=y_column, title=f"مخطط صندوقي لـ {y_column}")
365
+ else:
366
+ fig = px.box(self.data, x=x_column, y=y_column,
367
+ title=f"مخطط صندوقي لـ {y_column} حسب {x_column}")
368
+
369
+ st.plotly_chart(fig, use_container_width=True)
370
+
371
+ elif chart_type == "مخطط حراري":
372
+ st.subheader("مخطط حراري")
373
+
374
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
375
+
376
+ if len(numeric_columns) < 2:
377
+ st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإنشاء مخطط حراري")
378
+ return
379
+
380
+ # اختيار الأعمدة للمخطط الحراري
381
+ selected_columns = st.multiselect("اختر الأعمدة للمخطط الحراري:",
382
+ numeric_columns,
383
+ default=numeric_columns[:5] if len(numeric_columns) > 5 else numeric_columns)
384
+
385
+ if selected_columns:
386
+ # حساب مصفوفة الارتباط
387
+ correlation_matrix = self.data[selected_columns].corr()
388
+
389
+ # إنشاء الرسم البياني
390
+ fig = px.imshow(correlation_matrix, text_auto=True, aspect="auto",
391
+ title="مخطط حراري لمصفوفة الارتباط")
392
+ st.plotly_chart(fig, use_container_width=True)
393
+ else:
394
+ st.warning("الرجاء اختيار عمود واحد على الأقل")
395
 
396
+ def _reports_tab(self):
397
+ """علامة تبويب التقارير"""
398
+ st.header("التقارير")
399
+
400
+ if self.data is None:
401
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
402
+ return
403
+
404
+ st.subheader("إنشاء تقرير")
405
+
406
+ # خيارات التقرير
407
+ report_type = st.selectbox(
408
+ "اختر نوع التقرير:",
409
+ ["تقرير ملخص", "تقرير تحليلي", "تقرير مقارنة"]
410
+ )
411
+
412
+ if report_type == "تقرير ملخص":
413
+ st.write("محتوى التقرير:")
414
+
415
+ # إنشاء ملخص للبيانات
416
+ st.write("### ملخص البيانات")
417
+ st.write(f"عدد الصفوف: {self.data.shape[0]}")
418
+ st.write(f"عدد الأعمدة: {self.data.shape[1]}")
419
+
420
+ # إحصاءات وصفية
421
+ st.write("### إحصاءات وصفية")
422
+ st.dataframe(self.data.describe())
423
+
424
+ # معلومات عن القيم المفقودة
425
+ st.write("### القيم المفقودة")
426
+ missing_data = pd.DataFrame({
427
+ 'العمود': self.data.columns,
428
+ 'عدد القيم المفقودة': self.data.isna().sum().values,
429
+ 'نسبة القيم المفقودة (%)': (self.data.isna().sum().values / len(self.data) * 100).round(2)
430
+ })
431
+ st.dataframe(missing_data)
432
+
433
+ # توزيع البيانات الرقمية
434
+ st.write("### توزيع البيانات الرقمية")
435
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
436
+
437
+ if numeric_columns:
438
+ for i in range(0, len(numeric_columns), 2):
439
+ cols = st.columns(2)
440
+ for j in range(2):
441
+ if i + j < len(numeric_columns):
442
+ col = numeric_columns[i + j]
443
+ with cols[j]:
444
+ fig = px.histogram(self.data, x=col, title=f"توزيع {col}")
445
+ st.plotly_chart(fig, use_container_width=True)
446
+
447
+ # خيارات تصدير التقرير
448
+ st.subheader("تصدير التقرير")
449
+ export_format = st.radio("اختر صيغة التصدير:", ["PDF", "Excel", "HTML"])
450
+
451
+ if st.button("تصدير التقرير"):
452
+ st.success(f"تم تصدير التقرير بصيغة {export_format} بنجاح!")
453
+
454
+ elif report_type == "تقرير تحليلي":
455
+ st.info("هذه الميزة قيد التطوير")
456
+
457
+ elif report_type == "تقرير مقارنة":
458
+ st.info("هذه الميزة قيد التطوير")
459
 
460
+ def _create_sample_data(self):
461
+ """إنشاء بيانات نموذجية للمناقصات"""
462
+ # إنشاء تواريخ عشوائية
463
+ start_date = datetime(2023, 1, 1)
464
+ end_date = datetime(2025, 3, 31)
465
+ days = (end_date - start_date).days
466
+
467
+ # إنشاء بيانات نموذجية
468
+ data = {
469
+ 'رقم المناقصة': [f'T-{i:04d}' for i in range(1, 101)],
470
+ 'اسم المشروع': [f'مشروع {i}' for i in range(1, 101)],
471
+ 'نوع المشروع': np.random.choice(['بناء', 'صيانة', 'تطوير', 'توريد', 'خدمات'], 100),
472
+ 'الموقع': np.random.choice(['الرياض', 'جدة', 'الدمام', 'مكة', 'المدينة', 'تبوك', 'أبها'], 100),
473
+ 'تاريخ الإعلان': [start_date + pd.Timedelta(days=np.random.randint(0, days)) for _ in range(100)],
474
+ 'تاريخ الإغلاق': [start_date + pd.Timedelta(days=np.random.randint(30, days)) for _ in range(100)],
475
+ 'الميزانية التقديرية': np.random.uniform(1000000, 50000000, 100),
476
+ 'عدد المتقدمين': np.random.randint(1, 20, 100),
477
+ 'سعر العرض': np.random.uniform(900000, 55000000, 100),
478
+ 'نسبة الفوز (%)': np.random.uniform(0, 100, 100),
479
+ 'مدة التنفيذ (أشهر)': np.random.randint(3, 36, 100),
480
+ 'عدد العمال': np.random.randint(10, 500, 100),
481
+ 'تكلفة المواد': np.random.uniform(500000, 30000000, 100),
482
+ 'تكلفة العمالة': np.random.uniform(200000, 15000000, 100),
483
+ 'تكلفة المعدات': np.random.uniform(100000, 10000000, 100),
484
+ 'هامش الربح (%)': np.random.uniform(5, 25, 100),
485
+ 'درجة المخاطرة': np.random.choice(['منخفضة', 'متوسطة', 'عالية'], 100),
486
+ 'حالة المناقصة': np.random.choice(['جارية', 'مغلقة', 'ملغاة', 'فائزة', 'خاسرة'], 100)
487
  }
488
 
489
  # إنشاء DataFrame
490
+ df = pd.DataFrame(data)
491
+
492
+ # إضافة بعض العلاقات المنطقية
493
+ df['إجمالي التكلفة'] = df['تكلفة المواد'] + df['تكلفة العمالة'] + df['تكلفة المعدات']
494
+ df['الربح المتوقع'] = df['سعر العرض'] - df['إجمالي التكلفة']
495
+ df['نسبة التكلفة من العرض (%)'] = (df['إجمالي التكلفة'] / df['سعر العرض'] * 100).round(2)
496
+
497
+ return df