File size: 39,461 Bytes
a3b5c66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
# إذا كانت الجملة الأولى طويلة، اختصرها
        if len(first_sentence) > 50:
            if ":" in first_sentence:
                # استخدام النص قبل العلامة : كعنوان
                title = first_sentence.split(":")[0].strip()
            else:
                # اختصار الجملة الأولى
                words = first_sentence.split()
                title = " ".join(words[:7]) + "..."
        else:
            title = first_sentence
        
        return title
    
    def _categorize_requirements(self, requirements: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, Any]]]:
        """
        تصنيف المتطلبات إلى فئات
        
        المعاملات:
        ----------
        requirements : List[Dict[str, Any]]
            قائمة المتطلبات
            
        المخرجات:
        --------
        Dict[str, List[Dict[str, Any]]]
            المتطلبات مصنفة حسب الفئة
        """
        categorized = {
            "technical": [],
            "financial": [],
            "legal": []
        }
        
        for req in requirements:
            description = req.get("description", "").lower()
            title = req.get("title", "").lower()
            text = title + " " + description
            
            # تحديد الفئة بناءً على الكلمات المفتاحية
            if any(keyword in text for keyword in ["فني", "تقني", "هندسي", "مواصفات", "جودة", "معايير", "أداء", "تصميم", "مخطط"]):
                req["category"] = "technical"
                categorized["technical"].append(req)
            elif any(keyword in text for keyword in ["مالي", "سعر", "تكلفة", "دفع", "ضمان", "تأمين", "غرامة", "ميزانية", "ضريبة", "محاسبة"]):
                req["category"] = "financial"
                categorized["financial"].append(req)
            elif any(keyword in text for keyword in ["قانوني", "شرط", "عقد", "التزام", "تعاقد", "نظام", "لائحة", "تشريع", "ترخيص", "حقوق"]):
                req["category"] = "legal"
                categorized["legal"].append(req)
            else:
                # إذا لم يتطابق مع أي فئة، افترض أنه متطلب فني
                req["category"] = "technical"
                categorized["technical"].append(req)
        
        return categorized
    
    def _analyze_importance_difficulty(self, categorized_requirements: Dict[str, List[Dict[str, Any]]]) -> Dict[str, List[Dict[str, Any]]]:
        """
        تحليل أهمية وصعوبة المتطلبات
        
        المعاملات:
        ----------
        categorized_requirements : Dict[str, List[Dict[str, Any]]]
            المتطلبات مصنفة حسب الفئة
            
        المخرجات:
        --------
        Dict[str, List[Dict[str, Any]]]
            المتطلبات بعد تحليل الأهمية والصعوبة
        """
        analyzed = {}
        
        for category, reqs in categorized_requirements.items():
            analyzed_reqs = []
            
            for req in reqs:
                # تحليل الأهمية
                req["importance"] = self._determine_importance(req)
                
                # تحليل صعوبة التنفيذ
                req["difficulty"] = self._determine_difficulty(req)
                
                # تحديد الفجوات
                req["gaps"] = self._identify_gaps(req)
                
                # اقتراح تحسينات
                req["improvements"] = self._suggest_improvements(req)
                
                analyzed_reqs.append(req)
            
            analyzed[category] = analyzed_reqs
        
        return analyzed
    
    def _determine_importance(self, requirement: Dict[str, Any]) -> str:
        """
        تحديد أهمية المتطلب
        
        المعاملات:
        ----------
        requirement : Dict[str, Any]
            المتطلب
            
        المخرجات:
        --------
        str
            درجة الأهمية
        """
        text = requirement.get("description", "").lower()
        
        # البحث عن كلمات تدل على الإلزامية
        if any(keyword in text for keyword in ["يجب", "إلزامي", "ضروري", "لا بد", "لابد", "مطلوب", "يلتزم", "ملزم"]):
            return "إلزامي"
        
        # البحث عن كلمات تدل على الأهمية الثانوية
        elif any(keyword in text for keyword in ["يفضل", "مرغوب", "محبذ", "يحبذ", "يستحسن"]):
            return "ثانوي"
        
        # إذا لم يتطابق مع أي حالة، افترض أنه متطلب عادي
        else:
            return "عادي"
    
    def _determine_difficulty(self, requirement: Dict[str, Any]) -> int:
        """
        تحديد صعوبة تنفيذ المتطلب
        
        المعاملات:
        ----------
        requirement : Dict[str, Any]
            المتطلب
            
        المخرجات:
        --------
        int
            درجة الصعوبة (1-5)
        """
        text = requirement.get("description", "").lower()
        category = requirement.get("category", "")
        importance = requirement.get("importance", "")
        
        # تعيين درجة صعوبة أساسية
        base_difficulty = 3  # متوسط
        
        # زيادة الصعوبة للمتطلبات الإلزامية
        if importance == "إلزامي":
            base_difficulty += 1
        
        # زيادة الصعوبة بناءً على طول النص
        if len(text) > 200:
            base_difficulty += 0.5
        
        # زيادة الصعوبة بناءً على الكلمات المفتاحية
        if any(keyword in text for keyword in ["معقد", "صعب", "متقدم", "عالي", "متطور", "خبير", "مخصص", "خاص"]):
            base_difficulty += 1
        
        # تخفيض الصعوبة للمتطلبات البسيطة
        if any(keyword in text for keyword in ["بسيط", "سهل", "عادي", "أساسي", "تقليدي"]):
            base_difficulty -= 1
        
        # ضبط الصعوبة في النطاق 1-5
        return max(1, min(5, round(base_difficulty)))
    
    def _identify_gaps(self, requirement: Dict[str, Any]) -> str:
        """
        تحديد الفجوات في المتطلب
        
        المعاملات:
        ----------
        requirement : Dict[str, Any]
            المتطلب
            
        المخرجات:
        --------
        str
            الفجوات المحددة
        """
        text = requirement.get("description", "")
        
        gaps = []
        
        # التحقق من وضوح المتطلب
        if len(text) < 50:
            gaps.append("وصف قصير وغير مفصل")
        
        # التحقق من وجود معايير قياس
        if not any(keyword in text.lower() for keyword in ["معيار", "قياس", "مؤشر", "مستوى", "نسبة", "كمية", "عدد"]):
            gaps.append("لا يحتوي على معايير قياس واضحة")
        
        # التحقق من وجود توصيف زمني
        if not any(keyword in text.lower() for keyword in ["مدة", "فترة", "خلال", "زمن", "تاريخ", "موعد", "يوم", "أسبوع", "شهر"]):
            gaps.append("لا يحدد إطار زمني")
        
        # التحقق من وجود مسؤولية تنفيذ
        if not any(keyword in text.lower() for keyword in ["مسؤول", "مسؤولية", "مقاول", "مورد", "مقدم", "طرف", "مكلف"]):
            gaps.append("لا يحدد المسؤولية عن التنفيذ")
        
        # إرجاع الفجوات
        if gaps:
            return "، ".join(gaps)
        else:
            return "لا توجد فجوات واضحة"
    
    def _suggest_improvements(self, requirement: Dict[str, Any]) -> str:
        """
        اقتراح تحسينات للمتطلب
        
        المعاملات:
        ----------
        requirement : Dict[str, Any]
            المتطلب
            
        المخرجات:
        --------
        str
            التحسينات المقترحة
        """
        gaps = requirement.get("gaps", "")
        category = requirement.get("category", "")
        
        improvements = []
        
        # اقتراح تحسينات بناءً على الفجوات
        if "وصف قصير" in gaps:
            improvements.append("توضيح المتطلب بشكل أكثر تفصيلاً")
        
        if "معايير قياس" in gaps:
            improvements.append("إضافة معايير قياس كمية ونوعية")
        
        if "إطار زمني" in gaps:
            improvements.append("تحديد إطار زمني واضح للتنفيذ")
        
        if "المسؤولية" in gaps:
            improvements.append("تحديد المسؤولية عن التنفيذ بوضوح")
        
        # اقتراح تحسينات بناءً على الفئة
        if category == "technical":
            if not improvements:
                improvements.append("إضافة مراجع للمعايير الفنية والمواصفات")
        elif category == "financial":
            if not improvements:
                improvements.append("توضيح آلية الدفع والتسعير")
        elif category == "legal":
            if not improvements:
                improvements.append("إضافة المرجعية القانونية والتنظيمية")
        
        # إرجاع التحسينات
        if improvements:
            return "، ".join(improvements)
        else:
            return "المتطلب واضح ومكتمل"
    
    def _extract_local_content_requirements(self, text: str) -> List[Dict[str, Any]]:
        """
        استخراج متطلبات المحتوى المحلي
        
        المعاملات:
        ----------
        text : str
            النص المستخرج من المناقصة
            
        المخرجات:
        --------
        List[Dict[str, Any]]
            قائمة متطلبات المحتوى المحلي
        """
        # الكلمات المفتاحية للمحتوى المحلي
        local_content_keywords = [
            "المحتوى المحلي", "التوطين", "السعودة", "المنتجات المحلية", 
            "الصناعة المحلية", "نسبة المحتوى", "المكون المحلي", "منشأ محلي",
            "نطاقات", "توظيف السعوديين", "توظيف المواطنين", "القيمة المضافة المحلية"
        ]
        
        local_content_requirements = []
        
        # البحث عن أقسام المحتوى المحلي
        for keyword in local_content_keywords:
            pattern = rf"((?:{keyword}|{keyword.upper()}).*?(?:\n\n|\Z))"
            matches = re.finditer(pattern, text, re.DOTALL)
            
            for match in matches:
                section_text = match.group(1).strip()
                
                # استخراج النسب المئوية
                percentage_matches = re.findall(r'(\d+(?:\.\d+)?)(?:\s*%|\s*في المائة|\s*بالمائة)', section_text)
                
                if percentage_matches:
                    for percentage in percentage_matches:
                        requirement = {
                            "title": f"متطلب المحتوى المحلي - {percentage}%",
                            "description": section_text,
                            "category": "local_content",
                            "importance": "إلزامي",
                            "percentage": float(percentage),
                            "difficulty": 4,  # افتراضي: صعوبة عالية
                            "gaps": self._identify_gaps({"description": section_text}),
                            "improvements": "توضيح آلية حساب وتوثيق نسبة المحتوى المحلي"
                        }
                        local_content_requirements.append(requirement)
                else:
                    # إذا لم يتم العثور على نسب، أضف المتطلب بدون نسبة
                    requirement = {
                        "title": "متطلب المحتوى المحلي",
                        "description": section_text,
                        "category": "local_content",
                        "importance": "إلزامي",
                        "difficulty": 3,  # افتراضي: صعوبة متوسطة
                        "gaps": "لا يحدد نسبة محتوى محلي واضحة",
                        "improvements": "تحديد نسبة المحتوى المحلي المطلوبة بشكل صريح"
                    }
                    local_content_requirements.append(requirement)
        
        # إزالة التكرارات
        unique_requirements = []
        descriptions = set()
        
        for req in local_content_requirements:
            desc = req["description"]
            if desc not in descriptions:
                descriptions.add(desc)
                unique_requirements.append(req)
        
        return unique_requirements
    
    def _generate_requirements_summary(self, categorized_requirements: Dict[str, List[Dict[str, Any]]],
                                     local_content_requirements: List[Dict[str, Any]],
                                     total_count: int, mandatory_count: int, avg_difficulty: float) -> str:
        """
        إعداد ملخص المتطلبات
        
        المعاملات:
        ----------
        categorized_requirements : Dict[str, List[Dict[str, Any]]]
            المتطلبات مصنفة حسب الفئة
        local_content_requirements : List[Dict[str, Any]]
            متطلبات المحتوى المحلي
        total_count : int
            إجمالي عدد المتطلبات
        mandatory_count : int
            عدد المتطلبات الإلزامية
        avg_difficulty : float
            متوسط صعوبة التنفيذ
            
        المخرجات:
        --------
        str
            ملخص المتطلبات
        """
        # إعداد الملخص
        summary = f"تم تحليل {total_count} متطلب، منها {mandatory_count} متطلب إلزامي. "
        
        # توزيع المتطلبات حسب الفئة
        tech_count = len(categorized_requirements.get("technical", []))
        fin_count = len(categorized_requirements.get("financial", []))
        legal_count = len(categorized_requirements.get("legal", []))
        local_count = len(local_content_requirements)
        
        summary += f"تتوزع المتطلبات إلى {tech_count} متطلب فني، و{fin_count} متطلب مالي، و{legal_count} متطلب قانوني، و{local_count} متطلب للمحتوى المحلي. "
        
        # صعوبة التنفيذ
        if avg_difficulty >= 4:
            summary += "متوسط صعوبة تنفيذ المتطلبات مرتفع، مما يشير إلى تعقيد المشروع. "
        elif avg_difficulty >= 3:
            summary += "متوسط صعوبة تنفيذ المتطلبات متوسط. "
        else:
            summary += "متوسط صعوبة تنفيذ المتطلبات منخفض، مما يشير إلى إمكانية تنفيذ المشروع بسهولة نسبية. "
        
        # متطلبات المحتوى المحلي
        if local_count > 0:
            local_percentages = [req.get("percentage", 0) for req in local_content_requirements if "percentage" in req]
            if local_percentages:
                max_percentage = max(local_percentages)
                summary += f"يتطلب المشروع تحقيق نسبة محتوى محلي لا تقل عن {max_percentage}%. "
            else:
                summary += "يتضمن المشروع متطلبات للمحتوى المحلي، لكن النسبة المطلوبة غير محددة بوضوح. "
        else:
            summary += "لم يتم تحديد متطلبات واضحة للمحتوى المحلي. "
        
        # تقييم عام
        if mandatory_count / total_count > 0.7:
            summary += "نسبة المتطلبات الإلزامية مرتفعة، مما يشير إلى تشدد في شروط المناقصة. "
        
        if avg_difficulty > 3.5 and mandatory_count / total_count > 0.6:
            summary += "يجب الانتباه إلى ارتفاع صعوبة التنفيذ ونسبة المتطلبات الإلزامية، مما قد يزيد من مخاطر المشروع."
        
        return summary
    
    def _generate_template_requirements(self) -> List[Dict[str, Any]]:
        """
        إنشاء متطلبات افتراضية من القوالب
        
        المخرجات:
        --------
        List[Dict[str, Any]]
            قائمة المتطلبات الافتراضية
        """
        # إنشاء نسخة من قوالب المتطلبات
        requirements = []
        
        # إضافة متطلبات من كل فئة
        for category, templates in self.requirement_templates.items():
            for template in templates:
                requirement = template.copy()
                requirement["category"] = category
                requirements.append(requirement)
        
        return requirements
    
    def _clean_requirements(self, requirements: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        تنظيف وتوحيد المتطلبات
        
        المعاملات:
        ----------
        requirements : List[Dict[str, Any]]
            قائمة المتطلبات
            
        المخرجات:
        --------
        List[Dict[str, Any]]
            قائمة المتطلبات المنظفة
        """
        cleaned = []
        descriptions = set()
        
        for req in requirements:
            # تنظيف الوصف
            if "description" in req:
                req["description"] = req["description"].strip()
            
            # تجاهل المتطلبات المكررة
            if req.get("description", "") in descriptions:
                continue
            
            # إضافة الوصف إلى مجموعة الأوصاف
            descriptions.add(req.get("description", ""))
            
            # التأكد من وجود عنوان
            if "title" not in req or not req["title"]:
                req["title"] = self._extract_requirement_title(req.get("description", ""))
            
            # إضافة المتطلب إلى القائمة المنظفة
            cleaned.append(req)
        
        return cleaned
    
    def _load_requirement_templates(self) -> Dict[str, List[Dict[str, Any]]]:
        """
        تحميل قوالب المتطلبات
        
        المخرجات:
        --------
        Dict[str, List[Dict[str, Any]]]
            قوالب المتطلبات
        """
        try:
            file_path = 'data/templates/requirement_templates.json'
            if os.path.exists(file_path):
                with open(file_path, 'r', encoding='utf-8') as f:
                    return json.load(f)
            else:
                logger.warning(f"ملف قوالب المتطلبات غير موجود: {file_path}")
                # إنشاء قوالب افتراضية
                return self._create_default_requirement_templates()
        except Exception as e:
            logger.error(f"فشل في تحميل قوالب المتطلبات: {str(e)}")
            return self._create_default_requirement_templates()
    
    def _create_default_requirement_templates(self) -> Dict[str, List[Dict[str, Any]]]:
        """
        إنشاء قوالب متطلبات افتراضية
        
        المخرجات:
        --------
        Dict[str, List[Dict[str, Any]]]
            قوالب المتطلبات الافتراضية
        """
        templates = {
            "technical": [
                {
                    "title": "مؤهلات وخبرات الفريق الفني",
                    "description": "يجب توفير فريق فني مؤهل ذو خبرة لا تقل عن 5 سنوات في مجال المشروع، مع تقديم السير الذاتية للمهندسين الرئيسيين.",
                    "importance": "إلزامي",
                    "difficulty": 3
                },
                {
                    "title": "جودة المواد والمعدات",
                    "description": "يجب استخدام مواد ومعدات ذات جودة عالية ومطابقة للمواصفات القياسية السعودية والعالمية، مع تقديم شهادات الجودة والمنشأ.",
                    "importance": "إلزامي",
                    "difficulty": 4
                },
                {
                    "title": "خطة العمل والجدول الزمني",
                    "description": "تقديم خطة عمل تفصيلية وجدول زمني لتنفيذ المشروع، موضحاً المراحل الرئيسية والمدد الزمنية لكل مرحلة.",
                    "importance": "إلزامي",
                    "difficulty": 3
                },
                {
                    "title": "ضمان الأعمال",
                    "description": "تقديم ضمان للأعمال المنفذة لمدة لا تقل عن سنة من تاريخ الاستلام النهائي، مع الالتزام بإصلاح أي عيوب خلال فترة الضمان.",
                    "importance": "إلزامي",
                    "difficulty": 2
                },
                {
                    "title": "الصيانة الدورية",
                    "description": "يفضل تقديم برنامج للصيانة الدورية بعد انتهاء فترة الضمان، مع تحديد التكاليف والخدمات المشمولة.",
                    "importance": "ثانوي",
                    "difficulty": 2
                }
            ],
            "financial": [
                {
                    "title": "العرض المالي",
                    "description": "تقديم عرض مالي مفصل يوضح تكاليف كل بند من بنود المشروع، مع بيان القيمة الإجمالية شاملة ضريبة القيمة المضافة.",
                    "importance": "إلزامي",
                    "difficulty": 3
                },
                {
                    "title": "الضمان الابتدائي",
                    "description": "تقديم ضمان ابتدائي بنسبة 2% من قيمة العرض، ساري المفعول لمدة 90 يوماً من تاريخ فتح المظاريف.",
                    "importance": "إلزامي",
                    "difficulty": 2
                },
                {
                    "title": "الضمان النهائي",
                    "description": "تقديم ضمان نهائي بنسبة 5% من قيمة العقد عند الترسية، ساري المفعول حتى انتهاء فترة الضمان.",
                    "importance": "إلزامي",
                    "difficulty": 2
                },
                {
                    "title": "شروط الدفع",
                    "description": "يتم الدفع على دفعات مرحلية حسب نسب الإنجاز، مع احتجاز نسبة 10% من كل دفعة كضمان حسن التنفيذ.",
                    "importance": "إلزامي",
                    "difficulty": 2
                },
                {
                    "title": "غرامات التأخير",
                    "description": "تفرض غرامة تأخير بنسبة 1% من قيمة العقد عن كل أسبوع تأخير، بحد أقصى 10% من القيمة الإجمالية للعقد.",
                    "importance": "إلزامي",
                    "difficulty": 3
                }
            ],
            "legal": [
                {
                    "title": "السجل التجاري",
                    "description": "يجب أن يكون المتقدم حاصلاً على سجل تجاري ساري المفعول في نفس مجال المناقصة.",
                    "importance": "إلزامي",
                    "difficulty": 1
                },
                {
                    "title": "تصنيف المقاولين",
                    "description": "يجب أن يكون المقاول مصنفاً لدى وزارة الشؤون البلدية والقروية والإسكان في المجال المطلوب وبدرجة لا تقل عن الثالثة.",
                    "importance": "إلزامي",
                    "difficulty": 2
                },
                {
                    "title": "التأمينات",
                    "description": "تقديم شهادة من المؤسسة العامة للتأمينات الاجتماعية تثبت الوفاء بالالتزامات تجاه المؤسسة.",
                    "importance": "إلزامي",
                    "difficulty": 1
                },
                {
                    "title": "الزكاة والدخل",
                    "description": "تقديم شهادة من هيئة الزكاة والضريبة والجمارك تثبت سداد المستحقات.",
                    "importance": "إلزامي",
                    "difficulty": 1
                },
                {
                    "title": "نظام المنافسات والمشتريات",
                    "description": "الالتزام بأحكام نظام المنافسات والمشتريات الحكومية ولائحته التنفيذية.",
                    "importance": "إلزامي",
                    "difficulty": 2
                }
            ]
        }
        
        return templates"""
محلل متطلبات المناقصات
يقوم باستخراج وتحليل وتصنيف متطلبات المناقصات
"""

import re
import logging
import json
import os
from typing import Dict, List, Any, Tuple, Optional, Union
import numpy as np

logger = logging.getLogger(__name__)

class RequirementAnalyzer:
    """
    محلل متطلبات المناقصات
    """
    
    def __init__(self, model_loader, arabic_nlp, config=None):
        """
        تهيئة محلل المتطلبات
        
        المعاملات:
        ----------
        model_loader : ModelLoader
            محمّل النماذج المستخدمة للتحليل
        arabic_nlp : ArabicNLP
            أدوات معالجة اللغة العربية
        config : Dict, optional
            إعدادات المحلل
        """
        self.config = config or {}
        self.model_loader = model_loader
        self.arabic_nlp = arabic_nlp
        
        # تحميل النماذج
        self.ner_model = None
        if hasattr(model_loader, 'get_ner_model'):
            try:
                self.ner_model = model_loader.get_ner_model()
                logger.info("تم تحميل نموذج التعرف على الكيانات المسماة")
            except Exception as e:
                logger.warning(f"فشل في تحميل نموذج التعرف على الكيانات المسماة: {str(e)}")
        
        # تحميل قوالب المتطلبات
        self.requirement_templates = self._load_requirement_templates()
        
        logger.info("تم تهيئة محلل المتطلبات")
    
    def analyze(self, text: str) -> Dict[str, Any]:
        """
        تحليل متطلبات المناقصة من النص
        
        المعاملات:
        ----------
        text : str
            النص المستخرج من المناقصة
            
        المخرجات:
        --------
        Dict[str, Any]
            نتائج تحليل المتطلبات
        """
        try:
            logger.info("بدء تحليل متطلبات المناقصة")
            
            # استخراج المتطلبات من النص
            requirements = self._extract_requirements(text)
            
            # تصنيف المتطلبات
            categorized_requirements = self._categorize_requirements(requirements)
            
            # تحليل الأهمية والصعوبة
            analyzed_requirements = self._analyze_importance_difficulty(categorized_requirements)
            
            # تحليل متطلبات المحتوى المحلي
            local_content_requirements = self._extract_local_content_requirements(text)
            
            # تحديد عدد المتطلبات الإلزامية
            mandatory_count = sum(1 for req in requirements if req.get("importance", "") == "إلزامي")
            
            # حساب متوسط صعوبة التنفيذ
            difficulty_values = [req.get("difficulty", 3) for req in requirements if req.get("difficulty", 0) > 0]
            avg_difficulty = np.mean(difficulty_values) if difficulty_values else 3.0
            
            # حساب نسبة متطلبات المحتوى المحلي
            total_count = len(requirements)
            local_content_percentage = (len(local_content_requirements) / total_count * 100) if total_count > 0 else 0
            
            # إعداد ملخص المتطلبات
            summary = self._generate_requirements_summary(
                analyzed_requirements, local_content_requirements, total_count, mandatory_count, avg_difficulty
            )
            
            # إعداد النتائج
            results = {
                "summary": summary,
                "technical": analyzed_requirements.get("technical", []),
                "financial": analyzed_requirements.get("financial", []),
                "legal": analyzed_requirements.get("legal", []),
                "local_content": local_content_requirements,
                "total_count": total_count,
                "mandatory_count": mandatory_count,
                "avg_difficulty": avg_difficulty,
                "local_content_percentage": local_content_percentage
            }
            
            logger.info(f"اكتمل تحليل المتطلبات: {total_count} متطلبات، {mandatory_count} إلزامية")
            return results
            
        except Exception as e:
            logger.error(f"فشل في تحليل المتطلبات: {str(e)}")
            return {
                "summary": "حدث خطأ أثناء تحليل المتطلبات",
                "technical": [],
                "financial": [],
                "legal": [],
                "local_content": [],
                "total_count": 0,
                "mandatory_count": 0,
                "avg_difficulty": 0,
                "local_content_percentage": 0,
                "error": str(e)
            }
    
    def _extract_requirements(self, text: str) -> List[Dict[str, Any]]:
        """
        استخراج المتطلبات من النص
        
        المعاملات:
        ----------
        text : str
            النص المستخرج من المناقصة
            
        المخرجات:
        --------
        List[Dict[str, Any]]
            قائمة المتطلبات المستخرجة
        """
        requirements = []
        
        # البحث عن قسم المتطلبات أو الشروط
        requirement_sections = self._find_requirement_sections(text)
        
        # إذا لم يتم العثور على أقسام للمتطلبات، استخدم النص كاملاً
        if not requirement_sections:
            requirement_sections = [text]
        
        # معالجة كل قسم
        for section in requirement_sections:
            # استخراج المتطلبات من النص
            section_requirements = self._parse_requirements_text(section)
            
            # دمج المتطلبات المستخرجة
            requirements.extend(section_requirements)
        
        # إذا لم يتم العثور على متطلبات، استخدم القوالب
        if not requirements:
            requirements = self._generate_template_requirements()
        
        # تنظيف وتوحيد المتطلبات
        requirements = self._clean_requirements(requirements)
        
        return requirements
    
    def _find_requirement_sections(self, text: str) -> List[str]:
        """
        البحث عن أقسام المتطلبات في النص
        
        المعاملات:
        ----------
        text : str
            النص المستخرج من المناقصة
            
        المخرجات:
        --------
        List[str]
            أقسام المتطلبات المستخرجة
        """
        sections = []
        
        # الكلمات المفتاحية لأقسام المتطلبات
        section_keywords = [
            "المتطلبات", "الشروط", "المواصفات", "نطاق العمل", 
            "البنود", "المعايير", "الالتزامات", "المؤهلات", 
            "التأهيل", "الواجبات", "نطاق الأعمال", "الخدمات المطلوبة"
        ]
        
        # البحث عن أقسام المتطلبات
        for keyword in section_keywords:
            pattern = rf"((?:{keyword}|{keyword.upper()})[:\s].*?)(?:^(?:{section_keywords[0]}|{section_keywords[0].upper()})[:\s]|\Z)"
            matches = re.finditer(pattern, text, re.MULTILINE | re.DOTALL)
            
            for match in matches:
                section_text = match.group(1).strip()
                if len(section_text) > 50:  # تجاهل الأقسام القصيرة جدًا
                    sections.append(section_text)
        
        # البحث عن قوائم البنود
        bullet_lists = re.findall(r'(?:^|\n)(?:[•\-*]\s+.*(?:\n|$))+', text, re.MULTILINE)
        
        for bullet_list in bullet_lists:
            if len(bullet_list) > 100:  # تجاهل القوائم القصيرة
                sections.append(bullet_list)
        
        # البحث عن قوائم مرقمة
        numbered_lists = re.findall(r'(?:^|\n)(?:\d+[.)\s]+.*(?:\n|$))+', text, re.MULTILINE)
        
        for numbered_list in numbered_lists:
            if len(numbered_list) > 100:  # تجاهل القوائم القصيرة
                sections.append(numbered_list)
        
        return sections
    
    def _parse_requirements_text(self, text: str) -> List[Dict[str, Any]]:
        """
        تحليل نص المتطلبات لاستخراج المتطلبات الفردية
        
        المعاملات:
        ----------
        text : str
            نص قسم المتطلبات
            
        المخرجات:
        --------
        List[Dict[str, Any]]
            قائمة المتطلبات المستخرجة
        """
        requirements = []
        
        # استخراج المتطلبات من القوائم النقطية
        bullet_items = re.findall(r'[•\-*]\s+(.*?)(?:\n[•\-*]|\n\n|\Z)', text, re.DOTALL)
        
        for item in bullet_items:
            item = item.strip()
            if len(item) > 10:  # تجاهل العناصر القصيرة جدًا
                requirements.append({
                    "title": self._extract_requirement_title(item),
                    "description": item,
                    "source": "bullet_list"
                })
        
        # استخراج المتطلبات من القوائم المرقمة
        numbered_items = re.findall(r'(\d+)[.)\s]+(.*?)(?:\n\d+[.)\s]|\n\n|\Z)', text, re.DOTALL)
        
        for num, item in numbered_items:
            item = item.strip()
            if len(item) > 10:  # تجاهل العناصر القصيرة جدًا
                requirements.append({
                    "title": self._extract_requirement_title(item),
                    "description": item,
                    "source": "numbered_list",
                    "number": int(num)
                })
        
        # استخراج المتطلبات من الفقرات
        if not requirements:
            paragraphs = re.split(r'\n\s*\n', text)
            
            for paragraph in paragraphs:
                paragraph = paragraph.strip()
                if len(paragraph) > 50 and len(paragraph) < 500:  # تجاهل الفقرات القصيرة جدًا أو الطويلة جدًا
                    # تحقق مما إذا كانت الفقرة تحتوي على عبارات المتطلبات
                    if any(keyword in paragraph.lower() for keyword in ["يجب", "ضرورة", "إلزامي", "مطلوب", "يلتزم", "لا بد", "لابد", "شرط", "اشتراط"]):
                        requirements.append({
                            "title": self._extract_requirement_title(paragraph),
                            "description": paragraph,
                            "source": "paragraph"
                        })
        
        return requirements
    
    import re

    def _extract_requirement_title(self, text: str) -> str:
        """
        استخراج عنوان المتطلب من النص.

        المعاملات:
        ----------
        text : str
            نص المتطلب.

        المخرجات:
        --------
        str
            عنوان المتطلب المستخرج.
        """
        # تنظيف النص وإزالة أي فراغات زائدة
        text = text.strip()

        # محاولة استخراج العنوان من بداية النص باستخدام الجملة الأولى
        first_sentence = re.split(r'[.!?،؛]', text)[0].strip()

        # إذا كان النص قصيرًا جدًا، نعيده كما هو
        if len(first_sentence) < 5:
            return text
        
        # التحقق من أن العنوان لا يحتوي على أرقام أو رموز غير مفهومة
        if re.search(r'\d', first_sentence):
            return text  # إذا كان هناك أرقام، نعيد النص الأصلي

        return first_sentence