|
""" |
|
وحدة المساعد الذكي لنظام إدارة المناقصات - Hybrid Face |
|
""" |
|
|
|
import os |
|
import logging |
|
import threading |
|
import datetime |
|
import json |
|
import re |
|
from pathlib import Path |
|
|
|
|
|
logging.basicConfig( |
|
level=logging.INFO, |
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' |
|
) |
|
logger = logging.getLogger('ai_assistant') |
|
|
|
class AIAssistant: |
|
"""المساعد الذكي""" |
|
|
|
def __init__(self, config=None, db=None): |
|
"""تهيئة المساعد الذكي""" |
|
self.config = config |
|
self.db = db |
|
self.processing_in_progress = False |
|
self.current_query = None |
|
self.processing_results = {} |
|
self.conversation_history = [] |
|
|
|
|
|
self.ai_model = config.AI_MODEL if config and hasattr(config, 'AI_MODEL') else "gpt-4" |
|
self.ai_temperature = config.AI_TEMPERATURE if config and hasattr(config, 'AI_TEMPERATURE') else 0.7 |
|
self.ai_max_tokens = config.AI_MAX_TOKENS if config and hasattr(config, 'AI_MAX_TOKENS') else 2000 |
|
|
|
|
|
if config and hasattr(config, 'EXPORTS_PATH'): |
|
self.exports_path = Path(config.EXPORTS_PATH) |
|
else: |
|
self.exports_path = Path('data/exports') |
|
|
|
if not self.exports_path.exists(): |
|
self.exports_path.mkdir(parents=True, exist_ok=True) |
|
|
|
def process_query(self, query, context=None, callback=None): |
|
"""معالجة استعلام المستخدم""" |
|
if self.processing_in_progress: |
|
logger.warning("هناك عملية معالجة جارية بالفعل") |
|
return False |
|
|
|
self.processing_in_progress = True |
|
self.current_query = query |
|
self.processing_results = { |
|
"query": query, |
|
"context": context, |
|
"processing_start_time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), |
|
"status": "جاري المعالجة", |
|
"response": "", |
|
"suggestions": [], |
|
"references": [] |
|
} |
|
|
|
|
|
self.conversation_history.append({ |
|
"role": "user", |
|
"content": query, |
|
"timestamp": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
}) |
|
|
|
|
|
thread = threading.Thread( |
|
target=self._process_query_thread, |
|
args=(query, context, callback) |
|
) |
|
thread.daemon = True |
|
thread.start() |
|
|
|
return True |
|
|
|
def _process_query_thread(self, query, context, callback): |
|
"""خيط معالجة الاستعلام""" |
|
try: |
|
|
|
query_type = self._analyze_query(query) |
|
|
|
|
|
if query_type == "document_analysis": |
|
response = self._handle_document_analysis_query(query, context) |
|
elif query_type == "pricing": |
|
response = self._handle_pricing_query(query, context) |
|
elif query_type == "risk_analysis": |
|
response = self._handle_risk_analysis_query(query, context) |
|
elif query_type == "project_management": |
|
response = self._handle_project_management_query(query, context) |
|
elif query_type == "reporting": |
|
response = self._handle_reporting_query(query, context) |
|
else: |
|
response = self._handle_general_query(query, context) |
|
|
|
|
|
suggestions = self._generate_suggestions(query_type, query, response) |
|
|
|
|
|
self.processing_results["response"] = response |
|
self.processing_results["query_type"] = query_type |
|
self.processing_results["suggestions"] = suggestions |
|
self.processing_results["status"] = "اكتملت المعالجة" |
|
self.processing_results["processing_end_time"] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
|
|
|
|
self.conversation_history.append({ |
|
"role": "assistant", |
|
"content": response, |
|
"timestamp": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
}) |
|
|
|
logger.info(f"اكتملت معالجة الاستعلام: {query[:50]}...") |
|
|
|
except Exception as e: |
|
logger.error(f"خطأ في معالجة الاستعلام: {str(e)}") |
|
self.processing_results["status"] = "فشلت المعالجة" |
|
self.processing_results["error"] = str(e) |
|
|
|
|
|
self.conversation_history.append({ |
|
"role": "system", |
|
"content": f"حدث خطأ أثناء معالجة الاستعلام: {str(e)}", |
|
"timestamp": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
|
}) |
|
|
|
finally: |
|
self.processing_in_progress = False |
|
|
|
|
|
if callback and callable(callback): |
|
callback(self.processing_results) |
|
|
|
def _analyze_query(self, query): |
|
"""تحليل نوع الاستعلام""" |
|
query = query.lower() |
|
|
|
|
|
if any(keyword in query for keyword in ["تحليل المستند", "تحليل وثيقة", "استخراج بيانات", "قراءة مستند"]): |
|
return "document_analysis" |
|
elif any(keyword in query for keyword in ["تسعير", "سعر", "تكلفة", "ميزانية", "تقدير"]): |
|
return "pricing" |
|
elif any(keyword in query for keyword in ["مخاطر", "تحليل المخاطر", "تقييم المخاطر"]): |
|
return "risk_analysis" |
|
elif any(keyword in query for keyword in ["مشروع", "إدارة المشروع", "جدول زمني", "خطة"]): |
|
return "project_management" |
|
elif any(keyword in query for keyword in ["تقرير", "إحصائيات", "تحليل البيانات", "رسم بياني"]): |
|
return "reporting" |
|
else: |
|
return "general" |
|
|
|
def _handle_document_analysis_query(self, query, context): |
|
"""معالجة استعلام تحليل المستندات""" |
|
|
|
response = """ |
|
يمكنني مساعدتك في تحليل المستندات واستخراج المعلومات المهمة منها. لتحليل مستند، يرجى اتباع الخطوات التالية: |
|
|
|
1. انتقل إلى وحدة "تحليل المستندات" من القائمة الجانبية. |
|
2. انقر على زر "تحميل مستند" واختر المستند المراد تحليله. |
|
3. حدد نوع المستند (مناقصة، عقد، مواصفات فنية، إلخ). |
|
4. انقر على زر "تحليل" لبدء عملية التحليل. |
|
|
|
سيقوم النظام باستخراج المعلومات التالية من المستند: |
|
- البنود والكميات |
|
- الكيانات (العميل، الموقع، المقاول، إلخ) |
|
- التواريخ المهمة |
|
- المبالغ والتكاليف |
|
- المخاطر المحتملة |
|
|
|
بعد اكتمال التحليل، يمكنك مراجعة النتائج وتعديلها إذا لزم الأمر، ثم استخدامها في وحدات النظام الأخرى مثل التسعير وتحليل المخاطر. |
|
""" |
|
|
|
|
|
self.processing_results["references"] = [ |
|
{"title": "دليل استخدام وحدة تحليل المستندات", "type": "manual"}, |
|
{"title": "أنواع المستندات المدعومة", "type": "documentation"}, |
|
{"title": "تقنيات استخراج البيانات من المستندات", "type": "article"} |
|
] |
|
|
|
return response |
|
|
|
def _handle_pricing_query(self, query, context): |
|
"""معالجة استعلام التسعير""" |
|
|
|
response = """ |
|
يمكنني مساعدتك في تسعير المشاريع وتقدير التكاليف. لإنشاء تسعير لمشروع، يرجى اتباع الخطوات التالية: |
|
|
|
1. انتقل إلى وحدة "التسعير المتكامل" من القائمة الجانبية. |
|
2. اختر المشروع المراد تسعيره أو أنشئ مشروعاً جديداً. |
|
3. أدخل بنود المشروع والكميات التقديرية (يمكن استيرادها من نتائج تحليل المستندات). |
|
4. حدد الموارد المطلوبة (مواد، معدات، عمالة). |
|
5. اختر استراتيجية التسعير المناسبة: |
|
- شاملة: تغطية كاملة للتكاليف والمخاطر مع هامش ربح مناسب. |
|
- تنافسية: تخفيض الهوامش لتقديم سعر تنافسي. |
|
- متوازنة: توازن بين الربحية والتنافسية. |
|
6. انقر على زر "حساب التسعير" لإنشاء التسعير. |
|
|
|
سيقوم النظام بحساب: |
|
- التكاليف المباشرة (بنود المشروع) |
|
- التكاليف غير المباشرة (نفقات عامة، إدارية، ربح) |
|
- تكاليف المخاطر |
|
- ضريبة القيمة المضافة |
|
- السعر النهائي |
|
|
|
يمكنك تعديل المعلمات وإعادة حساب التسعير، ثم تصدير النتائج إلى تقرير مفصل. |
|
""" |
|
|
|
|
|
self.processing_results["references"] = [ |
|
{"title": "دليل استخدام وحدة التسعير المتكامل", "type": "manual"}, |
|
{"title": "استراتيجيات التسعير", "type": "documentation"}, |
|
{"title": "حساب التكاليف غير المباشرة", "type": "article"} |
|
] |
|
|
|
return response |
|
|
|
def _handle_risk_analysis_query(self, query, context): |
|
"""معالجة استعلام تحليل المخاطر""" |
|
|
|
response = """ |
|
يمكنني مساعدتك في تحليل وإدارة مخاطر المشروع. لإجراء تحليل للمخاطر، يرجى اتباع الخطوات التالية: |
|
|
|
1. انتقل إلى وحدة "تحليل المخاطر" من القائمة الجانبية. |
|
2. اختر المشروع المراد تحليل مخاطره. |
|
3. اختر طريقة التحليل: |
|
- شاملة: تحليل مفصل يغطي جميع جوانب المشروع. |
|
- أساسية: تحليل سريع للمخاطر الرئيسية. |
|
4. انقر على زر "تحليل المخاطر" لبدء التحليل. |
|
|
|
سيقوم النظام بما يلي: |
|
- تحديد المخاطر المحتملة بناءً على بيانات المشروع |
|
- تصنيف المخاطر إلى فئات (فني، مالي، إداري، إلخ) |
|
- إنشاء مصفوفة المخاطر (الاحتمالية × التأثير) |
|
- تطوير استراتيجيات التخفيف لكل مخاطرة |
|
- إنشاء ملخص للمخاطر وتوصيات |
|
|
|
يمكنك مراجعة نتائج التحليل وتعديلها، ثم تصدير التقرير النهائي واستخدامه في خطة إدارة المشروع. |
|
""" |
|
|
|
|
|
self.processing_results["references"] = [ |
|
{"title": "دليل استخدام وحدة تحليل المخاطر", "type": "manual"}, |
|
{"title": "منهجيات تحليل المخاطر", "type": "documentation"}, |
|
{"title": "استراتيجيات التخفيف من المخاطر", "type": "article"} |
|
] |
|
|
|
return response |
|
|
|
def _handle_project_management_query(self, query, context): |
|
"""معالجة استعلام إدارة المشاريع""" |
|
|
|
response = """ |
|
يمكنني مساعدتك في إدارة المشاريع وتتبع تقدمها. لإدارة مشروع، يرجى اتباع الخطوات التالية: |
|
|
|
1. انتقل إلى وحدة "إدارة المشاريع" من القائمة الجانبية. |
|
2. أنشئ مشروعاً جديداً أو اختر مشروعاً موجوداً. |
|
3. أدخل معلومات المشروع الأساسية (الاسم، العميل، الوصف، التواريخ). |
|
4. أضف بنود المشروع (يمكن استيرادها من نتائج تحليل المستندات). |
|
5. أنشئ الجدول الزمني للمشروع وحدد المراحل والمهام. |
|
6. عين الموارد للمهام وحدد التبعيات بينها. |
|
|
|
يمكنك استخدام وحدة إدارة المشاريع لـ: |
|
- تتبع تقدم المشروع ومقارنته بالخطة |
|
- إدارة الموارد وتوزيعها |
|
- متابعة المشكلات والمخاطر |
|
- إدارة التغييرات في نطاق العمل |
|
- إنشاء تقارير حالة المشروع |
|
|
|
كما يمكنك دمج نتائج التسعير وتحليل المخاطر في خطة المشروع لإدارة شاملة. |
|
""" |
|
|
|
|
|
self.processing_results["references"] = [ |
|
{"title": "دليل استخدام وحدة إدارة المشاريع", "type": "manual"}, |
|
{"title": "أفضل ممارسات إدارة المشاريع", "type": "documentation"}, |
|
{"title": "إنشاء جداول زمنية فعالة", "type": "article"} |
|
] |
|
|
|
return response |
|
|
|
def _handle_reporting_query(self, query, context): |
|
"""معالجة استعلام التقارير""" |
|
|
|
response = """ |
|
يمكنني مساعدتك في إنشاء تقارير وتحليلات للمشاريع والمناقصات. لإنشاء تقرير، يرجى اتباع الخطوات التالية: |
|
|
|
1. انتقل إلى وحدة "التقارير والتحليلات" من القائمة الجانبية. |
|
2. اختر نوع التقرير: |
|
- تقرير المشروع: معلومات شاملة عن مشروع محدد |
|
- تقرير التسعير: تفاصيل تسعير مشروع أو مناقصة |
|
- تقرير المخاطر: تحليل مخاطر المشروع واستراتيجيات التخفيف |
|
- تقرير الأداء: مقارنة الأداء الفعلي بالمخطط |
|
- تقرير مالي: تحليل مالي للمشاريع والمناقصات |
|
3. حدد معلمات التقرير (المشروع، الفترة الزمنية، إلخ). |
|
4. انقر على زر "إنشاء التقرير". |
|
|
|
يمكنك تخصيص التقارير بإضافة أو إزالة أقسام، وتغيير طريقة عرض البيانات (جداول، رسوم بيانية، إلخ). |
|
|
|
التقارير المنشأة يمكن: |
|
- تصديرها بتنسيقات مختلفة (PDF، Excel، Word) |
|
- مشاركتها مع أعضاء الفريق أو العملاء |
|
- جدولتها للإنشاء التلقائي بشكل دوري |
|
- حفظها كقوالب لاستخدامها في المستقبل |
|
""" |
|
|
|
|
|
self.processing_results["references"] = [ |
|
{"title": "دليل استخدام وحدة التقارير والتحليلات", "type": "manual"}, |
|
{"title": "أنواع التقارير المتاحة", "type": "documentation"}, |
|
{"title": "إنشاء رسوم بيانية فعالة", "type": "article"} |
|
] |
|
|
|
return response |
|
|
|
def _handle_general_query(self, query, context): |
|
"""معالجة استعلام عام""" |
|
|
|
response = """ |
|
مرحباً بك في المساعد الذكي لنظام إدارة المناقصات Hybrid Face. يمكنني مساعدتك في مجموعة متنوعة من المهام المتعلقة بإدارة المناقصات والمشاريع. |
|
|
|
يمكنني مساعدتك في: |
|
- تحليل مستندات المناقصات واستخراج المعلومات المهمة منها |
|
- تسعير المشاريع وتقدير التكاليف |
|
- تحليل وإدارة مخاطر المشاريع |
|
- إدارة المشاريع وتتبع تقدمها |
|
- إنشاء تقارير وتحليلات |
|
|
|
للحصول على مساعدة محددة، يرجى طرح سؤال يتعلق بإحدى هذه المجالات. على سبيل المثال: |
|
- "كيف يمكنني تحليل مستند مناقصة؟" |
|
- "ساعدني في تسعير مشروع جديد" |
|
- "كيف أقوم بتحليل مخاطر المشروع؟" |
|
- "أريد إنشاء تقرير عن حالة المشروع" |
|
|
|
يمكنك أيضاً استخدام الوحدات المختلفة في النظام مباشرة من القائمة الجانبية. |
|
""" |
|
|
|
|
|
self.processing_results["references"] = [ |
|
{"title": "دليل المستخدم الشامل", "type": "manual"}, |
|
{"title": "نظرة عامة على النظام", "type": "documentation"}, |
|
{"title": "الأسئلة الشائعة", "type": "faq"} |
|
] |
|
|
|
return response |
|
|
|
def _generate_suggestions(self, query_type, query, response): |
|
"""توليد اقتراحات للمستخدم بناءً على الاستعلام والاستجابة""" |
|
suggestions = [] |
|
|
|
if query_type == "document_analysis": |
|
suggestions = [ |
|
"كيف يمكنني استيراد نتائج تحليل المستندات إلى وحدة التسعير؟", |
|
"ما هي أنواع المستندات المدعومة للتحليل؟", |
|
"كيف يمكنني تحسين دقة تحليل المستندات؟" |
|
] |
|
elif query_type == "pricing": |
|
suggestions = [ |
|
"ما هي استراتيجية التسعير المناسبة لمشروعي؟", |
|
"كيف يمكنني حساب التكاليف غير المباشرة؟", |
|
"كيف أضيف تكاليف المخاطر إلى التسعير؟" |
|
] |
|
elif query_type == "risk_analysis": |
|
suggestions = [ |
|
"ما هي أفضل استراتيجيات التخفيف من المخاطر؟", |
|
"كيف يمكنني تحديد المخاطر الحرجة في المشروع؟", |
|
"كيف أدمج تحليل المخاطر في خطة المشروع؟" |
|
] |
|
elif query_type == "project_management": |
|
suggestions = [ |
|
"كيف أنشئ جدولاً زمنياً فعالاً للمشروع؟", |
|
"كيف أتتبع تقدم المشروع مقارنة بالخطة؟", |
|
"كيف أدير التغييرات في نطاق المشروع؟" |
|
] |
|
elif query_type == "reporting": |
|
suggestions = [ |
|
"ما هي أنواع التقارير المتاحة في النظام؟", |
|
"كيف يمكنني تخصيص تقرير المشروع؟", |
|
"كيف أقوم بجدولة إنشاء تقارير دورية؟" |
|
] |
|
else: |
|
suggestions = [ |
|
"كيف يمكنني البدء باستخدام النظام؟", |
|
"ما هي الوحدات المتاحة في النظام؟", |
|
"كيف يمكنني إنشاء مشروع جديد؟" |
|
] |
|
|
|
return suggestions |
|
|
|
def get_processing_status(self): |
|
"""الحصول على حالة المعالجة الحالية""" |
|
if not self.processing_in_progress: |
|
if not self.processing_results: |
|
return {"status": "لا توجد معالجة جارية"} |
|
else: |
|
return {"status": self.processing_results.get("status", "غير معروف")} |
|
|
|
return { |
|
"status": "جاري المعالجة", |
|
"query": self.current_query, |
|
"start_time": self.processing_results.get("processing_start_time") |
|
} |
|
|
|
def get_processing_results(self): |
|
"""الحصول على نتائج المعالجة""" |
|
return self.processing_results |
|
|
|
def get_conversation_history(self, limit=10): |
|
"""الحصول على سجل المحادثة""" |
|
if limit and limit > 0: |
|
return self.conversation_history[-limit:] |
|
return self.conversation_history |
|
|
|
def clear_conversation_history(self): |
|
"""مسح سجل المحادثة""" |
|
self.conversation_history = [] |
|
return True |
|
|
|
def export_conversation_history(self, output_path=None): |
|
"""تصدير سجل المحادثة إلى ملف JSON""" |
|
if not self.conversation_history: |
|
logger.warning("لا يوجد سجل محادثة للتصدير") |
|
return None |
|
|
|
if not output_path: |
|
|
|
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') |
|
filename = f"conversation_history_{timestamp}.json" |
|
output_path = os.path.join(self.exports_path, filename) |
|
|
|
try: |
|
with open(output_path, 'w', encoding='utf-8') as f: |
|
json.dump(self.conversation_history, f, ensure_ascii=False, indent=4) |
|
|
|
logger.info(f"تم تصدير سجل المحادثة إلى: {output_path}") |
|
return output_path |
|
|
|
except Exception as e: |
|
logger.error(f"خطأ في تصدير سجل المحادثة: {str(e)}") |
|
return None |
|
|