EGYADMIN commited on
Commit
caffd57
·
verified ·
1 Parent(s): e549e9f

Create utils/helpers.py

Browse files
Files changed (1) hide show
  1. utils/helpers.py +326 -0
utils/helpers.py ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import json
4
+ import pandas as pd
5
+ from typing import Dict, List, Any, Union, Tuple, Optional
6
+ from datetime import datetime
7
+
8
+ def format_currency(amount: float, currency: str = "ريال") -> str:
9
+ """
10
+ تنسيق المبالغ المالية
11
+
12
+ المعاملات:
13
+ ----------
14
+ amount : float
15
+ المبلغ
16
+ currency : str, optional
17
+ العملة (افتراضي: "ريال")
18
+
19
+ المخرجات:
20
+ --------
21
+ str
22
+ المبلغ بالتنسيق المناسب
23
+ """
24
+ if not amount and amount != 0:
25
+ return "غير محدد"
26
+
27
+ # تنسيق الأرقام بالفواصل الآلاف
28
+ formatted = f"{amount:,.2f}".replace(".00", "")
29
+
30
+ # ترتيب العملة حسب اللغة العربية (يمين)
31
+ return f"{formatted} {currency}"
32
+
33
+ def format_percentage(value: float) -> str:
34
+ """
35
+ تنسيق النسب المئوية
36
+
37
+ المعاملات:
38
+ ----------
39
+ value : float
40
+ القيمة
41
+
42
+ المخرجات:
43
+ --------
44
+ str
45
+ النسبة بالتنسيق المناسب
46
+ """
47
+ if not value and value != 0:
48
+ return "غير محدد"
49
+
50
+ return f"{value:.2f}%".replace(".00", "")
51
+
52
+ def format_date(date_obj: Any) -> str:
53
+ """
54
+ تنسيق التواريخ
55
+
56
+ المعاملات:
57
+ ----------
58
+ date_obj : Any
59
+ كائن التاريخ
60
+
61
+ المخرجات:
62
+ --------
63
+ str
64
+ التاريخ بالتنسيق المناسب
65
+ """
66
+ if not date_obj:
67
+ return "غير محدد"
68
+
69
+ # إذا كان التاريخ سلسلة نصية
70
+ if isinstance(date_obj, str):
71
+ try:
72
+ date_obj = datetime.strptime(date_obj, "%Y-%m-%d")
73
+ except ValueError:
74
+ try:
75
+ date_obj = datetime.strptime(date_obj, "%Y-%m-%d %H:%M:%S")
76
+ except ValueError:
77
+ return date_obj
78
+
79
+ # تنسيق التاريخ بالطريقة العربية
80
+ month_names = [
81
+ "يناير", "فبراير", "مارس", "إبريل", "مايو", "يونيو",
82
+ "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"
83
+ ]
84
+
85
+ return f"{date_obj.day} {month_names[date_obj.month - 1]} {date_obj.year}"
86
+
87
+ def extract_keywords(text: str, keywords: List[str], context_size: int = 50) -> List[Dict[str, str]]:
88
+ """
89
+ استخراج الكلمات المفتاحية من النص مع سياقها
90
+
91
+ المعاملات:
92
+ ----------
93
+ text : str
94
+ النص المراد البحث فيه
95
+ keywords : List[str]
96
+ قائمة الكلمات المفتاحية
97
+ context_size : int, optional
98
+ حجم السياق بالأحرف قبل وبعد الكلمة (افتراضي: 50)
99
+
100
+ المخرجات:
101
+ --------
102
+ List[Dict[str, str]]
103
+ قائمة بالكلمات المفتاحية وسياقها
104
+ """
105
+ results = []
106
+
107
+ for keyword in keywords:
108
+ # البحث عن الكلمة المفتاحية في النص
109
+ pattern = re.compile(r'\\b' + re.escape(keyword) + r'\\b', re.IGNORECASE | re.MULTILINE)
110
+ matches = pattern.finditer(text)
111
+
112
+ for match in matches:
113
+ start_idx = max(0, match.start() - context_size)
114
+ end_idx = min(len(text), match.end() + context_size)
115
+
116
+ # استخراج السياق
117
+ context = text[start_idx:end_idx]
118
+
119
+ # إضافة النتيجة
120
+ results.append({
121
+ "keyword": keyword,
122
+ "context": context,
123
+ "position": match.start()
124
+ })
125
+
126
+ # ترتيب النتائج حسب الموقع في النص
127
+ results = sorted(results, key=lambda x: x["position"])
128
+
129
+ return results
130
+
131
+ def calculate_progress(current: float, total: float) -> Dict[str, Any]:
132
+ """
133
+ حساب نسبة الإنجاز
134
+
135
+ المعاملات:
136
+ ----------
137
+ current : float
138
+ القيمة الحالية
139
+ total : float
140
+ القيمة الإجمالية
141
+
142
+ المخرجات:
143
+ --------
144
+ Dict[str, Any]
145
+ معلومات التقدم
146
+ """
147
+ if total == 0:
148
+ return {
149
+ "percentage": 0,
150
+ "status": "لم يبدأ",
151
+ "color": "gray"
152
+ }
153
+
154
+ percentage = min(100, (current / total) * 100)
155
+
156
+ if percentage == 0:
157
+ status = "لم يبدأ"
158
+ color = "gray"
159
+ elif percentage < 25:
160
+ status = "بداية"
161
+ color = "red"
162
+ elif percentage < 50:
163
+ status = "قيد التنفيذ"
164
+ color = "orange"
165
+ elif percentage < 75:
166
+ status = "متقدم"
167
+ color = "blue"
168
+ elif percentage < 100:
169
+ status = "شبه مكتمل"
170
+ color = "teal"
171
+ else:
172
+ status = "مكتمل"
173
+ color = "green"
174
+
175
+ return {
176
+ "percentage": round(percentage, 1),
177
+ "status": status,
178
+ "color": color
179
+ }
180
+
181
+ def create_directory_structure(base_dir: str, structure: Dict[str, Any]) -> None:
182
+ """
183
+ إنشاء هيكل المجلدات
184
+
185
+ المعاملات:
186
+ ----------
187
+ base_dir : str
188
+ المجلد الأساسي
189
+ structure : Dict[str, Any]
190
+ هيكل المجلدات
191
+ """
192
+ os.makedirs(base_dir, exist_ok=True)
193
+
194
+ for key, value in structure.items():
195
+ path = os.path.join(base_dir, key)
196
+
197
+ if isinstance(value, dict):
198
+ # إذا كانت القيمة قاموساً، استمر في إنشاء الهيكل الفرعي
199
+ create_directory_structure(path, value)
200
+ else:
201
+ # إنشاء المجلد
202
+ os.makedirs(path, exist_ok=True)
203
+
204
+ # إذا كانت القيمة قائمة، إنشاء ملفات فارغة
205
+ if isinstance(value, list):
206
+ for file_name in value:
207
+ file_path = os.path.join(path, file_name)
208
+ if not os.path.exists(file_path):
209
+ with open(file_path, 'w', encoding='utf-8') as f:
210
+ # يمكن كتابة محتوى افتراضي هنا
211
+ pass
212
+
213
+ def export_to_excel(data: List[Dict[str, Any]], file_path: str, sheet_name: str = "Sheet1") -> bool:
214
+ """
215
+ تصدير البيانات إلى ملف Excel
216
+
217
+ المعاملات:
218
+ ----------
219
+ data : List[Dict[str, Any]]
220
+ البيانات المراد تصديرها
221
+ file_path : str
222
+ مسار الملف
223
+ sheet_name : str, optional
224
+ اسم ورقة العمل (افتراضي: "Sheet1")
225
+
226
+ المخرجات:
227
+ --------
228
+ bool
229
+ نجاح أو فشل العملية
230
+ """
231
+ try:
232
+ # تحويل البيانات إلى DataFrame
233
+ df = pd.DataFrame(data)
234
+
235
+ # تصدير إلى Excel
236
+ df.to_excel(file_path, sheet_name=sheet_name, index=False)
237
+
238
+ return True
239
+ except Exception as e:
240
+ print(f"Error exporting to Excel: {str(e)}")
241
+ return False
242
+
243
+ def export_to_json(data: Any, file_path: str) -> bool:
244
+ """
245
+ تصدير البيانات إلى ملف JSON
246
+
247
+ المعاملات:
248
+ ----------
249
+ data : Any
250
+ البيانات المراد تصديرها
251
+ file_path : str
252
+ مسار الملف
253
+
254
+ المخرجات:
255
+ --------
256
+ bool
257
+ نجاح أو فشل العملية
258
+ """
259
+ try:
260
+ # إنشاء المجلد إذا لم يكن موجوداً
261
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
262
+
263
+ # تصدير إلى JSON
264
+ with open(file_path, 'w', encoding='utf-8') as f:
265
+ json.dump(data, f, ensure_ascii=False, indent=4)
266
+
267
+ return True
268
+ except Exception as e:
269
+ print(f"Error exporting to JSON: {str(e)}")
270
+ return False
271
+
272
+ def validate_input(input_data: Dict[str, Any], validation_rules: Dict[str, Dict[str, Any]]) -> Dict[str, List[str]]:
273
+ """
274
+ التحقق من صحة البيانات المدخلة
275
+
276
+ المعاملات:
277
+ ----------
278
+ input_data : Dict[str, Any]
279
+ البيانات المدخلة
280
+ validation_rules : Dict[str, Dict[str, Any]]
281
+ قواعد التحقق
282
+
283
+ المخرجات:
284
+ --------
285
+ Dict[str, List[str]]
286
+ قائمة بالأخطاء لكل حقل
287
+ """
288
+ errors = {}
289
+
290
+ for field, rules in validation_rules.items():
291
+ field_errors = []
292
+ value = input_data.get(field)
293
+
294
+ # التحقق من الحقول المطلوبة
295
+ if rules.get("required", False) and (value is None or (isinstance(value, str) and value.strip() == "")):
296
+ field_errors.append("هذا الحقل مطلوب")
297
+
298
+ # التحقق من النوع
299
+ if value is not None and "type" in rules:
300
+ expected_type = rules["type"]
301
+ if expected_type == "number" and not (isinstance(value, (int, float)) or (isinstance(value, str) and value.strip().replace(".", "", 1).isdigit())):
302
+ field_errors.append("يجب أن يكون هذا الحقل رقماً")
303
+ elif expected_type == "email" and not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', str(value)):
304
+ field_errors.append("يرجى إدخال بريد إلكتروني صحيح")
305
+ elif expected_type == "date" and not re.match(r'^\d{4}-\d{2}-\d{2}$', str(value)):
306
+ field_errors.append("يرجى إدخال تاريخ صحيح (YYYY-MM-DD)")
307
+
308
+ # التحقق من الحد الأدنى والأقصى
309
+ if value is not None and isinstance(value, (int, float)):
310
+ if "min" in rules and value < rules["min"]:
311
+ field_errors.append(f"يجب أن يكون هذا الحقل أكبر من أو يساوي {rules['min']}")
312
+ if "max" in rules and value > rules["max"]:
313
+ field_errors.append(f"يجب أن يكون هذا الحقل أصغر من أو يساوي {rules['max']}")
314
+
315
+ # التحقق من طول النص
316
+ if value is not None and isinstance(value, str):
317
+ if "min_length" in rules and len(value) < rules["min_length"]:
318
+ field_errors.append(f"يجب أن يحتوي هذا الحقل على {rules['min_length']} أحرف على الأقل")
319
+ if "max_length" in rules and len(value) > rules["max_length"]:
320
+ field_errors.append(f"يجب أن يحتوي هذا الحقل على {rules['max_length']} أحرف كحد أقصى")
321
+
322
+ # إضافة الأخطاء إذا وجدت
323
+ if field_errors:
324
+ errors[field] = field_errors
325
+
326
+ return errors