EGYADMIN commited on
Commit
c976442
·
verified ·
1 Parent(s): b1ed9b7

Create utils/database.py

Browse files
Files changed (1) hide show
  1. utils/database.py +594 -0
utils/database.py ADDED
@@ -0,0 +1,594 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import pandas as pd
4
+ import numpy as np
5
+ from typing import Dict, List, Any, Union, Tuple, Optional
6
+ from datetime import datetime
7
+
8
+ class VectorDBConnector:
9
+ """
10
+ فئة للاتصال بقاعدة بيانات المتجهات واسترجاع البيانات المتشابهة
11
+ """
12
+
13
+ def __init__(self, db_path: str = "data/vector_db"):
14
+ """
15
+ تهيئة الاتصال بقاعدة بيانات المتجهات
16
+
17
+ المعاملات:
18
+ ----------
19
+ db_path : str, optional
20
+ مسار قاعدة بيانات المتجهات (افتراضي: "data/vector_db")
21
+ """
22
+ self.db_path = db_path
23
+
24
+ # التحقق من وجود قاعدة البيانات وإنشائها إذا لم تكن موجودة
25
+ os.makedirs(os.path.dirname(db_path), exist_ok=True)
26
+
27
+ # تحميل البيانات المخزنة مسبقاً
28
+ self.vectors = self._load_vectors()
29
+
30
+ def _load_vectors(self) -> Dict[str, Any]:
31
+ """
32
+ تحميل المتجهات المخزنة مسبقاً
33
+ """
34
+ # في التطبيق الفعلي، ستُحمل المتجهات من قاعدة البيانات
35
+ # هنا نستخدم بيانات افتراضية للتوضيح
36
+ return {
37
+ "tender1": {
38
+ "text": "مناقصة لتطوير نظام إدارة موارد المؤسسة (ERP) لشركة متوسطة الحجم في قطاع التجارة والتوزيع",
39
+ "vector": np.random.rand(384).tolist(),
40
+ "metadata": {
41
+ "sector": "تقنية المعلومات",
42
+ "location": "جدة",
43
+ "date": "2023-08-22"
44
+ }
45
+ },
46
+ "tender3": {
47
+ "text": "مناقصة لتوريد وتركيب أنظمة تكييف مركزية لمجمع سكني يتكون من 5 مباني في المنطقة الشرقية",
48
+ "vector": np.random.rand(384).tolist(),
49
+ "metadata": {
50
+ "sector": "الإنشاءات",
51
+ "location": "الدمام",
52
+ "date": "2023-06-10"
53
+ }
54
+ },
55
+ "tender4": {
56
+ "text": "مناقصة لتقديم خدمات استشارية لتطوير استراتيجية التحول الرقمي لجهة حكومية",
57
+ "vector": np.random.rand(384).tolist(),
58
+ "metadata": {
59
+ "sector": "تقنية المعلومات",
60
+ "location": "الرياض",
61
+ "date": "2023-09-05"
62
+ }
63
+ },
64
+ "tender5": {
65
+ "text": "مناقصة لإنشاء مستودعات لوجستية بمساحة 10000 متر مربع في المدينة الصناعية الثانية بالرياض",
66
+ "vector": np.random.rand(384).tolist(),
67
+ "metadata": {
68
+ "sector": "الإنشاءات",
69
+ "location": "الرياض",
70
+ "date": "2023-07-18"
71
+ }
72
+ }
73
+ }
74
+
75
+ def store_vector(self, doc_id: str, text: str, vector: np.ndarray, metadata: Dict[str, Any] = None) -> bool:
76
+ """
77
+ تخزين متجه جديد في قاعدة البيانات
78
+
79
+ المعاملات:
80
+ ----------
81
+ doc_id : str
82
+ معرف المستند
83
+ text : str
84
+ النص الأصلي
85
+ vector : np.ndarray
86
+ متجه التمثيل
87
+ metadata : Dict[str, Any], optional
88
+ بيانات وصفية إضافية
89
+
90
+ المخرجات:
91
+ --------
92
+ bool
93
+ نجاح أو فشل العملية
94
+ """
95
+ try:
96
+ self.vectors[doc_id] = {
97
+ "text": text,
98
+ "vector": vector.tolist(),
99
+ "metadata": metadata or {}
100
+ }
101
+ return True
102
+ except Exception as e:
103
+ print(f"Error storing vector: {str(e)}")
104
+ return False
105
+
106
+ def retrieve_similar(self, query_vector: np.ndarray, top_k: int = 5) -> List[Dict[str, Any]]:
107
+ """
108
+ استرجاع المستندات المتشابهة
109
+
110
+ المعاملات:
111
+ ----------
112
+ query_vector : np.ndarray
113
+ متجه الاستعلام
114
+ top_k : int, optional
115
+ عدد المستندات المراد استرجاعها (افتراضي: 5)
116
+
117
+ المخرجات:
118
+ --------
119
+ List[Dict[str, Any]]
120
+ قائمة بالمستندات المتشابهة مع درجات التشابه
121
+ """
122
+ results = []
123
+
124
+ for doc_id, doc_data in self.vectors.items():
125
+ vector = np.array(doc_data["vector"])
126
+
127
+ # حساب ��لتشابه باستخدام جيب التمام (cosine similarity)
128
+ similarity = np.dot(query_vector, vector) / (np.linalg.norm(query_vector) * np.linalg.norm(vector))
129
+
130
+ results.append({
131
+ "doc_id": doc_id,
132
+ "text": doc_data["text"],
133
+ "similarity": float(similarity),
134
+ "metadata": doc_data["metadata"]
135
+ })
136
+
137
+ # ترتيب النتائج حسب درجة التشابه (تنازلياً)
138
+ results = sorted(results, key=lambda x: x["similarity"], reverse=True)
139
+
140
+ return results[:top_k]
141
+
142
+ def filter_by_metadata(self, filters: Dict[str, Any], top_k: int = 5) -> List[Dict[str, Any]]:
143
+ """
144
+ تصفية المستندات حسب البيانات الوصفية
145
+
146
+ المعاملات:
147
+ ----------
148
+ filters : Dict[str, Any]
149
+ معايير التصفية
150
+ top_k : int, optional
151
+ عدد المستندات المراد استرجاعها (افتراضي: 5)
152
+
153
+ المخرجات:
154
+ --------
155
+ List[Dict[str, Any]]
156
+ قائمة بالمستندات المطابقة للمعايير
157
+ """
158
+ results = []
159
+
160
+ for doc_id, doc_data in self.vectors.items():
161
+ metadata = doc_data["metadata"]
162
+ match = True
163
+
164
+ for key, value in filters.items():
165
+ if key not in metadata or metadata[key] != value:
166
+ match = False
167
+ break
168
+
169
+ if match:
170
+ results.append({
171
+ "doc_id": doc_id,
172
+ "text": doc_data["text"],
173
+ "metadata": metadata
174
+ })
175
+
176
+ if len(results) >= top_k:
177
+ break
178
+
179
+ return results
180
+
181
+
182
+ class TemplateLoader:
183
+ """
184
+ فئة لتحميل القوالب المختلفة للمناقصات
185
+ """
186
+
187
+ def __init__(self, templates_path: str = "data/templates"):
188
+ """
189
+ تهيئة محمل القوالب
190
+
191
+ المعاملات:
192
+ ----------
193
+ templates_path : str, optional
194
+ مسار ملفات القوالب (افتراضي: "data/templates")
195
+ """
196
+ self.templates_path = templates_path
197
+
198
+ # التحقق من وجود مجلد القوالب وإنشائه إذا لم يكن موجوداً
199
+ os.makedirs(templates_path, exist_ok=True)
200
+
201
+ def load_tender_templates(self) -> Dict[str, Any]:
202
+ """
203
+ تحميل قوالب المناقصات
204
+
205
+ المخرجات:
206
+ --------
207
+ Dict[str, Any]
208
+ قوالب المناقصات المختلفة
209
+ """
210
+ # في التطبيق الفعلي، ستُحمل القوالب من ملفات
211
+ # هنا نستخدم قوالب افتراضية للتوضيح
212
+ return {
213
+ "construction": {
214
+ "title": "نموذج مناقصة إنشائية",
215
+ "sections": [
216
+ {
217
+ "name": "معلومات عامة",
218
+ "fields": [
219
+ {"name": "عنوان المناقصة", "type": "text", "required": True},
220
+ {"name": "وصف المشروع", "type": "text", "required": True},
221
+ {"name": "الموقع", "type": "text", "required": True},
222
+ {"name": "المدة", "type": "number", "unit": "شهر", "required": True},
223
+ {"name": "الميزانية التقديرية", "type": "number", "unit": "ريال", "required": False}
224
+ ]
225
+ },
226
+ {
227
+ "name": "متطلبات فنية",
228
+ "fields": [
229
+ {"name": "مساحة البناء", "type": "number", "unit": "متر مربع", "required": True},
230
+ {"name": "عدد الطوابق", "type": "number", "required": True},
231
+ {"name": "نوع الهيكل", "type": "select", "options": ["خرساني", "معدني", "مختلط"], "required": True},
232
+ {"name": "مواد البناء", "type": "text", "required": True},
233
+ {"name": "المواصفات الإضافية", "type": "text", "required": False}
234
+ ]
235
+ },
236
+ {
237
+ "name": "متطلبات المحتوى المحلي",
238
+ "fields": [
239
+ {"name": "نسبة المحتوى المحلي", "type": "number", "unit": "%", "required": True},
240
+ {"name": "نسبة توطين الوظائف", "type": "number", "unit": "%", "required": True},
241
+ {"name": "��سبة المورِّدين المحليين", "type": "number", "unit": "%", "required": True}
242
+ ]
243
+ },
244
+ {
245
+ "name": "الجدول الزمني",
246
+ "fields": [
247
+ {"name": "تاريخ بدء المشروع", "type": "date", "required": True},
248
+ {"name": "تاريخ انتهاء المشروع", "type": "date", "required": True},
249
+ {"name": "المراحل الرئيسية", "type": "text", "required": True}
250
+ ]
251
+ }
252
+ ]
253
+ },
254
+ "it": {
255
+ "title": "نموذج مناقصة تقنية معلومات",
256
+ "sections": [
257
+ {
258
+ "name": "معلومات عامة",
259
+ "fields": [
260
+ {"name": "عنوان المناقصة", "type": "text", "required": True},
261
+ {"name": "وصف المشروع", "type": "text", "required": True},
262
+ {"name": "المدة", "type": "number", "unit": "شهر", "required": True},
263
+ {"name": "الميزانية التقديرية", "type": "number", "unit": "ريال", "required": False}
264
+ ]
265
+ },
266
+ {
267
+ "name": "متطلبات فنية",
268
+ "fields": [
269
+ {"name": "نوع النظام", "type": "select", "options": ["تطبيق ويب", "تطبيق جوال", "نظام ERP", "أخرى"], "required": True},
270
+ {"name": "التقنيات المطلوبة", "type": "text", "required": True},
271
+ {"name": "عدد المستخدمين", "type": "number", "required": True},
272
+ {"name": "متطلبات الأمان", "type": "text", "required": True},
273
+ {"name": "متطلبات الأداء", "type": "text", "required": True}
274
+ ]
275
+ },
276
+ {
277
+ "name": "متطلبات المحتوى المحلي",
278
+ "fields": [
279
+ {"name": "نسبة المحتوى المحلي", "type": "number", "unit": "%", "required": True},
280
+ {"name": "نسبة توطين الوظائف", "type": "number", "unit": "%", "required": True}
281
+ ]
282
+ },
283
+ {
284
+ "name": "الجدول الزمني",
285
+ "fields": [
286
+ {"name": "تاريخ بدء المشروع", "type": "date", "required": True},
287
+ {"name": "تاريخ انتهاء المشروع", "type": "date", "required": True},
288
+ {"name": "مراحل التطوير", "type": "text", "required": True}
289
+ ]
290
+ }
291
+ ]
292
+ },
293
+ "general": {
294
+ "title": "نموذج مناقصة عامة",
295
+ "sections": [
296
+ {
297
+ "name": "معلومات عامة",
298
+ "fields": [
299
+ {"name": "عنوان المناقصة", "type": "text", "required": True},
300
+ {"name": "وصف المشروع", "type": "text", "required": True},
301
+ {"name": "المدة", "type": "number", "unit": "شهر", "required": True},
302
+ {"name": "الميزانية التقديرية", "type": "number", "unit": "ريال", "required": False}
303
+ ]
304
+ },
305
+ {
306
+ "name": "متطلبات المشروع",
307
+ "fields": [
308
+ {"name": "المتطلبات الفنية", "type": "text", "required": True},
309
+ {"name": "المتطلبات الإدارية", "type": "text", "required": True},
310
+ {"name": "المتطلبات المالية", "type": "text", "required": True}
311
+ ]
312
+ },
313
+ {
314
+ "name": "متطلبات المحتوى المحلي",
315
+ "fields": [
316
+ {"name": "نسبة المحتوى المحلي", "type": "number", "unit": "%", "required": True}
317
+ ]
318
+ },
319
+ {
320
+ "name": "الجدول الزمني",
321
+ "fields": [
322
+ {"name": "تاريخ بدء المشروع", "type": "date", "required": True},
323
+ {"name": "تاريخ انتهاء المشروع", "type": "date", "required": True}
324
+ ]
325
+ }
326
+ ]
327
+ }
328
+ }
329
+
330
+ def load_risk_templates(self) -> Dict[str, Any]:
331
+ """
332
+ تحميل قوالب المخاطر
333
+
334
+ المخرجات:
335
+ --------
336
+ Dict[str, Any]
337
+ قوالب المخاطر المختلفة
338
+ """
339
+ # في التطبيق الفعلي، ستُحمل القوالب من ملفات
340
+ # هنا نستخدم قوالب افتراضية للتوضيح
341
+ return {
342
+ "construction": [
343
+ {
344
+ "id": "C001",
345
+ "title": "تأخير في الحصول على التصاريح",
346
+ "description": "تأخير في الحصول على التصاريح والموافقات اللازمة من الجهات المختصة",
347
+ "probability": "متوسطة",
348
+ "impact": "عالي",
349
+ "mitigation": [
350
+ "البدء في إجراءات الحصول على التصاريح مبكراً",
351
+ "التنسيق المستمر مع الجهات المختصة",
352
+ "الاستعانة بمستشارين متخصصين"
353
+ ]
354
+ },
355
+ {
356
+ "id": "C002",
357
+ "title": "ارتفاع أسعار مواد البناء",
358
+ "description": "ارتفاع غير متوقع في أسعار مواد البناء الرئيسية",
359
+ "probability": "عالية",
360
+ "impact": "عالي",
361
+ "mitigation": [
362
+ "تضمين بند تعديل الأسعار في العقود",
363
+ "شراء المواد الرئيسية مسبقاً",
364
+ "البحث عن موردين بديلين"
365
+ ]
366
+ },
367
+ {
368
+ "id": "C003",
369
+ "title": "نقص العمالة الماهرة",
370
+ "description": "عدم توفر العمالة الماهرة بالعدد المطلوب",
371
+ "probability": "متوسطة",
372
+ "impact": "متوسط",
373
+ "mitigation": [
374
+ "التعاقد المسبق مع مقاولي الباطن",
375
+ "توفير برامج تدريب للعمالة",
376
+ "زيادة الحوافز للعمالة الماهرة"
377
+ ]
378
+ }
379
+ ],
380
+ "it": [
381
+ {
382
+ "id": "IT001",
383
+ "title": "تغيير متطلبات المشروع",
384
+ "description": "تغييرات متكررة في متطلبات المشروع خلال مرحلة التطوير",
385
+ "probability": "عالية",
386
+ "impact": "عالي",
387
+ "mitigation": [
388
+ "توثيق المتطلبات بشكل دقيق والحصول على موافقة العميل",
389
+ "استخدام منهجية مرنة للتطوير",
390
+ "تحديد إجراءات واضحة لإدارة التغييرات"
391
+ ]
392
+ },
393
+ {
394
+ "id": "IT002",
395
+ "title": "مشاكل في تكامل الأنظمة",
396
+ "description": "صعوبات في تكامل النظام الجديد مع الأنظمة القائمة",
397
+ "probability": "متوسطة",
398
+ "impact": "عالي",
399
+ "mitigation": [
400
+ "إجراء دراسة تفصيلية للأنظمة القائمة",
401
+ "تطوير واجهات برمجة تطبيقات (APIs) قياسية",
402
+ "إجراء اختبارات تكامل شاملة"
403
+ ]
404
+ },
405
+ {
406
+ "id": "IT003",
407
+ "title": "مشاكل في أمن المعلومات",
408
+ "description": "ثغرات أمنية في النظام تؤدي إلى مخاطر أمنية",
409
+ "probability": "متوسطة",
410
+ "impact": "عالي",
411
+ "mitigation": [
412
+ "تطبيق معايير أمن المعلومات العالمية",
413
+ "إجراء اختبارات اختراق دورية",
414
+ "تطوير خطة استجابة للحوادث الأمنية"
415
+ ]
416
+ }
417
+ ],
418
+ "general": [
419
+ {
420
+ "id": "G001",
421
+ "title": "تجاوز الميزانية",
422
+ "description": "تجاوز الميزانية المخصصة للمشروع",
423
+ "probability": "متوسطة",
424
+ "impact": "عالي",
425
+ "mitigation": [
426
+ "وضع ميزانية واقعية مع احتياطي كافٍ",
427
+ "مراقبة التكاليف بشكل مستمر",
428
+ "تحديد بنود التكاليف الرئيسية ومراقبتها"
429
+ ]
430
+ },
431
+ {
432
+ "id": "G002",
433
+ "title": "تأخير في الجدول الزمني",
434
+ "description": "تأخير في تنفيذ المشروع وفق الجدول الزمني المحدد",
435
+ "probability": "عالية",
436
+ "impact": "متوسط",
437
+ "mitigation": [
438
+ "وضع جدول زمني واقعي مع احتياطي كافٍ",
439
+ "مراقبة التقدم بشكل مستمر",
440
+ "تحديد المسار الحرج وإعطاؤه الأولوية"
441
+ ]
442
+ },
443
+ {
444
+ "id": "G003",
445
+ "title": "مشاكل في جودة المنتج/الخدمة",
446
+ "description": "عدم مطابقة المنتج أو الخدمة للمواصفات المطلوبة",
447
+ "probability": "متوسطة",
448
+ "impact": "عالي",
449
+ "mitigation": [
450
+ "تحديد معايير الجودة بوضوح",
451
+ "إجراء اختبارات جودة دورية",
452
+ "تطبيق نظام إدارة الجودة"
453
+ ]
454
+ }
455
+ ]
456
+ }
457
+
458
+ def load_local_content_templates(self) -> Dict[str, Any]:
459
+ """
460
+ تحميل قوالب المحتوى المحلي
461
+
462
+ المخرجات:
463
+ --------
464
+ Dict[str, Any]
465
+ قوالب المحتوى المحلي المختلفة
466
+ """
467
+ # في التطبيق الفعلي، ستُحمل القوالب من ملفات
468
+ # هنا نستخدم قوالب افتراضية للتوضيح
469
+ return {
470
+ "construction": {
471
+ "min_percentage": 30,
472
+ "sections": [
473
+ {
474
+ "name": "المواد والمنتجات",
475
+ "weight": 0.5,
476
+ "items": [
477
+ {"name": "الحديد", "local_alternatives": ["حديد السعودية", "حديد الراجحي", "حديد صلب"]},
478
+ {"name": "الأسمنت", "local_alternatives": ["أسمنت اليمامة", "أسمنت السعودية", "أسمنت الراجحي"]},
479
+ {"name": "الخرسانة", "local_alternatives": ["الخرسانة السعودية", "خرسانة البناء", "الخرسانة المسلحة"]},
480
+ {"name": "البلاط", "local_alternatives": ["بلاط وطني", "بلاط السلطان", "بلاط الفخار"]}
481
+ ]
482
+ },
483
+ {
484
+ "name": "القوى العاملة",
485
+ "weight": 0.3,
486
+ "target_percentage": 50,
487
+ "categories": [
488
+ {"name": "المهندسون", "target_percentage": 70},
489
+ {"name": "الفنيون", "target_percentage": 50},
490
+ {"name": "العمال", "target_percentage": 30}
491
+ ]
492
+ },
493
+ {
494
+ "name": "المقاولون والموردون",
495
+ "weight": 0.2,
496
+ "target_percentage": 60
497
+ }
498
+ ]
499
+ },
500
+ "it": {
501
+ "min_percentage": 25,
502
+ "sections": [
503
+ {
504
+ "name": "البرمجيات والتراخيص",
505
+ "weight": 0.4,
506
+ "items": [
507
+ {"name": "أنظمة التشغيل", "local_alternatives": []},
508
+ {"name": "قواعد البيانات", "local_alternatives": []},
509
+ {"name": "برمجيات التطوير", "local_alternatives": []}
510
+ ]
511
+ },
512
+ {
513
+ "name": "الأجهزة والعتاد",
514
+ "weight": 0.2,
515
+ "items": [
516
+ {"name": "الخوادم", "local_alternatives": ["تجميع محلي"]},
517
+ {"name": "أجهزة الحاسب", "local_alternatives": ["تجميع محلي"]},
518
+ {"name": "أجهزة الشبكات", "local_alternatives": []}
519
+ ]
520
+ },
521
+ {
522
+ "name": "القوى العاملة",
523
+ "weight": 0.4,
524
+ "target_percentage": 70,
525
+ "categories": [
526
+ {"name": "المهندسون والمطورون", "target_percentage": 80},
527
+ {"name": "المحللون", "target_percentage": 90},
528
+ {"name": "الدعم الفني", "target_percentage": 100}
529
+ ]
530
+ }
531
+ ]
532
+ },
533
+ "general": {
534
+ "min_percentage": 20,
535
+ "sections": [
536
+ {
537
+ "name": "المواد والمنتجات",
538
+ "weight": 0.4
539
+ },
540
+ {
541
+ "name": "القوى العاملة",
542
+ "weight": 0.4,
543
+ "target_percentage": 60
544
+ },
545
+ {
546
+ "name": "المقاولون والموردون",
547
+ "weight": 0.2,
548
+ "target_percentage": 50
549
+ }
550
+ ]
551
+ }
552
+ }
553
+
554
+ def save_template(self, template_type: str, template_name: str, template_data: Dict[str, Any]) -> bool:
555
+ """
556
+ حفظ قالب جديد أو تحديث قالب موجود
557
+
558
+ المعاملات:
559
+ ----------
560
+ template_type : str
561
+ نوع القالب (tender, risk, local_content)
562
+ template_name : str
563
+ اسم القالب
564
+ template_data : Dict[str, Any]
565
+ بيانات القالب
566
+
567
+ المخرجات:
568
+ --------
569
+ bool
570
+ نجاح أو فشل العملية
571
+ """
572
+ try:
573
+ # التحقق من وجود المجلد الخاص بنوع القالب وإنشائه إذا لم يكن موجوداً
574
+ type_path = os.path.join(self.templates_path, template_type)
575
+ os.makedirs(type_path, exist_ok=True)
576
+
577
+ # حفظ القالب في ملف JSON
578
+ file_path = os.path.join(type_path, f"{template_name}.json")
579
+ with open(file_path, 'w', encoding='utf-8') as f:
580
+ json.dump(template_data, f, ensure_ascii=False, indent=4)
581
+
582
+ return True
583
+ except Exception as e:
584
+ print(f"Error saving template: {str(e)}")
585
+ return Falseإنشاء مبنى إداري من 4 طوابق بمساحة 2000 متر مربع في مدينة الرياض",
586
+ "vector": np.random.rand(384).tolist(), # تمثيل المتجه كقائمة
587
+ "metadata": {
588
+ "sector": "الإنشاءات",
589
+ "location": "الرياض",
590
+ "date": "2023-05-15"
591
+ }
592
+ },
593
+ "tender2": {
594
+ "text": "مناقصة ل