Spaces:
Paused
Paused
Create modules/local_content.py
Browse files- modules/local_content.py +706 -0
modules/local_content.py
ADDED
|
@@ -0,0 +1,706 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
}
|
| 2 |
+
|
| 3 |
+
def calculate(self, extracted_data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
|
| 4 |
+
"""
|
| 5 |
+
حساب نسبة المحتوى المحلي بناءً على البيانات المستخرجة من المستندات
|
| 6 |
+
|
| 7 |
+
المعاملات:
|
| 8 |
+
----------
|
| 9 |
+
extracted_data : Dict[str, Any]
|
| 10 |
+
البيانات المستخرجة من المستندات
|
| 11 |
+
**kwargs : Dict[str, Any]
|
| 12 |
+
معاملات إضافية مثل نوع المشروع، الميزانية، الموقع، المدة
|
| 13 |
+
|
| 14 |
+
المخرجات:
|
| 15 |
+
--------
|
| 16 |
+
Dict[str, Any]
|
| 17 |
+
نتائج حساب المحتوى المحلي
|
| 18 |
+
"""
|
| 19 |
+
# تهيئة نتائج حساب المحتوى المحلي
|
| 20 |
+
local_content_results = {
|
| 21 |
+
"overall_percentage": 0.0,
|
| 22 |
+
"breakdown": {},
|
| 23 |
+
"requirements": [],
|
| 24 |
+
"recommendations": [],
|
| 25 |
+
"nitaqat_analysis": {},
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
# تحديد نوع المشروع والقطاع
|
| 29 |
+
project_type = kwargs.get("project_type", "")
|
| 30 |
+
sector = self._determine_sector(project_type, extracted_data)
|
| 31 |
+
|
| 32 |
+
# استخراج متطلبات المحتوى المحلي من البيانات
|
| 33 |
+
local_content_info = self._extract_local_content_info(extracted_data)
|
| 34 |
+
|
| 35 |
+
# حساب المحتوى المحلي بناءً على القطاع
|
| 36 |
+
sector_data = self.local_content_data["sectors"].get(sector, self.local_content_data["sectors"]["عام"])
|
| 37 |
+
|
| 38 |
+
# تحليل القوى العاملة (الموارد البشرية)
|
| 39 |
+
manpower_analysis = self._analyze_manpower(extracted_data, sector_data)
|
| 40 |
+
local_content_results["breakdown"]["manpower"] = manpower_analysis["percentage"]
|
| 41 |
+
|
| 42 |
+
# تحليل المواد
|
| 43 |
+
materials_analysis = self._analyze_materials(extracted_data, sector_data)
|
| 44 |
+
local_content_results["breakdown"]["materials"] = materials_analysis["percentage"]
|
| 45 |
+
|
| 46 |
+
# تحليل الخدمات
|
| 47 |
+
services_analysis = self._analyze_services(extracted_data, sector_data)
|
| 48 |
+
local_content_results["breakdown"]["services"] = services_analysis["percentage"]
|
| 49 |
+
|
| 50 |
+
# حساب النسبة الإجمالية للمحتوى المحلي
|
| 51 |
+
weights = sector_data["weights"]
|
| 52 |
+
overall_percentage = (
|
| 53 |
+
weights["manpower"] * manpower_analysis["percentage"] +
|
| 54 |
+
weights["materials"] * materials_analysis["percentage"] +
|
| 55 |
+
weights["services"] * services_analysis["percentage"]
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
local_content_results["overall_percentage"] = round(overall_percentage, 2)
|
| 59 |
+
|
| 60 |
+
# تحليل الامتثال لمتطلبات نطاقات
|
| 61 |
+
nitaqat_analysis = self._analyze_nitaqat_compliance(extracted_data, sector)
|
| 62 |
+
local_content_results["nitaqat_analysis"] = nitaqat_analysis
|
| 63 |
+
|
| 64 |
+
# إعداد متطلبات المحتوى المحلي
|
| 65 |
+
requirements = self._prepare_local_content_requirements(sector_data, local_content_info)
|
| 66 |
+
local_content_results["requirements"] = requirements
|
| 67 |
+
|
| 68 |
+
# إعداد توصيات لتحسين المحتوى المحلي
|
| 69 |
+
recommendations = self._generate_recommendations(
|
| 70 |
+
overall_percentage,
|
| 71 |
+
sector_data,
|
| 72 |
+
manpower_analysis,
|
| 73 |
+
materials_analysis,
|
| 74 |
+
services_analysis,
|
| 75 |
+
nitaqat_analysis
|
| 76 |
+
)
|
| 77 |
+
local_content_results["recommendations"] = recommendations
|
| 78 |
+
|
| 79 |
+
return local_content_results
|
| 80 |
+
|
| 81 |
+
def _determine_sector(self, project_type: str, extracted_data: Dict[str, Any]) -> str:
|
| 82 |
+
"""
|
| 83 |
+
تحديد القطاع بناءً على نوع المشروع والبيانات المستخرجة
|
| 84 |
+
"""
|
| 85 |
+
# قاموس لتحويل أنواع المشاريع الشائعة إلى قطاعات
|
| 86 |
+
project_to_sector = {
|
| 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 |
+
if project_type:
|
| 120 |
+
for key, value in project_to_sector.items():
|
| 121 |
+
if key in project_type.lower():
|
| 122 |
+
return value
|
| 123 |
+
|
| 124 |
+
# إذا لم يتم تحديد القطاع من نوع المشروع، نحاول تحديده من البيانات المستخرجة
|
| 125 |
+
if "text" in extracted_data:
|
| 126 |
+
text = extracted_data["text"].lower()
|
| 127 |
+
sector_scores = {}
|
| 128 |
+
|
| 129 |
+
for sector in self.sectors:
|
| 130 |
+
score = text.count(sector.lower())
|
| 131 |
+
sector_scores[sector] = score
|
| 132 |
+
|
| 133 |
+
# اختيار القطاع الأكثر ذكراً
|
| 134 |
+
if sector_scores:
|
| 135 |
+
max_sector = max(sector_scores, key=sector_scores.get)
|
| 136 |
+
if sector_scores[max_sector] > 0:
|
| 137 |
+
return max_sector
|
| 138 |
+
|
| 139 |
+
# القطاع الافتراضي إذا لم نتمكن من تحديده
|
| 140 |
+
return "عام"
|
| 141 |
+
|
| 142 |
+
def _extract_local_content_info(self, extracted_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 143 |
+
"""
|
| 144 |
+
استخراج معلومات المحتوى المحلي من البيانات المستخرجة
|
| 145 |
+
"""
|
| 146 |
+
local_content_info = {
|
| 147 |
+
"mentioned_percentage": None,
|
| 148 |
+
"requirements": [],
|
| 149 |
+
"companies": []
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
# استخراج النسبة المذكورة للمحتوى المحلي
|
| 153 |
+
if "local_content" in extracted_data and extracted_data["local_content"]:
|
| 154 |
+
local_content_data = extracted_data["local_content"]
|
| 155 |
+
|
| 156 |
+
# استخراج النسب المئوية
|
| 157 |
+
percentages = local_content_data.get("percentages", [])
|
| 158 |
+
if percentages:
|
| 159 |
+
# اختيار أعلى نسبة مذكورة
|
| 160 |
+
max_percentage = max(percentages, key=lambda x: x["value"])
|
| 161 |
+
local_content_info["mentioned_percentage"] = max_percentage["value"]
|
| 162 |
+
|
| 163 |
+
# استخراج المتطلبات
|
| 164 |
+
requirements = local_content_data.get("requirements", [])
|
| 165 |
+
local_content_info["requirements"] = requirements
|
| 166 |
+
|
| 167 |
+
# استخراج الشركات المحلية المذكورة
|
| 168 |
+
if "entities" in extracted_data and "organizations" in extracted_data["entities"]:
|
| 169 |
+
organizations = extracted_data["entities"]["organizations"]
|
| 170 |
+
|
| 171 |
+
for org in organizations:
|
| 172 |
+
org_name = org["name"].lower()
|
| 173 |
+
|
| 174 |
+
# البحث في قاعدة بيانات الشركات المحلية
|
| 175 |
+
for company_id, company_data in self.local_companies.items():
|
| 176 |
+
company_name = company_data["name"].lower()
|
| 177 |
+
|
| 178 |
+
# إذا وجدنا تطابق
|
| 179 |
+
if company_name in org_name or org_name in company_name:
|
| 180 |
+
local_content_info["companies"].append(company_data)
|
| 181 |
+
|
| 182 |
+
return local_content_info
|
| 183 |
+
|
| 184 |
+
def _analyze_manpower(self, extracted_data: Dict[str, Any], sector_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 185 |
+
"""
|
| 186 |
+
تحليل المحتوى المحلي للقوى العاملة
|
| 187 |
+
"""
|
| 188 |
+
manpower_analysis = {
|
| 189 |
+
"percentage": 0.0,
|
| 190 |
+
"saudi_employees": 0,
|
| 191 |
+
"expat_employees": 0,
|
| 192 |
+
"total_employees": 0
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
# استخراج معلومات الموظفين من البيانات
|
| 196 |
+
saudi_keywords = ["سعودي", "مواطن", "توطين", "سعودة"]
|
| 197 |
+
expat_keywords = ["أجنبي", "وافد", "غير سعودي"]
|
| 198 |
+
|
| 199 |
+
# لدينا بعض البيانات الافتراضية للتوضيح
|
| 200 |
+
saudi_count = 0
|
| 201 |
+
expat_count = 0
|
| 202 |
+
|
| 203 |
+
# إذا كانت هناك شركات معروفة في البيانات المستخرجة، نستخدم نسب التوطين الخاصة بها
|
| 204 |
+
if "entities" in extracted_data and "organizations" in extracted_data["entities"]:
|
| 205 |
+
organizations = extracted_data["entities"]["organizations"]
|
| 206 |
+
|
| 207 |
+
for org in organizations:
|
| 208 |
+
org_name = org["name"].lower()
|
| 209 |
+
|
| 210 |
+
for company_id, company_data in self.local_companies.items():
|
| 211 |
+
company_name = company_data["name"].lower()
|
| 212 |
+
|
| 213 |
+
if company_name in org_name or org_name in company_name:
|
| 214 |
+
saudi_percentage = company_data["saudi_employees_percentage"] / 100
|
| 215 |
+
saudi_count += 50 * saudi_percentage # افتراض 50 موظف كمتوسط
|
| 216 |
+
expat_count += 50 * (1 - saudi_percentage)
|
| 217 |
+
|
| 218 |
+
# إذا لم نجد شركات معروفة، نحاول تقدير النسب من النص
|
| 219 |
+
if saudi_count == 0 and expat_count == 0 and "text" in extracted_data:
|
| 220 |
+
text = extracted_data["text"].lower()
|
| 221 |
+
|
| 222 |
+
# عدد مرات ذكر الموظفين السعوديين
|
| 223 |
+
saudi_mentions = sum(text.count(keyword) for keyword in saudi_keywords)
|
| 224 |
+
|
| 225 |
+
# عدد مرات ذكر الموظفين الأجانب
|
| 226 |
+
expat_mentions = sum(text.count(keyword) for keyword in expat_keywords)
|
| 227 |
+
|
| 228 |
+
# تقدير تقريبي للنسب
|
| 229 |
+
total_mentions = saudi_mentions + expat_mentions
|
| 230 |
+
if total_mentions > 0:
|
| 231 |
+
saudi_ratio = saudi_mentions / total_mentions
|
| 232 |
+
saudi_count = int(100 * saudi_ratio)
|
| 233 |
+
expat_count = 100 - saudi_count
|
| 234 |
+
|
| 235 |
+
# إذا لم نتمكن من تقدير النسب، نستخدم قيمة افتراضية
|
| 236 |
+
if saudi_count == 0 and expat_count == 0:
|
| 237 |
+
# القيمة الافتراضية بناءً على متوسط نسب التوطين في القطاع
|
| 238 |
+
if "الإنشاءات" in sector_data:
|
| 239 |
+
saudi_count = 25
|
| 240 |
+
elif "تقنية المعلومات" in sector_data:
|
| 241 |
+
saudi_count = 35
|
| 242 |
+
else:
|
| 243 |
+
saudi_count = 30
|
| 244 |
+
|
| 245 |
+
expat_count = 100 - saudi_count
|
| 246 |
+
|
| 247 |
+
# حساب النسبة المئوية للمحتوى المحلي في القوى العاملة
|
| 248 |
+
total_count = saudi_count + expat_count
|
| 249 |
+
|
| 250 |
+
if total_count > 0:
|
| 251 |
+
manpower_analysis["saudi_employees"] = saudi_count
|
| 252 |
+
manpower_analysis["expat_employees"] = expat_count
|
| 253 |
+
manpower_analysis["total_employees"] = total_count
|
| 254 |
+
|
| 255 |
+
# حساب النسبة المئوية باستخدام الأوزان
|
| 256 |
+
manpower_percentage = (
|
| 257 |
+
self.local_content_data["manpower_categories"]["saudi_employee"] * saudi_count +
|
| 258 |
+
self.local_content_data["manpower_categories"]["expat_employee"] * expat_count
|
| 259 |
+
) / total_count * 100
|
| 260 |
+
|
| 261 |
+
manpower_analysis["percentage"] = round(manpower_percentage, 2)
|
| 262 |
+
|
| 263 |
+
return manpower_analysis
|
| 264 |
+
|
| 265 |
+
def _analyze_materials(self, extracted_data: Dict[str, Any], sector_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 266 |
+
"""
|
| 267 |
+
تحليل المحتوى المحلي للمواد
|
| 268 |
+
"""
|
| 269 |
+
materials_analysis = {
|
| 270 |
+
"percentage": 0.0,
|
| 271 |
+
"local_manufacturing": 0,
|
| 272 |
+
"local_assembly": 0,
|
| 273 |
+
"imported_with_local_value_add": 0,
|
| 274 |
+
"fully_imported": 0,
|
| 275 |
+
"total_materials": 0
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
# كلمات دلالية للفئات المختلفة من المواد
|
| 279 |
+
local_manufacturing_keywords = ["تصنيع محلي", "منتج محلي", "صناعة محلية", "صنع في السعودية", "منشأ سعودي"]
|
| 280 |
+
local_assembly_keywords = ["تجميع محلي", "مجمع محلياً", "تم تجميعه في السعودية"]
|
| 281 |
+
imported_with_local_value_keywords = ["قيمة مضافة محلية", "معالجة محلية", "قيمة محلية"]
|
| 282 |
+
fully_imported_keywords = ["مستورد", "استيراد كامل", "منتج أجنبي", "صنع في الخارج"]
|
| 283 |
+
|
| 284 |
+
# تقدير نسب المواد من النص
|
| 285 |
+
if "text" in extracted_data:
|
| 286 |
+
text = extracted_data["text"].lower()
|
| 287 |
+
|
| 288 |
+
# عدد مرات ذكر كل فئة
|
| 289 |
+
local_manufacturing_count = sum(text.count(keyword) for keyword in local_manufacturing_keywords)
|
| 290 |
+
local_assembly_count = sum(text.count(keyword) for keyword in local_assembly_keywords)
|
| 291 |
+
imported_with_local_value_count = sum(text.count(keyword) for keyword in imported_with_local_value_keywords)
|
| 292 |
+
fully_imported_count = sum(text.count(keyword) for keyword in fully_imported_keywords)
|
| 293 |
+
|
| 294 |
+
# إجمالي عدد المرات
|
| 295 |
+
total_count = (local_manufacturing_count + local_assembly_count +
|
| 296 |
+
imported_with_local_value_count + fully_imported_count)
|
| 297 |
+
|
| 298 |
+
if total_count > 0:
|
| 299 |
+
materials_analysis["local_manufacturing"] = local_manufacturing_count
|
| 300 |
+
materials_analysis["local_assembly"] = local_assembly_count
|
| 301 |
+
materials_analysis["imported_with_local_value_add"] = imported_with_local_value_count
|
| 302 |
+
materials_analysis["fully_imported"] = fully_imported_count
|
| 303 |
+
materials_analysis["total_materials"] = total_count
|
| 304 |
+
|
| 305 |
+
# إذا لم نتمكن من تقدير النسب، نستخدم قيم افتراضية بناءً على القطاع
|
| 306 |
+
if materials_analysis["total_materials"] == 0:
|
| 307 |
+
if "الإنشاءات" in sector_data:
|
| 308 |
+
materials_analysis["local_manufacturing"] = 30
|
| 309 |
+
materials_analysis["local_assembly"] = 20
|
| 310 |
+
materials_analysis["imported_with_local_value_add"] = 15
|
| 311 |
+
materials_analysis["fully_imported"] = 35
|
| 312 |
+
elif "تقنية المعلومات" in sector_data:
|
| 313 |
+
materials_analysis["local_manufacturing"] = 10
|
| 314 |
+
materials_analysis["local_assembly"] = 25
|
| 315 |
+
materials_analysis["imported_with_local_value_add"] = 15
|
| 316 |
+
materials_analysis["fully_imported"] = 50
|
| 317 |
+
else:
|
| 318 |
+
materials_analysis["local_manufacturing"] = 20
|
| 319 |
+
materials_analysis["local_assembly"] = 20
|
| 320 |
+
materials_analysis["imported_with_local_value_add"] = 15
|
| 321 |
+
materials_analysis["fully_imported"] = 45
|
| 322 |
+
|
| 323 |
+
materials_analysis["total_materials"] = (
|
| 324 |
+
materials_analysis["local_manufacturing"] +
|
| 325 |
+
materials_analysis["local_assembly"] +
|
| 326 |
+
materials_analysis["imported_with_local_value_add"] +
|
| 327 |
+
materials_analysis["fully_imported"]
|
| 328 |
+
)
|
| 329 |
+
|
| 330 |
+
# حساب النسبة المئوية للمحتوى المحلي في المواد
|
| 331 |
+
total_materials = materials_analysis["total_materials"]
|
| 332 |
+
|
| 333 |
+
if total_materials > 0:
|
| 334 |
+
material_categories = self.local_content_data["material_categories"]
|
| 335 |
+
materials_percentage = (
|
| 336 |
+
material_categories["local_manufacturing"] * materials_analysis["local_manufacturing"] +
|
| 337 |
+
material_categories["local_assembly"] * materials_analysis["local_assembly"] +
|
| 338 |
+
material_categories["imported_with_local_value_add"] * materials_analysis["imported_with_local_value_add"] +
|
| 339 |
+
material_categories["fully_imported"] * materials_analysis["fully_imported"]
|
| 340 |
+
) / total_materials * 100
|
| 341 |
+
|
| 342 |
+
materials_analysis["percentage"] = round(materials_percentage, 2)
|
| 343 |
+
|
| 344 |
+
return materials_analysis
|
| 345 |
+
|
| 346 |
+
def _analyze_services(self, extracted_data: Dict[str, Any], sector_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 347 |
+
"""
|
| 348 |
+
تحليل المحتوى المحلي للخدمات
|
| 349 |
+
"""
|
| 350 |
+
services_analysis = {
|
| 351 |
+
"percentage": 0.0,
|
| 352 |
+
"local_service_provider": 0,
|
| 353 |
+
"joint_venture": 0,
|
| 354 |
+
"foreign_provider_with_local_partner": 0,
|
| 355 |
+
"foreign_provider": 0,
|
| 356 |
+
"total_services": 0
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
# كلمات دلالية للفئات المختلفة من الخدمات
|
| 360 |
+
local_provider_keywords = ["مزود خدمة محلي", "شركة محلية", "شركة سعودية", "مؤسسة محلية"]
|
| 361 |
+
joint_venture_keywords = ["مشروع مشترك", "شراكة", "تحالف", "ائتلاف"]
|
| 362 |
+
foreign_with_local_keywords = ["شريك محلي", "وكيل محلي", "وكيل سعودي", "تمثيل محلي"]
|
| 363 |
+
foreign_provider_keywords = ["مزود خدمة أجنبي", "شركة أجنبية", "مقاول أجنبي"]
|
| 364 |
+
|
| 365 |
+
# تقدير نسب الخدمات من النص
|
| 366 |
+
if "text" in extracted_data:
|
| 367 |
+
text = extracted_data["text"].lower()
|
| 368 |
+
|
| 369 |
+
# عدد مرات ذكر كل فئة
|
| 370 |
+
local_provider_count = sum(text.count(keyword) for keyword in local_provider_keywords)
|
| 371 |
+
joint_venture_count = sum(text.count(keyword) for keyword in joint_venture_keywords)
|
| 372 |
+
foreign_with_local_count = sum(text.count(keyword) for keyword in foreign_with_local_keywords)
|
| 373 |
+
foreign_provider_count = sum(text.count(keyword) for keyword in foreign_provider_keywords)
|
| 374 |
+
|
| 375 |
+
# إجمالي عدد المرات
|
| 376 |
+
total_count = (local_provider_count + joint_venture_count +
|
| 377 |
+
foreign_with_local_count + foreign_provider_count)
|
| 378 |
+
|
| 379 |
+
if total_count > 0:
|
| 380 |
+
services_analysis["local_service_provider"] = local_provider_count
|
| 381 |
+
services_analysis["joint_venture"] = joint_venture_count
|
| 382 |
+
services_analysis["foreign_provider_with_local_partner"] = foreign_with_local_count
|
| 383 |
+
services_analysis["foreign_provider"] = foreign_provider_count
|
| 384 |
+
services_analysis["total_services"] = total_count
|
| 385 |
+
|
| 386 |
+
# إذا لم نتمكن من تقدير النسب، نستخدم قيم افتراضية بناءً على القطاع
|
| 387 |
+
if services_analysis["total_services"] == 0:
|
| 388 |
+
if "الإنشاءات" in sector_data:
|
| 389 |
+
services_analysis["local_service_provider"] = 35
|
| 390 |
+
services_analysis["joint_venture"] = 25
|
| 391 |
+
services_analysis["foreign_provider_with_local_partner"] = 20
|
| 392 |
+
services_analysis["foreign_provider"] = 20
|
| 393 |
+
elif "تقنية المعلومات" in sector_data:
|
| 394 |
+
services_analysis["local_service_provider"] = 30
|
| 395 |
+
services_analysis["joint_venture"] = 20
|
| 396 |
+
services_analysis["foreign_provider_with_local_partner"] = 25
|
| 397 |
+
services_analysis["foreign_provider"] = 25
|
| 398 |
+
else:
|
| 399 |
+
services_analysis["local_service_provider"] = 30
|
| 400 |
+
services_analysis["joint_venture"] = 25
|
| 401 |
+
services_analysis["foreign_provider_with_local_partner"] = 20
|
| 402 |
+
services_analysis["foreign_provider"] = 25
|
| 403 |
+
|
| 404 |
+
services_analysis["total_services"] = (
|
| 405 |
+
services_analysis["local_service_provider"] +
|
| 406 |
+
services_analysis["joint_venture"] +
|
| 407 |
+
services_analysis["foreign_provider_with_local_partner"] +
|
| 408 |
+
services_analysis["foreign_provider"]
|
| 409 |
+
)
|
| 410 |
+
|
| 411 |
+
# حساب النسبة المئوية للمحتوى المحلي في الخدمات
|
| 412 |
+
total_services = services_analysis["total_services"]
|
| 413 |
+
|
| 414 |
+
if total_services > 0:
|
| 415 |
+
service_categories = self.local_content_data["service_categories"]
|
| 416 |
+
services_percentage = (
|
| 417 |
+
service_categories["local_service_provider"] * services_analysis["local_service_provider"] +
|
| 418 |
+
service_categories["joint_venture"] * services_analysis["joint_venture"] +
|
| 419 |
+
service_categories["foreign_provider_with_local_partner"] * services_analysis["foreign_provider_with_local_partner"] +
|
| 420 |
+
service_categories["foreign_provider"] * services_analysis["foreign_provider"]
|
| 421 |
+
) / total_services * 100
|
| 422 |
+
|
| 423 |
+
services_analysis["percentage"] = round(services_percentage, 2)
|
| 424 |
+
|
| 425 |
+
return services_analysis
|
| 426 |
+
|
| 427 |
+
def _analyze_nitaqat_compliance(self, extracted_data: Dict[str, Any], sector: str) -> Dict[str, Any]:
|
| 428 |
+
"""
|
| 429 |
+
تحليل الامتثال لمتطلبات نطاقات
|
| 430 |
+
"""
|
| 431 |
+
nitaqat_analysis = {
|
| 432 |
+
"compliant": False,
|
| 433 |
+
"nitaqat_category": "غير محدد",
|
| 434 |
+
"company_size": "غير محدد",
|
| 435 |
+
"required_saudi_percentage": 0,
|
| 436 |
+
"estimated_saudi_percentage": 0
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
# تحديد حجم الشركة بناءً على القيمة التقديرية للمشروع
|
| 440 |
+
project_value = 0
|
| 441 |
+
if "financial_data" in extracted_data and "total_cost" in extracted_data["financial_data"]:
|
| 442 |
+
project_value = extracted_data["financial_data"]["total_cost"]["value"]
|
| 443 |
+
|
| 444 |
+
if project_value == 0 and "text" in extracted_data:
|
| 445 |
+
# محاولة استخراج قيمة المشروع من النص
|
| 446 |
+
text = extracted_data["text"].lower()
|
| 447 |
+
value_patterns = [
|
| 448 |
+
r'قيمة المشروع[^\d]*([\d.,]+)[^\d]*(ريال|ر\.س)',
|
| 449 |
+
r'قيمة العقد[^\d]*([\d.,]+)[^\d]*(ريال|ر\.س)',
|
| 450 |
+
r'القيمة الإجمالية[^\d]*([\d.,]+)[^\d]*(ريال|ر\.س)'
|
| 451 |
+
]
|
| 452 |
+
|
| 453 |
+
for pattern in value_patterns:
|
| 454 |
+
matches = re.findall(pattern, text, re.IGNORECASE)
|
| 455 |
+
if matches:
|
| 456 |
+
try:
|
| 457 |
+
project_value = float(matches[0][0].replace(',', ''))
|
| 458 |
+
break
|
| 459 |
+
except:
|
| 460 |
+
pass
|
| 461 |
+
|
| 462 |
+
# تحديد حجم الشركة بناءً على قيمة المشروع
|
| 463 |
+
company_size = "صغيرة"
|
| 464 |
+
if project_value >= 10000000: # أكثر من 10 مليون ريال
|
| 465 |
+
company_size = "كبيرة"
|
| 466 |
+
elif project_value >= 3000000: # بين 3 و 10 مليون ريال
|
| 467 |
+
company_size = "متوسطة"
|
| 468 |
+
|
| 469 |
+
nitaqat_analysis["company_size"] = company_size
|
| 470 |
+
|
| 471 |
+
# تحديد نسبة التوطين المطلوبة
|
| 472 |
+
if sector in self.nitaqat_data and company_size in self.nitaqat_data[sector]:
|
| 473 |
+
# نفترض أننا نريد الامتثال للفئة الخضراء المتوسطة
|
| 474 |
+
required_percentage = (
|
| 475 |
+
self.nitaqat_data[sector][company_size]["أخضر متوسط"]["min"] +
|
| 476 |
+
self.nitaqat_data[sector][company_size]["أخضر متوسط"]["max"]
|
| 477 |
+
) / 2
|
| 478 |
+
|
| 479 |
+
nitaqat_analysis["required_saudi_percentage"] = required_percentage
|
| 480 |
+
else:
|
| 481 |
+
# قيمة افتراضية
|
| 482 |
+
nitaqat_analysis["required_saudi_percentage"] = 20
|
| 483 |
+
|
| 484 |
+
# تقدير نسبة التوطين الفعلية
|
| 485 |
+
estimated_saudi_percentage = 0
|
| 486 |
+
|
| 487 |
+
# إذا كانت هناك شركات معروفة في البيانات المستخرجة، نستخدم نسبة التوطين الخاصة بها
|
| 488 |
+
if "entities" in extracted_data and "organizations" in extracted_data["entities"]:
|
| 489 |
+
organizations = extracted_data["entities"]["organizations"]
|
| 490 |
+
|
| 491 |
+
for org in organizations:
|
| 492 |
+
org_name = org["name"].lower()
|
| 493 |
+
|
| 494 |
+
for company_id, company_data in self.local_companies.items():
|
| 495 |
+
company_name = company_data["name"].lower()
|
| 496 |
+
|
| 497 |
+
if company_name in org_name or org_name in company_name:
|
| 498 |
+
estimated_saudi_percentage = company_data["saudi_employees_percentage"]
|
| 499 |
+
nitaqat_analysis["nitaqat_category"] = company_data["nitaqat_category"]
|
| 500 |
+
break
|
| 501 |
+
|
| 502 |
+
# إذا لم نتمكن من تقدير النسبة، نستخدم قيمة افتراضية
|
| 503 |
+
if estimated_saudi_percentage == 0:
|
| 504 |
+
if "الإنشاءات" in sector:
|
| 505 |
+
estimated_saudi_percentage = 15
|
| 506 |
+
elif "تقنية المعلومات" in sector:
|
| 507 |
+
estimated_saudi_percentage = 25
|
| 508 |
+
else:
|
| 509 |
+
estimated_saudi_percentage = 20
|
| 510 |
+
|
| 511 |
+
nitaqat_analysis["estimated_saudi_percentage"] = estimated_saudi_percentage
|
| 512 |
+
|
| 513 |
+
# تحديد فئة نطاقات والامتثال
|
| 514 |
+
if sector in self.nitaqat_data and company_size in self.nitaqat_data[sector]:
|
| 515 |
+
for category, range_data in self.nitaqat_data[sector][company_size].items():
|
| 516 |
+
if range_data["min"] <= estimated_saudi_percentage <= range_data["max"]:
|
| 517 |
+
nitaqat_analysis["nitaqat_category"] = category
|
| 518 |
+
break
|
| 519 |
+
|
| 520 |
+
# تحديد الامتثال
|
| 521 |
+
nitaqat_analysis["compliant"] = (
|
| 522 |
+
nitaqat_analysis["nitaqat_category"] != "أحمر" and
|
| 523 |
+
estimated_saudi_percentage >= nitaqat_analysis["required_saudi_percentage"]
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
return nitaqat_analysis
|
| 527 |
+
|
| 528 |
+
def _prepare_local_content_requirements(self, sector_data: Dict[str, Any], local_content_info: Dict[str, Any]) -> List[Dict[str, Any]]:
|
| 529 |
+
"""
|
| 530 |
+
إعداد متطلبات المحتوى المحلي
|
| 531 |
+
"""
|
| 532 |
+
requirements = []
|
| 533 |
+
|
| 534 |
+
# الحد الأدنى للمimport os
|
| 535 |
+
import json
|
| 536 |
+
import numpy as np
|
| 537 |
+
import pandas as pd
|
| 538 |
+
from typing import Dict, List, Any, Union, Tuple, Optional
|
| 539 |
+
from datetime import datetime
|
| 540 |
+
|
| 541 |
+
class LocalContentCalculator:
|
| 542 |
+
"""
|
| 543 |
+
فئة لحساب وتحليل المحتوى المحلي في المناقصات
|
| 544 |
+
"""
|
| 545 |
+
|
| 546 |
+
def __init__(self):
|
| 547 |
+
"""
|
| 548 |
+
تهيئة حاسبة المحتوى المحلي
|
| 549 |
+
"""
|
| 550 |
+
# تحميل بيانات المحتوى المحلي
|
| 551 |
+
self.local_content_data = self._load_local_content_data()
|
| 552 |
+
|
| 553 |
+
# تحميل قاعدة بيانات الشركات المحلية
|
| 554 |
+
self.local_companies = self._load_local_companies()
|
| 555 |
+
|
| 556 |
+
# تحميل نطاقات الشركات
|
| 557 |
+
self.nitaqat_data = self._load_nitaqat_data()
|
| 558 |
+
|
| 559 |
+
# قائمة القطاعات
|
| 560 |
+
self.sectors = [
|
| 561 |
+
"الإنشاءات", "تقنية المعلومات", "الاتصالات", "الصحة",
|
| 562 |
+
"التعليم", "النقل", "الطاقة", "المياه", "البيئة",
|
| 563 |
+
"الصناعة", "التجارة", "السياحة", "الخدمات المالية"
|
| 564 |
+
]
|
| 565 |
+
|
| 566 |
+
def _load_local_content_data(self) -> Dict[str, Any]:
|
| 567 |
+
"""
|
| 568 |
+
تحميل بيانات المحتوى المحلي
|
| 569 |
+
"""
|
| 570 |
+
# في التطبيق الفعلي، قد تُحمل هذه البيانات من ملف أو قاعدة بيانات
|
| 571 |
+
return {
|
| 572 |
+
"sectors": {
|
| 573 |
+
"الإنشاءات": {
|
| 574 |
+
"min_percentage": 30,
|
| 575 |
+
"target_percentage": 60,
|
| 576 |
+
"weights": {
|
| 577 |
+
"manpower": 0.35,
|
| 578 |
+
"materials": 0.45,
|
| 579 |
+
"services": 0.2
|
| 580 |
+
}
|
| 581 |
+
},
|
| 582 |
+
"تقنية المعلومات": {
|
| 583 |
+
"min_percentage": 25,
|
| 584 |
+
"target_percentage": 50,
|
| 585 |
+
"weights": {
|
| 586 |
+
"manpower": 0.55,
|
| 587 |
+
"materials": 0.15,
|
| 588 |
+
"services": 0.3
|
| 589 |
+
}
|
| 590 |
+
},
|
| 591 |
+
"عام": {
|
| 592 |
+
"min_percentage": 20,
|
| 593 |
+
"target_percentage": 40,
|
| 594 |
+
"weights": {
|
| 595 |
+
"manpower": 0.4,
|
| 596 |
+
"materials": 0.3,
|
| 597 |
+
"services": 0.3
|
| 598 |
+
}
|
| 599 |
+
}
|
| 600 |
+
},
|
| 601 |
+
"material_categories": {
|
| 602 |
+
"local_manufacturing": 1.0,
|
| 603 |
+
"local_assembly": 0.7,
|
| 604 |
+
"imported_with_local_value_add": 0.4,
|
| 605 |
+
"fully_imported": 0.0
|
| 606 |
+
},
|
| 607 |
+
"manpower_categories": {
|
| 608 |
+
"saudi_employee": 1.0,
|
| 609 |
+
"expat_employee": 0.0
|
| 610 |
+
},
|
| 611 |
+
"service_categories": {
|
| 612 |
+
"local_service_provider": 1.0,
|
| 613 |
+
"joint_venture": 0.6,
|
| 614 |
+
"foreign_provider_with_local_partner": 0.3,
|
| 615 |
+
"foreign_provider": 0.0
|
| 616 |
+
}
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
def _load_local_companies(self) -> Dict[str, Dict[str, Any]]:
|
| 620 |
+
"""
|
| 621 |
+
تحميل قاعدة بيانات الشركات المحلية
|
| 622 |
+
"""
|
| 623 |
+
# في التطبيق الفعلي، قد تُحمل هذه البيانات من ملف أو قاعدة بيانات
|
| 624 |
+
return {
|
| 625 |
+
"company1": {
|
| 626 |
+
"name": "شركة البناء السعودية",
|
| 627 |
+
"cr_number": "1010XXXXXX",
|
| 628 |
+
"sector": "الإنشاءات",
|
| 629 |
+
"local_content_certificate": True,
|
| 630 |
+
"saudi_employees_percentage": 35,
|
| 631 |
+
"nitaqat_category": "أخضر متوسط",
|
| 632 |
+
"local_content_percentage": 45
|
| 633 |
+
},
|
| 634 |
+
"company2": {
|
| 635 |
+
"name": "شركة تقنية المستقبل",
|
| 636 |
+
"cr_number": "1020XXXXXX",
|
| 637 |
+
"sector": "تقنية المعلومات",
|
| 638 |
+
"local_content_certificate": True,
|
| 639 |
+
"saudi_employees_percentage": 42,
|
| 640 |
+
"nitaqat_category": "أخضر مرتفع",
|
| 641 |
+
"local_content_percentage": 52
|
| 642 |
+
},
|
| 643 |
+
"company3": {
|
| 644 |
+
"name": "مصنع المنتجات المعدنية",
|
| 645 |
+
"cr_number": "1030XXXXXX",
|
| 646 |
+
"sector": "الصناعة",
|
| 647 |
+
"local_content_certificate": True,
|
| 648 |
+
"saudi_employees_percentage": 28,
|
| 649 |
+
"nitaqat_category": "أخضر منخفض",
|
| 650 |
+
"local_content_percentage": 61
|
| 651 |
+
}
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
def _load_nitaqat_data(self) -> Dict[str, Dict[str, Any]]:
|
| 655 |
+
"""
|
| 656 |
+
تحميل بيانات نطاقات الشركات
|
| 657 |
+
"""
|
| 658 |
+
# في التطبيق الفعلي، قد تُحمل هذه البيانات من ملف أو قاعدة بيانات
|
| 659 |
+
return {
|
| 660 |
+
"الإنشاءات": {
|
| 661 |
+
"صغيرة": {
|
| 662 |
+
"أحمر": {"min": 0, "max": 5},
|
| 663 |
+
"أخضر منخفض": {"min": 6, "max": 10},
|
| 664 |
+
"أخضر متوسط": {"min": 11, "max": 15},
|
| 665 |
+
"أخضر مرتفع": {"min": 16, "max": 25},
|
| 666 |
+
"بلاتيني": {"min": 26, "max": 100}
|
| 667 |
+
},
|
| 668 |
+
"متوسطة": {
|
| 669 |
+
"أحمر": {"min": 0, "max": 7},
|
| 670 |
+
"أخضر منخفض": {"min": 8, "max": 14},
|
| 671 |
+
"أخضر متوسط": {"min": 15, "max": 20},
|
| 672 |
+
"أخضر مرتفع": {"min": 21, "max": 30},
|
| 673 |
+
"بلاتيني": {"min": 31, "max": 100}
|
| 674 |
+
},
|
| 675 |
+
"كبيرة": {
|
| 676 |
+
"أحمر": {"min": 0, "max": 9},
|
| 677 |
+
"أخضر منخفض": {"min": 10, "max": 16},
|
| 678 |
+
"أخضر متوسط": {"min": 17, "max": 23},
|
| 679 |
+
"أخضر مرتفع": {"min": 24, "max": 35},
|
| 680 |
+
"بلاتيني": {"min": 36, "max": 100}
|
| 681 |
+
}
|
| 682 |
+
},
|
| 683 |
+
"تقنية المعلومات": {
|
| 684 |
+
"صغيرة": {
|
| 685 |
+
"أحمر": {"min": 0, "max": 6},
|
| 686 |
+
"أخضر منخفض": {"min": 7, "max": 12},
|
| 687 |
+
"أخضر متوسط": {"min": 13, "max": 19},
|
| 688 |
+
"أخضر مرتفع": {"min": 20, "max": 29},
|
| 689 |
+
"بلاتيني": {"min": 30, "max": 100}
|
| 690 |
+
},
|
| 691 |
+
"متوسطة": {
|
| 692 |
+
"أحمر": {"min": 0, "max": 13},
|
| 693 |
+
"أخضر منخفض": {"min": 14, "max": 20},
|
| 694 |
+
"أخضر متوسط": {"min": 21, "max": 27},
|
| 695 |
+
"أخضر مرتفع": {"min": 28, "max": 35},
|
| 696 |
+
"بلاتيني": {"min": 36, "max": 100}
|
| 697 |
+
},
|
| 698 |
+
"كبيرة": {
|
| 699 |
+
"أحمر": {"min": 0, "max": 17},
|
| 700 |
+
"أخضر منخفض": {"min": 18, "max": 25},
|
| 701 |
+
"أخضر متوسط": {"min": 26, "max": 33},
|
| 702 |
+
"أخضر مرتفع": {"min": 34, "max": 40},
|
| 703 |
+
"بلاتيني": {"min": 41, "max": 100}
|
| 704 |
+
}
|
| 705 |
+
}
|
| 706 |
+
}
|