EGYADMIN commited on
Commit
ba5d79d
·
verified ·
1 Parent(s): 6bf0307

Update modules/ai_assistant/ai_app.py

Browse files
Files changed (1) hide show
  1. modules/ai_assistant/ai_app.py +601 -39
modules/ai_assistant/ai_app.py CHANGED
@@ -10,6 +10,7 @@ import pandas as pd
10
  import numpy as np
11
  import matplotlib.pyplot as plt
12
  import plotly.express as px
 
13
  import requests
14
  import json
15
  import time
@@ -23,6 +24,20 @@ import random
23
  from io import BytesIO
24
  from tempfile import NamedTemporaryFile
25
  from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  class ClaudeAIService:
28
  """
@@ -278,6 +293,118 @@ class ClaudeAIService:
278
  stack_trace = traceback.format_exc()
279
  return {"error": f"فشل في إكمال المحادثة: {str(e)}\n{stack_trace}"}
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
  class AIAssistantApp:
283
  """وحدة المساعد الذكي"""
@@ -515,6 +642,76 @@ class AIAssistantApp:
515
  # مربع إدخال الرسالة
516
  user_input = st.text_input("اكتب رسالتك هنا", key="ai_assistant_input")
517
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  # التحقق من وجود مفتاح API
519
  api_available = True
520
  try:
@@ -575,7 +772,13 @@ class AIAssistantApp:
575
  )
576
 
577
  # زر حساب التكلفة
578
- if st.button("حساب التكلفة التقديرية"):
 
 
 
 
 
 
579
  # حساب التكلفة (هذا مثال بسيط)
580
  overhead_cost = direct_cost * (overhead_percentage / 100)
581
  profit = direct_cost * (profit_percentage / 100)
@@ -598,12 +801,82 @@ class AIAssistantApp:
598
  st.metric("إجمالي التكلفة التقديرية", f"{total_cost:,.2f} ريال", delta="تقدير أولي")
599
 
600
  # عرض رسم بياني للتكاليف
601
- fig = px.pie(
602
- names=["التكاليف المباشرة", "المصاريف العامة", "الربح المتوقع"],
603
- values=[direct_cost, overhead_cost, profit],
604
- title="توزيع التكاليف"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  )
606
- st.plotly_chart(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
  def _render_risk_analysis_tab(self):
609
  """عرض تبويب تحليل المخاطر"""
@@ -675,41 +948,136 @@ class AIAssistantApp:
675
  )
676
  response_plan = st.text_area("خطة الاستجابة")
677
 
678
- if st.button("إضافة المخاطرة"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
679
  st.success("تمت إضافة المخاطرة بنجاح!")
680
 
681
  # عرض مصفوفة المخاطر
682
  st.subheader("مصفوفة المخاطر")
683
 
684
- # بيانات نموذجية لمصفوفة المخاطر
685
  risk_matrix = np.array([
686
  [1, 2, 3],
687
  [2, 4, 6],
688
  [3, 6, 9]
689
  ])
690
 
691
- fig, ax = plt.subplots(figsize=(6, 6))
692
- im = ax.imshow(risk_matrix, cmap="RdYlGn_r")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
 
694
- # إضافة التسميات
695
- ax.set_xticks(np.arange(3))
696
- ax.set_yticks(np.arange(3))
697
- ax.set_xticklabels(["منخفض", "متوسط", "عالي"])
698
- ax.set_yticklabels(["منخفضة", "متوسطة", "عالية"])
699
 
700
- # إضافة العناوين
701
- ax.set_xlabel("التأثير")
702
- ax.set_ylabel("الاحتمالية")
703
- ax.set_title("مصفوفة المخاطر")
704
 
705
- # إضافة القيم داخل المصفوفة
706
- for i in range(3):
707
- for j in range(3):
708
- text = ax.text(j, i, risk_matrix[i, j],
709
- ha="center", va="center", color="white")
710
 
711
- plt.colorbar(im, label="درجة الخطورة")
712
- st.pyplot(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
 
714
  def _render_document_analysis_tab(self):
715
  """عرض تبويب تحليل المستندات"""
@@ -747,7 +1115,13 @@ class AIAssistantApp:
747
  default=["استخراج المتطلبات", "تحديد المواعيد النهائية"]
748
  )
749
 
750
- if st.button("تحليل المستند"):
 
 
 
 
 
 
751
  # عرض شريط التقدم
752
  progress_bar = st.progress(0)
753
  status_text = st.empty()
@@ -763,7 +1137,7 @@ class AIAssistantApp:
763
  status_text.text("جاري تحليل المحتوى...")
764
  else:
765
  status_text.text("جاري إعداد النتائج...")
766
- time.sleep(0.05)
767
 
768
  st.success("تم تحليل المستند بنجاح!")
769
 
@@ -806,6 +1180,66 @@ class AIAssistantApp:
806
  ]
807
  for term in terms:
808
  st.markdown(f"- {term}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
 
810
  def _render_local_content_tab(self):
811
  """عرض تبويب المحتوى المحلي"""
@@ -851,7 +1285,13 @@ class AIAssistantApp:
851
  )
852
 
853
  # زر حساب المحتوى المحلي
854
- if st.button("حساب نسبة المحتوى المحلي"):
 
 
 
 
 
 
855
  # حساب نسبة المحتوى المحلي
856
  total_local_content = local_products + local_services + local_workforce
857
  local_content_percentage = (total_local_content / project_value) * 100
@@ -875,15 +1315,115 @@ class AIAssistantApp:
875
 
876
  with col2:
877
  # عرض رسم بياني للمحتوى المحلي
878
- fig = px.pie(
879
- names=["المنتجات المحلية", "الخدمات المحلية", "القوى العاملة المحلية", "غير محلي"],
880
- values=[local_products, local_services, local_workforce, project_value - total_local_content],
881
- title="توزيع المحتوى المحلي"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
882
  )
883
- st.plotly_chart(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
884
 
885
  # توصيات لتحسين المحتوى المحلي
886
- st.subheader("توصيات لتحسين المحتوى المحلي")
887
 
888
  recommendations = [
889
  "استخدام منتجات محلية الصنع بدلاً من المستوردة حيثما أمكن",
@@ -912,8 +1452,30 @@ class AIAssistantApp:
912
 
913
  new_question = st.text_input("اكتب سؤالك هنا")
914
 
915
- if st.button("إرسال السؤال"):
916
- if new_question:
917
- st.success("تم إرسال سؤالك بنجاح! سيتم الرد عليه في أقرب وقت.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
918
  else:
919
- st.error("يرجى كتابة السؤال قبل الإرسال.")
 
 
 
10
  import numpy as np
11
  import matplotlib.pyplot as plt
12
  import plotly.express as px
13
+ import plotly.graph_objects as go
14
  import requests
15
  import json
16
  import time
 
24
  from io import BytesIO
25
  from tempfile import NamedTemporaryFile
26
  from PIL import Image
27
+ import arabic_reshaper
28
+ from bidi.algorithm import get_display
29
+ import matplotlib.font_manager as fm
30
+ import seaborn as sns
31
+
32
+ # تكوين الخطوط العربية
33
+ plt.rcParams['font.family'] = 'Arial'
34
+
35
+ # دالة مساعدة لعرض النص العربي بشكل صحيح
36
+ def get_display_arabic(text):
37
+ """تحويل النص العربي للعرض الصحيح في الرسوم البيانية"""
38
+ reshaped_text = arabic_reshaper.reshape(text)
39
+ bidi_text = get_display(reshaped_text)
40
+ return bidi_text
41
 
42
  class ClaudeAIService:
43
  """
 
293
  stack_trace = traceback.format_exc()
294
  return {"error": f"فشل في إكمال المحادثة: {str(e)}\n{stack_trace}"}
295
 
296
+ def analyze_text(self, text, analysis_type="general", model_name="claude-3-5-haiku"):
297
+ """
298
+ تحليل النص باستخدام الذكاء الاصطناعي
299
+
300
+ المعلمات:
301
+ text: النص المراد تحليله
302
+ analysis_type: نوع التحليل (general, requirements, risks, costs)
303
+ model_name: اسم النموذج المستخدم
304
+
305
+ العوائد:
306
+ dict: نتائج التحليل
307
+ """
308
+ try:
309
+ # تحديد التوجيه المناسب حسب نوع التحليل
310
+ if analysis_type == "requirements":
311
+ prompt = f"""
312
+ قم بتحليل النص التالي واستخراج المتطلبات الرئيسية للمشروع. صنف المتطلبات إلى فئات (فنية، إدارية، مالية، قانونية) وحدد أولوية كل متطلب (عالية، متوسطة، منخفضة).
313
+
314
+ النص:
315
+ {text}
316
+
317
+ قدم النتائج بتنسيق JSON يحتوي على مصفوفة من المتطلبات، كل متطلب يحتوي على: الوصف، الفئة، الأولوية.
318
+ """
319
+ elif analysis_type == "risks":
320
+ prompt = f"""
321
+ قم بتحليل النص التالي وتحديد المخاطر المحتملة للمشروع. لكل خطر، حدد احتمالية حدوثه (عالية، متوسطة، منخفضة) وتأثيره (عالي، متوسط، منخفض) واقترح استراتيجية للتخفيف من حدته.
322
+
323
+ النص:
324
+ {text}
325
+
326
+ قدم النتائج بتنسيق JSON يحتوي على مصفوفة من المخاطر، كل خطر يحتوي على: الوصف، الاحتمالية، التأثير، استراتيجية التخفيف.
327
+ """
328
+ elif analysis_type == "costs":
329
+ prompt = f"""
330
+ قم بتحليل النص التالي وتحديد عناصر التكلفة المحتملة للمشروع. صنف التكاليف إلى فئات (مباشرة، غير مباشرة) وحدد ما إذا كانت ثابتة أو متغيرة.
331
+
332
+ النص:
333
+ {text}
334
+
335
+ قدم النتائج بتنسيق JSON يحتوي على مصفوفة من عناصر التكلفة، كل عنصر يحتوي على: الوصف، الفئة، النوع (ثابت/متغير)، تقدير نسبي للتكلفة (%).
336
+ """
337
+ elif analysis_type == "local_content":
338
+ prompt = f"""
339
+ قم بتحليل النص التالي وتحديد عناصر المحتوى المحلي المحتملة. صنف العناصر إلى فئات (منتجات، خدمات، قوى عاملة) وحدد مدى توفرها محلياً (متوفر بشكل كامل، متوفر جزئياً، غير متوفر).
340
+
341
+ النص:
342
+ {text}
343
+
344
+ قدم النتائج بتنسيق JSON يحتوي على مصفوفة من عناصر المحتوى المحلي، كل عنصر يحتوي على: الوصف، الفئة، مدى التوفر، تقدير نسبي للمساهمة في المحتوى المحلي (%).
345
+ """
346
+ else: # general
347
+ prompt = f"""
348
+ قم بتحليل النص التالي وتلخيص النقاط الرئيسية. حدد الموضوعات الأساسية والأفكار المهمة والتوصيات إن وجدت.
349
+
350
+ النص:
351
+ {text}
352
+
353
+ قدم النتائج بتنسيق JSON يحتوي على: ملخص عام، النقاط الرئيسية (مصفوفة)، التوصيات (مصفوفة).
354
+ """
355
+
356
+ # إنشاء رسائل المحادثة
357
+ messages = [
358
+ {"role": "user", "content": prompt}
359
+ ]
360
+
361
+ # استدعاء دالة إكمال المحادثة
362
+ result = self.chat_completion(messages, model_name)
363
+
364
+ # معالجة النتيجة
365
+ if "error" in result:
366
+ return result
367
+
368
+ # محاولة تحويل النتيجة إلى JSON
369
+ try:
370
+ # استخراج النص من النتيجة
371
+ content = result["content"]
372
+
373
+ # البحث عن بداية ونهاية JSON
374
+ json_start = content.find('{')
375
+ json_end = content.rfind('}') + 1
376
+
377
+ if json_start >= 0 and json_end > json_start:
378
+ json_str = content[json_start:json_end]
379
+ analysis_result = json.loads(json_str)
380
+
381
+ return {
382
+ "success": True,
383
+ "result": analysis_result,
384
+ "model": result["model"]
385
+ }
386
+ else:
387
+ # إذا لم يتم العثور على JSON، إرجاع النص كاملاً
388
+ return {
389
+ "success": True,
390
+ "result": {"text": content},
391
+ "model": result["model"]
392
+ }
393
+ except Exception as e:
394
+ # إذا فشل تحويل النتيجة إلى JSON، إرجاع النص كاملاً
395
+ return {
396
+ "success": True,
397
+ "result": {"text": content},
398
+ "model": result["model"],
399
+ "parse_error": str(e)
400
+ }
401
+
402
+ except Exception as e:
403
+ logging.error(f"خطأ أثناء تحليل النص: {str(e)}")
404
+ import traceback
405
+ stack_trace = traceback.format_exc()
406
+ return {"error": f"فشل في تحليل النص: {str(e)}\n{stack_trace}"}
407
+
408
 
409
  class AIAssistantApp:
410
  """وحدة المساعد الذكي"""
 
642
  # مربع إدخال الرسالة
643
  user_input = st.text_input("اكتب رسالتك هنا", key="ai_assistant_input")
644
 
645
+ # زر إرسال مع تحليل ذكي
646
+ col1, col2 = st.columns([3, 1])
647
+ with col1:
648
+ send_button = st.button("إرسال", key="send_message_button")
649
+ with col2:
650
+ analyze_options = st.selectbox(
651
+ "تحليل ذكي",
652
+ ["عام", "متطلبات", "مخاطر", "تكاليف", "محتوى محلي"],
653
+ key="analyze_type"
654
+ )
655
+
656
+ # معالجة الإدخال
657
+ if send_button and user_input:
658
+ # إضافة رسالة المستخدم إلى المحادثة
659
+ st.session_state.ai_assistant_messages.append(
660
+ {"role": "user", "content": user_input}
661
+ )
662
+
663
+ # تحديد نوع التحليل
664
+ analysis_type_map = {
665
+ "عام": "general",
666
+ "متطلبات": "requirements",
667
+ "مخاطر": "risks",
668
+ "تكاليف": "costs",
669
+ "محتوى محلي": "local_content"
670
+ }
671
+
672
+ analysis_type = analysis_type_map.get(analyze_options, "general")
673
+
674
+ # إظهار مؤشر التحميل
675
+ with st.spinner("جاري التحليل..."):
676
+ # تحليل النص باستخدام الذكاء الاصطناعي
677
+ result = self.claude_service.analyze_text(user_input, analysis_type, selected_model)
678
+
679
+ # إعداد الرد
680
+ if "error" in result:
681
+ response = f"عذراً، حدث خطأ أثناء التحليل: {result['error']}"
682
+ else:
683
+ # تنسيق النتيجة بشكل مقروء
684
+ if isinstance(result["result"], dict):
685
+ if "text" in result["result"]:
686
+ # إذا كانت النتيجة نصية
687
+ response = result["result"]["text"]
688
+ else:
689
+ # إذا كانت النتيجة هيكلية
690
+ response = "نتائج التحليل:\n\n"
691
+ for key, value in result["result"].items():
692
+ if isinstance(value, list):
693
+ response += f"**{key}**:\n"
694
+ for i, item in enumerate(value):
695
+ if isinstance(item, dict):
696
+ response += f"{i+1}. "
697
+ for k, v in item.items():
698
+ response += f"**{k}**: {v}, "
699
+ response = response[:-2] + "\n"
700
+ else:
701
+ response += f"{i+1}. {item}\n"
702
+ else:
703
+ response += f"**{key}**: {value}\n"
704
+ else:
705
+ response = str(result["result"])
706
+
707
+ # إضافة رد المساعد إلى المحادثة
708
+ st.session_state.ai_assistant_messages.append(
709
+ {"role": "assistant", "content": response}
710
+ )
711
+
712
+ # إعادة تحميل الصفحة لعرض الرد
713
+ st.experimental_rerun()
714
+
715
  # التحقق من وجود مفتاح API
716
  api_available = True
717
  try:
 
772
  )
773
 
774
  # زر حساب التكلفة
775
+ col1, col2 = st.columns([3, 1])
776
+ with col1:
777
+ calculate_button = st.button("حساب التكلفة التقديرية")
778
+ with col2:
779
+ ai_analysis = st.checkbox("تحليل ذكي", value=True)
780
+
781
+ if calculate_button:
782
  # حساب التكلفة (هذا مثال بسيط)
783
  overhead_cost = direct_cost * (overhead_percentage / 100)
784
  profit = direct_cost * (profit_percentage / 100)
 
801
  st.metric("إجمالي التكلفة التقديرية", f"{total_cost:,.2f} ريال", delta="تقدير أولي")
802
 
803
  # عرض رسم بياني للتكاليف
804
+ fig = go.Figure()
805
+
806
+ # إضافة البيانات
807
+ labels = ["التكاليف المباشرة", "المصاريف العامة", "الربح المتوقع"]
808
+ values = [direct_cost, overhead_cost, profit]
809
+ colors = ['#1f77b4', '#ff7f0e', '#2ca02c']
810
+
811
+ fig.add_trace(go.Pie(
812
+ labels=[get_display_arabic(label) for label in labels],
813
+ values=values,
814
+ textinfo='percent+label',
815
+ insidetextorientation='radial',
816
+ marker=dict(colors=colors),
817
+ hole=0.4
818
+ ))
819
+
820
+ # تخصيص الرسم البياني
821
+ fig.update_layout(
822
+ title=get_display_arabic("توزيع التكاليف"),
823
+ height=400,
824
+ margin=dict(l=0, r=0, t=40, b=0),
825
+ font=dict(size=14),
826
+ legend=dict(
827
+ orientation="h",
828
+ yanchor="bottom",
829
+ y=-0.2,
830
+ xanchor="center",
831
+ x=0.5
832
+ )
833
  )
834
+
835
+ st.plotly_chart(fig, use_container_width=True)
836
+
837
+ # تحليل ذكي للتكاليف إذا تم تحديد الخيار
838
+ if ai_analysis:
839
+ with st.spinner("جاري تحليل التكاليف باستخدام الذكاء الاصطناعي..."):
840
+ # إنشاء نص وصفي للمشروع
841
+ project_description = f"""
842
+ نوع المشروع: {project_type}
843
+ مدة المشروع: {project_duration} أشهر
844
+ موقع المشروع: {project_location}
845
+ التكاليف المباشرة: {direct_cost:,.2f} ريال
846
+ نسبة المصاريف العامة: {overhead_percentage}%
847
+ نسبة الربح المستهدفة: {profit_percentage}%
848
+ إجمالي التكلفة التقديرية: {total_cost:,.2f} ريال
849
+ """
850
+
851
+ # تحليل النص باستخدام الذكاء الاصطناعي
852
+ result = self.claude_service.analyze_text(project_description, "costs")
853
+
854
+ if "error" not in result:
855
+ st.subheader("تحليل التكاليف بالذكاء الاصطناعي")
856
+
857
+ # عرض نتائج التحليل
858
+ if isinstance(result["result"], dict) and "text" not in result["result"]:
859
+ # إذا كانت النتيجة هيكلية
860
+ if "عناصر التكلفة" in result["result"]:
861
+ cost_items = result["result"]["عناصر التكلفة"]
862
+
863
+ # إنشاء جدول لعناصر التكلفة
864
+ cost_data = []
865
+ for item in cost_items:
866
+ if isinstance(item, dict):
867
+ cost_data.append({
868
+ "الوصف": item.get("الوصف", ""),
869
+ "الفئة": item.get("الفئة", ""),
870
+ "النوع": item.get("النوع", ""),
871
+ "التقدير النسبي": item.get("تقدير نسبي للتكلفة", "")
872
+ })
873
+
874
+ if cost_data:
875
+ cost_df = pd.DataFrame(cost_data)
876
+ st.dataframe(cost_df, use_container_width=True)
877
+ else:
878
+ # إذا كانت النتيجة نصية
879
+ st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
880
 
881
  def _render_risk_analysis_tab(self):
882
  """عرض تبويب تحليل المخاطر"""
 
948
  )
949
  response_plan = st.text_area("خطة الاستجابة")
950
 
951
+ col1, col2 = st.columns([3, 1])
952
+ with col1:
953
+ add_button = st.button("إضافة المخاطرة")
954
+ with col2:
955
+ ai_suggestion = st.checkbox("اقتراح ذكي", value=True)
956
+
957
+ if ai_suggestion and new_risk:
958
+ with st.spinner("جاري تحليل المخاطرة باستخدام الذكاء الاصطناعي..."):
959
+ # تحليل المخاطرة باستخدام الذكاء الاصطناعي
960
+ risk_description = f"المخاطرة: {new_risk}"
961
+ result = self.claude_service.analyze_text(risk_description, "risks")
962
+
963
+ if "error" not in result and isinstance(result["result"], dict):
964
+ if "المخاطر" in result["result"] and isinstance(result["result"]["المخاطر"], list):
965
+ risk_analysis = result["result"]["المخاطر"][0]
966
+
967
+ if isinstance(risk_analysis, dict):
968
+ st.success("تم تحليل المخاطرة بنجاح!")
969
+
970
+ # عرض نتائج التحليل
971
+ col1, col2 = st.columns(2)
972
+ with col1:
973
+ st.write("**الاحتمالية المقترحة:**", risk_analysis.get("الاحتمالية", "غير محدد"))
974
+ st.write("**التأثير المقترح:**", risk_analysis.get("التأثير", "غير محدد"))
975
+ with col2:
976
+ st.write("**استراتيجية التخفيف المقترحة:**", risk_analysis.get("استراتيجية التخفيف", "غير محدد"))
977
+
978
+ if add_button and new_risk:
979
  st.success("تمت إضافة المخاطرة بنجاح!")
980
 
981
  # عرض مصفوفة المخاطر
982
  st.subheader("مصفوفة المخاطر")
983
 
984
+ # بيانات مصفوفة المخاطر
985
  risk_matrix = np.array([
986
  [1, 2, 3],
987
  [2, 4, 6],
988
  [3, 6, 9]
989
  ])
990
 
991
+ # إنشاء مصفوفة المخاطر باستخدام Plotly
992
+ fig = go.Figure()
993
+
994
+ # تحديد الألوان
995
+ colorscale = [
996
+ [0, '#1a9850'], # أخضر داكن (مخاطر منخفضة)
997
+ [0.3, '#91cf60'], # أخضر فاتح
998
+ [0.5, '#ffffbf'], # أصفر
999
+ [0.7, '#fc8d59'], # برتقالي
1000
+ [1, '#d73027'] # أحمر (مخاطر عالية)
1001
+ ]
1002
+
1003
+ # إنشاء مصفوفة المخاطر
1004
+ fig.add_trace(go.Heatmap(
1005
+ z=risk_matrix,
1006
+ x=['منخفض', 'متوسط', 'عالي'],
1007
+ y=['منخفضة', 'متوسطة', 'عالية'],
1008
+ text=risk_matrix,
1009
+ texttemplate="%{text}",
1010
+ textfont={"size":20},
1011
+ colorscale=colorscale,
1012
+ showscale=True,
1013
+ colorbar=dict(
1014
+ title=get_display_arabic("درجة الخطورة"),
1015
+ titleside="right",
1016
+ titlefont=dict(size=14),
1017
+ tickfont=dict(size=12),
1018
+ )
1019
+ ))
1020
+
1021
+ # تخصيص الرسم البياني
1022
+ fig.update_layout(
1023
+ title=get_display_arabic("مصفوفة المخاطر"),
1024
+ height=500,
1025
+ margin=dict(l=50, r=50, t=50, b=50),
1026
+ xaxis=dict(
1027
+ title=get_display_arabic("التأثير"),
1028
+ titlefont=dict(size=14),
1029
+ tickfont=dict(size=12),
1030
+ ),
1031
+ yaxis=dict(
1032
+ title=get_display_arabic("الاحتمالية"),
1033
+ titlefont=dict(size=14),
1034
+ tickfont=dict(size=12),
1035
+ ),
1036
+ font=dict(size=14)
1037
+ )
1038
 
1039
+ # عرض الرسم البياني
1040
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
1041
 
1042
+ # تحليل المخاطر باستخدام الذكاء الاصطناعي
1043
+ st.subheader("تحليل المخاطر بالذكاء الاصطناعي")
 
 
1044
 
1045
+ risk_text = st.text_area("أدخل وصف المشروع لتحليل المخاطر المحتملة", height=150)
 
 
 
 
1046
 
1047
+ if st.button("تحليل المخاطر"):
1048
+ if risk_text:
1049
+ with st.spinner("جاري تحليل المخاطر باستخدام الذكاء الاصطناعي..."):
1050
+ # تحليل النص باستخدام الذكاء الاصطناعي
1051
+ result = self.claude_service.analyze_text(risk_text, "risks")
1052
+
1053
+ if "error" not in result:
1054
+ st.success("تم تحليل المخاطر بنجاح!")
1055
+
1056
+ # عرض نتائج التحليل
1057
+ if isinstance(result["result"], dict) and "المخاطر" in result["result"]:
1058
+ risks = result["result"]["المخاطر"]
1059
+
1060
+ # إنشاء جدول للمخاطر
1061
+ risk_data = []
1062
+ for risk in risks:
1063
+ if isinstance(risk, dict):
1064
+ risk_data.append({
1065
+ "المخاطر": risk.get("الوصف", ""),
1066
+ "الاحتمالية": risk.get("الاحتمالية", ""),
1067
+ "التأثير": risk.get("التأثير", ""),
1068
+ "خطة الاستجابة": risk.get("استراتيجية التخفيف", "")
1069
+ })
1070
+
1071
+ if risk_data:
1072
+ risk_df = pd.DataFrame(risk_data)
1073
+ st.dataframe(risk_df, use_container_width=True)
1074
+ else:
1075
+ # إذا كانت النتيجة نصية
1076
+ st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
1077
+ else:
1078
+ st.error(f"حدث خطأ أثناء التحليل: {result['error']}")
1079
+ else:
1080
+ st.warning("يرجى إدخال وصف المشروع لتحليل المخاطر.")
1081
 
1082
  def _render_document_analysis_tab(self):
1083
  """عرض تبويب تحليل المستندات"""
 
1115
  default=["استخراج المتطلبات", "تحديد المواعيد النهائية"]
1116
  )
1117
 
1118
+ col1, col2 = st.columns([3, 1])
1119
+ with col1:
1120
+ analyze_button = st.button("تحليل المستند")
1121
+ with col2:
1122
+ ai_analysis = st.checkbox("تحليل ذكي", value=True)
1123
+
1124
+ if analyze_button:
1125
  # عرض شريط التقدم
1126
  progress_bar = st.progress(0)
1127
  status_text = st.empty()
 
1137
  status_text.text("جاري تحليل المحتوى...")
1138
  else:
1139
  status_text.text("جاري إعداد النتائج...")
1140
+ time.sleep(0.02)
1141
 
1142
  st.success("تم تحليل المستند بنجاح!")
1143
 
 
1180
  ]
1181
  for term in terms:
1182
  st.markdown(f"- {term}")
1183
+
1184
+ # تحليل ذكي للمستند إذا تم تحديد الخيار
1185
+ if ai_analysis:
1186
+ st.subheader("تحليل المستند بالذكاء الاصطناعي")
1187
+
1188
+ # محاكاة تحليل المستند
1189
+ with st.spinner("جاري تحليل المستند باستخدام الذكاء الاصطناعي..."):
1190
+ # محاكاة نص المستند
1191
+ document_text = """
1192
+ مناقصة رقم: 2025/123
1193
+
1194
+ المشروع: تطوير نظام إدارة المشاريع
1195
+
1196
+ المتطلبات الفنية:
1197
+ 1. تطوير نظام متكامل لإدارة المشاريع
1198
+ 2. توفير واجهة مستخدم سهلة الاستخدام
1199
+ 3. دعم اللغة العربية والإنجليزية
1200
+ 4. توفير تقارير تحليلية متقدمة
1201
+ 5. دعم الأجهزة المحمولة
1202
+
1203
+ المواعيد:
1204
+ - آخر موعد لتقديم العروض: 15/05/2025
1205
+ - بدء المشروع: 01/06/2025
1206
+ - تسليم المرحلة الأولى: 01/08/2025
1207
+ - تسليم المرحلة الثانية: 01/10/2025
1208
+ - التسليم النهائي: 31/12/2025
1209
+
1210
+ الشروط والأحكام:
1211
+ - مدة العقد: 12 شهر
1212
+ - غرامة التأخير: 1% من قيمة العقد عن كل أسبوع تأخير
1213
+ - شروط الدفع: 20% دفعة مقدمة، 60% على مراحل، 20% بعد الاستلام النهائي
1214
+ - الضمان: سنتان من تاريخ الاستلام النهائي
1215
+ """
1216
+
1217
+ # تحليل النص باستخدام الذكاء الاصطناعي
1218
+ result = self.claude_service.analyze_text(document_text, "requirements")
1219
+
1220
+ if "error" not in result:
1221
+ # عرض نتائج التحليل
1222
+ if isinstance(result["result"], dict) and "المتطلبات" in result["result"]:
1223
+ requirements = result["result"]["المتطلبات"]
1224
+
1225
+ # إنشاء جدول للمتطلبات
1226
+ req_data = []
1227
+ for req in requirements:
1228
+ if isinstance(req, dict):
1229
+ req_data.append({
1230
+ "الوصف": req.get("الوصف", ""),
1231
+ "الفئة": req.get("الفئة", ""),
1232
+ "الأولوية": req.get("الأولوية", "")
1233
+ })
1234
+
1235
+ if req_data:
1236
+ req_df = pd.DataFrame(req_data)
1237
+ st.dataframe(req_df, use_container_width=True)
1238
+ else:
1239
+ # إذا كانت النتيجة نصية
1240
+ st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
1241
+ else:
1242
+ st.error(f"حدث خطأ أثناء التحليل: {result['error']}")
1243
 
1244
  def _render_local_content_tab(self):
1245
  """عرض تبويب المحتوى المحلي"""
 
1285
  )
1286
 
1287
  # زر حساب المحتوى المحلي
1288
+ col1, col2 = st.columns([3, 1])
1289
+ with col1:
1290
+ calculate_button = st.button("حساب نسبة المحتوى المحلي")
1291
+ with col2:
1292
+ ai_optimization = st.checkbox("تحسين ذكي", value=True)
1293
+
1294
+ if calculate_button:
1295
  # حساب نسبة المحتوى المحلي
1296
  total_local_content = local_products + local_services + local_workforce
1297
  local_content_percentage = (total_local_content / project_value) * 100
 
1315
 
1316
  with col2:
1317
  # عرض رسم بياني للمحتوى المحلي
1318
+ fig = go.Figure()
1319
+
1320
+ # إضافة البيانات
1321
+ labels = ["المنتجات المحلية", "الخدمات المحلية", "القوى العاملة المحلية", "غير محلي"]
1322
+ values = [local_products, local_services, local_workforce, project_value - total_local_content]
1323
+ colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
1324
+
1325
+ fig.add_trace(go.Pie(
1326
+ labels=[get_display_arabic(label) for label in labels],
1327
+ values=values,
1328
+ textinfo='percent',
1329
+ insidetextorientation='radial',
1330
+ marker=dict(colors=colors),
1331
+ hole=0.4
1332
+ ))
1333
+
1334
+ # تخصيص الرسم البياني
1335
+ fig.update_layout(
1336
+ title=get_display_arabic("توزيع المحتوى المحلي"),
1337
+ height=400,
1338
+ margin=dict(l=0, r=0, t=40, b=0),
1339
+ font=dict(size=14),
1340
+ legend=dict(
1341
+ orientation="h",
1342
+ yanchor="bottom",
1343
+ y=-0.2,
1344
+ xanchor="center",
1345
+ x=0.5
1346
+ )
1347
  )
1348
+
1349
+ st.plotly_chart(fig, use_container_width=True)
1350
+
1351
+ # عرض تفاصيل المحتوى المحلي
1352
+ st.subheader("تفاصيل المحتوى المحلي")
1353
+
1354
+ # إنشاء جدول البيانات
1355
+ local_content_data = {
1356
+ "العنصر": ["المنتجات المحلية", "الخدمات المحلية", "القوى العاملة المحلية", "إجمالي المحتوى المحلي", "غير محلي", "إجمالي المشروع"],
1357
+ "القيمة (ريال)": [local_products, local_services, local_workforce, total_local_content, project_value - total_local_content, project_value],
1358
+ "النسبة (%)": [
1359
+ local_products / project_value * 100,
1360
+ local_services / project_value * 100,
1361
+ local_workforce / project_value * 100,
1362
+ local_content_percentage,
1363
+ (project_value - total_local_content) / project_value * 100,
1364
+ 100
1365
+ ]
1366
+ }
1367
+
1368
+ local_content_df = pd.DataFrame(local_content_data)
1369
+ local_content_df["النسبة (%)"] = local_content_df["النسبة (%)"].round(2)
1370
+ local_content_df["القيمة (ريال)"] = local_content_df["القيمة (ريال)"].apply(lambda x: f"{x:,.2f}")
1371
+
1372
+ st.dataframe(local_content_df, use_container_width=True)
1373
+
1374
+ # تحسين المحتوى المحلي باستخدام الذكاء الاصطناعي
1375
+ if ai_optimization:
1376
+ st.subheader("توصيات تحسين المحتوى المحلي")
1377
+
1378
+ with st.spinner("جاري تحليل وتحسين المحتوى المحلي باستخدام الذكاء الاصطناعي..."):
1379
+ # إنشاء وصف للمشروع
1380
+ project_description = f"""
1381
+ قطاع المشروع: {project_sector}
1382
+ القيمة الإجمالية للمشروع: {project_value:,.2f} ريال
1383
+ قيمة المنتجات المحلية: {local_products:,.2f} ريال ({local_products/project_value*100:.2f}%)
1384
+ قيمة الخدمات المحلية: {local_services:,.2f} ريال ({local_services/project_value*100:.2f}%)
1385
+ تكلفة القوى العاملة المحلية: {local_workforce:,.2f} ريال ({local_workforce/project_value*100:.2f}%)
1386
+ إجمالي المحتوى المحلي: {total_local_content:,.2f} ريال ({local_content_percentage:.2f}%)
1387
+ """
1388
+
1389
+ # تحليل النص باستخدام الذكاء الاصطناعي
1390
+ result = self.claude_service.analyze_text(project_description, "local_content")
1391
+
1392
+ if "error" not in result:
1393
+ # عرض نتائج التحليل
1394
+ if isinstance(result["result"], dict):
1395
+ if "توصيات" in result["result"]:
1396
+ recommendations = result["result"]["توصيات"]
1397
+
1398
+ for i, rec in enumerate(recommendations):
1399
+ st.markdown(f"**{i+1}. {rec}**")
1400
+ elif "عناصر المحتوى المحلي" in result["result"]:
1401
+ items = result["result"]["عناصر المحتوى المحلي"]
1402
+
1403
+ # إنشاء جدول للعناصر
1404
+ items_data = []
1405
+ for item in items:
1406
+ if isinstance(item, dict):
1407
+ items_data.append({
1408
+ "الوصف": item.get("الوصف", ""),
1409
+ "الفئة": item.get("الفئة", ""),
1410
+ "مدى التوفر": item.get("مدى التوفر", ""),
1411
+ "المساهمة": item.get("تقدير نسبي للمساهمة في المحتوى المحلي", "")
1412
+ })
1413
+
1414
+ if items_data:
1415
+ items_df = pd.DataFrame(items_data)
1416
+ st.dataframe(items_df, use_container_width=True)
1417
+ else:
1418
+ # إذا كانت النتيجة نصية
1419
+ st.write(result["result"].get("text", "لا توجد نتائج تحليل"))
1420
+ else:
1421
+ st.write("لا توجد توصيات متاحة.")
1422
+ else:
1423
+ st.error(f"حدث خطأ أثناء التحليل: {result['error']}")
1424
 
1425
  # توصيات لتحسين المحتوى المحلي
1426
+ st.subheader("توصيات عامة لتحسين المحتوى المحلي")
1427
 
1428
  recommendations = [
1429
  "استخدام منتجات محلية الصنع بدلاً من المستوردة حيثما أمكن",
 
1452
 
1453
  new_question = st.text_input("اكتب سؤالك هنا")
1454
 
1455
+ col1, col2 = st.columns([3, 1])
1456
+ with col1:
1457
+ send_button = st.button("إرسال السؤال")
1458
+ with col2:
1459
+ ai_answer = st.checkbox("إجابة ذكية", value=True)
1460
+
1461
+ if send_button and new_question:
1462
+ if ai_answer:
1463
+ with st.spinner("جاري تحليل السؤال باستخدام الذكاء الاصطناعي..."):
1464
+ # تحليل السؤال باستخدام الذكاء الاصطناعي
1465
+ result = self.claude_service.chat_completion([
1466
+ {"role": "user", "content": f"السؤال: {new_question}\n\nالرجاء الإجابة على هذا السؤال المتعلق بنظام تسعير المناقصات بشكل مختصر ومفيد."}
1467
+ ])
1468
+
1469
+ if "error" not in result:
1470
+ st.success("تم تحليل السؤال والإجابة عليه!")
1471
+
1472
+ # عرض الإجابة
1473
+ st.info(f"**سؤالك:** {new_question}")
1474
+ st.write(f"**الإجابة:** {result['content']}")
1475
+ else:
1476
+ st.error(f"حدث خطأ أثناء تحليل السؤال: {result['error']}")
1477
+ st.success("تم إرسال سؤالك بنجاح! سيتم الرد عليه في أقرب وقت.")
1478
  else:
1479
+ st.success("تم إرسال سؤالك بنجاح! سيتم الرد عليه في أقرب وقت.")
1480
+ elif send_button:
1481
+ st.error("يرجى كتابة السؤال قبل الإرسال.")