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"
@@ -10,7 +10,6 @@ import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
-import plotly.graph_objects as go
import requests
import json
import time
@@ -24,20 +23,6 @@ import random
from io import BytesIO
from tempfile import NamedTemporaryFile
from PIL import Image
-import arabic_reshaper
-from bidi.algorithm import get_display
-import matplotlib.font_manager as fm
-import seaborn as sns
-
-# تكوين الخطوط العربية
-plt.rcParams['font.family'] = 'Arial'
-
-# دالة مساعدة لعرض النص العربي بشكل صحيح
-def get_display_arabic(text):
- """تحويل النص العربي للعرض الصحيح في الرسوم البيانية"""
- reshaped_text = arabic_reshaper.reshape(text)
- bidi_text = get_display(reshaped_text)
- return bidi_text
class ClaudeAIService:
"""
@@ -86,53 +71,22 @@ class ClaudeAIService:
def analyze_image(self, image_path, prompt, model_name="claude-3-7-sonnet"):
"""
تحليل صورة باستخدام نموذج Claude AI
-
+
المعلمات:
- image_path: مسار الصورة المراد تحليلها
- prompt: التوجيه للنموذج
- model_name: اسم نموذج Claude المراد استخدامه
-
+ image_path: مسار الصورة المراد تحليلها
+ prompt: التوجيه للنموذج
+ model_name: اسم نموذج Claude المراد استخدامه
+
العوائد:
- dict: نتائج التحليل
+ dict: نتائج التحليل
"""
try:
# الحصول على مفتاح API
api_key = self.get_api_key()
- # قراءة محتوى الملف مع ضغط الصور فقط
- file_size = os.path.getsize(image_path)
- _, ext = os.path.splitext(image_path)
- ext = ext.lower()
-
- if ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp'] and file_size > 5 * 1024 * 1024:
- # فقط ضغط الصور العادية وليس ملفات PDF
- try:
- with Image.open(image_path) as img:
- # حفظ الصورة المضغوطة في ذاكرة مؤقتة
- compressed_img_buffer = io.BytesIO()
-
- # حساب معامل الجودة المناسب
- quality = min(95, int((5 * 1024 * 1024 / file_size) * 100))
-
- # حفظ الصورة بالجودة المحسوبة
- img.save(compressed_img_buffer, format='JPEG', quality=quality)
- compressed_img_buffer.seek(0)
-
- # استخدام البيانات المضغوطة
- file_content = compressed_img_buffer.read()
- logging.info(f"تم ضغط الصورة من {file_size/1024/1024:.2f} ميجابايت إلى {len(file_content)/1024/1024:.2f} ميجابايت")
- except Exception as e:
- logging.error(f"خطأ أثناء ضغط الصورة: {str(e)}")
- with open(image_path, 'rb') as f:
- file_content = f.read()
- else:
- # استخدام الملف الأصلي للملفات غير الصور أو الصور الصغيرة
- with open(image_path, 'rb') as f:
- file_content = f.read()
-
- # التحقق من حجم الملف بعد القراءة
- if len(file_content) > 5 * 1024 * 1024:
- raise ValueError(f"حجم الملف ({len(file_content)/1024/1024:.2f} ميجابايت) يتجاوز الحد الأقصى (5 ميجابايت). يرجى تقليل حجم الملف قبل الرفع.")
+ # قراءة محتوى الصورة
+ with open(image_path, 'rb') as f:
+ file_content = f.read()
# تحويل المحتوى إلى Base64
file_base64 = base64.b64encode(file_content).decode('utf-8')
@@ -293,118 +247,6 @@ class ClaudeAIService:
stack_trace = traceback.format_exc()
return {"error": f"فشل في إكمال المحادثة: {str(e)}\n{stack_trace}"}
- def analyze_text(self, text, analysis_type="general", model_name="claude-3-5-haiku"):
- """
- تحليل النص باستخدام الذكاء الاصطناعي
-
- المعلمات:
- text: النص المراد تحليله
- analysis_type: نوع التحليل (general, requirements, risks, costs)
- model_name: اسم النموذج المستخدم
-
- العوائد:
- dict: نتائج التحليل
- """
- try:
- # تحديد التوجيه المناسب حسب نوع ��لتحليل
- if analysis_type == "requirements":
- prompt = f"""
- قم بتحليل النص التالي واستخراج المتطلبات الرئيسية للمشروع. صنف المتطلبات إلى فئات (فنية، إدارية، مالية، قانونية) وحدد أولوية كل متطلب (عالية، متوسطة، منخفضة).
-
- النص:
- {text}
-
- قدم النتائج بتنسيق JSON يحتوي على مصفوفة من المتطلبات، كل متطلب يحتوي على: الوصف، الفئة، الأولوية.
- """
- elif analysis_type == "risks":
- prompt = f"""
- قم بتحليل النص التالي وتحديد المخاطر المحتملة للمشروع. لكل خطر، حدد احتمالية حدوثه (عالية، متوسطة، منخفضة) وتأثيره (عالي، متوسط، منخفض) واقترح استراتيجية للتخفيف من حدته.
-
- النص:
- {text}
-
- قدم النتائج بتنسيق JSON يحتوي على مصفوفة من المخاطر، كل خطر يحتوي على: الوصف، الاحتمالية، التأثير، استراتيجية التخفيف.
- """
- elif analysis_type == "costs":
- prompt = f"""
- قم بتحليل النص التالي وتحديد عناصر التكلفة المحتملة للمشروع. صنف التكاليف إلى فئات (مباشرة، غير مباشرة) وحدد ما إذا كانت ثابتة أو متغيرة.
-
- النص:
- {text}
-
- قدم النتائج بتنسيق JSON يحتوي على مصفوفة من عناصر التكلفة، كل عنصر يحتوي على: الوصف، الفئة، النوع (ثابت/متغير)، تقدير نسبي للتكلفة (%).
- """
- elif analysis_type == "local_content":
- prompt = f"""
- قم بتحليل النص التالي وتحديد عناصر المحتوى المحلي المحتملة. صنف العناصر إلى فئات (منتجات، خدمات، قوى عاملة) وحدد مدى توفرها محلياً (متوفر بشكل كامل، متوفر جزئياً، غير متوفر).
-
- النص:
- {text}
-
- قدم النتائج بتنسيق JSON يحتوي على مصفوفة من عناصر المحتوى المحلي، كل عنصر يحتوي على: الوصف، الفئة، مدى التوفر، تقدير نسبي للمساهمة في المحتوى المحلي (%).
- """
- else: # general
- prompt = f"""
- قم بتحليل النص التالي وتلخيص النقاط الرئيسية. حدد الموضوعات الأساسية والأفكار المهمة والتوصيات إن وجدت.
-
- النص:
- {text}
-
- قدم النتائج بتنسيق JSON يحتوي على: ملخص عام، النقاط الرئيسية (مصفوفة)، التوصيات (مصفوفة).
- """
-
- # إنشاء رسائل المحادثة
- messages = [
- {"role": "user", "content": prompt}
- ]
-
- # استدعاء دالة إكمال المحادثة
- result = self.chat_completion(messages, model_name)
-
- # معالجة النتيجة
- if "error" in result:
- return result
-
- # محاولة تحويل النتيجة إلى JSON
- try:
- # استخراج النص من النتيجة
- content = result["content"]
-
- # البحث عن بداية ونهاية JSON
- json_start = content.find('{')
- json_end = content.rfind('}') + 1
-
- if json_start >= 0 and json_end > json_start:
- json_str = content[json_start:json_end]
- analysis_result = json.loads(json_str)
-
- return {
- "success": True,
- "result": analysis_result,
- "model": result["model"]
- }
- else:
- # إذا لم يتم العثور على JSON، إرجاع النص كاملاً
- return {
- "success": True,
- "result": {"text": content},
- "model": result["model"]
- }
- except Exception as e:
- # إذا فشل تحويل النتيجة إلى JSON، إرجاع النص كاملاً
- return {
- "success": True,
- "result": {"text": content},
- "model": result["model"],
- "parse_error": str(e)
- }
-
- 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:
"""وحدة المساعد الذكي"""
@@ -452,17 +294,33 @@ class AIAssistantApp:
{
"question": "كيف يمكنني تقييم المخاطر للمشروع؟",
"answer": "يمكنك تقييم المخاطر للمشروع من خلال وحدة المخاطر، حيث يمكنك إضافة المخاطر المحتملة وتقييم تأثيرها واحتماليتها، ثم وضع خطة الاستجابة المناسبة."
+ }
+ ]
+
+ # تهيئة قائمة الأسئلة والإجابات الشائعة
+ self.faqs = [
+ {
+ "question": "كيف يمكنني إضافة مشروع جديد؟",
+ "answer": "يمكنك إضافة مشروع جديد من خلال الانتقال إلى وحدة إدارة المشاريع، ثم النقر على زر 'إضافة مشروع جديد'، وملء النموذج بالبيانات المطلوبة."
},
{
- "question": "ما هي طرق التسعير المتاحة في النظام؟",
- "answer": "يوفر النظام أربع طرق للتسعير: 1) التسعير القياسي، 2) التسعير غير المتزن، 3) التسعير التنافسي، 4) التسعير الموجه بالربحية. يمكنك اختيار الطريقة المناسبة حسب طبيعة المشروع واستراتيجية الشركة."
+ "question": "ما هي خطوات تسعير المناقصة؟",
+ "answer": "تتضمن خطوات تسعير المناقصة: 1) تحليل مستندات المناقصة، 2) تحديد بنود العمل، 3) تقدير التكاليف المباشرة، 4) إضافة المصاريف العامة والأرباح، 5) احتساب المحتوى المحلي، 6) مراجعة النتائج النهائية."
},
{
- "question": "كيف يمكنني معالجة مستندات المناقصة ضخمة الحجم؟",
- "answer": "يمكنك استخدام وحدة تحليل المستندات لمعالجة مستندات المناقصة ضخمة الحجم، حيث تقوم الوحدة بتحليل المستندات واستخراج المعلومات المهمة مثل مواصفات المشروع ومتطلباته وشروطه تلقائياً."
+ "question": "كيف يتم حساب المحتوى المحلي؟",
+ "answer": "يتم حساب المحتوى المحلي بتحديد نسبة المنتجات والخدمات والقوى العاملة المحلية من إجمالي التكاليف. يتم استخدام قاعدة بيانات الموردين المعتمدين وتطبيق معادلات خاصة حسب متطلبات هيئة المحتوى المحلي."
+ },
+ {
+ "question": "كيف يمكنني تصدير التقارير؟",
+ "answer": "يمكنك تصدير التقارير من وحدة التقارير والتحليلات، حيث يوجد زر 'تصدير' في كل تقرير. يمكن تصدير التقارير بتنسيقات مختلفة مثل Excel و PDF و CSV."
+ },
+ {
+ "question": "كيف يمكنني تقييم المخاطر للمشروع؟",
+ "answer": "يمكنك تقييم المخاطر للمشروع من خلال وحدة المخاطر، حيث يمكنك إضافة المخاطر المحتملة وتقييم تأثيرها واحتماليتها، ثم وضع خطة الاستجابة المناسبة."
}
]
-
+
def _load_cost_prediction_model(self):
"""تحميل نموذج التنبؤ بالتكاليف"""
# في البيئة الإنتاجية، سيتم تحميل نموذج حقيقي
@@ -484,6 +342,38 @@ class AIAssistantApp:
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": "يمكنك استخدام وحدة تحليل المستندات لمعالجة مستندات المناقصة ضخمة الحجم، حيث تقوم الوحدة بتحليل المستندات واستخراج المعلومات المهمة مثل مواصفات المشروع ومتطلباته وشروطه تلقائياً."
+ }
+ ]
def render(self):
"""عرض واجهة وحدة المساعد الذكي"""
@@ -642,840 +532,2751 @@ class AIAssistantApp:
# مربع إدخال الرسالة
user_input = st.text_input("اكتب رسالتك هنا", key="ai_assistant_input")
- # زر إرسال مع تحليل ذكي
- col1, col2 = st.columns([3, 1])
- with col1:
- send_button = st.button("إرسال", key="send_message_button")
- with col2:
- analyze_options = st.selectbox(
- "تحليل ذكي",
- ["عام", "متطلبات", "مخاطر", "تكاليف", "محتوى محلي"],
- key="analyze_type"
- )
-
- # معالجة الإدخال
- if send_button and user_input:
- # إضافة رسالة المستخدم إلى المحادثة
- st.session_state.ai_assistant_messages.append(
- {"role": "user", "content": user_input}
- )
-
- # تحديد نوع التحليل
- analysis_type_map = {
- "عام": "general",
- "متطلبات": "requirements",
- "مخاطر": "risks",
- "تكاليف": "costs",
- "محتوى محلي": "local_content"
- }
-
- analysis_type = analysis_type_map.get(analyze_options, "general")
-
- # إظهار مؤشر التحميل
- with st.spinner("جاري التحليل..."):
- # تحليل النص باستخدام الذكاء الاصطناعي
- result = self.claude_service.analyze_text(user_input, analysis_type, selected_model)
-
- # إعداد الرد
- if "error" in result:
- response = f"عذراً، حدث خطأ أثناء التحليل: {result['error']}"
- else:
- # ت��سيق النتيجة بشكل مقروء
- if isinstance(result["result"], dict):
- if "text" in result["result"]:
- # إذا كانت النتيجة نصية
- response = result["result"]["text"]
+ # التحقق من وجود مفتاح API
+ api_available = True
+ try:
+ self.claude_service.get_api_key()
+ except ValueError:
+ api_available = False
+ st.warning("مفتاح API لـ Claude غير متوفر. يرجى التأكد من تعيين متغير البيئة 'anthropic'.")
+
+ if user_input and api_available:
+ # إضافة رسالة المستخدم إلى المحفوظات
+ st.session_state.ai_assistant_messages.append({"role": "user", "content": user_input})
+
+ # عرض محفوظات المحادثة المحدثة
+ with chat_container:
+ st.markdown(f"""
+
+ """, 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
+
+ # إذا كان الملف 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:
- # إذا كانت النتيجة هيكلية
- response = "نتائج التحليل:\n\n"
- for key, value in result["result"].items():
- if isinstance(value, list):
- response += f"**{key}**:\n"
- for i, item in enumerate(value):
- if isinstance(item, dict):
- response += f"{i+1}. "
- for k, v in item.items():
- response += f"**{k}**: {v}, "
- response = response[:-2] + "\n"
- else:
- response += f"{i+1}. {item}\n"
- else:
- response += f"**{key}**: {value}\n"
+ st.error("تحليل ملفات PDF يتطلب تثبيت مكتبة pdf2image.")
+ response = "عذراً، لا يمكنني تحليل ملفات PDF في الوقت الحالي. يرجى تحويل الملف إلى صورة أو مشاركة المعلومات كنص."
+
+ # تحليل الصورة باستخدام Claude
+ prompt = f"المستخدم قام برفع هذه الصورة وسأل: {user_input}\nقم بتحليل الصورة والرد على سؤال المستخدم بشكل تفصيلي."
+ results = self.claude_service.analyze_image(temp_file_path, prompt, model_name=selected_model)
+
+ # حذف الملف المؤقت
+ try:
+ os.remove(temp_file_path)
+ except:
+ pass
+
+ 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 "error" in results:
+ response = f"عذراً، حدث خطأ أثناء معالجة طلبك: {results['error']}"
else:
- response = str(result["result"])
+ response = results["content"]
- # إضافة رد المساعد إلى المحادثة
- st.session_state.ai_assistant_messages.append(
- {"role": "assistant", "content": response}
- )
+ # إضافة رد المساعد إلى المحفوظات
+ st.session_state.ai_assistant_messages.append({"role": "assistant", "content": response})
+
+ # عرض رد المساعد
+ with chat_container:
+ st.markdown(f"""
+
+ """, unsafe_allow_html=True)
- # إعادة تحميل الصفحة لعرض الرد
- st.experimental_rerun()
+ # إعادة تعيين قيمة الإدخال
+ 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
- api_available = True
try:
self.claude_service.get_api_key()
except ValueError:
- api_available = False
- st.warning("مفتاح API لـ Claude غير متوفر. يرجى إضافته في إعدادات النظام.")
+ 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"]
+
+ 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 "شكراً لاستفسارك. يمكنني مساعدتك في تسعير المناقصات، وتحليل المخاطر، وتحليل المستندات، وحساب المحتوى المحلي. يرجى توضيح استفسارك أكثر أو اختيار أحد الخيارات في الأعلى للحصول على المساعدة المطلوبة."
def _render_cost_prediction_tab(self):
"""عرض تبويب التنبؤ بالتكاليف"""
+
st.markdown("### التنبؤ بالتكاليف")
- st.info("هذه الوحدة تساعدك في تقدير تكاليف المشاريع بناءً على بيانات المناقصات السابقة ومعايير التسعير المعتمدة.")
+ # عرض نموذج إدخال بيانات المشروع
+ st.markdown("#### بيانات المشروع")
- # إنشاء نموذج إدخال البيانات
col1, col2 = st.columns(2)
with col1:
- st.subheader("معلومات المشروع")
project_type = st.selectbox(
"نوع المشروع",
- ["مشروع إنشائي", "مشروع تقنية معلومات", "مشروع استشاري", "مشروع توريد", "أخرى"]
+ [
+ "مباني سكنية",
+ "مباني تجارية",
+ "مباني حكومية",
+ "مراكز صحية",
+ "مدارس",
+ "بنية تحتية",
+ "طرق",
+ "جسور",
+ "صرف صحي",
+ "مياه",
+ "كهرباء"
+ ],
+ key="cost_project_type"
)
- project_duration = st.number_input(
- "مدة المشروع (بالأشهر)",
- min_value=1,
- max_value=60,
- value=12
+ location = st.selectbox(
+ "الموقع",
+ [
+ "الرياض",
+ "جدة",
+ "الدمام",
+ "مكة",
+ "المدينة",
+ "تبوك",
+ "حائل",
+ "عسير",
+ "جازان",
+ "نجران",
+ "الباحة",
+ "الجوف",
+ "القصيم"
+ ],
+ key="cost_location"
)
- project_location = st.selectbox(
- "موقع المشروع",
- ["الرياض", "جدة", "الدمام", "مكة المكرمة", "المدينة المنورة", "أخرى"]
+ client_type = st.selectbox(
+ "نوع العميل",
+ [
+ "حكومي",
+ "شبه حكومي",
+ "شركة كبيرة",
+ "شركة متوسطة",
+ "شركة صغيرة",
+ "أفراد"
+ ],
+ key="cost_client_type"
)
with col2:
- st.subheader("معايير التكلفة")
+ area = st.number_input("المساحة (م²)", min_value=100, max_value=1000000, value=5000, key="cost_area")
- direct_cost = st.number_input(
- "التكاليف المباشرة المقدرة (ريال)",
- min_value=0,
- value=1000000
- )
+ floors = st.number_input("عدد الطوابق", min_value=1, max_value=100, value=3, key="cost_floors")
- overhead_percentage = st.slider(
- "نسبة المصاريف العامة (%)",
- min_value=5,
- max_value=30,
- value=15
- )
+ duration = st.number_input("مدة التنفيذ (ش��ور)", min_value=1, max_value=60, value=12, key="cost_duration")
- profit_percentage = st.slider(
- "نسبة الربح المستهدفة (%)",
- min_value=5,
- max_value=30,
- value=20
+ tender_type = st.selectbox(
+ "نوع المناقصة",
+ [
+ "عامة",
+ "خاصة",
+ "أمر مباشر"
+ ],
+ key="cost_tender_type"
)
- # زر حساب التكلفة
- col1, col2 = st.columns([3, 1])
+ st.markdown("#### متغيرات إضافية")
+
+ col1, col2, col3 = st.columns(3)
+
with col1:
- calculate_button = st.button("حساب التكلفة التقديرية")
+ has_basement = st.checkbox("يتضمن بدروم", key="cost_has_basement")
+ has_special_finishing = st.checkbox("تشطيبات خاصة", key="cost_has_special_finishing")
+
with col2:
- ai_analysis = st.checkbox("تحليل ذكي", value=True)
+ has_landscape = st.checkbox("أعمال تنسيق المواقع", key="cost_has_landscape")
+ has_parking = st.checkbox("مواقف متعددة الطوابق", key="cost_has_parking")
- if calculate_button:
- # حساب التكلفة (هذا مثال بسيط)
- overhead_cost = direct_cost * (overhead_percentage / 100)
- profit = direct_cost * (profit_percentage / 100)
- total_cost = direct_cost + overhead_cost + profit
-
- # عرض النتائج
- st.success("تم حساب التكلفة التقديرية بنجاح!")
-
- col1, col2, col3 = st.columns(3)
-
- with col1:
- st.metric("التكاليف المباشرة", f"{direct_cost:,.2f} ريال")
-
- with col2:
- st.metric("المصاريف العامة", f"{overhead_cost:,.2f} ريال")
-
- with col3:
- st.metric("الربح المتوقع", f"{profit:,.2f} ريال")
-
- st.metric("إجمالي التكلفة التقديرية", f"{total_cost:,.2f} ريال", delta="تقدير أولي")
-
- # عرض رسم بياني للتكاليف
- fig = go.Figure()
-
- # إضافة البيانات
- labels = ["التكاليف المباشرة", "المصاريف العامة", "الربح المتوقع"]
- values = [direct_cost, overhead_cost, profit]
- colors = ['#1f77b4', '#ff7f0e', '#2ca02c']
-
- fig.add_trace(go.Pie(
- labels=[get_display_arabic(label) for label in labels],
- values=values,
- textinfo='percent+label',
- insidetextorientation='radial',
- marker=dict(colors=colors),
- hole=0.4
- ))
-
- # تخصيص الرسم البياني
- fig.update_layout(
- title=get_display_arabic("توزيع التكاليف"),
- height=400,
- margin=dict(l=0, r=0, t=40, b=0),
- font=dict(size=14),
- legend=dict(
- orientation="h",
- yanchor="bottom",
- y=-0.2,
- xanchor="center",
- x=0.5
- )
- )
-
- st.plotly_chart(fig, use_container_width=True)
+ 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")
- # تحليل ذكي للتكاليف إذا تم تحديد الخيار
- if ai_analysis:
- with st.spinner("جاري تحليل التكاليف باستخدام الذكاء الاصطناعي..."):
- # إنشاء نص وصفي للمشروع
- project_description = f"""
- نوع المشروع: {project_type}
- مدة المشروع: {project_duration} أشهر
- موقع المشروع: {project_location}
- التكاليف المباشرة: {direct_cost:,.2f} ريال
- نسبة المصاريف العامة: {overhead_percentage}%
- نسبة الربح المستهدفة: {profit_percentage}%
- إجمالي التكلفة ��لتقديرية: {total_cost:,.2f} ريال
- """
-
- # تحليل النص باستخدام الذكاء الاصطناعي
- result = self.claude_service.analyze_text(project_description, "costs")
-
- if "error" not in result:
- st.subheader("تحليل التكاليف بالذكاء الاصطناعي")
+ 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
+ }
+
+ # استدعاء النموذج للتنبؤ
+ cost_prediction_results = self._predict_cost(features)
+
+ # إضافة تحليل إضافي باستخدام 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 'لا'}
- # عرض نتائج التحليل
- if isinstance(result["result"], dict) and "text" not in result["result"]:
- # إذا كانت النتيجة هيكلية
- if "عناصر التكلفة" in result["result"]:
- cost_items = result["result"]["عناصر التكلفة"]
-
- # إنشاء جدول لعناصر التكلفة
- cost_data = []
- for item in cost_items:
- if isinstance(item, dict):
- cost_data.append({
- "الوصف": item.get("الوصف", ""),
- "الفئة": item.get("الفئة", ""),
- "النوع": item.get("النوع", ""),
- "التقدير النسبي": item.get("تقدير نسبي للتكلفة", "")
- })
-
- if cost_data:
- cost_df = pd.DataFrame(cost_data)
- st.dataframe(cost_df, use_container_width=True)
- else:
- # إذا كانت النتيجة نصية
- st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
+ نتائج التنبؤ الأولية:
+ - التكلفة الإجمالية المقدرة: {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 _render_risk_analysis_tab(self):
- """عرض تبويب تحليل المخاطر"""
- st.markdown("### تحليل المخاطر")
-
- st.info("هذه الوحدة تساعدك في تحديد وتقييم المخاطر المحتملة للمشروع ووضع خطط الاستجابة المناسبة.")
-
- # إنشاء جدول المخاطر
- st.subheader("سجل المخاطر")
-
- # بيانات نموذجية للمخاطر
- risk_data = {
- "المخاطر": [
- "تأخر توريد المواد",
- "تغيير متطلبات المشروع",
- "نقص العمالة الماهرة",
- "مشاكل فنية غير متوقعة",
- "تغيرات في الأنظمة واللوائح"
- ],
- "الاحتمالية": [
- "متوسطة",
- "عالية",
- "منخفضة",
- "متوسطة",
- "منخفضة"
- ],
- "التأثير": [
- "عالي",
- "عالي",
- "متوسط",
- "عالي",
- "عالي"
- ],
- "درجة الخطورة": [
- "عالية",
- "عالية",
- "متوسطة",
- "عالية",
- "متوسطة"
- ],
- "خطة الاستجابة": [
- "التعاقد مع موردين بدلاء",
- "توثيق المتطلبات وإدارة التغيير",
- "التعاقد المبكر مع فرق العمل",
- "إجراء اختبارات مبكرة",
- "متابعة التحديثات التنظيمية"
- ]
+ 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
+ }
}
- risk_df = pd.DataFrame(risk_data)
- st.dataframe(risk_df, use_container_width=True)
+ return results
+
+ def _display_cost_prediction_results(self, results):
+ """عرض نتائج التنبؤ بالتكاليف"""
- # إضافة مخاطر جديدة
- st.subheader("إضافة مخاطر جديدة")
+ st.markdown("### نتائج التنبؤ بالتكاليف")
- col1, col2 = st.columns(2)
+ # عرض التكلفة الإجمالية وتكلفة المتر المربع
+ col1, col2, col3 = st.columns(3)
with col1:
- new_risk = st.text_input("وصف المخاطرة")
- probability = st.selectbox(
- "احتمالية الحدوث",
- ["منخفضة", "متوسطة", "عالية"]
+ st.metric(
+ "التكلفة الإجمالية المتوقعة",
+ f"{results['total_cost']:,.0f} ريال",
+ delta=f"{(results['total_cost'] - results['comparison']['historical_projects']):,.0f} ريال"
)
with col2:
- impact = st.selectbox(
- "التأثير",
- ["منخفض", "متوسط", "عالي"]
+ st.metric(
+ "تكلفة المتر المربع",
+ f"{results['cost_per_sqm']:,.0f} ريال/م²"
)
- response_plan = st.text_area("خطة الاستجابة")
- col1, col2 = st.columns([3, 1])
- with col1:
- add_button = st.button("إضافة المخاطرة")
- with col2:
- ai_suggestion = st.checkbox("اقتراح ذكي", value=True)
+ with col3:
+ st.metric(
+ "مستوى الثقة في التنبؤ",
+ f"{results['confidence_level'] * 100:.0f}%"
+ )
- if ai_suggestion and new_risk:
- with st.spinner("جاري تحليل المخاطرة باستخدام الذكاء الاصطناعي..."):
- # تحليل المخاطرة باستخدام الذكاء الاصطناعي
- risk_description = f"المخاطرة: {new_risk}"
- result = self.claude_service.analyze_text(risk_description, "risks")
-
- if "error" not in result and isinstance(result["result"], dict):
- if "المخاطر" in result["result"] and isinstance(result["result"]["المخاطر"], list):
- risk_analysis = result["result"]["المخاطر"][0]
-
- if isinstance(risk_analysis, dict):
- st.success("تم تحليل المخاطرة بنجاح!")
-
- # عرض نتائج التحليل
- col1, col2 = st.columns(2)
- with col1:
- st.write("**الاحتمالية المقترحة:**", risk_analysis.get("الاحتمالية", "غير محدد"))
- st.write("**التأثير المقترح:**", risk_analysis.get("التأثير", "غير محدد"))
- with col2:
- st.write("**استراتيجية التخفيف المقترحة:**", risk_analysis.get("استراتيجية التخفيف", "غير محدد"))
-
- if add_button and new_risk:
- st.success("تمت إضافة المخاطرة بنجاح!")
-
- # عرض مصفوفة المخاطر
- st.subheader("مصفوفة المخاطر")
-
- # بيانات مصفوفة المخاطر
- risk_matrix = np.array([
- [1, 2, 3],
- [2, 4, 6],
- [3, 6, 9]
- ])
+ # عرض تفصيل التكاليف
+ st.markdown("#### تفصيل التكاليف")
- # إنشاء مصفوفة المخاطر باستخدام Plotly
- fig = go.Figure()
+ # رسم مخطط دائري للتكاليف المفصلة
+ fig = px.pie(
+ values=[
+ results['material_cost'],
+ results['labor_cost'],
+ results['equipment_cost']
+ ],
+ names=["تكلفة المواد", "تكلفة العمالة", "تكلفة المعدات"],
+ title="توزيع التكاليف الرئيسية"
+ )
- # تحديد الألوان
- colorscale = [
- [0, '#1a9850'], # أخضر داكن (مخاطر منخفضة)
- [0.3, '#91cf60'], # أخضر فاتح
- [0.5, '#ffffbf'], # أصفر
- [0.7, '#fc8d59'], # برتقالي
- [1, '#d73027'] # أحمر (مخاطر عالية)
- ]
+ st.plotly_chart(fig, use_container_width=True)
- # إنشاء مصفوفة المخاطر
- fig.add_trace(go.Heatmap(
- z=risk_matrix,
- x=['منخفض', 'متوسط', 'عالي'],
- y=['منخفضة', 'متوسطة', 'عالية'],
- text=risk_matrix,
- texttemplate="%{text}",
- textfont={"size":20},
- colorscale=colorscale,
- showscale=True,
- colorbar=dict(
- title=get_display_arabic("درجة الخطورة"),
- titleside="right",
- titlefont=dict(size=14),
- tickfont=dict(size=12),
- )
- ))
-
- # تخصيص الرسم البياني
- fig.update_layout(
- title=get_display_arabic("مصفوفة المخاطر"),
- height=500,
- margin=dict(l=50, r=50, t=50, b=50),
- xaxis=dict(
- title=get_display_arabic("التأثير"),
- titlefont=dict(size=14),
- tickfont=dict(size=12),
- ),
- yaxis=dict(
- title=get_display_arabic("الاحتمالية"),
- titlefont=dict(size=14),
- tickfont=dict(size=12),
- ),
- font=dict(size=14)
+ # رسم مخطط شريطي لتفصيل الأعمال
+ 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.subheader("تحليل المخاطر بالذكاء الاصطناعي")
+ # عرض مقارنة مع متوسط السوق
+ st.markdown("#### مقارنة مع متوسط السوق")
- risk_text = st.text_area("أدخل وصف المشروع لتحليل المخاطر المحتملة", height=150)
+ 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"
+ }
+ )
- if st.button("تحليل المخاطر"):
- if risk_text:
- with st.spinner("جاري تحليل المخاطر باستخدام الذكاء الاصطناعي..."):
- # تحليل النص باستخدام الذكاء الاصطناعي
- result = self.claude_service.analyze_text(risk_text, "risks")
-
- if "error" not in result:
- st.success("تم تحليل المخاطر بنجاح!")
-
- # عرض نتائج التحليل
- if isinstance(result["result"], dict) and "المخاطر" in result["result"]:
- risks = result["result"]["المخاطر"]
-
- # إنشاء جدول للمخاطر
- risk_data = []
- for risk in risks:
- if isinstance(risk, dict):
- risk_data.append({
- "المخاطر": risk.get("الوصف", ""),
- "الاحتمالية": risk.get("الاحتمالية", ""),
- "التأثير": risk.get("التأثير", ""),
- "خطة الاستجابة": risk.get("استراتيجية التخفيف", "")
- })
-
- if risk_data:
- risk_df = pd.DataFrame(risk_data)
- st.dataframe(risk_df, use_container_width=True)
- else:
- # إذا كانت النتيجة نصية
- st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
- else:
- st.error(f"حدث خطأ أثناء التحليل: {result['error']}")
- else:
- st.warning("يرجى إدخال وصف المشروع لتحليل المخاطر.")
-
- def _render_document_analysis_tab(self):
- """عرض تبويب تحليل المستندات"""
- st.markdown("### تحليل المستندات")
+ fig.update_traces(texttemplate='%{text:,.0f} ريال', textposition='outside')
- st.info("هذه الوحدة تساعدك في تحليل مستندات المناقصات واستخراج المعلومات المهمة منها.")
+ st.plotly_chart(fig, use_container_width=True)
- # رفع المستندات
- uploaded_file = st.file_uploader(
- "ارفع مستند المناقصة (PDF, DOCX, XLSX)",
- type=["pdf", "docx", "xlsx"],
- key="document_analysis_upload"
- )
+ # عرض تحليل Claude AI إذا كان مت��فراً
+ if "claude_analysis" in results:
+ st.markdown("### تحليل Claude AI المتقدم")
+ st.info(results["claude_analysis"])
- if uploaded_file is not None:
- # عرض معلومات الملف
- file_details = {
- "اسم الملف": uploaded_file.name,
- "نوع الملف": uploaded_file.type,
- "حجم الملف": f"{uploaded_file.size / 1024:.2f} كيلوبايت"
- }
-
- st.json(file_details)
-
- # خيارات التحليل
- analysis_options = st.multiselect(
- "اختر أنواع التحليل",
+ # عرض ملاحظات وتوصيات
+ 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(
+ "نوع المشروع",
[
- "استخراج المتطلبات",
- "تحديد المواعيد النهائية",
- "تحليل الشروط والأحكام",
- "استخراج جداول الكميات",
- "تحديد المعايير الفنية"
+ "مباني سكنية",
+ "مباني تجارية",
+ "مباني حكومية",
+ "مراكز صحية",
+ "مدارس",
+ "بنية تحتية",
+ "طرق",
+ "جسور",
+ "صرف صحي",
+ "مياه",
+ "كهرباء"
],
- default=["استخراج المتطلبات", "تحديد المواعيد النهائية"]
+ key="risk_project_type"
)
- col1, col2 = st.columns([3, 1])
- with col1:
- analyze_button = st.button("تحليل المستند")
- with col2:
- ai_analysis = st.checkbox("تحليل ذكي", value=True)
+ location = st.selectbox(
+ "الموقع",
+ [
+ "الرياض",
+ "جدة",
+ "الدمام",
+ "مكة",
+ "المدينة",
+ "تبوك",
+ "حائل",
+ "عسير",
+ "جازان",
+ "نجران",
+ "الباحة",
+ "الجوف",
+ "القصيم"
+ ],
+ key="risk_location"
+ )
+
+ with col2:
+ client_type = st.selectbox(
+ "نوع العميل",
+ [
+ "حكومي",
+ "شبه حكومي",
+ "شركة كبيرة",
+ "شركة متوسطة",
+ "شركة صغيرة",
+ "أفراد"
+ ],
+ key="risk_client_type"
+ )
- if analyze_button:
- # عرض شريط التقدم
- progress_bar = st.progress(0)
- status_text = st.empty()
-
- # محاكاة عملية التحليل
- for i in range(101):
- progress_bar.progress(i)
- if i < 30:
- status_text.text("جاري معالجة المستند...")
- elif i < 60:
- status_text.text("جاري استخراج النصوص...")
- elif i < 90:
- status_text.text("جاري تحليل ال��حتوى...")
- else:
- status_text.text("جاري إعداد النتائج...")
- time.sleep(0.02)
-
- st.success("تم تحليل المستند بنجاح!")
-
- # عرض نتائج التحليل
- st.subheader("نتائج التحليل")
-
- # بيانات نموذجية للنتائج
- if "استخراج المتطلبات" in analysis_options:
- st.write("#### المتطلبات الرئيسية")
- requirements = [
- "توفير فريق عمل مؤهل لا يقل عن 10 أفراد",
- "خبرة سابقة في مشاريع مماثلة لا تقل عن 5 سنوات",
- "شهادة ISO 9001 للجودة",
- "تقديم ضمان بنكي بنسبة 5% من قيمة العقد",
- "الالتزام بمعايير السلامة المهنية"
- ]
- for req in requirements:
- st.markdown(f"- {req}")
+ 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)
- if "تحديد المواعيد النهائية" in analysis_options:
- st.write("#### المواعيد النهائية")
- deadlines = {
- "تقديم العروض": "15/05/2025",
- "بدء المشروع": "01/06/2025",
- "المرحلة الأولى": "01/08/2025",
- "المرحلة الثانية": "01/10/2025",
- "تسليم المشروع": "31/12/2025"
- }
- deadline_df = pd.DataFrame(list(deadlines.items()), columns=["المرحلة", "التاريخ"])
- st.dataframe(deadline_df, use_container_width=True)
+ # تجهيز البيانات للنموذج
+ 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
+ }
- if "تحليل الشروط والأحكام" in analysis_options:
- st.write("#### الشروط والأحكام الهامة")
- terms = [
- "غرامة التأخير: 1% من قيمة العقد عن كل أسبوع تأخير",
- "مدة الضمان: سنتان من تاريخ الاستلام النهائي",
- "شروط الدفع: 20% دفعة مقدمة، 60% على مراحل، 20% بعد الاستلام النهائي",
- "التحكيم: وفقاً لأنظمة المملكة العربية السعودية",
- "التأمين: يجب توفير تأمين شامل للمشروع"
- ]
- for term in terms:
- st.markdown(f"- {term}")
+ # استدعاء النموذج لتحليل المخاطر
+ risk_analysis_results = self._analyze_risks(features)
- # تحليل ذكي للمستند إذا تم تحديد الخيار
- if ai_analysis:
- st.subheader("تحليل المستند بالذكاء الاصطناعي")
-
- # محاكاة تحليل المستند
- with st.spinner("جاري تحليل المستند باستخدام الذكاء الاصطناعي..."):
- # محاكاة نص المستند
- document_text = """
- مناقصة رقم: 2025/123
+ # إضافة تحليل إضافي باستخدام 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
+ """
- المتطلبات الفنية:
- 1. تطوير نظام متكامل لإدارة المشاريع
- 2. توفير واجهة مستخدم سهلة الاستخدام
- 3. دعم اللغة العربية والإنجليزية
- 4. توفير تقارير تحليلية متقدمة
- 5. دعم الأجهزة المحمولة
+ prompt = f"""تحليل مخاطر مشروع:
- المواعيد:
- - آخر موعد لتقديم العروض: 15/05/2025
- - بدء المشروع: 01/06/2025
- - تسليم المرحلة الأولى: 01/08/2025
- - تسليم المرحلة الثانية: 01/10/2025
- - التسليم النهائي: 31/12/2025
+ {features_text}
- الشروط والأحكام:
- - مدة العقد: 12 شهر
- - غرامة التأخير: 1% من قيمة العقد عن كل أسبوع تأخير
- - شروط الدفع: 20% دفعة مقدمة، 60% على مراحل، 20% بعد الاستلام النهائي
- - الضمان: سنتان من تاريخ الاستلام النهائي
+ المطلوب:
+ 1. تحليل عوامل المخاطرة وتأثيرها على المشروع
+ 2. تقديم توصيات إضافية لإدارة المخاطر
+ 3. اقتراح استراتيجيات استجابة للمخاطر الرئيسية
+ 4. تقديم نصائح لتحسين شروط العقد لتقليل المخاطر
+ 5. تقييم مدى ملاءمة المشروع لاستراتيجية الشركة
+
+ يرجى تقديم تحليل مهني ومختصر يركز على الجوانب الأكثر أهمية.
"""
- # تحليل النص باستخدام الذكاء الاصطناعي
- result = self.claude_service.analyze_text(document_text, "requirements")
+ # استدعاء Claude للتحليل
+ claude_analysis = self.claude_service.chat_completion(
+ [{"role": "user", "content": prompt}]
+ )
- if "error" not in result:
- # عرض نتائج التحليل
- if isinstance(result["result"], dict) and "المتطلبات" in result["result"]:
- requirements = result["result"]["المتطلبات"]
-
- # إنشاء جدول للمتطلبات
- req_data = []
- for req in requirements:
- if isinstance(req, dict):
- req_data.append({
- "الوصف": req.get("الوصف", ""),
- "الفئة": req.get("الفئة", ""),
- "الأولوية": req.get("الأولوية", "")
- })
-
- if req_data:
- req_df = pd.DataFrame(req_data)
- st.dataframe(req_df, use_container_width=True)
- else:
- # إذا كانت النتيجة نصية
- st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
- else:
- st.error(f"حدث خطأ أثناء التحليل: {result['error']}")
+ 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)
- def _render_local_content_tab(self):
- """عرض تبويب المحتوى المحلي"""
- st.markdown("### تحليل المحتوى المحلي")
+ def _analyze_risks(self, features):
+ """تحليل مخاطر المشروع"""
- st.info("هذه الوحدة تساعدك في حساب وتحسين نسبة المحتوى المحلي في المشاريع وفقاً لمتطلبات هيئة المحتوى المحلي.")
+ # في البيئة الحقيقية، سيتم استدعاء نموذج تحليل المخاطر
+ # محاكاة نتائج تحليل المخاطر للعرض
- # إنشاء نموذج إدخال البيانات
- col1, col2 = st.columns(2)
+ # تعريف قائمة من المخاطر المحتملة
+ 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
+
+ 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.subheader("بيانات المشروع")
- project_value = st.number_input(
- "القيمة الإجمالية للمشروع (ريال)",
- min_value=0,
- value=5000000
+ 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="ملف المخاطر حسب الفئة"
+ )
+
+ 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("تم تصدير تقرير المخاطر بنجاح!")
+
+ 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"
+ )
+
+ # اختيار نموذج التحليل
+ analysis_model = st.radio(
+ "اختر نموذج التحليل",
+ [
+ "استخراج البنود والمواصفات",
+ "استخراج الشروط التعاقدية",
+ "تحليل الكميات",
+ "تحليل المتطلبات القانونية",
+ "تحليل شامل (يستخدم Claude AI)"
+ ],
+ horizontal=True
+ )
+
+ # زر بدء التحليل
+ 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)
+
+ 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
+
+ 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"])
+
+ # عرض الجدول بشكل منسق
+ st.dataframe(
+ items_df[["id", "description", "unit", "quantity", "estimated_price"]],
+ use_container_width=True
)
- project_sector = st.selectbox(
- "قطاع المشروع",
- ["البناء والتشييد", "تقنية المعلومات", "الطاقة", "النقل", "الصحة", "أخرى"]
+ # عرض مخطط للتكاليف المقدرة
+ costs = [item["quantity"] * item["estimated_price"] for item in results["tender_items"]]
+ labels = [item["description"] for item in results["tender_items"]]
+
+ fig = px.pie(
+ names=labels,
+ values=costs,
+ title="توزيع التكاليف المقدرة حسب البنود"
)
+
+ st.plotly_chart(fig, use_container_width=True)
- with col2:
- st.subheader("بيانات المحتوى المحلي")
+ # عرض الشروط التعاقدية إذا وجدت
+ if results["contract_terms"]:
+ st.markdown("#### الشروط التعاقدية الهامة")
- local_products = st.number_input(
- "قيمة المنتجات المحلية (ريال)",
- min_value=0,
- value=2000000
+ # عرض كل شرط في قسم منفصل
+ 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": "التكلفة المقدرة (ريال)"}
)
- local_services = st.number_input(
- "قيمة الخدمات المحلية (ريال)",
- min_value=0,
- value=1000000
+ fig.update_traces(text=quantities_df["estimated_cost"], textposition="outside")
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض المتطلبات القانونية إذا وجدت
+ if results["legal_requirements"]:
+ st.markdown("#### المتطلبات القانونية")
+
+ # عرض المتطلبات في جدول
+ legal_df = pd.DataFrame(results["legal_requirements"])
+
+ # عرض الجدول بشكل منسق
+ st.dataframe(
+ legal_df[["id", "title", "description", "compliance_status", "required_documents"]],
+ use_container_width=True
)
- local_workforce = st.number_input(
- "تكلفة القوى العاملة المحلية (ريال)",
- min_value=0,
- value=1500000
+ # عرض قائمة تحقق للمتطلبات القانونية
+ 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()
+
+ with lc_tabs[1]:
+ self._render_lc_suppliers_tab()
+
+ 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(
+ "طريقة الحساب",
+ [
+ "الطريقة القياسية (المدخلات)",
+ "طريقة القيمة المضافة",
+ "الطريقة المختلطة"
+ ]
)
+ # جدول مكونات المشروع
+ 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([3, 1])
+ col1, col2 = st.columns([1, 3])
+
with col1:
- calculate_button = st.button("حساب نسبة المحتوى المحلي")
+ calculate_button = st.button("حساب المحتوى المحلي", use_container_width=True)
+
with col2:
- ai_optimization = st.checkbox("تحسين ذكي", value=True)
+ use_claude = st.checkbox("استخدام Claude AI للتحليل المتقدم", value=True, key="lc_use_claude")
if calculate_button:
- # حساب نسبة المحتوى المحلي
- total_local_content = local_products + local_services + local_workforce
- local_content_percentage = (total_local_content / project_value) * 100
+ with st.spinner("جاري حساب وتحليل المحتوى المحلي..."):
+ # محاكاة وقت المعالجة
+ time.sleep(2)
+
+ # حساب المحتوى المحلي
+ lc_results = self._calculate_local_content(st.session_state.lc_components, target_lc, calculation_method)
+
+ # إضافة تحليل إضافي باستخدام 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)}")
+
+ # عرض نتائج حساب المحتوى المحلي
+ self._display_local_content_results(lc_results, target_lc)
+
+ 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
+ }
- # عرض النتائج
- st.success("تم حساب نسبة المحتوى المحلي بنجاح!")
+ 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
+ )
+
+ # تقديم توصيات لتحسين المحتوى المحلي
+ 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("### نتائج حساب المحتوى المحلي")
+
+ # عرض نسبة المحتوى المحلي الإجمالية
+ 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"
+ )
+
+ with col2:
+ st.metric(
+ "النسبة المستهدفة",
+ f"{target_lc}%"
+ )
+
+ 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)
+
+ with col1:
+ fig = px.pie(
+ categories_df,
+ values='القيمة الإجمالية',
+ names='الفئة',
+ title="توزيع قيمة المشروع حسب الفئة"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ 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'
+ )
+
+ 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"])
+
+ def _render_lc_suppliers_tab(self):
+ """عرض تبويب قاعدة بيانات الموردين للمحتوى المحلي"""
+
+ st.markdown("#### قاعدة بيانات الموردين المحليين")
+
+ # قائمة الفئات
+ categories = [
+ "جميع الفئات",
+ "مواد بناء",
+ "أنظمة كهربائية",
+ "أنظمة ميكانيكية",
+ "تشطيبات",
+ "أثاث ومفروشات",
+ "خدمات هندسية",
+ "أنظمة أمنية",
+ "معدات وآليات"
+ ]
+
+ # اختيار الفئة
+ selected_category = st.selectbox("فئة الموردين", categories)
+
+ # البحث
+ search_query = st.text_input("البحث عن مورد")
+
+ # إعداد قائمة الموردين
+ 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": "مصنع متخصص في إنتاج وتوريد منتجات الحديد والصلب للمشاريع الإنشائية."
+ }
+ ]
+
+ # تطبيق الفلترة حسب الفئة
+ if selected_category != "جميع الفئات":
+ filtered_suppliers = [s for s in suppliers if s["category"] == selected_category]
+ 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 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)
+
+ 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:
- st.metric("إجمالي المحتوى المحلي", f"{total_local_content:,.2f} ريال")
- st.metric("نسبة المحتوى المحلي", f"{local_content_percentage:.2f}%")
+ fig = px.pie(
+ project_data,
+ values='القيمة',
+ names='المكون',
+ title="توزيع قيمة المشروع"
+ )
- # تقييم النسبة
- if local_content_percentage >= 70:
- st.success("ممتاز! نسبة المحتوى المحلي تتجاوز المتطلبات.")
- elif local_content_percentage >= 50:
- st.info("جيد. نسبة المحتوى المحلي تلبي الحد الأدنى من المتطلبات.")
- else:
- st.warning("تحذير: نسبة المحتوى المحلي أقل من المتطلبات. يرجى العمل على تحسينها.")
+ st.plotly_chart(fig, use_container_width=True)
with col2:
- # عرض رسم بياني للمحتوى المحلي
- fig = go.Figure()
-
- # إضافة البيانات
- labels = ["المنتجات المحلية", "الخدمات المحلية", "القوى العاملة المحلية", "غير محلي"]
- values = [local_products, local_services, local_workforce, project_value - total_local_content]
- colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
-
- fig.add_trace(go.Pie(
- labels=[get_display_arabic(label) for label in labels],
- values=values,
- textinfo='percent',
- insidetextorientation='radial',
- marker=dict(colors=colors),
- hole=0.4
- ))
-
- # تخصيص الرسم البياني
- fig.update_layout(
- title=get_display_arabic("توزيع المحتوى المحلي"),
- height=400,
- margin=dict(l=0, r=0, t=40, b=0),
- font=dict(size=14),
- legend=dict(
- orientation="h",
- yanchor="bottom",
- y=-0.2,
- xanchor="center",
- x=0.5
- )
+ fig = px.pie(
+ project_data,
+ values='قيمة المحتوى المحلي',
+ names='المكون',
+ title="توزيع قيمة المحتوى المحلي"
)
st.plotly_chart(fig, use_container_width=True)
- # عرض تفاصيل المحتوى المحلي
- st.subheader("تفاصيل المحتوى المحلي")
-
- # إنشاء جدول البيانات
- local_content_data = {
- "العنصر": ["المنتجات المحلية", "الخدمات المحلية", "القوى العاملة المحلية", "إجمالي المحتوى المحلي", "غير محلي", "إجمالي المشروع"],
- "القيمة (ريال)": [local_products, local_services, local_workforce, total_local_content, project_value - total_local_content, project_value],
- "النسبة (%)": [
- local_products / project_value * 100,
- local_services / project_value * 100,
- local_workforce / project_value * 100,
- local_content_percentage,
- (project_value - total_local_content) / project_value * 100,
- 100
- ]
- }
+ # مخطط شريطي للمحتوى المحلي
+ fig = px.bar(
+ project_data,
+ x='المكون',
+ y='نسبة المحتوى المحلي',
+ title="نسبة المحتوى المحلي لكل مكون",
+ text_auto='.1f',
+ color='نسبة من المشروع'
+ )
- local_content_df = pd.DataFrame(local_content_data)
- local_content_df["النسبة (%)"] = local_content_df["النسبة (%)"].round(2)
- local_content_df["القيمة (ريال)"] = local_content_df["القيمة (ريال)"].apply(lambda x: f"{x:,.2f}")
+ fig.update_traces(texttemplate='%{text}%', textposition='outside')
- st.dataframe(local_content_df, use_container_width=True)
+ 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='سنة الإنجاز'
+ )
- # تحسين المحتوى المحلي باستخدام الذكاء الاصطناعي
- if ai_optimization:
- st.subheader("توصيات تحسين المحتوى المحلي")
-
- with st.spinner("جاري تحليل وتحسين المحتوى المحلي باستخدام الذكاء الاصطناعي..."):
- # إنشاء وصف للمشروع
- project_description = f"""
- قطاع المشروع: {project_sector}
- القيمة الإجمالية للمشروع: {project_value:,.2f} ريال
- قيمة المنتجات المحلية: {local_products:,.2f} ريال ({local_products/project_value*100:.2f}%)
- قيمة الخدمات المحلية: {local_services:,.2f} ريال ({local_services/project_value*100:.2f}%)
- تكلفة القوى العاملة المحلية: {local_workforce:,.2f} ريال ({local_workforce/project_value*100:.2f}%)
- إجمالي المحتوى المحلي: {total_local_content:,.2f} ريال ({local_content_percentage:.2f}%)
- """
-
- # تحليل النص باستخدام الذكاء الاصطناعي
- result = self.claude_service.analyze_text(project_description, "local_content")
-
- if "error" not in result:
- # عرض نتائج التحليل
- if isinstance(result["result"], dict):
- if "توصيات" in result["result"]:
- recommendations = result["result"]["توصيات"]
-
- for i, rec in enumerate(recommendations):
- st.markdown(f"**{i+1}. {rec}**")
- elif "عناصر المحتوى المحلي" in result["result"]:
- items = result["result"]["عناصر المحتوى المحلي"]
-
- # إنشاء جدول للعناصر
- items_data = []
- for item in items:
- if isinstance(item, dict):
- items_data.append({
- "الوصف": item.get("الوصف", ""),
- "الفئة": item.get("الفئة", ""),
- "مدى التوفر": item.get("مدى التوفر", ""),
- "المساهمة": item.get("تقدير نسبي للمساهمة في المحتوى المحلي", "")
- })
-
- if items_data:
- items_df = pd.DataFrame(items_data)
- st.dataframe(items_df, use_container_width=True)
- else:
- # إذا كانت النتيجة نصية
- st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
- else:
- st.write("لا توجد توصيات متاحة.")
- else:
- st.error(f"حدث خطأ أثناء التحليل: {result['error']}")
+ 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)
- # توصيات لتحسين المحتوى المحلي
- st.subheader("توصيات عامة لتحسين المحتوى المحلي")
+ 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)
- recommendations = [
- "استخدام منتجات محلية الصنع بدلاً من المستوردة حيثما أمكن",
- "التعاقد مع موردين محليين معتمدين من هيئة المحتوى المحلي",
- "توظيف وتدريب كوادر سعودية للعمل في المشروع",
- "الاستفادة من برامج دعم المحتوى المحلي المقدمة من الجهات الحكومية",
- "تطوير شراكات مع مصنعين محليين لتوطين التقنيات المطلوبة"
+ 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": "شركة الزجاج المتطورة"
+ }
]
- for rec in recommendations:
- st.markdown(f"- {rec}")
+ # عرض جدول المكونات
+ 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
+
+ # القيمة المتوقعة للمحتوى المحلي مع البديل
+ new_component_value = selected_comp_data["value"] * alt["cost_factor"]
+ new_component_lc_value = new_component_value * alt["local_content"] / 100
+
+ # الفرق في قيمة المحتوى المحلي
+ lc_value_diff = new_component_lc_value - current_component_lc_value
+
+ # القيمة الإجمالية الجديدة للمشروع
+ new_total_value = total_value - selected_comp_data["value"] + new_component_value
+
+ # قيمة المحتوى المحلي الإجمالية الجديدة
+ new_total_lc_value = current_lc_value + lc_value_diff
+
+ # نسبة المحتوى المحلي الإجمالية الجديدة
+ new_total_lc_percent = new_total_lc_value / new_total_value * 100
+
+ # إضافة البيانات
+ 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)
+
+ 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("تم تطبيق البديل المختار على المشروع وتحديث نسبة المحتوى المحلي.")
def _render_faq_tab(self):
"""عرض تبويب الأسئلة الشائعة"""
+
st.markdown("### الأسئلة الشائعة")
- st.info("هذه الصفحة تحتوي على إجابات للأسئلة الشائعة حول استخدام نظام تسعير المناقصات.")
+ # البحث في الأسئلة الشائعة
+ search_query = st.text_input("البحث في الأسئلة الشائعة", key="faq_search")
- # عرض الأسئلة الشائعة
- for i, faq in enumerate(self.faqs):
- with st.expander(faq["question"]):
- st.write(faq["answer"])
+ # فلترة الأسئلة حسب البحث
+ 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
- # إضافة سؤال جديد
- st.subheader("هل لديك سؤال آخر؟")
+ # عرض الأسئلة والأجوبة
+ for i, faq in enumerate(filtered_faqs):
+ with st.expander(faq["question"]):
+ st.markdown(faq["answer"])
- new_question = st.text_input("اكتب سؤالك هنا")
+ # زر التواصل مع الدعم
+ st.markdown("##### لم تجد إجابة لسؤالك؟")
+ col1, col2 = st.columns(2)
- col1, col2 = st.columns([3, 1])
with col1:
- send_button = st.button("إرسال السؤال")
+ if st.button("التواصل مع الدعم الفني", use_container_width=True):
+ st.info("سيتم التواصل معك قريباً من قبل فريق الدعم الفني.")
+
with col2:
- ai_answer = st.checkbox("إجابة ذكية", value=True)
-
- if send_button and new_question:
- if ai_answer:
- with st.spinner("جاري تحليل السؤال باستخدام الذكاء الاصطناعي..."):
- # تحليل السؤال باستخدام الذكاء الاصطناعي
- result = self.claude_service.chat_completion([
- {"role": "user", "content": f"السؤال: {new_question}\n\nالرجاء الإجابة على هذا السؤال المتعلق بنظام تسعير المناقصات بشكل مختصر ومفيد."}
- ])
-
- if "error" not in result:
- st.success("تم تحليل السؤال والإجابة عليه!")
-
- # عرض الإجابة
- st.info(f"**سؤالك:** {new_question}")
- st.write(f"**الإجابة:** {result['content']}")
- else:
- st.error(f"حدث خطأ أثناء تحليل السؤال: {result['error']}")
- st.success("تم إرسال سؤالك بنجاح! سيتم الرد عليه في أقرب وقت.")
- else:
- st.success("تم إرسال سؤالك بنجاح! سيتم الرد عليه في أقرب وقت.")
- elif send_button:
- st.error("يرجى كتابة السؤال قبل الإرسال.")
+ if st.button("طرح سؤال جديد", use_container_width=True):
+ st.text_area("اكتب سؤالك هنا")
+ st.button("إرسال")
\ No newline at end of file