EGYADMIN commited on
Commit
b66f0a9
·
verified ·
1 Parent(s): ee08f53

Update modules/modules/requirement_analyzer.py

Browse files
modules/modules/requirement_analyzer.py CHANGED
@@ -208,4 +208,424 @@ class RequirementAnalyzer:
208
  categorized[category].append(req)
209
 
210
  # تصنيف ثانوي حسب الأهمية
211
- for category
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  categorized[category].append(req)
209
 
210
  # تصنيف ثانوي حسب الأهمية
211
+ for category in categorized:
212
+ categorized[category] = sorted(
213
+ categorized[category],
214
+ key=lambda x: 0 if x.get("importance", "عادية") == "عالية" else 1
215
+ )
216
+
217
+ return categorized
218
+
219
+ def _analyze_compliance(self, requirements: List[Dict[str, Any]]) -> Dict[str, Any]:
220
+ """
221
+ تحليل امتثال المتطلبات للمعايير القياسية
222
+ """
223
+ compliance_results = {
224
+ "compliant": [],
225
+ "non_compliant": [],
226
+ "missing": [],
227
+ "compliance_rate": 0.0
228
+ }
229
+
230
+ # قائمة بالمتطلبات القياسية المهمة للمقارنة
231
+ standard_reqs_flat = []
232
+ for category, reqs in self.standard_requirements.items():
233
+ for req in reqs:
234
+ if req.get("importance", "") == "عالية":
235
+ standard_reqs_flat.append(req)
236
+
237
+ # تحليل المتطلبات المستخرجة
238
+ for std_req in standard_reqs_flat:
239
+ found = False
240
+
241
+ for req in requirements:
242
+ # البحث عن تطابق في العنوان أو الوصف
243
+ title_match = std_req["title"].lower() in req.get("title", "").lower()
244
+ desc_match = std_req["description"].lower() in req.get("description", "").lower()
245
+
246
+ if title_match or desc_match:
247
+ found = True
248
+ compliance_results["compliant"].append({
249
+ "standard_requirement": std_req,
250
+ "found_requirement": req
251
+ })
252
+ break
253
+
254
+ if not found:
255
+ compliance_results["missing"].append(std_req)
256
+
257
+ # حساب نسبة الامتثال
258
+ total_std_reqs = len(standard_reqs_flat)
259
+ if total_std_reqs > 0:
260
+ compliance_rate = len(compliance_results["compliant"]) / total_std_reqs
261
+ compliance_results["compliance_rate"] = round(compliance_rate * 100, 2)
262
+
263
+ return compliance_results
264
+
265
+ def _identify_gaps(self, requirements: List[Dict[str, Any]], extracted_data: Dict[str, Any]) -> List[Dict[str, Any]]:
266
+ """
267
+ تحديد الفجوات في المتطلبات
268
+ """
269
+ gaps = []
270
+
271
+ # تحديد الفجوات بناءً على المتطلبات القياسية المفقودة
272
+ for missing_req in self.standard_requirements.get("عام", []):
273
+ found = False
274
+
275
+ for req in requirements:
276
+ if missing_req["title"].lower() in req.get("title", "").lower():
277
+ found = True
278
+ break
279
+
280
+ if not found:
281
+ gaps.append({
282
+ "type": "متطلب قياسي مفقود",
283
+ "requirement": missing_req,
284
+ "severity": "عالية" if missing_req.get("importance", "") == "عالية" else "متوسطة",
285
+ "recommendation": f"إضافة متطلب: {missing_req['title']}"
286
+ })
287
+
288
+ # تحديد الفجوات في المحتوى المحلي
289
+ if "local_content" in extracted_data:
290
+ local_content = extracted_data["local_content"]
291
+
292
+ # التحقق من وجود متطلبات المحتوى المحلي
293
+ local_content_req_found = False
294
+
295
+ for req in requirements:
296
+ if "محتوى محلي" in req.get("title", "").lower() or "محتوى محلي" in req.get("description", "").lower():
297
+ local_content_req_found = True
298
+ break
299
+
300
+ if not local_content_req_found:
301
+ gaps.append({
302
+ "type": "متطلب محتوى محلي مفقود",
303
+ "severity": "عالية",
304
+ "recommendation": "إضافة متطلبات محددة للمحتوى المحلي ونسبة التوطين المطلوبة"
305
+ })
306
+
307
+ # تحديد الفجوات في تحديد المسؤوليات
308
+ responsibilities_found = False
309
+ for req in requirements:
310
+ if "مسؤولية" in req.get("title", "").lower() or "مسؤولية" in req.get("description", "").lower():
311
+ responsibilities_found = True
312
+ break
313
+
314
+ if not responsibilities_found:
315
+ gaps.append({
316
+ "type": "تحديد المسؤوليات",
317
+ "severity": "متوسطة",
318
+ "recommendation": "إضافة قسم يحدد مسؤوليات الأطراف بوضوح"
319
+ })
320
+
321
+ # تحديد الفجوات في آلية فض النزاعات
322
+ dispute_resolution_found = False
323
+ for req in requirements:
324
+ if "نزاع" in req.get("title", "").lower() or "نزاع" in req.get("description", "").lower():
325
+ dispute_resolution_found = True
326
+ break
327
+
328
+ if not dispute_resolution_found:
329
+ gaps.append({
330
+ "type": "آلية فض النزاعات",
331
+ "severity": "متوسطة",
332
+ "recommendation": "إضافة قسم يوضح آلية فض النزاعات بين الأطراف"
333
+ })
334
+
335
+ return gaps
336
+
337
+ def _analyze_risks(self, requirements: List[Dict[str, Any]], extracted_data: Dict[str, Any]]) -> List[Dict[str, Any]]:
338
+ """
339
+ تحليل المخاطر المتعلقة بالمتطلبات
340
+ """
341
+ risks = []
342
+
343
+ # تحليل مخاطر الامتثال للمتطلبات
344
+ vague_requirements = []
345
+ for req in requirements:
346
+ # تحديد المتطلبات الغامضة
347
+ if len(req.get("description", "")) < 30:
348
+ vague_requirements.append(req)
349
+
350
+ if vague_requirements:
351
+ risks.append({
352
+ "title": "متطلبات غامضة",
353
+ "description": f"تم العثور على {len(vague_requirements)} متطلبات غير محددة بوضوح",
354
+ "severity": "عالية",
355
+ "probability": "عالية",
356
+ "impact": "قد يؤدي إلى نزاعات وتأخير في تنفيذ المشروع",
357
+ "mitigation": "توضيح المتطلبات الغامضة وتحديدها بشكل أكثر دقة"
358
+ })
359
+
360
+ # تحليل مخاطر المحتوى المحلي
361
+ if "local_content" in extracted_data:
362
+ local_content = extracted_data["local_content"]
363
+
364
+ # التحقق من وجود نسب محددة للمحتوى المحلي
365
+ local_content_percentage_found = False
366
+ for percentage in local_content.get("percentages", []):
367
+ local_content_percentage_found = True
368
+ break
369
+
370
+ if not local_content_percentage_found:
371
+ risks.append({
372
+ "title": "عدم تحديد نسبة المحتوى المحلي",
373
+ "description": "لم يتم تحديد نسبة واضحة للمحتوى المحلي المطلوب",
374
+ "severity": "متوسطة",
375
+ "probability": "عالية",
376
+ "impact": "قد يؤدي إلى عدم الامتثال لمتطلبات المحتوى المحلي والتعرض للغرامات",
377
+ "mitigation": "تحديد نسبة المحتوى المحلي المطلوبة بوضوح وآلية التحقق منها"
378
+ })
379
+
380
+ # تحليل مخاطر الجدول الزمني
381
+ if "dates" in extracted_data:
382
+ dates = extracted_data["dates"]
383
+
384
+ if len(dates) < 2:
385
+ risks.append({
386
+ "title": "عدم وضوح الجدول الزمني",
387
+ "description": "لم يتم تحديد جدول زمني واضح للمشروع",
388
+ "severity": "عالية",
389
+ "probability": "متوسطة",
390
+ "impact": "تأخير في تنفيذ المشروع وصعوبة في متابعة التقدم",
391
+ "mitigation": "تحديد جدول زمني تفصيلي مع مراحل ومعالم واضحة"
392
+ })
393
+
394
+ # تحليل مخاطر سلسلة الإمداد
395
+ if "supply_chain" in extracted_data:
396
+ supply_chain = extracted_data["supply_chain"]
397
+
398
+ if len(supply_chain.get("suppliers", [])) < 2:
399
+ risks.append({
400
+ "title": "مخاطر سلسلة الإمداد",
401
+ "description": "لم يتم تحديد موردين بدلاء أو خطة لإدارة مخاطر سلسلة الإمداد",
402
+ "severity": "عالية",
403
+ "probability": "متوسطة",
404
+ "impact": "تأخير في توريد المواد والمعدات المطلوبة",
405
+ "mitigation": "تحديد موردين بدلاء وخطة لإدارة مخاطر سلسلة الإمداد"
406
+ })
407
+
408
+ return risks
409
+
410
+ def _generate_recommendations(self, requirements: List[Dict[str, Any]], compliance_results: Dict[str, Any],
411
+ gaps: List[Dict[str, Any]], risks: List[Dict[str, Any]],
412
+ extracted_data: Dict[str, Any]) -> List[Dict[str, Any]]:
413
+ """
414
+ إعداد توصيات لتحسين المتطلبات
415
+ """
416
+ recommendations = []
417
+
418
+ # توصيات لسد الفجوات
419
+ for gap in gaps:
420
+ recommendations.append({
421
+ "title": f"معالجة فجوة: {gap['type']}",
422
+ "description": gap.get("recommendation", ""),
423
+ "priority": "عالية" if gap.get("severity", "") == "عالية" else "متوسطة",
424
+ "benefits": "تحسين جودة المتطلبات وتقليل مخاطر النزاعات"
425
+ })
426
+
427
+ # توصيات للتخفيف من المخاطر
428
+ for risk in risks:
429
+ recommendations.append({
430
+ "title": f"معالجة خطر: {risk['title']}",
431
+ "description": risk.get("mitigation", ""),
432
+ "priority": "عالية" if risk.get("severity", "") == "عالية" else "متوسطة",
433
+ "benefits": "تقليل المخاطر وتحسين فرص نجاح المشروع"
434
+ })
435
+
436
+ # توصيات لتحسين المحتوى المحلي
437
+ local_content_recommendation = {
438
+ "title": "تحسين متطلبات المحتوى المحلي",
439
+ "description": "تحديد نسبة المحتوى المحلي المطلوبة بوضوح، وتوضيح آلية التحقق والقياس، وتحديد متطلبات توطين الوظائف",
440
+ "priority": "عالية",
441
+ "benefits": "الامتثال لمتطلبات المحتوى المحلي وتحقيق أهداف رؤية 2030",
442
+ "implementation": [
443
+ "تحديد نسبة المحتوى المحلي المطلوبة بدقة",
444
+ "توضيح آلية حساب وقياس نسبة المحتوى المحلي",
445
+ "تحديد متطلبات توظيف الكوادر السعودية",
446
+ "تحديد آلية التحقق من الامتثال لمتطلبات المحتوى المحلي"
447
+ ]
448
+ }
449
+ recommendations.append(local_content_recommendation)
450
+
451
+ # توصيات لتحسين متطلبات سلسلة الإمداد
452
+ supply_chain_recommendation = {
453
+ "title": "تحسين متطلبات سلسلة الإمداد",
454
+ "description": "تطوير خطة شاملة لإدارة سلسلة الإمداد وتحديد المصادر البديلة",
455
+ "priority": "متوسطة",
456
+ "benefits": "تقليل مخاطر انقطاع سلسلة الإمداد وضمان استمرارية المشروع",
457
+ "implementation": [
458
+ "تحديد الموردين الرئيسيين والبدلاء",
459
+ "وضع خطة للتعامل مع انقطاع سلسلة الإمداد",
460
+ "تحديد المواد والمعدات الحرجة والمصادر البديلة",
461
+ "تطوير إجراءات لمتابعة وتقييم أداء الموردين"
462
+ ]
463
+ }
464
+ recommendations.append(supply_chain_recommendation)
465
+
466
+ return recommendations
467
+
468
+ def _evaluate_requirements(self, requirements: List[Dict[str, Any]], project_type: str) -> Dict[str, Any]:
469
+ """
470
+ تقييم المتطلبات وفق معايير التقييم
471
+ """
472
+ evaluation = {
473
+ "scores": {},
474
+ "overall_score": 0.0,
475
+ "comments": []
476
+ }
477
+
478
+ # تقييم المتطلبات الفنية
479
+ technical_score = self._evaluate_technical_requirements(requirements)
480
+ evaluation["scores"]["فنية"] = technical_score
481
+
482
+ # تقييم المتطلبات المالية
483
+ financial_score = self._evaluate_financial_requirements(requirements)
484
+ evaluation["scores"]["مالية"] = financial_score
485
+
486
+ # تقييم متطلبات المحتوى المحلي
487
+ local_content_score = self._evaluate_local_content_requirements(requirements)
488
+ evaluation["scores"]["المحتوى المحلي"] = local_content_score
489
+
490
+ # حساب التقييم الإجمالي
491
+ weights = {
492
+ "فنية": self.evaluation_criteria["فنية"]["weight"],
493
+ "مالية": self.evaluation_criteria["مالية"]["weight"],
494
+ "المحتوى المحلي": self.evaluation_criteria["المحتوى المحلي"]["weight"]
495
+ }
496
+
497
+ overall_score = (
498
+ technical_score * weights["فنية"] +
499
+ financial_score * weights["مالية"] +
500
+ local_content_score * weights["المحتوى المحلي"]
501
+ )
502
+
503
+ evaluation["overall_score"] = round(overall_score, 2)
504
+
505
+ # إضافة تعليقات
506
+ if overall_score >= 0.8:
507
+ evaluation["comments"].append("المتطلبات شاملة وتغطي جميع الجوانب الرئيسية")
508
+ elif overall_score >= 0.6:
509
+ evaluation["comments"].append("المتطلبات جيدة ولكنها تحتاج إلى بعض التحسينات")
510
+ else:
511
+ evaluation["comments"].append("المتطلبات غير كافية وتحتاج إلى مراجعة شاملة")
512
+
513
+ if technical_score < 0.6:
514
+ evaluation["comments"].append("المتطلبات الفنية غير كافية وغير واضحة")
515
+
516
+ if financial_score < 0.6:
517
+ evaluation["comments"].append("المتطلبات المالية غير محددة بوضوح")
518
+
519
+ if local_content_score < 0.6:
520
+ evaluation["comments"].append("متطلبات المحتوى المحلي غير كافية وتحتاج إلى تحسين")
521
+
522
+ return evaluation
523
+
524
+ def _evaluate_technical_requirements(self, requirements: List[Dict[str, Any]]) -> float:
525
+ """
526
+ تقييم المتطلبات الفنية
527
+ """
528
+ # عدد المتطلبات الفنية
529
+ technical_reqs = [req for req in requirements if req.get("category", "") == "فنية"]
530
+
531
+ if not technical_reqs:
532
+ return 0.0
533
+
534
+ # تقييم وضوح المتطلبات
535
+ clarity_score = sum(1 for req in technical_reqs if len(req.get("description", "")) > 50) / max(1, len(technical_reqs))
536
+
537
+ # تقييم اكتمال المتطلبات
538
+ completeness_score = min(1.0, len(technical_reqs) / 10) # افتراض أن 10 متطلبات تقنية هي الحد الأعلى المثالي
539
+
540
+ # تقييم تحديد المسؤوليات
541
+ responsibility_score = sum(1 for req in technical_reqs if "مسؤولية" in req.get("description", "").lower()) / max(1, len(technical_reqs))
542
+
543
+ # حساب النتيجة الإجمالية
544
+ total_score = (clarity_score * 0.4) + (completeness_score * 0.4) + (responsibility_score * 0.2)
545
+
546
+ return round(total_score, 2)
547
+
548
+ def _evaluate_financial_requirements(self, requirements: List[Dict[str, Any]]) -> float:
549
+ """
550
+ تقييم المتطلبات المالية
551
+ """
552
+ # عدد المتطلبات المالية
553
+ financial_reqs = [req for req in requirements if req.get("category", "") == "مالية"]
554
+
555
+ if not financial_reqs:
556
+ return 0.0
557
+
558
+ # تقييم وضوح المتطلبات
559
+ clarity_score = sum(1 for req in financial_reqs if len(req.get("description", "")) > 50) / max(1, len(financial_reqs))
560
+
561
+ # تقييم اكتمال المتطلبات
562
+ completeness_score = min(1.0, len(financial_reqs) / 5) # افتراض أن 5 متطلبات مالية هي الحد الأعلى المثالي
563
+
564
+ # تقييم تحديد شروط الدفع
565
+ payment_score = sum(1 for req in financial_reqs if "دفع" in req.get("description", "").lower()) / max(1, len(financial_reqs))
566
+
567
+ # حساب النتيجة الإجمالية
568
+ total_score = (clarity_score * 0.3) + (completeness_score * 0.3) + (payment_score * 0.4)
569
+
570
+ return round(total_score, 2)
571
+
572
+ def _evaluate_local_content_requirements(self, requirements: List[Dict[str, Any]]) -> float:
573
+ """
574
+ تقييم متطلبات المحتوى المحلي
575
+ """
576
+ # عدد متطلبات المحتوى المحلي
577
+ local_content_reqs = []
578
+ for req in requirements:
579
+ if req.get("category", "") == "محتوى محلي":
580
+ local_content_reqs.append(req)
581
+ elif "محتوى محلي" in req.get("title", "").lower() or "محتوى محلي" in req.get("description", "").lower():
582
+ local_content_reqs.append(req)
583
+
584
+ if not local_content_reqs:
585
+ return 0.0
586
+
587
+ # تقييم وضوح المتطلبات
588
+ clarity_score = sum(1 for req in local_content_reqs if len(req.get("description", "")) > 50) / max(1, len(local_content_reqs))
589
+
590
+ # تقييم تحديد النسب المطلوبة
591
+ percentage_score = sum(1 for req in local_content_reqs if "%" in req.get("description", "")) / max(1, len(local_content_reqs))
592
+
593
+ # تقييم آلية التحقق
594
+ verification_score = sum(1 for req in local_content_reqs if "تحقق" in req.get("description", "").lower() or "قياس" in req.get("description", "").lower()) / max(1, len(local_content_reqs))
595
+
596
+ # حساب النتيجة الإجمالية
597
+ total_score = (clarity_score * 0.2) + (percentage_score * 0.5) + (verification_score * 0.3)
598
+
599
+ return round(total_score, 2)
600
+
601
+ def _analyze_with_ai(self, requirements: List[Dict[str, Any]], extracted_data: Dict[str, Any]) -> Dict[str, Any]:
602
+ """
603
+ استخدام الذكاء الاصطناعي لتحليل المتطلبات بشكل متقدم
604
+ """
605
+ ai_analysis = {
606
+ "summary": "",
607
+ "insights": [],
608
+ "recommendations": []
609
+ }
610
+
611
+ try:
612
+ # إعداد سياق للتحليل
613
+ context = {
614
+ "requirements_count": len(requirements),
615
+ "categories": list(set(req.get("category", "عامة") for req in requirements)),
616
+ "has_local_content": any("محتوى محلي" in req.get("category", "") for req in requirements),
617
+ "text_samples": [req.get("description", "")[:200] for req in requirements[:5]]
618
+ }
619
+
620
+ # استخدام نموذج LLM لتحليل المتطلبات
621
+ # في التطبيق الفعلي، هذا سيستدعي واجهة برمجة تطبيقات مثل Claude أو OpenAI
622
+ ai_response = self.llm_processor.analyze_requirements(requirements, context)
623
+
624
+ # معالجة استجابة الذكاء الاصطناعي
625
+ if isinstance(ai_response, dict):
626
+ ai_analysis.update(ai_response)
627
+
628
+ except Exception as e:
629
+ ai_analysis["error"] = f"حدث خطأ أثناء تحليل المتطلبات باستخدام الذكاء الاصطناعي: {str(e)}"
630
+
631
+ return ai_analysis