diff --git "a/modules/ai_assistant/ai_app.py" "b/modules/ai_assistant/ai_app.py" --- "a/modules/ai_assistant/ai_app.py" +++ "b/modules/ai_assistant/ai_app.py" @@ -1,28 +1,156 @@ -# -*- coding: utf-8 -*- -""" -وحدة المساعد الذكي - -هذا الملف يحتوي على الفئة الرئيسية لتطبيق المساعد الذكي مع دعم نموذج Claude AI. -""" - -import streamlit as st -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -import plotly.express as px -import requests +import os +import sys +import logging +import base64 import json import time -import base64 -import logging -import os -from datetime import datetime, timedelta +from datetime import datetime import io import tempfile -import random -from io import BytesIO -from tempfile import NamedTemporaryFile +from pathlib import Path +import streamlit as st +import requests from PIL import Image +import pandas as pd +import numpy as np + +# استيراد وحدة التحقق من الصور +from image_validator import validate_image + +# محاولة استيراد المحللات المختلفة +try: + from document_analyzer import TextExtractor, ItemExtractor + DOCUMENT_ANALYZER_AVAILABLE = True +except ImportError: + DOCUMENT_ANALYZER_AVAILABLE = False + +try: + from contract_analyzer import ContractAnalyzer + CONTRACT_ANALYZER_AVAILABLE = True +except ImportError: + CONTRACT_ANALYZER_AVAILABLE = False + +try: + from bim_analyzer import BIMAnalyzer + BIM_ANALYZER_AVAILABLE = True +except ImportError: + BIM_ANALYZER_AVAILABLE = False + +try: + from cad_bim_analyzer import CADBIMAnalyzer + CAD_BIM_ANALYZER_AVAILABLE = True +except ImportError: + CAD_BIM_ANALYZER_AVAILABLE = False + +try: + from engineering_drawing_analyzer import EngineeringDrawingAnalyzer + ENGINEERING_DRAWING_ANALYZER_AVAILABLE = True +except ImportError: + ENGINEERING_DRAWING_ANALYZER_AVAILABLE = False + +try: + from data_integration import DataAIIntegration + DATA_INTEGRATION_AVAILABLE = True +except ImportError: + DATA_INTEGRATION_AVAILABLE = False + +# تكوين التسجيل +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +class AIAssistantApp: + """ + تطبيق المساعد الذكي المتكامل + """ + def __init__(self): + """تهيئة تطبيق المساعد الذكي""" + self.claude_service = ClaudeAIService() + self.initialize_analyzers() + self.setup_session_state() + + def initialize_analyzers(self): + """تهيئة المحللات المختلفة""" + # تهيئة محلل المستندات + if DOCUMENT_ANALYZER_AVAILABLE: + self.text_extractor = TextExtractor() + self.item_extractor = ItemExtractor() + + # تهيئة محلل العقود + if CONTRACT_ANALYZER_AVAILABLE: + self.contract_analyzer = ContractAnalyzer(api_key_source="security_section") + + # تهيئة محلل BIM + if BIM_ANALYZER_AVAILABLE: + self.bim_analyzer = BIMAnalyzer() + + # تهيئة محلل CAD/BIM + if CAD_BIM_ANALYZER_AVAILABLE: + self.cad_bim_analyzer = CADBIMAnalyzer(claude_client=self.claude_service) + + # تهيئة محلل الرسومات الهندسية + if ENGINEERING_DRAWING_ANALYZER_AVAILABLE: + self.engineering_drawing_analyzer = EngineeringDrawingAnalyzer(claude_client=self.claude_service) + + # تهيئة وحدة تكامل البيانات + if DATA_INTEGRATION_AVAILABLE: + self.data_integration = DataAIIntegration() + + def setup_session_state(self): + """إعداد حالة الجلسة""" + if 'messages' not in st.session_state: + st.session_state.messages = [] + + if 'selected_model' not in st.session_state: + st.session_state.selected_model = "claude-3-7-sonnet" + + if 'analysis_results' not in st.session_state: + st.session_state.analysis_results = {} + + if 'uploaded_files' not in st.session_state: + st.session_state.uploaded_files = {} + + def render(self): + """عرض واجهة المستخدم الرئيسية""" + st.title("وحبي - المساعد الذكي للمناقصات والعقود") + + # عرض التبويبات + tabs = st.tabs([ + "المساعد الذكي", + "تحليل المستندات", + "تحليل العقود", + "تحليل المخاطر", + "تحليل التكاليف", + "تحليل المحتوى المحلي", + "الأسئلة الشائعة" + ]) + + # تبويب المساعد الذكي + with tabs[0]: + self._render_ai_assistant_tab() + + # تبويب تحليل المستندات + with tabs[1]: + self._render_document_analysis_tab() + + # تبويب تحليل العقود + with tabs[2]: + self._render_contract_analysis_tab() + + # تبويب تحليل المخاطر + with tabs[3]: + self._render_risk_analysis_tab() + + # تبويب تحليل التكاليف + with tabs[4]: + self._render_cost_prediction_tab() + + # تبويب تحليل المحتوى المحلي + with tabs[5]: + self._render_local_content_tab() + + # تبويب الأسئلة الشائعة + with tabs[6]: + self._render_faq_tab() class ClaudeAIService: """ @@ -32,11 +160,38 @@ class ClaudeAIService: """تهيئة خدمة Claude AI""" self.api_url = "https://api.anthropic.com/v1/messages" + def _get_huggingface_secret(self, secret_name): + """ + الحصول على قيمة السر من متغيرات البيئة في Hugging Face + + المعلمات: + secret_name: اسم السر + + العوائد: + str: قيمة السر + """ + # في بيئة Hugging Face، الأسرار متاحة كمتغيرات بيئة بتنسيق HF_SECRET_ + env_var_name = f"HF_SECRET_{secret_name.upper()}" + secret_value = os.environ.get(env_var_name) + + # إذا لم يتم العثور على السر في تنسيق Hugging Face، نحاول البحث عنه مباشرة + if not secret_value: + secret_value = os.environ.get(secret_name) + + return secret_value + def get_api_key(self): """الحصول على مفتاح API من متغيرات البيئة""" - api_key = os.environ.get("anthropic") + # محاولة الحصول على المفتاح من أسرار Hugging Face أولاً + api_key = self._get_huggingface_secret("anthropic") + + if not api_key: + # محاولة الحصول على المفتاح من متغيرات البيئة العادية + api_key = os.environ.get("anthropic") + if not api_key: raise ValueError("مفتاح API لـ Claude غير موجود في متغيرات البيئة") + return api_key def get_available_models(self): @@ -48,7 +203,8 @@ class ClaudeAIService: """ return { "claude-3-7-sonnet": "Claude 3.7 Sonnet - نموذج ذكي للمهام المتقدمة", - "claude-3-5-haiku": "Claude 3.5 Haiku - أسرع نموذج للمهام اليومية" + "claude-3-5-haiku": "Claude 3.5 Haiku - أسرع نموذج للمهام اليومية", + "claude-3-opus": "Claude 3 Opus - أقوى نموذج للمهام المعقدة" } def get_model_full_name(self, short_name): @@ -63,7 +219,8 @@ class ClaudeAIService: """ valid_models = { "claude-3-7-sonnet": "claude-3-7-sonnet-20250219", - "claude-3-5-haiku": "claude-3-5-haiku-20240307" + "claude-3-5-haiku": "claude-3-5-haiku-20240307", + "claude-3-opus": "claude-3-opus-20240229" } return valid_models.get(short_name, short_name) @@ -84,27 +241,11 @@ class ClaudeAIService: # الحصول على مفتاح API api_key = self.get_api_key() - # قراءة محتوى الصورة - with open(image_path, 'rb') as f: - file_content = f.read() + # التحقق من صحة الصورة وتحسينها + file_base64, file_type, is_valid, error_message = validate_image(image_path) - # تحويل المحتوى إلى Base64 - file_base64 = base64.b64encode(file_content).decode('utf-8') - - # تحديد نوع الملف من امتداده - _, ext = os.path.splitext(image_path) - ext = ext.lower() - - if ext in ('.jpg', '.jpeg'): - file_type = "image/jpeg" - elif ext == '.png': - file_type = "image/png" - elif ext == '.gif': - file_type = "image/gif" - elif ext == '.webp': - file_type = "image/webp" - else: - file_type = "image/jpeg" # افتراضي + if not is_valid: + return {"error": f"عذراً، حدث خطأ أثناء تحليل الملف: {error_message}"} # التحقق من اسم النموذج وتصحيحه إذا لزم الأمر model_name = self.get_model_full_name(model_name) @@ -207,9 +348,8 @@ class ClaudeAIService: payload = { "model": model_name, - "max_tokens": 2048, - "messages": claude_messages, - "temperature": 0.7 + "max_tokens": 4096, + "messages": claude_messages } # إرسال الطلب إلى API @@ -217,7 +357,7 @@ class ClaudeAIService: self.api_url, headers=headers, json=payload, - timeout=30 + timeout=60 ) # التحقق من نجاح الطلب @@ -243,3040 +383,928 @@ class ClaudeAIService: except Exception as e: logging.error(f"خطأ أثناء إكمال المحادثة: {str(e)}") - import traceback - stack_trace = traceback.format_exc() - return {"error": f"فشل في إكمال المحادثة: {str(e)}\n{stack_trace}"} - - -class AIAssistantApp: - """وحدة المساعد الذكي""" + return {"error": f"فشل في إكمال المحادثة: {str(e)}"} - def __init__(self): - """تهيئة وحدة المساعد الذكي""" - # التحقق من توفر مكتبة pdf2image + def analyze_document(self, file_path, analysis_type="comprehensive", model_name="claude-3-7-sonnet"): + """ + تحليل مستند باستخدام نموذج Claude AI + + المعلمات: + file_path: مسار المستند المراد تحليله + analysis_type: نوع التحليل المطلوب + model_name: اسم نموذج Claude المراد استخدامه + + العوائد: + dict: نتائج التحليل + """ try: - from pdf2image import convert_from_path - pdf_conversion_available = True - self.pdf_conversion_available = True - self.convert_from_path = convert_from_path - except ImportError: - pdf_conversion_available = False - self.pdf_conversion_available = False + # التحقق من نوع الملف + _, ext = os.path.splitext(file_path) + ext = ext.lower() - # تحميل النماذج عند بدء التشغيل - self.cost_model = self._load_cost_prediction_model() - self.document_model = self._load_document_classifier_model() - self.risk_model = self._load_risk_assessment_model() - self.local_content_model = self._load_local_content_model() - self.entity_model = self._load_entity_recognition_model() - - # إنشاء خدمة Claude AI - self.claude_service = ClaudeAIService() - - # تهيئة قائمة الأسئلة والإجابات الشائعة - self.faqs = [ - { - "question": "كيف يمكنني إضافة مشروع جديد؟", - "answer": "يمكنك إضافة مشروع جديد من خلال الانتقال إلى وحدة إدارة المشاريع، ثم النقر على زر 'إضافة مشروع جديد'، وملء النموذج بالبيانات المطلوبة." - }, - { - "question": "ما هي خطوات تسعير المناقصة؟", - "answer": "تتضمن خطوات تسعير المناقصة: 1) تحليل مستندات المناقصة، 2) تحديد بنود العمل، 3) تقدير التكاليف المباشرة، 4) إضافة المصاريف العامة والأرباح، 5) احتساب المحتوى المحلي، 6) مراجعة النتائج النهائية." - }, - { - "question": "كيف يتم حساب المحتوى المحلي؟", - "answer": "يتم حساب المحتوى المحلي بتحديد نسبة المنتجات والخدمات والقوى العاملة المحلية من إجمالي التكاليف. يتم استخدام قاعدة بيانات الموردين المعتمدين وتطبيق معادلات خاصة حسب متطلبات هيئة المحتوى المحلي." - }, - { - "question": "كيف يمكنني تصدير التقارير؟", - "answer": "يمكنك تصدير التقارير من وحدة التقارير والتحليلات، حيث يوجد زر 'تصدير' في كل تقرير. يمكن تصدير التقارير بتنسيقات مختلفة مثل Excel و PDF و CSV." - }, - { - "question": "كيف يمكنني تقييم المخاطر للمشروع؟", - "answer": "يمكنك تقييم المخاطر للمشروع من خلال وحدة المخاطر، حيث يمكنك إضافة المخاطر المحتملة وتقييم تأثيرها واحتماليتها، ثم وضع خطة الاستجابة المناسبة." - } - ] - - # تهيئة قائمة الأسئلة والإجابات الشائعة - self.faqs = [ - { - "question": "كيف يمكنني إضافة مشروع جديد؟", - "answer": "يمكنك إضافة مشر��ع جديد من خلال الانتقال إلى وحدة إدارة المشاريع، ثم النقر على زر 'إضافة مشروع جديد'، وملء النموذج بالبيانات المطلوبة." - }, - { - "question": "ما هي خطوات تسعير المناقصة؟", - "answer": "تتضمن خطوات تسعير المناقصة: 1) تحليل مستندات المناقصة، 2) تحديد بنود العمل، 3) تقدير التكاليف المباشرة، 4) إضافة المصاريف العامة والأرباح، 5) احتساب المحتوى المحلي، 6) مراجعة النتائج النهائية." - }, - { - "question": "كيف يتم حساب المحتوى المحلي؟", - "answer": "يتم حساب المحتوى المحلي بتحديد نسبة المنتجات والخدمات والقوى العاملة المحلية من إجمالي التكاليف. يتم استخدام قاعدة بيانات الموردين المعتمدين وتطبيق معادلات خاصة حسب متطلبات هيئة المحتوى المحلي." - }, - { - "question": "كيف يمكنني تصدير التقارير؟", - "answer": "يمكنك تصدير التقارير من وحدة التقارير والتحليلات، حيث يوجد زر 'تصدير' في كل تقرير. يمكن تصدير التقارير بتنسيقات مختلفة مثل Excel و PDF و CSV." - }, - { - "question": "كيف يمكنني تقييم المخاطر للمشروع؟", - "answer": "يمكنك تقييم المخاطر للمشروع من خلال وحدة المخاطر، حيث يمكنك إضافة المخاطر المحتملة وتقييم تأثيرها واحتماليتها، ثم وضع خطة الاستجابة المناسبة." - } - ] - - def _load_cost_prediction_model(self): - """تحميل نموذج التنبؤ بالتكاليف""" - # في البيئة الإنتاجية، سيتم تحميل نموذج حقيقي - # هنا نقوم بإنشاء كائن محاكاة للنموذج - return {"name": "cost_prediction_model", "version": "1.0.0", "status": "loaded"} - - def _load_document_classifier_model(self): - """تحميل نموذج تصنيف المستندات""" - return {"name": "document_classifier_model", "version": "1.0.0", "status": "loaded"} - - def _load_risk_assessment_model(self): - """تحميل نموذج تقييم المخاطر""" - return {"name": "risk_assessment_model", "version": "1.0.0", "status": "loaded"} - - def _load_local_content_model(self): - """تحميل نموذج تحليل المحتوى المحلي""" - return {"name": "local_content_model", "version": "1.0.0", "status": "loaded"} - - def _load_entity_recognition_model(self): - """تحميل نموذج التعرف على الكيانات""" - return {"name": "entity_recognition_model", "version": "1.0.0", "status": "loaded"} - - # تهيئة قائمة الأسئلة والإجابات الشائعة - self.faqs = [ - { - "question": "كيف يمكنني إضافة مشروع جديد؟", - "answer": "يمكنك إضافة مشروع جديد من خلال الانتقال إلى وحدة إدارة المشاريع، ثم النقر على زر 'إضافة مشروع جديد'، وملء النموذج بالبيانات المطلوبة." - }, - { - "question": "ما هي خطوات تسعير المناقصة؟", - "answer": "تتضمن خطوات تسعير المناقصة: 1) تحليل مستندات المناقصة، 2) تحديد بنود العمل، 3) تقدير التكاليف المباشرة، 4) إضافة المصاريف العامة والأرباح، 5) احتساب المحتوى المحلي، 6) مراجعة النتائج النهائية." - }, - { - "question": "كيف يتم حساب المحتوى المحلي؟", - "answer": "يتم حساب المحتوى المحلي بتحديد نسبة المنتجات والخدمات والقوى العاملة المحلية من إجمالي التكاليف. يتم استخدام قاعدة بيانات الموردين المعتمدين وتطبيق معادلات خاصة حسب متطلبات هيئة المحتوى المحلي." - }, - { - "question": "كيف يمكنني تصدير التقارير؟", - "answer": "يمكنك تصدير التقارير من وحدة التقارير والتحليلات، حيث يوجد زر 'تصدير' في كل تقرير. يمكن تصدير التقارير بتنسيقات مختلفة مثل Excel و PDF و CSV." - }, - { - "question": "كيف يمكنني تقييم المخاطر للمشروع؟", - "answer": "يمكنك تقييم المخاطر للمشروع من خلال وحدة المخاطر، حيث يمكنك إضافة المخاطر المحتملة وتقييم تأثيرها واحتماليتها، ثم وضع خطة الاستجابة المناسبة." - }, - { - "question": "ما هي طرق التسعير المتاحة في النظام؟", - "answer": "يوفر النظام أربع طرق للتسعير: 1) التسعير القياسي، 2) التسعير غير المتزن، 3) التسعير التنافسي، 4) التسعير الموجه بالربحية. يمكنك اختيار الطريقة المناسبة حسب طبيعة المشروع واستراتيجية الشركة." - }, - { - "question": "كيف يمكنني معالجة مستندات المناقصة ضخمة الحجم؟", - "answer": "يمكنك استخدام وحدة تحليل المستندات لمعالجة مستندات المناقصة ضخمة الحجم، حيث تقوم الوحدة بتحليل المستندات واستخراج المعلومات المهمة مثل مواصفات المشروع ومتطلباته وشروطه تلقائياً." - } - ] + # تحديد نوع التحليل المناسب + if ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']: + # تحليل الصورة + prompt = self._get_analysis_prompt(analysis_type, ext) + return self.analyze_image(file_path, prompt, model_name) + elif ext in ['.pdf', '.doc', '.docx', '.txt']: + # تحليل المستند النصي (محاكاة) + return { + "success": True, + "content": f"تم تحليل المستند {file_path} بنجاح. نوع التحليل: {analysis_type}", + "model": model_name + } + else: + return {"error": f"نوع الملف غير مدعوم: {ext}"} + + except Exception as e: + logging.error(f"خطأ أثناء تحليل المستند: {str(e)}") + return {"error": f"فشل في تحليل المستند: {str(e)}"} - def render(self): - """عرض واجهة وحدة المساعد الذكي""" - - st.markdown("

وحدة المساعد الذكي

", unsafe_allow_html=True) - - tabs = st.tabs([ - "المساعد الذكي", - "التنبؤ بالتكاليف", - "تحليل المخاطر", - "تحليل المستندات", - "المحتوى المحلي", - "الأسئلة الشائعة" - ]) - - with tabs[0]: - self._render_ai_assistant_tab() - - with tabs[1]: - self._render_cost_prediction_tab() - - with tabs[2]: - self._render_risk_analysis_tab() - - with tabs[3]: - self._render_document_analysis_tab() - - with tabs[4]: - self._render_local_content_tab() + def _get_analysis_prompt(self, analysis_type, file_ext): + """ + الحصول على التوجيه المناسب لنوع التحليل - with tabs[5]: - self._render_faq_tab() + المعلمات: + analysis_type: نوع التحليل المطلوب + file_ext: امتداد الملف + + العوائد: + str: التوجيه المناسب + """ + if analysis_type == "items_extraction": + return """ + قم بتحليل هذه الصورة من مستند المناقصة واستخراج جميع البنود والمواصفات الفنية. + قدم النتائج بتنسيق منظم مع ترقيم البنود وتصنيفها حسب الأقسام. + """ + elif analysis_type == "contract_terms": + return """ + قم بتحليل هذه الصورة من العقد واستخراج جميع الشروط التعاقدية المهمة. + حدد الالتزامات والحقوق لكل طرف، والمواعيد النهائية، وشروط الدفع، والضمانات. + """ + elif analysis_type == "quantities": + return """ + قم بتحليل هذه الصورة من جدول الكميات واستخراج جميع البنود والكميات والأسعار. + قدم ملخصاً للتكاليف الإجمالية وتحليلاً للبنود الرئيسية. + """ + elif analysis_type == "legal_requirements": + return """ + قم بتحليل هذه الصورة واستخراج جميع المتطلبات القانونية والتنظيمية. + حدد الشروط القانونية الهامة، والمتطلبات التنظيمية، والالتزامات القانونية. + """ + else: # comprehensive + return """ + قم بتحليل هذه الصورة من مستند المناقصة وتقديم تحليل شامل للمحتوى. + استخرج المعلومات الرئيسية، والبنود المهمة، والمواصفات الفنية، والشروط التعاقدية. + """ + +# إضافة دوال تبويبات واجهة المستخدم +def _render_ai_assistant_tab(self): + """عرض تبويب المساعد الذكي""" + st.header("المساعد الذكي") - def _render_ai_assistant_tab(self): - """عرض تبويب المساعد الذكي مع دعم Claude AI""" - - st.markdown("### المساعد الذكي لتسعير المناقصات") - - # اختيار نموذج Claude - claude_models = self.claude_service.get_available_models() - - selected_model = st.radio( - "اختر نموذج الذكاء الاصطناعي", - options=list(claude_models.keys()), - format_func=lambda x: claude_models[x], - horizontal=True, - key="assistant_ai_model" + # اختيار نموذج الذكاء الاصطناعي + models = self.claude_service.get_available_models() + model_options = list(models.keys()) + model_descriptions = list(models.values()) + + col1, col2 = st.columns([1, 3]) + with col1: + selected_index = st.selectbox( + "اختر نموذج التحليل", + range(len(model_options)), + format_func=lambda i: model_descriptions[i] ) + st.session_state.selected_model = model_options[selected_index] + + # عرض المحادثة السابقة + for message in st.session_state.messages: + with st.chat_message(message["role"]): + st.markdown(message["content"]) + + # إدخال المستخدم + if prompt := st.chat_input("اكتب سؤالك هنا..."): + # إضافة رسالة المستخدم إلى المحادثة + st.session_state.messages.append({"role": "user", "content": prompt}) + with st.chat_message("user"): + st.markdown(prompt) + + # معالجة الرسالة وإضافة رد المساعد + with st.chat_message("assistant"): + with st.spinner("جاري التفكير..."): + # إرسال المحادثة إلى Claude + response = self.claude_service.chat_completion( + st.session_state.messages, + model_name=st.session_state.selected_model + ) + + if "error" in response: + st.error(response["error"]) + content = "عذراً، حدث خطأ أثناء معالجة طلبك. يرجى المحاولة مرة أخرى." + else: + content = response["content"] + + st.markdown(content) - # عرض واجهة المحادثة - st.markdown(""" -
-
-

المساعد الذكي

-

تحدث مع المساعد الذكي للحصول على المساعدة في تسعير المناقصات وتحليل البيانات

-
-
- """, unsafe_allow_html=True) - - # تهيئة محفوظات المحادثة في حالة الجلسة إذا لم تكن موجودة - if 'ai_assistant_messages' not in st.session_state: - st.session_state.ai_assistant_messages = [ - {"role": "assistant", "content": "مرحباً! أنا المساعد الذكي لنظام تسعير المناقصات. كيف يمكنني مساعدتك اليوم؟"} - ] + # إضافة رد المساعد إلى المحادثة + st.session_state.messages.append({"role": "assistant", "content": content}) - # عرض محفوظات المحادثة بتنسيق محسن - chat_container = st.container() - with chat_container: - st.markdown(""" - - """, unsafe_allow_html=True) + # إعادة تحميل الصفحة لتحديث المحادثة + st.rerun() - for message in st.session_state.ai_assistant_messages: - if message["role"] == "user": - st.markdown(f""" -
-
- {message["content"]} -
أنت • الآن
-
-
أ
-
- """, unsafe_allow_html=True) - else: - st.markdown(f""" -
-
م
-
- {message["content"]} -
المساعد • الآن
-
-
- """, unsafe_allow_html=True) - - # إضافة خيار رفع الملفات - uploaded_file = st.file_uploader( - "اختياري: ارفع ملفًا للمساعدة (صورة، PDF)", - type=["jpg", "jpeg", "png", "pdf"], - key="assistant_file_upload" - ) +def _render_document_analysis_tab(self): + """عرض تبويب تحليل المستندات""" + st.header("تحليل المستندات") + + # اختيار نوع التحليل + analysis_type = st.selectbox( + "اختر نوع التحليل", + [ + "تحليل شامل", + "استخراج البنود والمواصفات", + "استخراج الشروط التعاقدية", + "تحليل الكميات", + "تحليل المتطلبات القانونية" + ] + ) + + # تحويل اختيار المستخدم إلى قيمة برمجية + analysis_type_map = { + "تحليل شامل": "comprehensive", + "استخراج البنود والمواصفات": "items_extraction", + "استخراج الشروط التعاقدية": "contract_terms", + "تحليل الكميات": "quantities", + "تحليل المتطلبات القانونية": "legal_requirements" + } + + selected_analysis = analysis_type_map.get(analysis_type, "comprehensive") + + # رفع الملف + uploaded_file = st.file_uploader( + "ارفع ملف المستند (PDF, Word, صورة)", + type=["pdf", "docx", "doc", "jpg", "jpeg", "png"] + ) + + if uploaded_file is not None: + # حفظ الملف مؤقتاً + with tempfile.NamedTemporaryFile(delete=False, suffix=f".{uploaded_file.name.split('.')[-1]}") as tmp_file: + tmp_file.write(uploaded_file.getvalue()) + tmp_path = tmp_file.name + + # عرض معلومات الملف + file_details = { + "اسم الملف": uploaded_file.name, + "نوع الملف": uploaded_file.type, + "حجم الملف": f"{uploaded_file.size / 1024:.2f} كيلوبايت" + } - # مربع إدخال الرسالة - user_input = st.text_input("اكتب رسالتك هنا", key="ai_assistant_input") + st.json(file_details) - # التحقق من وجود مفتاح API - api_available = True + # زر التحليل + if st.button("تحليل المستند"): + with st.spinner("جاري تحليل المستند..."): + # تحليل المستند باستخدام Claude + result = self.claude_service.analyze_document( + tmp_path, + analysis_type=selected_analysis, + model_name=st.session_state.selected_model + ) + + if "error" in result: + st.error(result["error"]) + else: + st.success("تم تحليل المستند بنجاح!") + st.subheader("نتائج التحليل") + st.markdown(result["content"]) + + # حفظ النتائج في حالة الجلسة + if "document_analysis" not in st.session_state.analysis_results: + st.session_state.analysis_results["document_analysis"] = [] + + st.session_state.analysis_results["document_analysis"].append({ + "file_name": uploaded_file.name, + "analysis_type": analysis_type, + "result": result, + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + + # حذف الملف المؤقت بعد الانتهاء try: - self.claude_service.get_api_key() - except ValueError: - api_available = False - st.warning("مفتاح API لـ Claude غير متوفر. يرجى التأكد من تعيين متغير البيئة 'anthropic'.") + os.unlink(tmp_path) + except: + pass + +def _render_contract_analysis_tab(self): + """عرض تبويب تحليل العقود""" + st.header("تحليل العقود") + + if not CONTRACT_ANALYZER_AVAILABLE: + st.warning("وحدة تحليل العقود غير متاحة حالياً.") + return + + # اختيار نوع التحليل + analysis_type = st.selectbox( + "اختر نوع التحليل", + [ + "تحليل شامل للعقد", + "تحليل الشروط المالية", + "تحليل الشروط القانونية", + "تحليل المخاطر التعاقدية", + "تحليل الجدول الزمني" + ], + key="contract_analysis_type" + ) + + # تحويل اختيار المستخدم إلى قيمة برمجية + analysis_type_map = { + "تحليل شامل للعقد": "comprehensive", + "تحليل الشروط المالية": "financial", + "تحليل الشروط القانونية": "legal", + "تحليل المخاطر التعاقدية": "risk", + "تحليل الجدول الزمني": "timeline" + } + + selected_analysis = analysis_type_map.get(analysis_type, "comprehensive") + + # رفع الملف + uploaded_file = st.file_uploader( + "ارفع ملف العقد (PDF, Word)", + type=["pdf", "docx", "doc"], + key="contract_file_uploader" + ) + + if uploaded_file is not None: + # حفظ الملف مؤقتاً + with tempfile.NamedTemporaryFile(delete=False, suffix=f".{uploaded_file.name.split('.')[-1]}") as tmp_file: + tmp_file.write(uploaded_file.getvalue()) + tmp_path = tmp_file.name + + # عرض معلومات الملف + file_details = { + "اسم الملف": uploaded_file.name, + "نوع الملف": uploaded_file.type, + "حجم الملف": f"{uploaded_file.size / 1024:.2f} كيلوبايت" + } - if user_input and api_available: - # إضافة رسالة المستخدم إلى المحفوظات - st.session_state.ai_assistant_messages.append({"role": "user", "content": user_input}) - - # عرض محفوظات المحادثة المحدثة - with chat_container: - st.markdown(f""" -
-
- {user_input} -
-
- """, unsafe_allow_html=True) - - # معالجة الرد - with st.spinner("جاري التفكير..."): - # التحقق مما إذا كان هناك ملف مرفق - if uploaded_file: - # حفظ الملف المرفوع مؤقتاً - with tempfile.NamedTemporaryFile(delete=False, suffix=f".{uploaded_file.name.split('.')[-1]}") as temp_file: - temp_file.write(uploaded_file.getbuffer()) - temp_file_path = temp_file.name + st.json(file_details) + + # زر التحليل + if st.button("تحليل العقد"): + with st.spinner("جاري تحليل العقد..."): + # تحليل العقد باستخدام محلل العقود + result = self.contract_analyzer.analyze_contract( + tmp_path, + analysis_type=selected_analysis + ) + + if isinstance(result, dict) and "error" in result: + st.error(result["error"]) + else: + st.success("تم تحليل العقد بنجاح!") - # إذا كان الملف PDF، تحويله إلى صورة - if uploaded_file.name.lower().endswith('.pdf'): - if self.pdf_conversion_available: - try: - # تحويل الصفحة الأولى فقط - images = convert_from_path(temp_file_path, first_page=1, last_page=1) - if images: - # حفظ الصورة بشكل مؤقت - temp_image_path = f"{temp_file_path}_image.jpg" - images[0].save(temp_image_path, 'JPEG') - # استخدام مسار الصورة بدلاً من PDF - os.remove(temp_file_path) - temp_file_path = temp_image_path - except Exception as e: - st.error(f"فشل في تحويل ملف PDF إلى صورة: {str(e)}") - else: - st.error("تحليل ملفات PDF يتطلب تثبيت مكتبة pdf2image.") - response = "عذراً، لا يمكنني تحليل ملفات PDF في الوقت الحالي. يرجى تحويل الملف إلى صورة أو مشاركة المعلومات كنص." + # عرض نتائج التحليل + st.subheader("ملخص التحليل") + st.markdown(result.get("summary", "لا يوجد ملخص متاح")) - # تحليل الصورة باستخدام Claude - prompt = f"المستخدم قام برفع هذه الصورة وسأل: {user_input}\nقم بتحليل الصورة والرد على سؤال المستخدم بشكل تفصيلي." - results = self.claude_service.analyze_image(temp_file_path, prompt, model_name=selected_model) + st.subheader("البنود الرئيسية") + for i, clause in enumerate(result.get("key_clauses", [])): + st.markdown(f"**{i+1}. {clause['title']}**") + st.markdown(clause["description"]) - # حذف الملف المؤقت - try: - os.remove(temp_file_path) - except: - pass + st.subheader("المخاطر المحتملة") + risk_data = [] + for risk in result.get("risks", []): + risk_data.append({ + "المخاطرة": risk["description"], + "المستوى": risk["level"], + "التأثير": risk["impact"] + }) - if "error" in results: - response = f"عذراً، حدث خطأ أثناء تحليل الملف: {results['error']}" - else: - response = results["content"] - else: - # استخدام خدمة Claude للرد على الرسائل النصية - results = self.claude_service.chat_completion(st.session_state.ai_assistant_messages, model_name=selected_model) + if risk_data: + st.dataframe(pd.DataFrame(risk_data)) - if "error" in results: - response = f"عذراً، حدث خطأ أثناء معالجة طلبك: {results['error']}" - else: - response = results["content"] - - # إضافة رد المساعد إلى المحفوظات - st.session_state.ai_assistant_messages.append({"role": "assistant", "content": response}) - - # عرض رد المساعد - with chat_container: - st.markdown(f""" -
-
- {response} -
-
- """, unsafe_allow_html=True) - - # إعادة تعيين قيمة الإدخال - st.text_input("اكتب رسالتك هنا", value="", key="ai_assistant_input_reset") - - def _generate_ai_response(self, user_input, model_name="claude-3-7-sonnet"): - """توليد رد المساعد الذكي باستخدام Claude AI""" - - # التحقق من وجود مفتاح API + # حفظ النتائج في حالة الجلسة + if "contract_analysis" not in st.session_state.analysis_results: + st.session_state.analysis_results["contract_analysis"] = [] + + st.session_state.analysis_results["contract_analysis"].append({ + "file_name": uploaded_file.name, + "analysis_type": analysis_type, + "result": result, + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + + # حذف الملف المؤقت بعد الانتهاء try: - self.claude_service.get_api_key() - except ValueError: - return "عذراً، لا يمكنني الاتصال بخدمة الذكاء الاصطناعي في الوقت الحالي. يرجى التحقق من إعدادات API." - - # البحث في الأسئلة الشائعة أولاً - for faq in self.faqs: - if any(keyword in user_input.lower() for keyword in faq["question"].lower().split()): - return f"{faq['answer']}\n\nهل تحتاج إلى مساعدة أخرى؟" - - # إنشاء محادثة لإرسالها إلى Claude - messages = [ - {"role": "user", "content": user_input} - ] - - # استدعاء خدمة Claude - results = self.claude_service.chat_completion(messages, model_name=model_name) - - if "error" in results: - # إذا فشل الاتصال، استخدم التوليد الافتراضي - logging.warning(f"فشل الاتصال بـ Claude AI: {results['error']}. استخدام التوليد الافتراضي.") - return self._generate_default_response(user_input) - else: - return results["content"] + os.unlink(tmp_path) + except: + pass + +def _render_risk_analysis_tab(self): + """عرض تبويب تحليل المخاطر""" + st.header("تحليل المخاطر") - def _generate_default_response(self, user_input): - """توليد رد افتراضي في حالة عدم توفر Claude AI""" - - if "تسعير" in user_input or "سعر" in user_input or "تكلفة" in user_input: - return "يمكنك استخدام وحدة التنبؤ بالتكاليف لتقدير تكاليف المشروع بناءً على خصائصه. انتقل إلى تبويب 'التنبؤ بالتكاليف' وأدخل بيانات المشروع لتحصل على تقدير دقيق للتكاليف." - - elif "مخاطر" in user_input or "مخاطرة" in user_input: - return "يمكنك استخدام وحدة تحليل المخاطر لتقييم المخاطر المحتملة للمشروع. انتقل إلى تبويب 'تحليل المخاطر' وأدخل بيانات المشروع وعوامل المخاطرة لتحصل على تحليل شامل للمخاطر واستراتيجيات الاستجابة المقترحة." - - elif "مستند" in user_input or "ملف" in user_input or "وثيقة" in user_input or "مناقصة" in user_input: - return "يمكنك استخدام وحدة تحليل المستندات لتحليل مستندات المناقصة واستخراج المعلومات المهمة منها. انتقل إلى تبويب 'تحليل المستندات' وقم بتحميل ملفات المناقصة لتحصل على تحليل تفصيلي للمستندات." - - elif "محتوى محلي" in user_input or "محلي" in user_input: - return "يمكنك استخدام وحدة المحتوى المحلي لحساب وتحسين نسبة المحتوى المحلي في مشروعك. انتقل إلى تبويب 'المحتوى المحلي' وأدخل بيانات مكونات المشروع لتحصل على تحليل شامل للمحتوى المحلي واقتراحات لتحسينه." - - elif "تقرير" in user_input or "إحصائيات" in user_input or "بيانات" in user_input: - return "يمكنك استخدام وحدة التقارير والتحليلات للحصول على تقارير تفصيلية وإحصائيات عن المشاريع. يمكنك الوصول إليها من القائمة الرئيسية للنظام." - - else: - return "شكراً لاستفسارك. يمكنني مساعدتك في تسعير المناقصات، وتحليل المخاطر، وتحليل المستندات، وحساب ال��حتوى المحلي. يرجى توضيح استفسارك أكثر أو اختيار أحد الخيارات في الأعلى للحصول على المساعدة المطلوبة." + # إدخال بيانات المشروع + st.subheader("بيانات المشروع") - def _render_cost_prediction_tab(self): - """عرض تبويب التنبؤ بالتكاليف""" - - st.markdown("### التنبؤ بالتكاليف") - - # عرض نموذج إدخال بيانات المشروع - st.markdown("#### بيانات المشروع") - - col1, col2 = st.columns(2) - - with col1: - project_type = st.selectbox( - "نوع المشروع", - [ - "مباني سكنية", - "مباني تجارية", - "مباني حكومية", - "مراكز صحية", - "مدارس", - "بنية تحتية", - "طرق", - "جسور", - "صرف صحي", - "مياه", - "كهرباء" - ], - key="cost_project_type" - ) + col1, col2 = st.columns(2) + with col1: + project_name = st.text_input("اسم المشروع") + project_type = st.selectbox( + "نوع المشروع", + ["إنشائي", "طرق", "جسور", "مباني", "بنية تحتية", "أخرى"] + ) + + with col2: + project_value = st.number_input("قيمة المشروع (ريال)", min_value=0, value=1000000) + project_duration = st.number_input("مدة المشروع (شهر)", min_value=1, value=12) + + # تحليل المخاطر + if st.button("تحليل المخاطر"): + with st.spinner("جاري تحليل المخاطر..."): + # محاكاة تحليل المخاطر باستخدام الذكاء الاصطناعي + time.sleep(1) # محاكاة وقت المعالجة + + # إنشاء مصفوفة المخاطر + st.subheader("مصفوفة المخاطر") + + # إنشاء بيانات مصفوفة المخاطر + risk_matrix = np.array([ + [1, 2, 3], + [2, 4, 6], + [3, 6, 9] + ]) - location = st.selectbox( - "الموقع", - [ - "الرياض", - "جدة", - "الدمام", - "مكة", - "المدينة", - "تبوك", - "حائل", - "عسير", - "جازان", - "نجران", - "الباحة", - "الجوف", - "القصيم" - ], - key="cost_location" + # إنشاء مصفوفة المخاطر باستخدام plotly + fig = px.imshow( + risk_matrix, + labels=dict(x="احتمال الحدوث", y="التأثير", color="درجة الخطورة"), + x=["منخفض", "متوسط", "مرتفع"], + y=["منخفض", "متوسط", "مرتفع"], + color_continuous_scale=["green", "yellow", "red"], + text_auto=True ) - client_type = st.selectbox( - "نوع العميل", - [ - "حكومي", - "شبه حكومي", - "شركة كبيرة", - "شركة متوسطة", - "شركة صغيرة", - "أفراد" - ], - key="cost_client_type" + # تحسين مظهر المصفوفة + fig.update_layout( + width=600, + height=500, + title="مصفوفة المخاطر", + font=dict(size=14), + coloraxis_colorbar=dict( + title=dict( + text="درجة الخطورة", + side="right" + ) + ) ) - - with col2: - area = st.number_input("المساحة (م²)", min_value=100, max_value=1000000, value=5000, key="cost_area") - floors = st.number_input("عدد الطوابق", min_value=1, max_value=100, value=3, key="cost_floors") + st.plotly_chart(fig) + + # عرض جدول المخاطر + st.subheader("تحليل المخاطر المحتملة") + + # إنشاء بيانات المخاطر + risks_data = [ + {"المخاطرة": "تأخر توريد المواد", "الاحتمال": "مرتفع", "التأثير": "متوسط", "الدرجة": 6, "الإجراء": "التعاقد مع موردين بدلاء"}, + {"المخاطرة": "تغير أسعار المواد", "الاحتمال": "متوسط", "التأثير": "مرتفع", "الدرجة": 6, "الإجراء": "تضمين بند تعديل الأسعار في العقد"}, + {"المخاطرة": "ظروف جوية قاسية", "الاحتمال": "منخفض", "التأثير": "مرتفع", "الدرجة": 3, "الإجراء": "وضع خطة طوارئ للعمل"}, + {"المخاطرة": "نقص العمالة", "الاحتمال": "متوسط", "التأثير": "متوسط", "الدرجة": 4, "الإجراء": "التعاقد مع شركات توريد عمالة"}, + {"المخاطرة": "مشاكل فنية", "الاحتمال": "متوسط", "التأثير": "مرتفع", "الدرجة": 6, "الإجراء": "توفير استشاريين فنيين"} + ] + + # عرض جدول المخاطر + st.dataframe(pd.DataFrame(risks_data)) + + # تحليل ذكي للمخاطر باستخدام Claude + st.subheader("تحليل ذكي للمخاطر") - duration = st.number_input("مدة التنفيذ (شهور)", min_value=1, max_value=60, value=12, key="cost_duration") + # إنشاء توجيه للنموذج + prompt = f""" + قم بتحليل المخاطر المحتملة لمشروع بالمواصفات التالية: + - اسم المشروع: {project_name} + - نوع المشروع: {project_type} + - قيمة المشروع: {project_value} ريال + - مدة المشروع: {project_duration} شهر - tender_type = st.selectbox( - "نوع المناقصة", - [ - "عامة", - "خاصة", - "أمر مباشر" - ], - key="cost_tender_type" + قدم تحليلاً شاملاً للمخاطر المحتملة وتوصيات للتخفيف منها. + """ + + # استدعاء النموذج + response = self.claude_service.chat_completion( + [{"role": "user", "content": prompt}], + model_name=st.session_state.selected_model ) - - st.markdown("#### متغيرات إضافية") - - col1, col2, col3 = st.columns(3) - - with col1: - has_basement = st.checkbox("يتضمن بدروم", key="cost_has_basement") - has_special_finishing = st.checkbox("تشطيبات خاصة", key="cost_has_special_finishing") - - with col2: - has_landscape = st.checkbox("أعمال تنسيق المواقع", key="cost_has_landscape") - has_parking = st.checkbox("مواقف متعددة الطوابق", key="cost_has_parking") - - with col3: - has_smart_systems = st.checkbox("أنظمة ذكية", key="cost_has_smart_systems") - has_sustainability = st.checkbox("متطلبات استدامة", key="cost_has_sustainability") - - # زر التنبؤ بالتكلفة مع دعم Claude AI - col1, col2 = st.columns([1, 3]) - - with col1: - predict_button = st.button("التنبؤ بالتكلفة", use_container_width=True, key="cost_predict_button") - with col2: - use_claude = st.checkbox("استخدام Claude AI للتحليل المتقدم", value=True, key="cost_use_claude") - - if predict_button: - with st.spinner("جاري تحليل البيانات والتنبؤ بالتكاليف..."): - # محاكاة وقت المعالجة - time.sleep(2) - - # تجهيز البيانات للنموذج - features = { - 'project_type': project_type, - 'location': location, - 'area': area, - 'floors': floors, - 'duration_months': duration, - 'tender_type': tender_type, - 'client_type': client_type, - 'has_basement': has_basement, - 'has_special_finishing': has_special_finishing, - 'has_landscape': has_landscape, - 'has_parking': has_parking, - 'has_smart_systems': has_smart_systems, - 'has_sustainability': has_sustainability - } + if "error" in response: + st.error(response["error"]) + else: + st.markdown(response["content"]) - # استدعاء النموذج للتنبؤ - cost_prediction_results = self._predict_cost(features) + # حفظ النتائج في حالة الجلسة + if "risk_analysis" not in st.session_state.analysis_results: + st.session_state.analysis_results["risk_analysis"] = [] - # إضافة تحليل إضافي باستخدام Claude AI إذا تم تفعيل الخيار - if use_claude: - try: - # إنشاء نص الميزات للتحليل - features_text = f""" - بيانات المشروع: - - نوع المشروع: {project_type} - - الموقع: {location} - - المساحة: {area} م² - - عدد الطوابق: {floors} - - مدة التنفيذ: {duration} شهر - - نوع المناقصة: {tender_type} - - نوع العميل: {client_type} - - يتضمن بدروم: {'نعم' if has_basement else 'لا'} - - تشطيبات خاصة: {'نعم' if has_special_finishing else 'لا'} - - أعمال تنسيق المواقع: {'نعم' if has_landscape else 'لا'} - - مواقف متعددة الطوابق: {'نعم' if has_parking else 'لا'} - - أنظمة ذكية: {'نعم' if has_smart_systems else 'لا'} - - متطلبات استدامة: {'نعم' if has_sustainability else 'لا'} - - نتائج التنبؤ الأولية: - - التكلفة الإجمالية المقدرة: {cost_prediction_results['total_cost']:,.0f} ريال - - تكلفة المتر المربع: {cost_prediction_results['cost_per_sqm']:,.0f} ريال/م² - - تكلفة المواد: {cost_prediction_results['material_cost']:,.0f} ريال - - تكلفة العمالة: {cost_prediction_results['labor_cost']:,.0f} ريال - - تكلفة المعدات: {cost_prediction_results['equipment_cost']:,.0f} ريال - """ - - prompt = f"""تحليل بيانات مشروع وتكاليفه: - - {features_text} - - المطلوب: - 1. تحليل التكاليف المتوقعة ومعقوليتها مقارنة بمشاريع مماثلة في السوق السعودي - 2. تقديم توصيات وملاحظات لتحسين التكلفة - 3. تحديد أي مخاطر محتملة قد تؤثر على التكلفة - 4. تقديم نصائح لزيادة فعالية التكلفة - 5. تقديم رأي حول مدى تنافسية هذه التكلفة في السوق الحالي - - يرجى تقديم تحليل مهني ومختصر يركز على الجوانب الأكثر أهمية. - """ - - # استدعاء Claude للتحليل - claude_analysis = self.claude_service.chat_completion( - [{"role": "user", "content": prompt}] - ) - - if "error" not in claude_analysis: - # إضافة تحليل Claude إلى النتائج - cost_prediction_results["claude_analysis"] = claude_analysis["content"] - except Exception as e: - st.warning(f"تعذر إجراء التحليل المتقدم: {str(e)}") - - # عرض نتائج التنبؤ - self._display_cost_prediction_results(cost_prediction_results) - - def _predict_cost(self, features): - """التنبؤ بتكاليف المشروع""" - - # في البيئة الحقيقية، سيتم استدعاء نموذج التنبؤ بالتكاليف - # محاكاة نتائج التنبؤ للعرض - - # حساب القيمة الأساسية للمتر المربع حسب نوع المشروع - base_cost_per_sqm = { - "مباني سكنية": 2500, - "مباني تجارية": 3000, - "مباني حكومية": 3500, - "مراكز صحية": 4000, - "مدارس": 3200, - "بنية تحتية": 2000, - "طرق": 1500, - "جسور": 5000, - "صرف صحي": 2200, - "مياه": 2000, - "كهرباء": 2500 - }.get(features['project_type'], 2500) - - # تطبيق معاملات التعد��ل حسب المتغيرات - location_factor = { - "الرياض": 1.1, - "جدة": 1.15, - "الدمام": 1.05, - "مكة": 1.2, - "المدينة": 1.1, - "تبوك": 0.95, - "حائل": 0.9, - "عسير": 0.95, - "جازان": 0.9, - "نجران": 0.85, - "الباحة": 0.9, - "الجوف": 0.85, - "القصيم": 0.9 - }.get(features['location'], 1.0) - - client_factor = { - "حكومي": 1.05, - "شبه حكومي": 1.0, - "شركة كبيرة": 0.95, - "شركة متوسطة": 0.9, - "شركة صغيرة": 0.85, - "أفراد": 0.8 - }.get(features['client_type'], 1.0) - - tender_factor = { - "عامة": 1.0, - "خاصة": 0.95, - "أمر مباشر": 0.9 - }.get(features['tender_type'], 1.0) - - # معاملات للميزات الإضافية - basement_factor = 1.1 if features['has_basement'] else 1.0 - special_finishing_factor = 1.2 if features['has_special_finishing'] else 1.0 - landscape_factor = 1.05 if features['has_landscape'] else 1.0 - parking_factor = 1.1 if features['has_parking'] else 1.0 - smart_systems_factor = 1.15 if features['has_smart_systems'] else 1.0 - sustainability_factor = 1.1 if features['has_sustainability'] else 1.0 - - # معامل لعدد الطوابق - floors_factor = 1.0 + (features['floors'] - 1) * 0.05 - - # حساب التكلفة الإجمالية - total_sqm_cost = base_cost_per_sqm * location_factor * client_factor * tender_factor * \ - basement_factor * special_finishing_factor * landscape_factor * \ - parking_factor * smart_systems_factor * sustainability_factor * \ - floors_factor - - total_cost = total_sqm_cost * features['area'] - - # حساب التكاليف المفصلة - material_cost = total_cost * 0.6 - labor_cost = total_cost * 0.25 - equipment_cost = total_cost * 0.15 - - # إضافة هامش خطأ عشوائي للمحاكاة - error_margin = 0.05 # 5% - total_cost = total_cost * (1 + np.random.uniform(-error_margin, error_margin)) - - # إعداد النتائج - results = { - "total_cost": total_cost, - "cost_per_sqm": total_cost / features['area'], - "material_cost": material_cost, - "labor_cost": labor_cost, - "equipment_cost": equipment_cost, - "breakdown": { - "structural_works": total_cost * 0.35, - "architectural_works": total_cost * 0.25, - "mep_works": total_cost * 0.25, - "site_works": total_cost * 0.1, - "general_requirements": total_cost * 0.05 - }, - "confidence_level": 0.85, # مستوى الثقة في التنبؤ - "comparison": { - "market_average": total_cost * 1.1, - "historical_projects": total_cost * 0.95 - } - } - - return results - - def _display_cost_prediction_results(self, results): - """عرض نتائج التنبؤ بالتكاليف""" - - st.markdown("### نتائج التنبؤ بالتكاليف") - - # عرض التكلفة الإجمالية وتكلفة المتر المربع - col1, col2, col3 = st.columns(3) - - with col1: - st.metric( - "التكلفة الإجمالية المتوقعة", - f"{results['total_cost']:,.0f} ريال", - delta=f"{(results['total_cost'] - results['comparison']['historical_projects']):,.0f} ريال" - ) - - with col2: - st.metric( - "تكلفة المتر المربع", - f"{results['cost_per_sqm']:,.0f} ريال/م²" - ) - - with col3: - st.metric( - "مستوى الثقة في التنبؤ", - f"{results['confidence_level'] * 100:.0f}%" - ) - - # عرض تفصيل التكاليف - st.markdown("#### تفصيل التكاليف") - - # رسم مخطط دائري للتكاليف المفصلة - fig = px.pie( - values=[ - results['material_cost'], - results['labor_cost'], - results['equipment_cost'] - ], - names=["تكلفة المواد", "تكلفة العمالة", "تكلفة المعدات"], - title="توزيع التكاليف الرئيسية" - ) - - st.plotly_chart(fig, use_container_width=True) - - # رسم مخطط شريطي لتفصيل ��لأعمال - breakdown_data = pd.DataFrame({ - 'فئة الأعمال': [ - "الأعمال الإنشائية", - "الأعمال المعمارية", - "الأعمال الكهروميكانيكية", - "أعمال الموقع", - "المتطلبات العامة" - ], - 'التكلفة': [ - results['breakdown']['structural_works'], - results['breakdown']['architectural_works'], - results['breakdown']['mep_works'], - results['breakdown']['site_works'], - results['breakdown']['general_requirements'] - ] - }) - - fig = px.bar( - breakdown_data, - x='فئة الأعمال', - y='التكلفة', - title="تفصيل التكاليف حسب فئة الأعمال", - text_auto='.3s' - ) - - fig.update_traces(texttemplate='%{text:,.0f} ريال', textposition='outside') - - st.plotly_chart(fig, use_container_width=True) - - # عرض مقارنة مع متوسط السوق - st.markdown("#### مقارنة مع متوسط السوق") - - comparison_data = pd.DataFrame({ - 'المصدر': [ - "التكلفة المتوقعة", - "متوسط السوق", - "مشاريع مماثلة سابقة" - ], - 'التكلفة': [ - results['total_cost'], - results['comparison']['market_average'], - results['comparison']['historical_projects'] - ] - }) - - fig = px.bar( - comparison_data, - x='المصدر', - y='التكلفة', - title="مقارنة التكلفة المتوقعة مع السوق", - text_auto='.3s', - color='المصدر', - color_discrete_map={ - "التكلفة المتوقعة": "#1f77b4", - "متوسط السوق": "#ff7f0e", - "مشاريع مماثلة سابقة": "#2ca02c" - } - ) - - fig.update_traces(texttemplate='%{text:,.0f} ريال', textposition='outside') - - st.plotly_chart(fig, use_container_width=True) - - # عرض تحليل Claude AI إذا كان متوفراً - if "claude_analysis" in results: - st.markdown("### تحليل Claude AI المتقدم") - st.info(results["claude_analysis"]) - - # عرض ملاحظات وتوصيات - st.markdown("#### ملاحظات وتوصيات") - - st.info(""" - - تم التنبؤ بالتكاليف بناءً على البيانات المدخلة ونماذج التعلم الآلي المدربة على مشاريع مماثلة. - - مستوى الثقة في التنبؤ جيد، ولكن يجب مراجعة التكاليف بشكل تفصيلي قبل اتخاذ القرار النهائي. - - تكلفة المتر المربع متوافقة مع متوسط السوق لهذا النوع من المشاريع. - - ينصح بمراجعة التصميم لتحسين التكلفة وزيادة الكفاءة. - """) - - # زر تصدير التقرير - if st.button("تصدير تقرير التكاليف"): - st.success("تم تصدير تقرير التكاليف بنجاح!") - - def _render_risk_analysis_tab(self): - """عرض تبويب تحليل المخاطر""" - - st.markdown("### تحليل المخاطر") - - # عرض نموذج إدخال بيانات المشروع للمخاطر - st.markdown("#### بيانات المشروع") - - col1, col2 = st.columns(2) - - with col1: - project_type = st.selectbox( - "نوع المشروع", - [ - "مباني سكنية", - "مباني تجارية", - "مباني حكومية", - "مراكز صحية", - "مدارس", - "بنية تحتية", - "طرق", - "جسور", - "صرف صحي", - "مياه", - "كهرباء" - ], - key="risk_project_type" - ) - - location = st.selectbox( - "الموقع", - [ - "الرياض", - "جدة", - "الدمام", - "مكة", - "المدينة", - "تبوك", - "حائل", - "عسير", - "جازان", - "نجران", - "الباحة", - "الجوف", - "القصيم" - ], - key="risk_location" - ) - - with col2: - client_type = st.selectbox( - "نوع العميل", - [ - "حكومي", - "شبه حكومي", - "شركة كبيرة", - "شركة متوسطة", - "شركة صغيرة", - "أفراد" - ], - key="risk_client_type" - ) - - tender_type = st.selectbox( - "نوع المناقصة", - [ - "عامة", - "خاصة", - "أمر مباشر" - ], - key="risk_tender_type" - ) - - st.markdown("#### عوامل المخاطرة") - - col1, col2, col3 = st.columns(3) - - with col1: - payment_terms = st.slider("شروط الدفع (1-10)", 1, 10, 5, - help="1: شروط دفع سيئة جداً، 10: شروط دفع ممتازة", - key="risk_payment_terms") - completion_deadline = st.slider("مهلة الإنجاز (1-10)", 1, 10, 5, - help="1: مهلة قصيرة جداً، 10: مهلة مريحة", - key="risk_completion_deadline") - - with col2: - penalty_clause = st.slider("شروط الغرامات (1-10)", 1, 10, 5, - help="1: غرامات مرتفعة جداً، 10: غرامات معقولة", - key="risk_penalty_clause") - technical_complexity = st.slider("التعقيد الفني (1-10)", 1, 10, 5, - help="1: بسيط جداً، 10: معقد للغاية", - key="risk_technical_complexity") - - with col3: - company_experience = st.slider("خبرة الشركة (1-10)", 1, 10, 7, - help="1: لا توجد خبرة، 10: خبرة عالية", - key="risk_company_experience") - market_volatility = st.slider("تقلبات السوق (1-10)", 1, 10, 5, - help="1: مستقر جداً، 10: متقلب للغاية", - key="risk_market_volatility") - - # زر تحليل المخاطر مع دعم Claude AI - col1, col2 = st.columns([1, 3]) - - with col1: - analyze_button = st.button("تحليل المخاطر", use_container_width=True, key="risk_analyze_button") - - with col2: - # Añadimos un key único para este checkbox - use_claude = st.checkbox("استخدام Claude AI للتحليل المتقدم", value=True, key="risk_use_claude") - - if analyze_button: - with st.spinner("جاري تحليل المخاطر..."): - # محاكاة وقت المعالجة - time.sleep(2) - - # تجهيز البيانات للنموذج - features = { - 'project_type': project_type, - 'location': location, - 'client_type': client_type, - 'tender_type': tender_type, - 'payment_terms': payment_terms, - 'completion_deadline': completion_deadline, - 'penalty_clause': penalty_clause, - 'technical_complexity': technical_complexity, - 'company_experience': company_experience, - 'market_volatility': market_volatility - } - - # استدعاء النموذج لتحليل المخاطر - risk_analysis_results = self._analyze_risks(features) - - # إضافة تحليل إضافي باستخدام Claude AI إذا تم تفعيل الخيار - if use_claude: - try: - # إنشاء نص الميزات للتحليل - features_text = f""" - بيانات المشروع: - - نوع المشروع: {project_type} - - الموقع: {location} - - نوع العميل: {client_type} - - نوع المناقصة: {tender_type} - - عوامل المخاطرة: - - شروط الدفع: {payment_terms}/10 - - مهلة الإنجاز: {completion_deadline}/10 - - شروط ��لغرامات: {penalty_clause}/10 - - التعقيد الفني: {technical_complexity}/10 - - خبرة الشركة: {company_experience}/10 - - تقلبات السوق: {market_volatility}/10 - - ملخص التحليل الأولي: - - متوسط درجة المخاطرة: {risk_analysis_results['avg_risk_score']:.1f}/10 - - عدد المخاطر العالية: {risk_analysis_results['high_risks']} - - عدد المخاطر المتوسطة: {risk_analysis_results['medium_risks']} - - عدد المخاطر المنخفضة: {risk_analysis_results['low_risks']} - - أعلى المخاطر: - """ - - # إضافة تفاصيل أعلى المخاطر - for i, risk in enumerate(risk_analysis_results['top_risks'][:3]): - features_text += f""" - {i+1}. {risk['name']} ({risk['category']}) - - الاحتمالية: {risk['probability'] * 100:.0f}% - - التأثير: {risk['impact'] * 100:.0f}% - - درجة المخاطرة: {risk['risk_score']}/10 - """ - - prompt = f"""تحليل مخاطر مشروع: - - {features_text} - - المطلوب: - 1. تحليل عوامل المخاطرة وتأثيرها على المشروع - 2. تقديم توصيات إضافية لإدارة المخاطر - 3. اقتراح استراتيجيات استجابة للمخاطر الرئيسية - 4. تقديم نصائح لتحسين شروط العقد لتقليل المخاطر - 5. تقييم مدى ملاءمة المشروع لاستراتيجية الشركة - - يرجى تقديم تحليل مهني ومختصر يركز على الجوانب الأكثر أهمية. - """ - - # استدعاء Claude للتحليل - claude_analysis = self.claude_service.chat_completion( - [{"role": "user", "content": prompt}] - ) - - if "error" not in claude_analysis: - # إضافة تحليل Claude إلى النتائج - risk_analysis_results["claude_analysis"] = claude_analysis["content"] - except Exception as e: - st.warning(f"تعذر إجراء التحليل المتقدم: {str(e)}") - - # عرض نتائج تحليل المخاطر - self._display_risk_analysis_results(risk_analysis_results) + st.session_state.analysis_results["risk_analysis"].append({ + "project_name": project_name, + "project_type": project_type, + "project_value": project_value, + "project_duration": project_duration, + "result": response["content"], + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + +def _render_cost_prediction_tab(self): + """عرض تبويب التنبؤ بالتكاليف""" + st.header("تحليل وتنبؤ التكاليف") - def _analyze_risks(self, features): - """تحليل مخاطر المشروع""" - - # في البيئة الحقيقية، سيتم استدعاء نموذج تحليل المخاطر - # محاكاة نتائج تحليل المخاطر للعرض - - # تعريف قائمة من المخاطر المحتملة - potential_risks = [ - { - "id": "R-001", - "name": "غرامة تأخير مرتفعة", - "category": "مخاطر مالية", - "description": "غرامة تأخير مرتفعة تصل إلى 10% من قيمة العقد، مما قد يؤثر سلباً على ربحية المشروع في حال التأخير.", - "probability": 0.6, - "impact": 0.8, - "risk_score": 7.8, - "response_strategy": "تخطيط مفصل للمشروع مع وضع مخزون زمني مناسب وتحديد نقاط التسليم المبكر." - }, - { - "id": "R-002", - "name": "تقلبات أسعار المواد", - "category": "مخاطر السوق", - "description": "ارتفاع محتمل في أسعار المواد الخام خلال فترة تنفيذ المشروع، مما يؤثر على التكلفة الإجمالية.", - "probability": 0.7, - "impact": 0.7, - "risk_score": 7.5, - "response_strategy": "التعاقد المبكر مع الموردين وتثبيت الأسعار، أو إضافة بند تعديل سعري في العقد." - }, - { - "id": "R-003", - "name": "ضعف تدفق المدفوعات", - "category": "مخاطر مالية", - "description": "تأخر العميل في سداد المستخلصات مما يؤثر على التدفق النقدي للمشروع.", - "probability": 0.5, - "impact": 0.8, - "risk_score": 7.2, - "response_strategy": "التفاوض على شروط دفع واضحة ومواعيد محددة، وإمكانية طلب دفعة مقدمة." - }, - { - "id": "R-004", - "name": "نقص العمالة الماهرة", - "category": "مخاطر الموارد", - "description": "صعوبة توفير عمالة ماهرة لتنفيذ أجزاء محددة من المشروع.", - "probability": 0.5, - "impact": 0.6, - "risk_score": 6.5, - "response_strategy": "التخطيط المبكر للموارد البشرية وتوقيع عقود مع مقاولي الباطن المتخصصين." - }, - { - "id": "R-005", - "name": "تغييرات في نطاق العمل", - "category": "مخاطر تعاقدية", - "description": "طلبات تغيير من العميل تؤدي إلى زيادة نطاق العمل دون تعديل مناسب للتكلفة والجدول الزمني.", - "probability": 0.6, - "impact": 0.6, - "risk_score": 6.0, - "response_strategy": "تضمين آلية واضحة لإدارة التغيير في العقد وتقييم تأثير أي تغييرات على التكلفة والزمن." - }, - { - "id": "R-006", - "name": "مشاكل في الموقع", - "category": "مخاطر فنية", - "description": "ظروف موقع غير متوقعة تؤثر على تنفيذ الأعمال، مثل مشاكل في التربة أو مرافق تحت الأرض.", - "probability": 0.4, - "impact": 0.7, - "risk_score": 5.8, - "response_strategy": "إجراء دراسات واختبارات مفصلة للموقع قبل بدء التنفيذ، وتخصيص احتياطي للطوارئ." - }, - { - "id": "R-007", - "name": "تضارب في التصاميم", - "category": "مخاطر فنية", - "description": "تعارض بين مختلف تخصصات التصميم (معماري، إنشائي، كهروميكانيكي) يؤدي إلى تأخير وإعادة عمل.", - "probability": 0.4, - "impact": 0.6, - "risk_score": 5.4, - "response_strategy": "مراجعة شاملة للتصاميم قبل البدء في التنفيذ واستخدام نمذجة معلومات البناء (BIM) لكشف التعارضات." - }, - { - "id": "R-008", - "name": "تأخر الموافقات", - "category": "مخاطر تنظيمية", - "description": "تأخر في الحصول على الموافقات والتصاريح اللازمة من الجهات المختصة.", - "probability": 0.5, - "impact": 0.5, - "risk_score": 5.0, - "response_strategy": "التخطيط المبكر للتصاريح المطلوبة وبناء علاقات جيدة مع الجهات التنظيمية." - }, - { - "id": "R-009", - "name": "عدم توفر المعدات", - "category": "مخاطر الموارد", - "description": "صعوبة في توفير المعدات المتخصصة في الوقت المطلوب.", - "probability": 0.3, - "impact": 0.6, - "risk_score": 4.8, - "response_strategy": "حجز المعدات مبكراً وتوفير بدائل محتملة في حالة عدم توفر المعدات الأساسية." - }, - { - "id": "R-010", - "name": "ظروف جوية قاسية", - "category": "مخاطر خارجية", - "description": "تأثير الظروف ��لجوية القاسية (حرارة شديدة، أمطار غزيرة، عواصف رملية) على سير العمل.", - "probability": 0.3, - "impact": 0.5, - "risk_score": 4.5, - "response_strategy": "تخطيط الجدول الزمني مع مراعاة المواسم وإضافة مخزون زمني للظروف الجوية غير المتوقعة." - } - ] - - # حساب درجات المخاطرة بناءً على الميزات المدخلة - for risk in potential_risks: - # تعديل احتمالية حدوث المخاطر بناءً على العوامل المدخلة - if risk["id"] == "R-001": # غرامة تأخير - risk["probability"] = risk["probability"] * (10 - features["penalty_clause"]) / 10 - risk["probability"] = risk["probability"] * (10 - features["completion_deadline"]) / 10 - - elif risk["id"] == "R-002": # تقلبات أسعار المواد - risk["probability"] = risk["probability"] * features["market_volatility"] / 10 - - elif risk["id"] == "R-003": # ضعف تدفق المدفوعات - risk["probability"] = risk["probability"] * (10 - features["payment_terms"]) / 10 - - if features["client_type"] == "حكومي": - risk["probability"] = risk["probability"] * 0.6 # احتمالية أقل مع العملاء الحكوميين - elif features["client_type"] == "أفراد": - risk["probability"] = risk["probability"] * 1.3 # احتمالية أعلى مع العملاء الأفراد - - elif risk["id"] == "R-004": # نقص العمالة الماهرة - risk["probability"] = risk["probability"] * features["technical_complexity"] / 10 - - elif risk["id"] == "R-005": # تغييرات في نطاق العمل - risk["probability"] = risk["probability"] * features["technical_complexity"] / 10 - - if features["client_type"] == "حكومي": - risk["probability"] = risk["probability"] * 1.2 # احتمالية أعلى للتغييرات مع العملاء الحكوميين - - # تعديل تأثير المخاطر بناءً على العوامل المدخلة - if risk["category"] == "مخاطر فنية": - risk["impact"] = risk["impact"] * (10 - features["company_experience"]) / 10 - - # إعادة حساب درجة المخاطرة - risk["risk_score"] = round(risk["probability"] * risk["impact"] * 10, 1) - - # ترتيب المخاطر تنازلياً حسب درجة المخاطرة - sorted_risks = sorted(potential_risks, key=lambda x: x["risk_score"], reverse=True) - - # حساب عدد المخاطر حسب شدتها - high_risks = sum(1 for risk in sorted_risks if risk["risk_score"] >= 6.0) - medium_risks = sum(1 for risk in sorted_risks if 3.0 <= risk["risk_score"] < 6.0) - low_risks = sum(1 for risk in sorted_risks if risk["risk_score"] < 3.0) - - # حساب متوسط درجة المخاطرة - avg_risk_score = sum(risk["risk_score"] for risk in sorted_risks) / len(sorted_risks) - - # تجهيز النتائج - results = { - "top_risks": sorted_risks, - "high_risks": high_risks, - "medium_risks": medium_risks, - "low_risks": low_risks, - "avg_risk_score": avg_risk_score, - "risk_profile": { - "financial_risk": sum(risk["risk_score"] for risk in sorted_risks if risk["category"] == "مخاطر مالية") / sum(1 for risk in sorted_risks if risk["category"] == "مخاطر مالية") if sum(1 for risk in sorted_risks if risk["category"] == "مخاطر مالية") > 0 else 0, - "technical_risk": sum(risk["risk_score"] for risk in sorted_risks if risk["category"] == "مخاطر فنية") / sum(1 for risk in sorted_risks if risk["category"] == "مخاطر فنية") if sum(1 for risk in sorted_risks if risk["category"] == "مخاطر فنية") > 0 else 0, - "market_risk": sum(risk["risk_score"] for risk in sorted_risks if risk["category"] == "مخاطر السوق") / sum(1 for risk in sorted_risks if risk["category"] == "مخاطر السوق") if sum(1 for risk in sorted_risks if risk["category"] == "مخاطر السوق") > 0 else 0, - "resource_risk": sum(risk["risk_score"] for risk in sorted_risks if risk["category"] == "مخاطر الموارد") / sum(1 for risk in sorted_risks if risk["category"] == "مخاطر الموارد") if sum(1 for risk in sorted_risks if risk["category"] == "مخاطر الموارد") > 0 else 0, - "contract_risk": sum(risk["risk_score"] for risk in sorted_risks if risk["category"] == "مخاطر تعاقدية") / sum(1 for risk in sorted_risks if risk["category"] == "مخاطر تعاقدية") if sum(1 for risk in sorted_risks if risk["category"] == "مخاطر تعاقدية") > 0 else 0, - "regulatory_risk": sum(risk["risk_score"] for risk in sorted_risks if risk["category"] == "مخاطر تنظيمية") / sum(1 for risk in sorted_risks if risk["category"] == "مخاطر تنظيمية") if sum(1 for risk in sorted_risks if risk["category"] == "مخاطر تنظيمية") > 0 else 0 - }, - "overall_assessment": "", - "recommendation": "" - } - - # تقييم شامل للمخاطر - if avg_risk_score >= 6.0: - results["overall_assessment"] = "مشروع عالي المخاطر" - results["recommendation"] = "ينصح بإعادة التفاوض على شروط العقد أو إضافة هامش ربح أعلى لتغطية المخاطر." - elif avg_risk_score >= 4.0: - results["overall_assessment"] = "مشروع متوسط المخاطر" - results["recommendation"] = "متابعة دقيقة للمخاطر العالية ووضع خطط استجابة مفصلة لها." - else: - results["overall_assessment"] = "مشروع منخفض المخاطر" - results["recommendation"] = "مراقبة المخاطر بشكل دوري والتركيز على تحسين الأداء." - - return results + # إدخال بيانات المشروع + st.subheader("بيانات المشروع") - def _display_risk_analysis_results(self, results): - """عرض نتائج تحليل المخاطر""" - - st.markdown("### نتائج تحليل المخاطر") - - # عرض ملخص تقييم المخاطر - st.markdown(f"#### التقييم العام: {results['overall_assessment']}") - - # عرض الإحصائيات الرئيسية للمخاطر - col1, col2, col3, col4 = st.columns(4) - - with col1: - st.metric("متوسط درجة المخاطرة", f"{results['avg_risk_score']:.1f}/10") - - with col2: - st.metric("المخاطر العالية", f"{results['high_risks']}") - - with col3: - st.metric("المخاطر المتوسطة", f"{results['medium_risks']}") - - with col4: - st.metric("المخاطر المنخفضة", f"{results['low_risks']}") - - # عرض ملف المخاطر حسب الفئة - st.markdown("#### ملف المخاطر حسب الفئة") - - # تجهيز البيانات للرسم البياني - risk_profile_data = pd.DataFrame({ - 'الفئة': [ - "مخاطر مالية", - "مخاطر فنية", - "مخاطر السوق", - "مخاطر الموارد", - "مخاطر تعاقدية", - "مخاطر تنظيمية" - ], - 'درجة المخاطرة': [ - results['risk_profile']['financial_risk'], - results['risk_profile']['technical_risk'], - results['risk_profile']['market_risk'], - results['risk_profile']['resource_risk'], - results['risk_profile']['contract_risk'], - results['risk_profile']['regulatory_risk'] - ] - }) - - # رسم مخطط شعاعي لملف المخاطر - fig = px.line_polar( - risk_profile_data, - r='درجة المخاطرة', - theta='الفئة', - line_close=True, - range_r=[0, 10], - title="ملف المخاطر حسب الفئة" + col1, col2 = st.columns(2) + with col1: + project_name = st.text_input("اسم المشروع", key="cost_project_name") + project_type = st.selectbox( + "نوع المشروع", + ["إنشائي", "طرق", "جسور", "مباني", "بنية تحتية", "أخرى"], + key="cost_project_type" ) - - st.plotly_chart(fig, use_container_width=True) - - # عرض المخاطر الرئيسية - st.markdown("#### المخاطر الرئيسية") - - # إنشاء جدول المخاطر - risk_table_data = [] - - for risk in results['top_risks'][:5]: # عرض أعلى 5 مخاطر فقط - risk_level = "عالية" if risk["risk_score"] >= 6.0 else "متوسطة" if risk["risk_score"] >= 3.0 else "منخفضة" - risk_color = "red" if risk_level == "عالية" else "orange" if risk_level == "متوسطة" else "green" - - risk_table_data.append({ - "المعرف": risk["id"], - "الوصف": risk["name"], - "الفئة": risk["category"], - "الاحتمالية": f"{risk['probability'] * 100:.0f}%", - "التأثير": f"{risk['impact'] * 100:.0f}%", - "درجة المخاطرة": risk["risk_score"], - "المستوى": risk_level, - "استراتيجية الاستجابة": risk["response_strategy"], - "color": risk_color - }) - - # عرض جدول المخاطر - risk_df = pd.DataFrame(risk_table_data) - - # استخدام تنسيق HTML مخصص لعرض المخاطر الرئيسية - for index, row in risk_df.iterrows(): - with st.container(): - st.markdown(f""" -
-
{row['المعرف']} - {row['الوصف']} {row['المستوى']}
-

الفئة: {row['الفئة']} | الاحتمالية: {row['الاحتمالية']} | التأثير: {row['التأثير']} | درجة المخاطرة: {row['درجة المخاطرة']}/10

-

استراتيجية الاستجابة: {row['استراتيجية الاستجابة']}

-
- """, unsafe_allow_html=True) - - # عرض توصيات عامة - st.markdown("#### التوصيات العامة") - st.info(results["recommendation"]) - - # عرض تحليل Claude AI إذا كان متوفراً - if "claude_analysis" in results: - st.markdown("### تحليل Claude AI المتقدم") - st.success(results["claude_analysis"]) - - # زر تصدير تقرير المخاطر - if st.button("تصدير تقرير المخاطر"): - st.success("تم تصدير تقرير المخاطر بنجاح!") + project_area = st.number_input("المساحة (متر مربع)", min_value=0, value=1000) - def _render_document_analysis_tab(self): - """عرض تبويب تحليل المستندات""" - - st.markdown("### تحليل المستندات") - - # خيارات رفع الملفات - st.markdown("#### رفع ملفات المناقصة") - - # رفع الملفات - uploaded_files = st.file_uploader( - "اختر ملفات المناقصة للتحليل", - type=["pdf", "docx", "doc", "xls", "xlsx", "jpg", "jpeg", "png"], - accept_multiple_files=True, - key="document_analysis_files" + with col2: + project_location = st.selectbox( + "الموقع", + ["الرياض", "جدة", "الدمام", "مكة", "المدينة", "أخرى"] ) - - # اختيار نموذج التحليل - analysis_model = st.radio( - "اختر نموذج التحليل", - [ - "استخراج البنود والمواصفات", - "استخراج الشروط التعاقدية", - "تحليل الكميات", - "تحليل المتطلبات القانونية", - "تحليل شامل (يستخدم Claude AI)" - ], - horizontal=True + project_quality = st.selectbox( + "مستوى الجودة", + ["اقتصادي", "متوسط", "فاخر", "ممتاز"] ) - - # زر بدء التحليل - if uploaded_files and st.button("بدء تحليل المستندات"): - with st.spinner("جاري تحليل المستندات..."): - # محاكاة وقت المعالجة - time.sleep(3) - - # معالجة الملفات المرفوعة - analysis_results = self._analyze_documents(uploaded_files, analysis_model) - - # عرض نتائج التحليل - self._display_document_analysis_results(analysis_results) + has_basement = st.checkbox("يتضمن بدروم") - def _analyze_documents(self, files, analysis_model): - """تحليل المستندات المرفوعة""" - - # في البيئة الحقيقية، سيتم استدعاء نموذج تحليل المستندات - # محاكاة نتائج تحليل المستندات للعرض - - # نتائج التحليل المبدئية - basic_results = { - "file_count": len(files), - "file_names": [file.name for file in files], - "file_sizes": [f"{file.size / 1024:.1f} KB" for file in files], - "file_types": [file.type or "غير محدد" for file in files], - "extracted_text_samples": {}, - "entities": [], - "tender_items": [], - "contract_terms": [], - "quantities": [], - "legal_requirements": [], - "summary": "" - } - - # محاكاة استخراج نص من الملفات - for file in files: - # استخراج عينة نصية (في البيئة الحقيقية سيتم استخراج النص الكامل) - sample_text = f"عينة نصية مستخرجة من الملف {file.name}. هذا النص لأغراض العرض فقط." - basic_results["extracted_text_samples"][file.name] = sample_text - - # محاكاة تحليل المحتوى حسب نموذج التحليل المختار - if analysis_model == "استخراج البنود والمواصفات" or analysis_model == "تحليل شامل (يستخدم Claude AI)": - basic_results["tender_items"] = [ - { - "id": "T-001", - "description": "أعمال الحفر والردم", - "unit": "م³", - "quantity": 1500, - "estimated_price": 85, - "specifications": "حفر في أي نوع من التربة بما في ذلك الصخور والردم باستخدام مواد معتمدة." - }, - { - "id": "T-002", - "description": "أعمال الخرسانة المسلحة للأساسات", - "unit": "م³", - "quantity": 750, - "estimated_price": 1200, - "specifications": "خرسانة مسلحة بقوة 30 نيوتن/مم² بعد 28 يوم، مع حديد تسليح من الفئة 60." - }, - { - "id": "T-003", - "description": "أعمال الخرسانة المسلحة للهيكل", - "unit": "م³", - "quantity": 1200, - "estimated_price": 1350, - "specifications": "خرسانة مسلحة بقوة 30 نيوتن/مم² بعد 28 يوم، مع حديد تسليح من الفئة 60." - }, - { - "id": "T-004", - "description": "أعمال الطابوق", - "unit": "م²", - "quantity": 3500, - "estimated_price": 120, - "specifications": "جدران طابوق مفرغ سمك 20 سم مع مونة إسمنتية." - }, - { - "id": "T-005", - "description": "أعمال التشطيبات الداخلية", - "unit": "م²", - "quantity": 5000, - "estimated_price": 200, - "specifications": "تشطيبات داخلية تشمل اللياسة والدهان والأرضيات حسب المواصفات المرفقة." - } - ] - - if analysis_model == "استخراج الشروط التعاقدية" or analysis_model == "تحليل شامل (يستخدم Claude AI)": - basic_results["contract_terms"] = [ - { - "id": "C-001", - "title": "مدة تنفيذ المشروع", - "description": "يجب إنجاز جميع الأعمال خلال 18 شهراً من تاريخ تسليم الموقع.", - "risk_level": "متوسط", - "notes": "مدة تنفيذ معقولة نسبياً للحجم المتوقع من الأعمال." - }, - { - "id": "C-002", - "title": "غرامة التأخير", - "description": "تفرض غرامة تأخير بنسبة 0.1% من قيمة العقد عن كل يوم تأخير، بحد أقصى 10% من القيمة الإجمالية للعقد.", - "risk_level": "عالي", - "notes": "غرامة مرتفعة نسبياً، تتطلب جدولة دقيقة وإدارة استباقية للمخاطر." - }, - { - "id": "C-003", - "title": "شروط الدفع", - "description": "يتم صرف المستخلصات خلال 45 يوماً من تاريخ تقديمها، مع خصم نسبة 10% كضمان حسن التنفيذ تسترد بعد فترة الضمان.", - "risk_level": "متوسط", - "notes": "فترة 45 يوماً طويلة نسبياً وقد تؤثر على التدفق النقدي." - }, - { - "id": "C-004", - "title": "التزامات المحتوى المحلي", - "description": "يجب أن لا تقل نسبة المحتوى المحلي عن 30% من إجمالي قيمة العقد.", - "risk_level": "منخفض", - "notes": "يمكن تحقيق النسبة المطلوبة من خلال توريد المواد والعمالة المحلية." - }, - { - "id": "C-005", - "title": "التغييرات والأعمال الإضافية", - "description": "يحق للمالك طلب تغييرات بنسبة ±10% من قيمة العقد دون تعديل أسعار الوحدات.", - "risk_level": "متوسط", - "notes": "نسبة معقولة، لكن يجب مراعاة احتمالية الطلبات الإضافية عند تسعير البنود." - } - ] - - if analysis_model == "تحليل الكميات" or analysis_model == "تحليل شامل (يستخدم Claude AI)": - basic_results["quantities"] = [ - { - "category": "أعمال الحفر والردم", - "volume": 1500, - "unit": "م³", - "estimated_cost": 127500 - }, - { - "category": "أعمال الخرسانة", - "volume": 1950, - "unit": "م³", - "estimated_cost": 2437500 - }, - { - "category": "أعمال الطابوق", - "volume": 3500, - "unit": "م²", - "estimated_cost": 420000 - }, - { - "category": "أعمال التشطيبات الداخلية", - "volume": 5000, - "unit": "م²", - "estimated_cost": 1000000 - }, - { - "category": "أعمال التشطيبات الخارجية", - "volume": 2200, - "unit": "م²", - "estimated_cost": 660000 - }, - { - "category": "أعمال الكهروميكانيكية", - "volume": 1, - "unit": "مقطوعية", - "estimated_cost": 1750000 - } - ] - - if analysis_model == "تحليل المتطلبات القانونية" or analysis_model == "تحليل شامل (يستخدم Claude AI)": - basic_results["legal_requirements"] = [ - { - "id": "L-001", - "title": "متطلبات التراخيص", - "description": "يجب أن يكون المقاول حاصلاً على تصنيف في الفئة الأولى في مجال المباني.", - "compliance_status": "مطلوب التحقق", - "required_documents": "شهادة التصنيف سارية المفعول" - }, - { - "id": "L-002", - "title": "متطلبات التأمين", - "description": "يجب تقديم بوليصة تأمين شاملة تغطي جميع مخاطر المشروع بقيمة لا تقل عن 100% من قيمة العقد.", - "compliance_status": "مطلوب التحقق", - "required_documents": "وثائق التأمين الشاملة" - }, - { - "id": "L-003", - "title": "متطلبات الضمان البنكي", - "description": "يجب تقديم ضمان بنكي ابتدائي بنسبة 2% من قيمة العطاء، وضمان نهائي بنسبة 5% من قيمة العقد.", - "compliance_status": "مطلوب التحقق", - "required_documents": "نماذج الضمانات البنكية" - }, - { - "id": "L-004", - "title": "متطلبات السعودة", - "description": "يجب الالتزام بنسبة السعودة المطلوبة حسب برنامج نطاقات وأن يكون المقاول في النطاق الأخضر.", - "compliance_status": "مطلوب التحقق", - "required_documents": "شهادة نطاقات سارية المفعول" - }, - { - "id": "L-005", - "title": "متطلبات الزكاة والدخل", - "description": "يجب تقديم شهادة سداد الزكاة والضريبة سارية المفعول.", - "compliance_status": "مطلوب التحقق", - "required_documents": "شهادة الزكاة والدخل" - } - ] - - # إعداد ملخص التحليل - basic_results["summary"] = f""" - تم تحليل {len(files)} ملفات بإجمالي حجم {sum([file.size for file in files]) / 1024 / 1024:.2f} ميجابايت. - - نتائج التحليل الرئيسية: - - تم استخراج {len(basic_results.get('tender_items', []))} بنود رئيسية للمناقصة. - - تم تحديد {len(basic_results.get('contract_terms', []))} شروط تعاقدية هامة. - - تم تحليل الكميات لـ {len(basic_results.get('quantities', []))} فئات من الأعمال. - - تم تحديد {len(basic_results.get('legal_requirements', []))} متطلبات قانونية. - - التوصيات: - - مراجعة شروط التعاقد وخاصة البنود المتعلقة بالغرامات والدفعات. - - تدقيق جداول الكميات والتأكد من تغطية جميع البنود اللازمة للتنفيذ. - - التحقق من استيفاء جميع المتطلبات القانونية قبل تقديم العطاء. - """ - - # إضافة تحليل متقدم باستخدام Claude AI إذا تم اختياره - if analysis_model == "تحليل شامل (يستخدم Claude AI)": - try: - # إنشاء مدخلات للتحليل - analysis_input = f""" - المناقصة: تطوير مبنى إداري متعدد الطوابق - - ملفات تم تحليلها: - {', '.join(basic_results['file_names'])} - - بنود رئيسية: - - أعمال الحفر والردم: 1500 م³ - - أعمال الخرسانة المسلحة للأساسات: 750 م³ - - أعمال الخرسانة المسلحة للهيكل: 1200 م³ - - أعمال الطابوق: 3500 م² - - أعمال التشطيبات الداخلية: 5000 م² - - شروط تعاقدية رئيسية: - - مدة التنفيذ: 18 شهر - - غرامة التأخير: 0.1% يومياً بحد أقصى 10% - - شروط الدفع: 45 يوم للمستخلصات مع خصم 10% ضمان - - المحتوى المحلي: 30% كحد أدنى - - متطلبات قانونية: - - تصنيف الفئة الأولى مباني - - تأمين شامل بنسبة 100% - - ضمان بنكي ابتدائي 2% ونهائي 5% - - الالتزام بمتطلبات السعودة (النطاق الأخضر) - - من فضلك قم بتحليل هذه المناقصة وتقديم: - 1. تقييم عام للمناقصة وجاذبيتها - 2. نقاط القوة والضعف الرئيسية - 3. المخاطر المحتملة التي يجب مراعاتها - 4. توصيات للتسعير المناسب - 5. استراتيجية مقترحة للتنافس على المناقصة - """ - - # استدعاء خدمة Claude للتحليل - claude_response = self.claude_service.chat_completion( - [{"role": "user", "content": analysis_input}] - ) - - if "error" not in claude_response: - # إضافة تحليل Claude إلى النتائج - basic_results["claude_analysis"] = claude_response["content"] - except Exception as e: - logging.error(f"فشل في تحليل المستندات باستخدام Claude AI: {str(e)}") - - return basic_results + # أقسام التكاليف + st.subheader("أقسام التكاليف") - def _display_document_analysis_results(self, results): - """عرض نتائج تحليل المستندات""" - - st.markdown("### نتائج تحليل المستندات") - - # عرض ملخص التحليل - st.markdown("#### ملخص التحليل") - st.info(results["summary"]) - - # عرض البنود المستخرجة من المناقصة إذا وجدت - if results["tender_items"]: - st.markdown("#### بنود المناقصة المستخرجة") - - # إنشاء DataFrame للبنود - items_df = pd.DataFrame(results["tender_items"]) + col1, col2, col3 = st.columns(3) + with col1: + structural_percent = st.slider("الهيكل الإنشائي (%)", 0, 100, 35) + with col2: + finishing_percent = st.slider("التشطيبات (%)", 0, 100, 40) + with col3: + mep_percent = st.slider("الكهروميكانيك (%)", 0, 100, 25) + + # التنبؤ بالتكاليف + if st.button("تحليل التكاليف"): + with st.spinner("جاري تحليل التكاليف..."): + # محاكاة تحليل التكاليف باستخدام الذكاء الاصطناعي + time.sleep(1) # محاكاة وقت المعالجة + + # حساب التكلفة التقديرية بناءً على المدخلات + base_cost = 0 + if project_type == "إنشائي": + base_cost = 2000 + elif project_type == "طرق": + base_cost = 1500 + elif project_type == "جسور": + base_cost = 3000 + elif project_type == "مباني": + base_cost = 2500 + elif project_type == "بنية تحتية": + base_cost = 1800 + else: + base_cost = 2200 + + # تعديل التكلفة بناءً على الموقع + location_factor = 1.0 + if project_location == "الرياض": + location_factor = 1.1 + elif project_location == "جدة": + location_factor = 1.15 + elif project_location == "الدمام": + location_factor = 1.05 + elif project_location == "مكة": + location_factor = 1.2 + elif project_location == "المدينة": + location_factor = 1.1 + + # تعديل التكلفة بناءً على مستوى الجودة + quality_factor = 1.0 + if project_quality == "اقتصادي": + quality_factor = 0.8 + elif project_quality == "متوسط": + quality_factor = 1.0 + elif project_quality == "فاخر": + quality_factor = 1.3 + elif project_quality == "ممتاز": + quality_factor = 1.5 + + # تعديل التكلفة إذا كان يتضمن بدروم + basement_factor = 1.2 if has_basement else 1.0 + + # حساب التكلفة الإجمالية + total_cost = base_cost * project_area * location_factor * quality_factor * basement_factor + + # حساب تكلفة كل قسم + structural_cost = total_cost * (structural_percent / 100) + finishing_cost = total_cost * (finishing_percent / 100) + mep_cost = total_cost * (mep_percent / 100) + + # عرض النتائج + st.subheader("نتائج تحليل التكاليف") - # عرض الجدول بشكل منسق - st.dataframe( - items_df[["id", "description", "unit", "quantity", "estimated_price"]], - use_container_width=True - ) + col1, col2 = st.columns(2) + with col1: + st.metric("التكلفة الإجمالية التقديرية", f"{total_cost:,.2f} ريال") + st.metric("تكلفة المتر المربع", f"{total_cost/project_area:,.2f} ريال/م²") - # عرض مخطط للتكاليف المقدرة - costs = [item["quantity"] * item["estimated_price"] for item in results["tender_items"]] - labels = [item["description"] for item in results["tender_items"]] + with col2: + st.metric("تكلفة الهيكل الإنشائي", f"{structural_cost:,.2f} ريال") + st.metric("تكلفة التشطيبات", f"{finishing_cost:,.2f} ريال") + st.metric("تكلفة الكهروميكانيك", f"{mep_cost:,.2f} ريال") + + # عرض الرسم البياني للتكاليف + cost_data = pd.DataFrame({ + "القسم": ["الهيكل الإنشائي", "التشطيبات", "الكهروميكانيك"], + "التكلفة": [structural_cost, finishing_cost, mep_cost] + }) fig = px.pie( - names=labels, - values=costs, - title="توزيع التكاليف المقدرة حسب البنود" + cost_data, + values="التكلفة", + names="القسم", + title="توزيع التكاليف", + color_discrete_sequence=px.colors.qualitative.Set2 ) - st.plotly_chart(fig, use_container_width=True) - - # عرض الشروط التعاقدية إذا وجدت - if results["contract_terms"]: - st.markdown("#### الشروط التعاقدية الهامة") - - # عرض كل شرط في قسم منفصل - for term in results["contract_terms"]: - risk_color = "red" if term["risk_level"] == "عالي" else "orange" if term["risk_level"] == "متوسط" else "green" - - st.markdown(f""" -
-
{term['id']} - {term['title']} مستوى الخطورة: {term['risk_level']}
-

{term['description']}

-

ملاحظات: {term['notes']}

-
- """, unsafe_allow_html=True) - - # عرض تحليل الكميات إذا وجد - if results["quantities"]: - st.markdown("#### تحليل الكميات") - - # إنشاء DataFrame للكميات - quantities_df = pd.DataFrame(results["quantities"]) - - # عرض الجدول بشكل منسق - st.dataframe(quantities_df, use_container_width=True) - - # عرض مخطط شريطي للتكاليف المقدرة - fig = px.bar( - quantities_df, - x="category", - y="estimated_cost", - title="التكاليف المقدرة حسب فئة الأعمال", - labels={"category": "فئة الأعمال", "estimated_cost": "التكلفة المقدرة (ريال)"} + fig.update_layout( + font=dict(size=14), + legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1) ) - fig.update_traces(text=quantities_df["estimated_cost"], textposition="outside") - - st.plotly_chart(fig, use_container_width=True) - - # عرض المتطلبات القانونية إذا وجدت - if results["legal_requirements"]: - st.markdown("#### المتطلبات القانونية") + st.plotly_chart(fig) - # عرض المتطلبات في جدول - legal_df = pd.DataFrame(results["legal_requirements"]) + # تحليل ذكي للتكاليف باستخدام Claude + st.subheader("تحليل ذكي للتكاليف") - # عرض الجدول بشكل منسق - st.dataframe( - legal_df[["id", "title", "description", "compliance_status", "required_documents"]], - use_container_width=True - ) + # إنشاء توجيه للنموذج + prompt = f""" + قم بتحليل تكاليف مشروع بالمواصفات التالية: + - اسم المشروع: {project_name} + - نوع المشروع: {project_type} + - المساحة: {project_area} متر مربع + - الموقع: {project_location} + - مستوى الجودة: {project_quality} + - يتضمن بدروم: {"نعم" if has_basement else "لا"} - # عرض قائمة تحقق للمتطلبات القانونية - st.markdown("##### قائمة التحقق من المتطلبات القانونية") - - for req in results["legal_requirements"]: - st.checkbox(f"{req['title']} - {req['description']}", key=f"req_{req['id']}") - - # عرض تحليل Claude AI المتقدم إذا وجد - if "claude_analysis" in results: - st.markdown("### تحليل Claude AI المتقدم") - st.success(results["claude_analysis"]) - - # أزرار إضافية - col1, col2 = st.columns(2) - - with col1: - if st.button("تصدير تقرير تحليل المستندات"): - st.success("تم تصدير تقرير تحليل المستندات بنجاح!") - - with col2: - if st.button("استخراج جدول الكميات"): - st.success("تم استخراج جدول الكميات بنجاح!") - - def _render_local_content_tab(self): - """عرض تبويب المحتوى المحلي""" - - st.markdown("### المحتوى المحلي") - - st.markdown(""" - وحدة حساب المحتوى المحلي تساعدك في تحليل وتحسين نسبة المحتوى المحلي في مشروعك طبقاً لمتطلبات هيئة المحتوى المحلي والمشتريات الحكومية. - """) - - # عرض علامات تبويب فرعية - lc_tabs = st.tabs([ - "حساب المحتوى المحلي", - "قاعدة بيانات الموردين", - "التقارير", - "التحسين" - ]) - - with lc_tabs[0]: - self._render_lc_calculator_tab() + التكلفة الإجمالية التقديرية: {total_cost:,.2f} ريال + تكلفة المتر المربع: {total_cost/project_area:,.2f} ريال/م² - with lc_tabs[1]: - self._render_lc_suppliers_tab() + توزيع التكاليف: + - الهيكل الإنشائي: {structural_cost:,.2f} ريال ({structural_percent}%) + - التشطيبات: {finishing_cost:,.2f} ريال ({finishing_percent}%) + - الكهروميكانيك: {mep_cost:,.2f} ريال ({mep_percent}%) - with lc_tabs[2]: - self._render_lc_reports_tab() + قدم تحليلاً شاملاً للتكاليف وتوصيات لتحسين الكفاءة وتقليل التكاليف. + """ - with lc_tabs[3]: - self._render_lc_optimization_tab() - - def _render_lc_calculator_tab(self): - """عرض تبويب حساب المحتوى المحلي""" - - st.markdown("#### حساب المحتوى المحلي") - - # نموذج إدخال بيانات المشروع - st.markdown("##### بيانات المشروع") - - col1, col2 = st.columns(2) - - with col1: - project_name = st.text_input("اسم المشروع", "مبنى إداري الرياض") - project_value = st.number_input("القيمة الإجمالية للمشروع (ريال)", min_value=1000, value=10000000) - - with col2: - target_lc = st.slider("نسبة المحتوى المحلي المستهدفة (%)", 0, 100, 40) - calculation_method = st.selectbox( - "طريقة الحساب", - [ - "الطريقة القياسية (المدخلات)", - "طريقة القيمة المضافة", - "الطريقة المختلطة" - ] + # استدعاء النموذج + response = self.claude_service.chat_completion( + [{"role": "user", "content": prompt}], + model_name=st.session_state.selected_model ) - - # جدول مكونات المشروع - st.markdown("##### مكونات المشروع") - - # إعداد بيانات المكونات الافتراضية - if 'lc_components' not in st.session_state: - st.session_state.lc_components = [ - { - "id": 1, - "name": "الخرسانة المسلحة", - "category": "مواد", - "value": 3000000, - "local_content": 85, - "supplier": "شركة الإنشاءات السعودية" - }, - { - "id": 2, - "name": "الأعمال الكهربائية", - "category": "أنظمة", - "value": 1500000, - "local_content": 65, - "supplier": "مؤسسة الطاقة المتقدمة" - }, - { - "id": 3, - "name": "أعمال التكييف", - "category": "أنظمة", - "value": 1200000, - "local_content": 55, - "supplier": "شركة التبريد العالمية" - }, - { - "id": 4, - "name": "الواجهات والنوافذ", - "category": "مواد", - "value": 800000, - "local_content": 45, - "supplier": "شركة الزجاج المتطورة" - }, - { - "id": 5, - "name": "أعمال التشطيبات", - "category": "مواد وعمالة", - "value": 1200000, - "local_content": 80, - "supplier": "مؤسسة التشطيبات الحديثة" - }, - { - "id": 6, - "name": "الأثاث والتجهيزات", - "category": "أثاث", - "value": 900000, - "local_content": 30, - "supplier": "شركة الأثاث المكتبي" - }, - { - "id": 7, - "name": "أنظمة الأمن والمراقبة", - "category": "أنظمة", - "value": 600000, - "local_content": 40, - "supplier": "شركة الأنظمة الأمنية المتقدمة" - }, - { - "id": 8, - "name": "العمالة المباشرة", - "category": "عمالة", - "value": 800000, - "local_content": 50, - "supplier": "داخلي" - } - ] - - # عرض جدول المكونات للتعديل - for i, component in enumerate(st.session_state.lc_components): - col1, col2, col3, col4, col5, col6 = st.columns([2, 1, 1, 1, 2, 1]) - - with col1: - st.session_state.lc_components[i]["name"] = st.text_input( - "المكون", - component["name"], - key=f"comp_name_{i}" - ) - - with col2: - st.session_state.lc_components[i]["category"] = st.selectbox( - "الفئة", - ["مواد", "أنظمة", "عمالة", "مواد وعمالة", "أثاث", "خدمات"], - index=["مواد", "أنظمة", "عمالة", "مواد وعمالة", "أثاث", "خدمات"].index(component["category"]), - key=f"comp_category_{i}" - ) - with col3: - st.session_state.lc_components[i]["value"] = st.number_input( - "القيمة (ريال)", - min_value=0, - value=int(component["value"]), - key=f"comp_value_{i}" - ) - - with col4: - st.session_state.lc_components[i]["local_content"] = st.slider( - "المحتوى المحلي (%)", - 0, 100, int(component["local_content"]), - key=f"comp_lc_{i}" - ) - - with col5: - st.session_state.lc_components[i]["supplier"] = st.text_input( - "المورد", - component["supplier"], - key=f"comp_supplier_{i}" - ) - - with col6: - if st.button("حذف", key=f"delete_comp_{i}"): - st.session_state.lc_components.pop(i) - st.rerun() - - # زر إضافة مكون جديد - if st.button("إضافة مكون جديد"): - new_id = max([c["id"] for c in st.session_state.lc_components]) + 1 if st.session_state.lc_components else 1 - st.session_state.lc_components.append({ - "id": new_id, - "name": f"مكون جديد {new_id}", - "category": "مواد", - "value": 100000, - "local_content": 50, - "supplier": "غير محدد" - }) - st.rerun() - - # زر حساب المحتوى المحلي - col1, col2 = st.columns([1, 3]) - - with col1: - calculate_button = st.button("حساب المحتوى المحلي", use_container_width=True) - - with col2: - use_claude = st.checkbox("استخدام Claude AI للتحليل المتقدم", value=True, key="lc_use_claude") - - if calculate_button: - with st.spinner("جاري حساب وتحليل المحتوى المحلي..."): - # محاكاة وقت المعالجة - time.sleep(2) - - # حساب المحتوى المحلي - lc_results = self._calculate_local_content(st.session_state.lc_components, target_lc, calculation_method) + if "error" in response: + st.error(response["error"]) + else: + st.markdown(response["content"]) - # إضافة تحليل إضافي باستخدام Claude AI إذا تم تفعيل الخيار - if use_claude: - try: - # إنشاء نص المكونات للتحليل - components_text = "" - for comp in st.session_state.lc_components: - components_text += f""" - - {comp['name']} ({comp['category']}): - القيمة: {comp['value']:,} ريال | المحتوى المحلي: {comp['local_content']}% | المورد: {comp['supplier']} - """ - - prompt = f"""تحليل وتحسين المحتوى المحلي: - - بيانات المشروع: - - اسم المشروع: {project_name} - - القيمة الإجمالية: {project_value:,} ريال - - نسبة المحتوى المحلي المستهدفة: {target_lc}% - - النسبة المحسوبة: {lc_results['total_local_content']:.1f}% - - مكونات المشروع: - {components_text} - - المطلوب: - 1. تحليل نسبة المحتوى المحلي المحسوبة ومقارنتها بالمستهدف - 2. تحديد المكونات ذات المحتوى المحلي المنخفض التي يمكن تحسينها - 3. اقتراح بدائل محلية أو استراتيجيات لزيادة المحتوى المحلي - 4. تقديم توصيات عملية لتحقيق النسبة المستهدفة - 5. تحديد أي فرص إضافية لتحسين المحتوى المحلي في المشروع - - يرجى تقديم تحليل مهني ومختصر يركز على الجوانب الأكثر أهمية. - """ - - # استدعاء Claude للتحليل - claude_analysis = self.claude_service.chat_completion( - [{"role": "user", "content": prompt}] - ) - - if "error" not in claude_analysis: - # إضافة تحليل Claude إلى النتائج - lc_results["claude_analysis"] = claude_analysis["content"] - except Exception as e: - st.warning(f"تعذر إجراء التحليل المتقدم: {str(e)}") + # حفظ النتائج في حالة الجلسة + if "cost_analysis" not in st.session_state.analysis_results: + st.session_state.analysis_results["cost_analysis"] = [] - # عرض نتائج حساب المحتوى المحلي - self._display_local_content_results(lc_results, target_lc) + st.session_state.analysis_results["cost_analysis"].append({ + "project_name": project_name, + "project_type": project_type, + "project_area": project_area, + "project_location": project_location, + "project_quality": project_quality, + "has_basement": has_basement, + "total_cost": total_cost, + "result": response["content"], + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + +def _render_local_content_tab(self): + """عرض تبويب تحليل المحتوى المحلي""" + st.header("تحليل المحتوى المحلي") - def _calculate_local_content(self, components, target_lc, calculation_method): - """حساب المحتوى المحلي""" - - # حساب إجمالي قيمة المشروع - total_value = sum([comp["value"] for comp in components]) - - # حساب المحتوى المحلي الإجمالي - total_local_content_value = sum([comp["value"] * comp["local_content"] / 100 for comp in components]) - - # حساب نسبة المحتوى المحلي الإجمالية - total_local_content_percent = (total_local_content_value / total_value) * 100 if total_value > 0 else 0 - - # تحليل المحتوى المحلي حسب الفئة - categories = {} - for comp in components: - category = comp["category"] - if category not in categories: - categories[category] = { - "total_value": 0, - "local_content_value": 0 - } - - categories[category]["total_value"] += comp["value"] - categories[category]["local_content_value"] += comp["value"] * comp["local_content"] / 100 - - # حساب نسبة المحتوى المحلي لكل فئة - for category in categories: - if categories[category]["total_value"] > 0: - categories[category]["local_content_percent"] = (categories[category]["local_content_value"] / categories[category]["total_value"]) * 100 - else: - categories[category]["local_content_percent"] = 0 - - # تحديد المكونات ذات المحتوى المحلي المنخفض - low_lc_components = sorted( - [comp for comp in components if comp["local_content"] < 50], - key=lambda x: x["local_content"] - ) - - # تحديد المكونات ذات المحتوى المحلي المرتفع - high_lc_components = sorted( - [comp for comp in components if comp["local_content"] >= 80], - key=lambda x: x["local_content"], - reverse=True + # إدخال بيانات المشروع + st.subheader("بيانات المشروع") + + col1, col2 = st.columns(2) + with col1: + project_name = st.text_input("اسم المشروع", key="local_project_name") + project_type = st.selectbox( + "نوع المشروع", + ["إنشائي", "طرق", "جسور", "مباني", "بنية تحتية", "أخرى"], + key="local_project_type" ) - - # تقديم توصيات لتحسين المحتوى المحلي - improvement_recommendations = [] - - # توصيات للمكونات ذات المحتوى المحلي المنخفض - for comp in low_lc_components[:3]: # أخذ أقل 3 مكونات - improvement_recommendations.append({ - "component": comp["name"], - "current_lc": comp["local_content"], - "recommendation": f"البحث عن بدائل محلية لـ {comp['name']} التي تمثل {comp['value'] / total_value * 100:.1f}% من قيمة المشروع." - }) - - # حساب الفجوة بين المحتوى المحلي الفعلي والمستهدف - lc_gap = target_lc - total_local_content_percent - - # إعداد النتائج - results = { - "total_value": total_value, - "total_local_content_value": total_local_content_value, - "total_local_content": total_local_content_percent, - "target_lc": target_lc, - "lc_gap": lc_gap, - "categories": categories, - "low_lc_components": low_lc_components, - "high_lc_components": high_lc_components, - "improvement_recommendations": improvement_recommendations, - "calculation_method": calculation_method, - "components": components - } - - # تحديد حالة المحتوى المحلي - if lc_gap <= 0: - results["status"] = "تم تحقيق المستهدف" - results["color"] = "green" - elif lc_gap <= 5: - results["status"] = "قريب من المستهدف" - results["color"] = "orange" - else: - results["status"] = "بعيد عن المستهدف" - results["color"] = "red" - - return results - def _display_local_content_results(self, results, target_lc): - """عرض نتائج حساب المحتوى المحلي""" - - st.markdown("### نتائج حساب المحتوى المحلي") - - # عرض نسبة المحتوى المحلي الإجمالية + with col2: + project_value = st.number_input("قيمة المشروع (ريال)", min_value=0, value=1000000, key="local_project_value") + project_duration = st.number_input("مدة المشروع (شهر)", min_value=1, value=12, key="local_project_duration") + + # إدخال بيانات المحتوى المحلي + st.subheader("بيانات المحتوى المحلي") + + # إضافة مكونات المشروع + components = [] + + st.write("أضف مكونات المشروع:") + + # نموذج إضافة مكون جديد + with st.form("add_component_form"): col1, col2, col3 = st.columns(3) with col1: - st.metric( - "نسبة المحتوى المحلي الحالية", - f"{results['total_local_content']:.1f}%", - delta=f"{results['lc_gap']:.1f}%" if results['lc_gap'] < 0 else f"-{results['lc_gap']:.1f}%", - delta_color="normal" if results['lc_gap'] < 0 else "inverse" - ) + component_name = st.text_input("اسم المكون") with col2: - st.metric( - "النسبة المستهدفة", - f"{target_lc}%" - ) + component_value = st.number_input("القيمة (ريال)", min_value=0) with col3: - # Aquí está el problema - no podemos usar 'green' como valor para delta_color - # En lugar de eso, usamos un texto formateado para mostrar el estado - st.markdown(f""" -
-

{results["status"]}

-
- """, unsafe_allow_html=True) - - # Alternativa sin usar delta_color - # st.metric( - # "حالة المحتوى المحلي", - # results["status"] - # ) - - - # عرض مخطط مقارنة بين النسبة الحالية والمستهدفة - comparison_data = pd.DataFrame({ - 'النوع': ['النسبة الحالية', 'النسبة المستهدفة'], - 'النسبة': [results['total_local_content'], target_lc] - }) - - fig = px.bar( - comparison_data, - x='النوع', - y='النسبة', - title="مقارنة نسبة المحتوى المحلي الحالية مع المستهدفة", - color='النوع', - color_discrete_map={ - 'النسبة الحالية': results["color"], - 'النسبة المستهدفة': 'blue' - } - ) - - fig.update_layout(yaxis_range=[0, 100]) - - st.plotly_chart(fig, use_container_width=True) - - # عرض توزيع المحتوى المحلي حسب الفئة - st.markdown("#### توزيع المحتوى المحلي حسب الفئة") - - categories_data = [] - for category, data in results["categories"].items(): - categories_data.append({ - 'الفئة': category, - 'القيمة الإجمالية': data["total_value"], - 'قيمة المحتوى المحلي': data["local_content_value"], - 'نسبة المحتوى المحلي': data["local_content_percent"] - }) - - categories_df = pd.DataFrame(categories_data) - - col1, col2 = st.columns(2) + local_percentage = st.slider("نسبة المحتوى المحلي (%)", 0, 100, 50) - with col1: - fig = px.pie( - categories_df, - values='القيمة الإجمالية', - names='الفئة', - title="توزيع قيمة المشروع حسب الفئة" - ) - - st.plotly_chart(fig, use_container_width=True) + submitted = st.form_submit_button("إضافة المكون") - with col2: - fig = px.bar( - categories_df, - x='الفئة', - y='نسبة المحتوى المحلي', - title="نسبة المحتوى المحلي لكل فئة", - text_auto='.1f' - ) - - fig.update_traces(texttemplate='%{text}%', textposition='outside') - fig.update_layout(yaxis_range=[0, 100]) - - st.plotly_chart(fig, use_container_width=True) - - # عرض المكونات ذات المحتوى المحلي المنخفض - st.markdown("#### المكونات ذات المحتوى المحلي المنخفض") - - if results["low_lc_components"]: - low_lc_df = pd.DataFrame([ - { - 'المكون': comp["name"], - 'الفئة': comp["category"], - 'القيمة': comp["value"], - 'نسبة المحتوى المحلي': comp["local_content"], - 'المورد': comp["supplier"] - } - for comp in results["low_lc_components"] - ]) - - st.dataframe(low_lc_df, use_container_width=True) - - # مخطط المكونات ذات المحتوى المحلي المنخفض - fig = px.bar( - low_lc_df, - x='المكون', - y='نسبة المحتوى المحلي', - color='القيمة', - title="المكونات ذات المحتوى المحلي المنخفض", - text_auto='.1f' - ) + if submitted and component_name and component_value > 0: + # إضافة المكون إلى القائمة + if "local_content_components" not in st.session_state: + st.session_state.local_content_components = [] - fig.update_traces(texttemplate='%{text}%', textposition='outside') - - st.plotly_chart(fig, use_container_width=True) - else: - st.info("لا توجد مكونات ذات محتوى محلي منخفض (أقل من 50%).") - - # عرض توصيات لتحسين المحتوى المحلي - st.markdown("#### توصيات لتحسين المحتوى المحلي") - - if results["improvement_recommendations"]: - for recommendation in results["improvement_recommendations"]: - st.markdown(f""" -
-
{recommendation['component']} (المحتوى المحلي الحالي: {recommendation['current_lc']}%)
-

{recommendation['recommendation']}

-
- """, unsafe_allow_html=True) - else: - st.success("المحتوى المحلي جيد ولا توجد توصيات للتحسين.") - - # عرض تحليل Claude AI المتقدم إذا كان متوفراً - if "claude_analysis" in results: - st.markdown("### تحليل Claude AI المتقدم") - st.info(results["claude_analysis"]) + st.session_state.local_content_components.append({ + "name": component_name, + "value": component_value, + "local_percentage": local_percentage + }) - def _render_lc_suppliers_tab(self): - """عرض تبويب قاعدة بيانات الموردين للمحتوى المحلي""" - - st.markdown("#### قاعدة بيانات الموردين المحليين") - - # قائمة الفئات - categories = [ - "جميع الفئات", - "مواد بناء", - "أنظمة كهربائية", - "أنظمة ميكانيكية", - "تشطيبات", - "أثاث ومفروشات", - "خدمات هندسية", - "أنظمة أمنية", - "معدات وآليات" - ] - - # اختيار الفئة - selected_category = st.selectbox("فئة الموردين", categories) - - # البحث - search_query = st.text_input("البحث عن مورد") + # عرض المكونات المضافة + if "local_content_components" in st.session_state and st.session_state.local_content_components: + st.subheader("المكونات المضافة") + + components_data = [] + for i, component in enumerate(st.session_state.local_content_components): + components_data.append({ + "#": i + 1, + "المكون": component["name"], + "القيمة (ريال)": component["value"], + "نسبة المحتوى المحلي (%)": component["local_percentage"], + "قيمة المحتوى المحلي (ريال)": component["value"] * (component["local_percentage"] / 100) + }) - # إعداد قائمة الموردين - suppliers = [ - { - "id": 1, - "name": "شركة الإنشاءات السعودية", - "category": "مواد بناء", - "lc_rating": 95, - "quality_rating": 4.5, - "location": "الرياض", - "contact": "info@saudiconstruction.com", - "description": "شركة متخصصة في توريد جميع أنواع مواد البناء ذات المنشأ المحلي." - }, - { - "id": 2, - "name": "مؤسسة الطاقة المتقدمة", - "category": "أنظمة كهربائية", - "lc_rating": 85, - "quality_rating": 4.2, - "location": "جدة", - "contact": "sales@advancedpower.com", - "description": "مؤسسة متخصصة في توريد وتركيب الأنظمة الكهربائية والطاقة المتجددة." - }, - { - "id": 3, - "name": "شركة التبريد العالمية", - "category": "أنظمة ميكانيكية", - "lc_rating": 75, - "quality_rating": 4.0, - "location": "الدمام", - "contact": "info@globalcooling.com", - "description": "شركة متخصصة في أنظمة التكييف والتبريد المركزي للمشاريع الكبرى." - }, - { - "id": 4, - "name": "شركة الزجاج المتطورة", - "category": "مواد بناء", - "lc_rating": 80, - "quality_rating": 4.3, - "location": "الرياض", - "contact": "sales@advancedglass.com", - "description": "شركة متخصصة في إنتاج وتوريد الزجاج والواجهات الزجاجية للمباني." - }, - { - "id": 5, - "name": "مؤسسة التشطيبات الحديثة", - "category": "تشطيبات", - "lc_rating": 90, - "quality_rating": 4.7, - "location": "جدة", - "contact": "info@modernfinishing.com", - "description": "مؤسسة متخصصة في أعمال التشطيبات الداخلية والخارجية بجودة عالية." - }, - { - "id": 6, - "name": "شركة الأثاث المكتبي", - "category": "أثاث ومفروشات", - "lc_rating": 70, - "quality_rating": 4.0, - "location": "الرياض", - "contact": "sales@officefurniture.com", - "description": "شركة متخصصة في تصنيع وتوريد الأثاث المكتبي والتجهيزات المكتبية." - }, - { - "id": 7, - "name": "شركة الأنظمة الأمنية المتقدمة", - "category": "أنظمة أمنية", - "lc_rating": 65, - "quality_rating": 4.1, - "location": "الدمام", - "contact": "info@advancedsecurity.com", - "description": "شركة متخصصة في أنظمة الأمن والمراقبة والإنذار للمباني والمنشآت." - }, - { - "id": 8, - "name": "شركة المعدات الهندسية", - "category": "معدات وآليات", - "lc_rating": 85, - "quality_rating": 4.5, - "location": "جدة", - "contact": "sales@engineeringequipment.com", - "description": "شركة متخصصة في توريد وصيانة المعدات الهندسية والآليات للمشاريع." - }, - { - "id": 9, - "name": "مكتب الاستشارات الهندسية", - "category": "خدمات هندسية", - "lc_rating": 100, - "quality_rating": 4.8, - "location": "الرياض", - "contact": "info@engineeringconsultants.com", - "description": "مكتب استشاري متخصص في تقديم الخدمات الهندسية والاستشارية للمشاريع." - }, - { - "id": 10, - "name": "مصنع الحديد السعودي", - "category": "مواد بناء", - "lc_rating": 100, - "quality_rating": 4.6, - "location": "جدة", - "contact": "sales@saudisteel.com", - "description": "مصنع متخصص في إنتاج وتوريد منتجات الحديد والصلب للمشاريع الإنشائية." - } - ] + st.dataframe(pd.DataFrame(components_data)) - # تطبيق الفلترة حسب الفئة - if selected_category != "جميع الفئات": - filtered_suppliers = [s for s in suppliers if s["category"] == selected_category] + # زر حذف جميع المكونات + if st.button("حذف جميع المكونات"): + st.session_state.local_content_components = [] + st.rerun() + + # تحليل المحتوى المحلي + if st.button("تحليل المحتوى المحلي"): + if "local_content_components" not in st.session_state or not st.session_state.local_content_components: + st.warning("يرجى إضافة مكونات المشروع أولاً") else: - filtered_suppliers = suppliers - - # تطبيق فلترة البحث - if search_query: - filtered_suppliers = [s for s in filtered_suppliers if search_query.lower() in s["name"].lower() or search_query.lower() in s["description"].lower()] - - # عرض الموردين - for supplier in filtered_suppliers: - with st.container(): - col1, col2 = st.columns([3, 1]) + with st.spinner("جاري تحليل المحتوى المحلي..."): + # حساب إجمالي قيمة المشروع من المكونات + total_value = sum(component["value"] for component in st.session_state.local_content_components) + + # حساب قيمة المحتوى المحلي + local_content_value = sum( + component["value"] * (component["local_percentage"] / 100) + for component in st.session_state.local_content_components + ) + + # حساب نسبة المحتوى المحلي الإجمالية + overall_local_percentage = (local_content_value / total_value) * 100 if total_value > 0 else 0 + + # عرض النتائج + st.subheader("نتائج تحليل المحتوى المحلي") + col1, col2 = st.columns(2) with col1: - st.markdown(f""" -
-
{supplier['name']} ({supplier['category']})
-

الموقع: {supplier['location']} | التواصل: {supplier['contact']}

-

تصنيف المحتوى المحلي: {supplier['lc_rating']}% | تقييم الجودة: {supplier['quality_rating']}/5

-

{supplier['description']}

-
- """, unsafe_allow_html=True) + st.metric("إجمالي قيمة المشروع", f"{total_value:,.2f} ريال") + st.metric("قيمة المحتوى المحلي", f"{local_content_value:,.2f} ريال") with col2: - st.button(f"عرض التفاصيل #{supplier['id']}", key=f"supplier_details_{supplier['id']}") - st.button(f"إضافة للمشروع #{supplier['id']}", key=f"add_supplier_{supplier['id']}") - - # زر إضافة مورد جديد - st.button("إضافة مورد جديد") - - def _render_lc_reports_tab(self): - """عرض تبويب تقارير المحتوى المحلي""" - - st.markdown("#### تقارير المحتوى المحلي") - - # اختيار نوع التقرير - report_type = st.selectbox( - "نوع التقرير", - [ - "تقرير المحتوى المحلي للمشروع الحالي", - "تقرير مقارنة المحتوى المحلي بين المشاريع", - "تقرير التطور التاريخي للمحتوى المحلي", - "تقرير الموردين ذوي المحتوى المحلي المرتفع", - "تقرير الامتثال لمتطلبات هيئة المحتوى المحلي" - ] - ) - - # عرض محاكاة للتقرير المختار - st.markdown(f"##### {report_type}") - - if report_type == "تقرير المحتوى المحلي للمشروع الحالي": - # محاكاة تقرير المشروع الحالي - project_data = pd.DataFrame({ - 'المكون': ['الخرسانة المسلحة', 'الأعمال الكهربائية', 'أعمال التكييف', 'الواجهات والنوافذ', - 'أعمال التشطيبات', 'الأثاث والتجهيزات', 'أنظمة الأمن والمراقبة', 'العمالة المباشرة'], - 'القيمة': [3000000, 1500000, 1200000, 800000, 1200000, 900000, 600000, 800000], - 'نسبة المحتوى المحلي': [85, 65, 55, 45, 80, 30, 40, 50] - }) - - # حساب قيمة المحتوى المحلي - project_data['قيمة المحتوى المحلي'] = project_data['القيمة'] * project_data['نسبة المحتوى المحلي'] / 100 - - # إضافة نسبة من إجمالي المشروع - total_value = project_data['القيمة'].sum() - project_data['نسبة من المشروع'] = project_data['القيمة'] / total_value * 100 - - # حساب النسبة الإجمالية للمحتوى المحلي - total_lc = project_data['قيمة المحتوى المحلي'].sum() / total_value * 100 - - # عرض الإجمالي - st.metric("نسبة المحتوى المحلي الإجمالية", f"{total_lc:.1f}%") - - # عرض تفاصيل المكونات - st.dataframe(project_data.style.format({ - 'القيمة': '{:,.0f} ريال', - 'قيمة المحتوى المحلي': '{:,.0f} ريال', - 'نسبة المحتوى المحلي': '{:.1f}%', - 'نسبة من المشروع': '{:.1f}%' - }), use_container_width=True) - - # مخطط توزيع المحتوى المحلي - col1, col2 = st.columns(2) - - with col1: - fig = px.pie( - project_data, - values='القيمة', - names='المكون', - title="توزيع قيمة المشروع" + st.metric("نسبة المحتوى المحلي", f"{overall_local_percentage:.2f}%") + + # تقييم مستوى المحتوى المحلي + if overall_local_percentage >= 70: + st.success("مستوى ممتاز من المحتوى المحلي") + elif overall_local_percentage >= 50: + st.info("مستوى جيد من المحتوى المحلي") + elif overall_local_percentage >= 30: + st.warning("مستوى متوسط من المحتوى المحلي") + else: + st.error("مستوى منخفض من المحتوى المحلي") + + # عرض الرسم البياني للمحتوى المحلي + st.subheader("توزيع المحتوى المحلي") + + # إعداد بيانات الرسم البياني + chart_data = [] + for component in st.session_state.local_content_components: + local_value = component["value"] * (component["local_percentage"] / 100) + non_local_value = component["value"] - local_value + + chart_data.append({ + "المكون": component["name"], + "محتوى محلي": local_value, + "محتوى غير محلي": non_local_value + }) + + chart_df = pd.DataFrame(chart_data) + + # رسم بياني شريطي مكدس + fig = px.bar( + chart_df, + x="المكون", + y=["محتوى محلي", "محتوى غير محلي"], + title="توزيع المحتوى المحلي حسب المكونات", + color_discrete_sequence=["#2ca02c", "#d62728"] ) - st.plotly_chart(fig, use_container_width=True) - - with col2: - fig = px.pie( - project_data, - values='قيمة المحتوى المحلي', - names='المكون', - title="توزيع قيمة المحتوى المحلي" + fig.update_layout( + font=dict(size=14), + legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1) ) - st.plotly_chart(fig, use_container_width=True) - - # مخطط شريطي للمحتوى المحلي - fig = px.bar( - project_data, - x='المكون', - y='نسبة المحتوى المحلي', - title="نسبة المحتوى المحلي لكل مكون", - text_auto='.1f', - color='نسبة من المشروع' - ) - - fig.update_traces(texttemplate='%{text}%', textposition='outside') - - st.plotly_chart(fig, use_container_width=True) - - elif report_type == "تقرير مقارنة المحتوى المحلي بين المشاريع": - # محاكاة بيانات مقارنة المشاريع - projects_data = pd.DataFrame({ - 'المشروع': ['مبنى إداري الرياض', 'مجمع سكني جدة', 'مستشفى الدمام', 'مركز تجاري المدينة', 'فندق مكة'], - 'القيمة': [10000000, 15000000, 20000000, 12000000, 18000000], - 'نسبة المحتوى المحلي': [65, 55, 70, 60, 50], - 'سنة الإنجاز': [2022, 2022, 2023, 2023, 2024] - }) - - # عرض جدول المقارنة - st.dataframe(projects_data.style.format({ - 'القيمة': '{:,.0f} ريال', - 'نسبة المحتوى المحلي': '{:.1f}%' - }), use_container_width=True) - - # مخطط شريطي للمقارنة - fig = px.bar( - projects_data, - x='المشروع', - y='نسبة المحتوى المحلي', - title="مقارنة نسبة المحتوى المحلي بين المشاريع", - text_auto='.1f', - color='سنة الإنجاز' - ) - - fig.update_traces(texttemplate='%{text}%', textposition='outside') - - st.plotly_chart(fig, use_container_width=True) - - # مخطط فقاعي للمقارنة - fig = px.scatter( - projects_data, - x='القيمة', - y='نسبة المحتوى المحلي', - size='القيمة', - color='سنة الإنجاز', - text='المشروع', - title="العلاقة بين قيمة المشروع ونسبة المحتوى المحلي" - ) - - fig.update_traces(textposition='top center') - fig.update_layout(xaxis_title="قيمة المشروع (ريال)", yaxis_title="نسبة المحتوى المحلي (%)") - - st.plotly_chart(fig, use_container_width=True) - - elif report_type == "تقرير التطور التاريخي للمحتوى المحلي": - # محاكاة بيانات التطور التاريخي - historical_data = pd.DataFrame({ - 'السنة': [2019, 2020, 2021, 2022, 2023, 2024], - 'نسبة المحتوى المحلي': [45, 48, 52, 58, 62, 66], - 'المستهدف': [40, 45, 50, 55, 60, 65] - }) - - # عرض جدول التطور التاريخي - st.dataframe(historical_data.style.format({ - 'نسبة المحتوى المحلي': '{:.1f}%', - 'المستهدف': '{:.1f}%' - }), use_container_width=True) - - # مخطط خطي للتطور التاريخي - fig = px.line( - historical_data, - x='السنة', - y=['نسبة المحتوى المحلي', 'المستهدف'], - title="التطور التاريخي لنسبة المحتوى المحلي", - markers=True, - labels={'value': 'النسبة (%)', 'variable': ''} - ) - - fig.update_layout(legend_title_text='') - - st.plotly_chart(fig, use_container_width=True) - - # مخطط شريطي للمقارنة بين الفعلي والمستهدف - historical_data['الفرق'] = historical_data['نسبة المحتوى المحلي'] - historical_data['المستهدف'] - - fig = px.bar( - historical_data, - x='السنة', - y='الفرق', - title="الفرق بين نسبة المحتوى المحلي الفعلية والمستهدفة", - text_auto='.1f', - color='الفرق', - color_continuous_scale=['red', 'green'] - ) - - fig.update_traces(texttemplate='%{text}%', textposition='outside') - - st.plotly_chart(fig, use_container_width=True) - - elif report_type == "تقرير الموردين ذوي المحتوى المحلي المرتفع": - # محاكاة بيانات الموردين - suppliers_data = pd.DataFrame({ - 'المورد': ['شركة الإنشاءات السعودية', 'مؤسسة الطاقة المتقدمة', 'شركة التبريد العالمية', - 'شركة الزجاج المتطورة', 'مؤسسة التشطيبات الحديثة', 'مصنع الحديد السعودي', - 'شركة المعدات الهندسية', 'مكتب الاستشارات الهندسية'], - 'الفئة': ['مواد بناء', 'أنظمة كهربائية', 'أنظمة ميكانيكية', 'مواد بناء', - 'تشطيبات', 'مواد بناء', 'معدات وآليات', 'خدمات هندسية'], - 'نسبة المحتوى المحلي': [95, 85, 75, 80, 90, 100, 85, 100], - 'حجم التعامل': [3000000, 1500000, 1200000, 800000, 1200000, 2500000, 900000, 500000] - }) - - # عرض جدول الموردين - st.dataframe(suppliers_data.style.format({ - 'نسبة المحتوى المحلي': '{:.0f}%', - 'حجم التعامل': '{:,.0f} ريال' - }), use_container_width=True) - - # مخطط شريطي للموردين - fig = px.bar( - suppliers_data, - x='المورد', - y='نسبة المحتوى المحلي', - title="نسبة المحتوى المحلي للموردين", - text_auto='.0f', - color='الفئة' - ) - - fig.update_traces(texttemplate='%{text}%', textposition='outside') - fig.update_layout(xaxis_tickangle=-45) - - st.plotly_chart(fig, use_container_width=True) - - # مخطط فقاعي للموردين - fig = px.scatter( - suppliers_data, - x='نسبة المحتوى المحلي', - y='حجم التعامل', - size='حجم التعامل', - color='الفئة', - text='المورد', - title="العلاقة بين نسبة المحتوى المحلي وحجم التعامل مع الموردين" - ) - - fig.update_traces(textposition='top center') - fig.update_layout(xaxis_title="نسبة المحتوى المحلي (%)", yaxis_title="حجم التعامل (ريال)") - - st.plotly_chart(fig, use_container_width=True) - - elif report_type == "تقرير الامتثال لمتطلبات هيئة المحتوى المحلي": - # محاكاة بيانات الامتثال - compliance_data = pd.DataFrame({ - 'المتطلب': [ - 'نسبة المحتوى المحلي الإجمالية', - 'نسبة السعودة في القوى العاملة', - 'نسبة المنتجات المحلية', - 'نسبة الخدمات المحلية', - 'نسبة الموردين المحليين', - 'المساهمة في تطوير المحتوى المحلي' - ], - 'المستهدف': [40, 30, 50, 60, 70, 20], - 'المحقق': [38, 35, 45, 65, 75, 25], - 'حالة الامتثال': ['قريب', 'ممتثل', 'غير ممتثل', 'ممتثل', 'ممتثل', 'ممتثل'] - }) - - # إضافة ألوان لح��لة الامتثال - colors = [] - for status in compliance_data['حالة الامتثال']: - if status == 'ممتثل': - colors.append('green') - elif status == 'قريب': - colors.append('orange') - else: - colors.append('red') - - compliance_data['اللون'] = colors - - # عرض جدول الامتثال - st.dataframe(compliance_data.style.format({ - 'المستهدف': '{:.0f}%', - 'المحقق': '{:.0f}%' - }), use_container_width=True) - - # مخطط شريطي للامتثال - fig = px.bar( - compliance_data, - x='المتطلب', - y=['المستهدف', 'المحقق'], - title="مقارنة المتطلبات المستهدفة والمحققة", - barmode='group', - labels={'value': 'النسبة (%)', 'variable': ''} - ) - - st.plotly_chart(fig, use_container_width=True) - - # مخطط دائري لحالة الامتثال - status_counts = compliance_data['حالة الامتثال'].value_counts().reset_index() - status_counts.columns = ['حالة الامتثال', 'العدد'] - - fig = px.pie( - status_counts, - values='العدد', - names='حالة الامتثال', - title="توزيع حالة الامتثال للمتطلبات", - color='حالة الامتثال', - color_discrete_map={ - 'ممتثل': 'green', - 'قريب': 'orange', - 'غير ممتثل': 'red' - } - ) - - st.plotly_chart(fig, use_container_width=True) - - # أزرار التصدير - col1, col2 = st.columns(2) - - with col1: - st.download_button( - "تصدير التقرير كملف Excel", - "بيانات التقرير", - file_name=f"{report_type}.xlsx", - mime="application/vnd.ms-excel" - ) - - with col2: - st.download_button( - "تصدير التقرير كملف PDF", - "بيانات التقرير", - file_name=f"{report_type}.pdf", - mime="application/pdf" - ) - - def _render_lc_optimization_tab(self): - """عرض تبويب تحسين المحتوى المحلي""" - - st.markdown("#### تحسين المحتوى المحلي") - - st.markdown(""" - تساعدك هذه الأداة في تحسين نسبة المحتوى المحلي في المشروع من خلال تقديم توصيات وبدائل للمكونات ذات المحتوى المحلي المنخفض. - """) - - # عرض المكونات ذات المحتوى المحلي المنخفض - st.markdown("##### المكونات ذات المحتوى المحلي المنخفض") - - # محاكاة بيانات المكونات ذات المحتوى المحلي المنخفض - low_lc_components = [ - { - "id": 1, - "name": "الأثاث والتجهيزات", - "category": "أثاث", - "value": 900000, - "local_content": 30, - "supplier": "شركة الأثاث المكتبي" - }, - { - "id": 2, - "name": "أنظمة الأمن والمراقبة", - "category": "أنظمة", - "value": 600000, - "local_content": 40, - "supplier": "شركة الأنظمة الأمنية المتقدمة" - }, - { - "id": 3, - "name": "الواجهات والنوافذ", - "category": "مواد", - "value": 800000, - "local_content": 45, - "supplier": "شركة الزجاج المتطورة" - } - ] - - # عرض جدول المكونات - low_lc_df = pd.DataFrame(low_lc_components) - - st.dataframe( - low_lc_df[["name", "category", "value", "local_content", "supplier"]].rename(columns={ - "name": "المكون", - "category": "الفئة", - "value": "القيمة", - "local_content": "المحتوى المحلي", - "supplier": "المورد" - }).style.format({ - "القيمة": "{:,.0f} ريال", - "المحتوى المحلي": "{:.0f}%" - }), - use_container_width=True - ) - - # اختيار مكون للتحسين - selected_component = st.selectbox( - "اختر المكون للتحسين", - options=[comp["name"] for comp in low_lc_components], - index=0 - ) - - # الحصول على المكون المختار - selected_comp_data = next((comp for comp in low_lc_components if comp["name"] == selected_component), None) - - # عرض بدائل المكون المختار - if selected_comp_data: - st.markdown(f"##### البدائل المقترحة لـ {selected_component}") - - # محاكاة بيانات البدائل - alternatives = [] - - if selected_component == "الأثاث والتجهيزات": - alternatives = [ - { - "id": 1, - "name": "شركة الأثاث الوطني", - "description": "شركة متخصصة في تصنيع الأثاث المكتبي محلياً", - "local_content": 80, - "cost_factor": 1.05, - "quality_rating": 4.2 - }, - { - "id": 2, - "name": "مصنع التجهيزات المكتبية", - "description": "مصنع متخصص في إنتاج الأثاث المكتبي بخامات محلية", - "local_content": 90, - "cost_factor": 1.10, - "quality_rating": 4.5 - }, - { - "id": 3, - "name": "توزيع المكونات على موردين محليين", - "description": "تقسيم توريد الأثاث على عدة موردين محليين", - "local_content": 75, - "cost_factor": 1.00, - "quality_rating": 4.0 - } - ] - elif selected_component == "أنظمة الأمن والمراقبة": - alternatives = [ - { - "id": 1, - "name": "شركة التقنية الأمنية السعودية", - "description": "شركة متخصصة في تركيب وتجميع أنظمة الأمن محلياً", - "local_content": 70, - "cost_factor": 1.08, - "quality_rating": 4.0 - }, - { - "id": 2, - "name": "مؤسسة تقنيات الحماية", - "description": "توريد وتركيب أنظمة أمنية معتمدة من هيئة المحتوى المحلي", - "local_content": 65, - "cost_factor": 0.95, - "quality_rating": 3.8 - }, - { - "id": 3, - "name": "تجميع الأنظمة محلياً", - "description": "استيراد المكونات وتجميعها وبرمجتها محلياً", - "local_content": 60, - "cost_factor": 0.90, - "quality_rating": 3.7 - } - ] - elif selected_component == "الواجهات والنوافذ": - alternatives = [ - { - "id": 1, - "name": "مصنع الزجاج السعودي", - "description": "مصنع متخصص في إنتاج الزجاج والواجهات الزجاجية محلياً", - "local_content": 85, - "cost_factor": 1.15, - "quality_rating": 4.3 - }, - { - "id": 2, - "name": "شركة الألمنيوم الوطنية", - "description": "شركة متخصصة في إنتاج الواجهات والنوافذ من الألمنيوم محلياً", - "local_content": 90, - "cost_factor": 1.20, - "quality_rating": 4.5 - }, - { - "id": 3, - "name": "تعديل التصميم لاستخدام مواد محلية", - "description": "تعديل تصميم الواجهات لاستخدام نسبة أكبر من المواد المتوفرة محلياً", - "local_content": 75, - "cost_factor": 1.00, - "quality_rating": 4.0 - } - ] - - # عرض البدائل - for alt in alternatives: - with st.container(): - col1, col2, col3 = st.columns([3, 1, 1]) - - with col1: - st.markdown(f""" -
-
{alt['name']}
-

{alt['description']}

-

المحتوى المحلي: {alt['local_content']}% | معامل التكلفة: {alt['cost_factor']:.2f} | تقييم الجودة: {alt['quality_rating']}/5

-
- """, unsafe_allow_html=True) - - with col2: - st.button(f"تفاصيل #{alt['id']}", key=f"alt_details_{alt['id']}") - - with col3: - if st.button(f"اختيار #{alt['id']}", key=f"select_alt_{alt['id']}"): - st.success(f"تم اختيار {alt['name']} كبديل لـ {selected_component}.") - - # حساب تأثير البدائل على المحتوى المحلي الإجمالي - st.markdown("##### تأثير البدائل على المحتوى المحلي الإجمالي") - - # محاكاة البيانات الإجمالية - total_value = 10000000 - current_lc_value = 6000000 - current_lc_percent = current_lc_value / total_value * 100 - - # حساب التأثير لكل بديل - impact_data = [] - for alt in alternatives: - # القيمة الحالية للمحتوى المحلي في المكون - current_component_lc_value = selected_comp_data["value"] * selected_comp_data["local_content"] / 100 + st.plotly_chart(fig) - # القيمة المتوقعة للمحتوى المحلي مع البديل - new_component_value = selected_comp_data["value"] * alt["cost_factor"] - new_component_lc_value = new_component_value * alt["local_content"] / 100 + # تحليل ذكي للمحتوى المحلي باستخدام Claude + st.subheader("تحليل ذكي للمحتوى المحلي") - # الفرق في قيمة المحتوى المحلي - lc_value_diff = new_component_lc_value - current_component_lc_value + # إنشاء توجيه للنموذج + components_text = "\n".join([ + f"- {component['name']}: {component['value']:,.2f} ريال (نسبة المحتوى المحلي: {component['local_percentage']}%)" + for component in st.session_state.local_content_components + ]) - # القيمة الإجمالية الجديدة للمشروع - new_total_value = total_value - selected_comp_data["value"] + new_component_value + prompt = f""" + قم بتحليل المحتوى المحلي لمشروع بالمواصفات التالية: + - اسم المشروع: {project_name} + - نوع المشروع: {project_type} + - قيمة المشروع: {total_value:,.2f} ريال + - مدة المشروع: {project_duration} شهر - # قيمة المحتوى المحلي الإجمالية الجديدة - new_total_lc_value = current_lc_value + lc_value_diff + مكونات المشروع: + {components_text} - # نسبة المحتوى المحلي الإجمالية الجديدة - new_total_lc_percent = new_total_lc_value / new_total_value * 100 + نتائج التحليل: + - إجمالي قيمة المشروع: {total_value:,.2f} ريال + - قيمة المحتوى المحلي: {local_content_value:,.2f} ريال + - نسبة المحتوى المحلي: {overall_local_percentage:.2f}% - # إضافة البيانات - impact_data.append({ - "البديل": alt["name"], - "نسبة المحتوى المحلي الحالية": current_lc_percent, - "نسبة المحتوى المحلي المتوقعة": new_total_lc_percent, - "التغير": new_total_lc_percent - current_lc_percent, - "القيمة الإجمالية الجديدة": new_total_value, - "تقييم الجودة": alt["quality_rating"] - }) - - # عرض جدول التأثير - impact_df = pd.DataFrame(impact_data) - - st.dataframe( - impact_df.style.format({ - "نسبة المحتوى المحلي الحالية": "{:.1f}%", - "نسبة المحتوى المحلي المتوقعة": "{:.1f}%", - "التغير": "{:+.1f}%", - "القيمة الإجمالية الجديدة": "{:,.0f} ريال", - "تقييم الجودة": "{:.1f}/5" - }), - use_container_width=True - ) - - # مخطط مقارنة للبدائل - fig = px.bar( - impact_df, - x="البديل", - y=["نسبة المحتوى المحلي الحالية", "نسبة المحتوى المحلي المتوقعة"], - barmode="group", - title="مقارنة تأثير البدائل على نسبة المحتوى المحلي الإجمالية", - labels={"value": "نسبة المحتوى المحلي (%)", "variable": ""} - ) - - st.plotly_chart(fig, use_container_width=True) - - # استخدام Claude AI للتحليل المتقدم - if st.checkbox("استخدام Claude AI لتحليل البدائل", value=False, key="lc_optimization_use_claude"): - with st.spinner("جاري تحليل البدائل..."): - # محاكاة وقت المعالجة - time.sleep(2) + قدم تحليلاً شاملاً للمحتوى المحلي وتوصيات لتحسين نسبة المحتوى المحلي في المشروع. + اشرح كيف يمكن زيادة المحتوى المحلي مع الحفاظ على جودة المشروع وتكلفته التنافسية. + قدم أمثلة على موردين محليين يمكن الاستعانة بهم لزيادة المحتوى المحلي. + """ + + # استدعاء النموذج + response = self.claude_service.chat_completion( + [{"role": "user", "content": prompt}], + model_name=st.session_state.selected_model + ) + + if "error" in response: + st.error(response["error"]) + else: + st.markdown(response["content"]) - try: - # إنشاء نص المدخلات للتحليل - prompt = f"""تحليل بدائل المحتوى المحلي لمكون {selected_component}: - - المكون الحالي: - - الاسم: {selected_component} - - الفئة: {selected_comp_data['category']} - - القيمة: {selected_comp_data['value']:,} ريال - - نسبة المحتوى المحلي: {selected_comp_data['local_content']}% - - المورد: {selected_comp_data['supplier']} - - البدائل المقترحة: - 1. {alternatives[0]['name']}: - - المحتوى المحلي: {alternatives[0]['local_content']}% - - معامل التكلفة: {alternatives[0]['cost_factor']:.2f} - - تقييم الجودة: {alternatives[0]['quality_rating']}/5 - - الوصف: {alternatives[0]['description']} - - 2. {alternatives[1]['name']}: - - المحتوى المحلي: {alternatives[1]['local_content']}% - - معامل التكلفة: {alternatives[1]['cost_factor']:.2f} - - تقييم الجودة: {alternatives[1]['quality_rating']}/5 - - الوصف: {alternatives[1]['description']} - - 3. {alternatives[2]['name']}: - - المحتوى المحلي: {alternatives[2]['local_content']}% - - معامل التكلفة: {alternatives[2]['cost_factor']:.2f} - - تقييم الجودة: {alternatives[2]['quality_rating']}/5 - - الوصف: {alternatives[2]['description']} - - المطلوب: - 1. تحليل مقارن شامل للبدائل من حيث المحتوى المحلي والتكلفة والجودة - 2. تحديد البديل الأفضل مع شرح أسباب اختياره - 3. تقديم توصيات إضافية لتحسين المحتوى المحلي ل��ذا المكون - 4. تحديد أي مخاطر محتملة في الانتقال للبديل المقترح - - يرجى تقديم تحليل مهني ومختصر يركز على الجوانب الأكثر أهمية. - """ - - # استدعاء Claude للتحليل - claude_analysis = self.claude_service.chat_completion( - [{"role": "user", "content": prompt}] - ) - - if "error" not in claude_analysis: - # عرض تحليل Claude - st.markdown("##### تحليل متقدم للبدائل") - st.info(claude_analysis["content"]) - else: - st.warning(f"تعذر إجراء التحليل المتقدم: {claude_analysis['error']}") - except Exception as e: - st.warning(f"تعذر إجراء التحليل المتقدم: {str(e)}") - - # زر تطبيق البديل المختار - if st.button("تطبيق البديل المختار على المشروع"): - st.success("تم تطبيق البديل المختار على المشروع وتحديث نسبة المحتوى المحلي.") + # حفظ النتائج في حالة الجلسة + if "local_content_analysis" not in st.session_state.analysis_results: + st.session_state.analysis_results["local_content_analysis"] = [] + + st.session_state.analysis_results["local_content_analysis"].append({ + "project_name": project_name, + "project_type": project_type, + "components": st.session_state.local_content_components.copy(), + "total_value": total_value, + "local_content_value": local_content_value, + "overall_local_percentage": overall_local_percentage, + "result": response["content"], + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + +def _render_faq_tab(self): + """عرض تبويب الأسئلة الشائعة""" + st.header("الأسئلة الشائعة") - def _render_faq_tab(self): - """عرض تبويب الأسئلة الشائعة""" - - st.markdown("### الأسئلة الشائعة") - - # البحث في الأسئلة الشائعة - search_query = st.text_input("البحث في الأسئلة الشائعة", key="faq_search") - - # فلترة الأسئلة حسب البحث - if search_query: - filtered_faqs = [ - faq for faq in self.faqs - if search_query.lower() in faq["question"].lower() or search_query.lower() in faq["answer"].lower() - ] - else: - filtered_faqs = self.faqs - - # عرض الأسئلة والأجوبة - for i, faq in enumerate(filtered_faqs): - with st.expander(faq["question"]): - st.markdown(faq["answer"]) - - # زر التواصل مع الدعم - st.markdown("##### لم تجد إجابة لسؤالك؟") - col1, col2 = st.columns(2) - - with col1: - if st.button("التواصل مع الدعم الفني", use_container_width=True): - st.info("سيتم التواصل معك قريباً من قبل فريق الدعم الفني.") - - with col2: - if st.button("طرح سؤال جديد", use_container_width=True): - st.text_area("اكتب سؤالك هنا") - st.button("إرسال") \ No newline at end of file + # قائمة الأسئلة الشائعة + faqs = [ + { + "question": "ما هو وحبي - المساعد الذكي للمناقصات والعقود؟", + "answer": """ + وحبي هو نظام ذكاء اصطناعي متكامل مصمم خصيصاً لتحليل المناقصات والعقود في قطاع البناء والإنشاءات. + يوفر النظام مجموعة من الأدوات المتقدمة لتحليل المستندات، وتقييم المخاطر، وتحليل التكاليف، وتحليل المحتوى المحلي، + بالإضافة إلى مساعد ذكي يمكنه الإجابة على الاستفسارات المتعلقة بالمناقصات والعقود. + """ + }, + { + "question": "ما هي أنواع الملفات المدعومة للتحليل؟", + "answer": """ + يدعم النظام مجموعة واسعة من أنواع الملفات، بما في ذلك: + - ملفات PDF + - مستندات Word (DOCX, DOC) + - ملفات Excel (XLSX, XLS) + - الصور (JPG, PNG) + - رسومات CAD (DWG, DXF) + - نماذج BIM (IFC, RVT) + """ + }, + { + "question": "كيف يمكنني تحليل مستند مناقصة؟", + "answer": """ + لتحليل مستند مناقصة، اتبع الخطوات التالية: + 1. انتقل إلى تبويب "تحليل المستندات" + 2. اختر نوع التحليل المناسب (تحليل شامل، استخراج البنود، إلخ) + 3. ارفع ملف المستند باستخدام زر رفع الملفات + 4. انقر على زر "تحليل المستند" + 5. انتظر حتى يكتمل التحليل وعرض النتائج + """ + }, + { + "question": "كيف يمكنني تحليل المخاطر المحتملة في مشروع؟", + "answer": """ + لتحليل المخاطر المحتملة في مشروع، اتبع الخطوات التالية: + 1. انتقل إلى تبويب "تحليل المخاطر" + 2. أدخل بيانات المشروع (الاسم، النوع، القيمة، المدة) + 3. انقر على زر "تحليل المخاطر" + 4. سيعرض النظام مصفوفة المخاطر وجدول المخاطر المحتملة + 5. سيقدم النظام أيضاً تحليلاً ذكياً للمخاطر وتوصيات للتخفيف منها + """ + }, + { + "question": "كيف يمكنني حساب نسبة المحتوى المحلي في مشروع؟", + "answer": """ + لحساب نسبة المحتوى المحلي في مشروع، اتبع الخطوات التالية: + 1. انتقل إلى تبويب "تحليل المحتوى المحلي" + 2. أدخل بيانات المشروع (الاسم، النوع، القيمة، المدة) + 3. أضف مكونات المشروع مع تحديد قيمة كل مكون ونسبة المحتوى المحلي فيه + 4. انقر على زر "تحليل المحتوى المحلي" + 5. سيعرض النظام نتائج التحليل ورسوماً بيانية توضح توزيع المحتوى المحلي + 6. سيقدم النظام أيضاً تحليلاً ذكياً وتوصيات لتحسين نسبة المحتوى المحلي + """ + }, + { + "question": "هل يمكنني استخدام المساعد الذكي للإجابة على أسئلة محددة؟", + "answer": """ + نعم، يمكنك استخدام المساعد الذكي للإجابة على أسئلة محددة حول المناقصات والعقود. + انتقل إلى تبويب "المساعد الذكي" واكتب سؤالك في مربع الدردشة. + يمكنك اختيار نموذج الذكاء الاصطناعي المناسب لاحتياجاتك من القائمة المنسدلة. + """ + }, + { + "question": "هل يمكنني تحليل رسومات CAD وملفات BIM؟", + "answer": """ + نعم، يدعم النظام تحليل رسومات CAD وملفات BIM. يمكنك رفع ملفات DWG أو DXF أو IFC أو RVT، + وسيقوم النظام بتحليلها واستخراج المعلومات المهمة منها، مثل العناصر والطبقات والأبعاد. + يمكن أيضاً تحليل التكاليف والمخاطر المرتبطة بالرسومات والنماذج. + """ + }, + { + "question": "كيف يمكنني الحصول على المساعدة؟", + "answer": """ + للحصول على المساعدة، يمكنك: + 1. استخدام المساعد الذكي في تبويب "المساعد الذكي" لطرح أسئلتك + 2. الاطلاع على الأسئلة الشائعة في هذا التبويب + 3. التواصل مع فريق الدعم الفني عبر البريد الإلكتروني: support@wahbi-ai.com + 4. زيارة موقع الدعم الفني: https://support.wahbi-ai.com + """ + } + ] + + # عرض الأسئلة الشائعة + for i, faq in enumerate(faqs): + with st.expander(faq["question"]): + st.markdown(faq["answer"]) + +# إضافة الدوال إلى فئة AIAssistantApp +AIAssistantApp._render_ai_assistant_tab = _render_ai_assistant_tab +AIAssistantApp._render_document_analysis_tab = _render_document_analysis_tab +AIAssistantApp._render_contract_analysis_tab = _render_contract_analysis_tab +AIAssistantApp._render_risk_analysis_tab = _render_risk_analysis_tab +AIAssistantApp._render_cost_prediction_tab = _render_cost_prediction_tab +AIAssistantApp._render_local_content_tab = _render_local_content_tab +AIAssistantApp._render_faq_tab = _render_faq_tab + +# نقطة الدخول الرئيسية للتطبيق +def main(): + # تهيئة تطبيق المساعد الذكي + ai_app = AIAssistantApp() + + # عرض واجهة المستخدم + ai_app.render() + +if __name__ == "__main__": + main()