Spaces:
Sleeping
Sleeping
""" | |
تطبيق وحدة تحليل المخاطر | |
""" | |
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import matplotlib.pyplot as plt | |
import plotly.express as px | |
import plotly.graph_objects as go | |
from datetime import datetime | |
import random | |
import os | |
import time | |
import io | |
from utils.helpers import format_number, format_currency | |
from utils.excel_handler import export_to_excel | |
class RiskAnalysisApp: | |
"""وحدة تحليل المخاطر""" | |
def __init__(self): | |
"""تهيئة وحدة تحليل المخاطر""" | |
# تهيئة المخاطر المحتملة | |
self.risk_categories = [ | |
"مخاطر مالية", | |
"مخاطر زمنية", | |
"مخاطر فنية", | |
"مخاطر إدارية", | |
"مخاطر تنظيمية", | |
"مخاطر سوقية", | |
"مخاطر تعاقدية" | |
] | |
self.impact_levels = ["منخفض", "متوسط", "عالي"] | |
self.probability_levels = ["غير محتمل", "محتمل", "مؤكد"] | |
def render(self): | |
"""عرض واجهة وحدة تحليل المخاطر""" | |
st.markdown("<h1 class='module-title'>وحدة تحليل المخاطر</h1>", unsafe_allow_html=True) | |
tabs = st.tabs([ | |
"تحليل المخاطر", | |
"سجل المخاطر", | |
"مصفوفة المخاطر", | |
"خطة الاستجابة للمخاطر" | |
]) | |
with tabs[0]: | |
self._render_risk_analysis_tab() | |
with tabs[1]: | |
self._render_risk_register_tab() | |
with tabs[2]: | |
self._render_risk_matrix_tab() | |
with tabs[3]: | |
self._render_risk_response_tab() | |
def _render_risk_analysis_tab(self): | |
"""عرض تبويب تحليل المخاطر""" | |
st.markdown("### تحليل المخاطر") | |
# التحقق من وجود مشروع حالي | |
if 'current_project' not in st.session_state or st.session_state.current_project is None: | |
# إذا لم يكن هناك مشروع محدد، اعرض قائمة باختيار المشروع | |
if 'projects' in st.session_state and st.session_state.projects: | |
project_names = [p['name'] for p in st.session_state.projects] | |
selected_project_name = st.selectbox("اختر المشروع", project_names) | |
if selected_project_name: | |
selected_project = next((p for p in st.session_state.projects if p['name'] == selected_project_name), None) | |
if selected_project: | |
st.session_state.current_project = selected_project | |
else: | |
st.warning("لم يتم العثور على المشروع المحدد.") | |
return | |
else: | |
st.info("يرجى اختيار مشروع لتحليل مخاطره.") | |
return | |
else: | |
st.warning("لا توجد مشاريع متاحة. يرجى إنشاء مشروع جديد أولاً.") | |
return | |
# عرض معلومات المشروع | |
project = st.session_state.current_project | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.metric("اسم المشروع", project['name']) | |
with col2: | |
st.metric("رقم المناقصة", project['number']) | |
with col3: | |
st.metric("الجهة المالكة", project['client']) | |
# التحقق من وجود سجل المخاطر للمشروع | |
if 'risks' not in project: | |
project['risks'] = [] | |
# نموذج إضافة مخاطر | |
with st.form("add_risk_form"): | |
st.markdown("#### إضافة مخاطرة جديدة") | |
col1, col2 = st.columns(2) | |
with col1: | |
risk_code = st.text_input("رمز المخاطرة", f"R{len(project['risks']) + 1}") | |
risk_category = st.selectbox("فئة المخاطرة", self.risk_categories) | |
impact = st.select_slider("التأثير", self.impact_levels, value="متوسط") | |
with col2: | |
risk_description = st.text_area("وصف المخاطرة", height=80) | |
probability = st.select_slider("الاحتمالية", self.probability_levels, value="محتمل") | |
response_strategy = st.text_area("استراتيجية الاستجابة", height=80) | |
submitted = st.form_submit_button("إضافة المخاطرة") | |
if submitted: | |
# التحقق من تعبئة الحقول الإلزامية | |
if not risk_description: | |
st.error("يرجى إدخال وصف المخاطرة.") | |
else: | |
# إنشاء مخاطرة جديدة | |
new_risk = { | |
'id': len(project['risks']) + 1, | |
'risk_code': risk_code, | |
'description': risk_description, | |
'category': risk_category, | |
'impact': impact, | |
'probability': probability, | |
'response_strategy': response_strategy, | |
'status': "نشط", | |
'created_at': datetime.now().strftime('%Y-%m-%d'), | |
'risk_score': self._calculate_risk_score(impact, probability) | |
} | |
# إضافة المخاطرة إلى سجل المخاطر | |
project['risks'].append(new_risk) | |
st.success(f"تمت إضافة المخاطرة [{risk_code}] بنجاح!") | |
st.balloons() | |
# خيارات تحليل المخاطر | |
st.markdown("#### خيارات تحليل المخاطر") | |
col1, col2 = st.columns(2) | |
with col1: | |
automated_analysis = st.button("تحليل تلقائي للمخاطر") | |
with col2: | |
from_document_analysis = st.button("استيراد المخاطر من تحليل المستندات") | |
if automated_analysis: | |
with st.spinner("جاري تحليل المخاطر..."): | |
time.sleep(2) | |
self._generate_automated_risks(project) | |
st.success("تم تحليل المخاطر بنجاح!") | |
st.balloons() | |
if from_document_analysis: | |
with st.spinner("جاري استيراد المخاطر من تحليل المستندات..."): | |
time.sleep(2) | |
# هذه مجرد محاكاة، في الواقع يجب استدعاء الوظيفة الفعلية لاستيراد المخاطر | |
document_risks = self._get_risks_from_documents() | |
if document_risks: | |
existing_risk_codes = [r['risk_code'] for r in project['risks']] | |
for risk in document_risks: | |
# تجنب تكرار المخاطر | |
if risk['risk_code'] not in existing_risk_codes: | |
project['risks'].append(risk) | |
st.success(f"تم استيراد {len(document_risks)} مخاطرة من تحليل المستندات!") | |
else: | |
st.warning("لم يتم العثور على مخاطر في المستندات.") | |
# عرض ملخص المخاطر | |
if project['risks']: | |
self._show_risk_summary(project['risks']) | |
def _render_risk_register_tab(self): | |
"""عرض تبويب سجل المخاطر""" | |
st.markdown("### سجل المخاطر") | |
# التحقق من وجود مشروع حالي | |
if 'current_project' not in st.session_state or st.session_state.current_project is None: | |
st.info("يرجى اختيار مشروع من تبويب تحليل المخاطر أولاً.") | |
return | |
project = st.session_state.current_project | |
if 'risks' not in project or not project['risks']: | |
st.info("لا توجد مخاطر مسجلة لهذا المشروع. يمكنك إضافة مخاطر من تبويب تحليل المخاطر.") | |
return | |
# فلترة سجل المخاطر | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
search_term = st.text_input("البحث في سجل المخاطر") | |
with col2: | |
category_filter = st.multiselect("فلترة حسب الفئة", self.risk_categories) | |
with col3: | |
impact_filter = st.multiselect("فلترة حسب التأثير", self.impact_levels) | |
# تطبيق الفلترة | |
filtered_risks = project['risks'] | |
if search_term: | |
filtered_risks = [r for r in filtered_risks if search_term.lower() in r.get('description', '').lower()] | |
if category_filter: | |
filtered_risks = [r for r in filtered_risks if r.get('category') in category_filter] | |
if impact_filter: | |
filtered_risks = [r for r in filtered_risks if r.get('impact') in impact_filter] | |
# عرض سجل المخاطر | |
if filtered_risks: | |
# تحويل المخاطر إلى DataFrame | |
risk_df = pd.DataFrame(filtered_risks) | |
# تحديد الأعمدة المراد عرضها وترتيبها | |
display_columns = [ | |
'risk_code', 'description', 'category', 'impact', | |
'probability', 'risk_score', 'status' | |
] | |
# تغيير أسماء الأعمدة للعرض | |
column_names = { | |
'risk_code': 'رمز المخاطرة', | |
'description': 'وصف المخاطرة', | |
'category': 'الفئة', | |
'impact': 'التأثير', | |
'probability': 'الاحتمالية', | |
'risk_score': 'درجة المخاطرة', | |
'status': 'الحالة', | |
'response_strategy': 'استراتيجية الاستجابة', | |
'created_at': 'تاريخ الإنشاء' | |
} | |
# إعداد DataFrame للعرض | |
if 'response_strategy' in risk_df.columns: | |
display_columns.append('response_strategy') | |
if 'created_at' in risk_df.columns: | |
display_columns.append('created_at') | |
# الحصول على الأعمدة المتوفرة فقط | |
available_columns = [col for col in display_columns if col in risk_df.columns] | |
if available_columns: | |
display_df = risk_df[available_columns].rename(columns=column_names) | |
# عرض الجدول | |
st.dataframe(display_df, use_container_width=True, hide_index=True) | |
# أزرار العمليات | |
col1, col2 = st.columns(2) | |
with col1: | |
if st.button("تصدير سجل المخاطر إلى Excel"): | |
st.success("تم تصدير سجل المخاطر بنجاح!") | |
with col2: | |
if st.button("طباعة تقرير المخاطر"): | |
st.success("تم إنشاء تقرير المخاطر بنجاح!") | |
else: | |
st.warning("هناك مشكلة في بنية بيانات المخاطر. يرجى التحقق من سلامة البيانات.") | |
else: | |
st.info("لا توجد مخاطر تطابق معايير البحث.") | |
def _render_risk_matrix_tab(self): | |
"""عرض تبويب مصفوفة المخاطر""" | |
st.markdown("### مصفوفة المخاطر") | |
# التحقق من وجود مشروع حالي | |
if 'current_project' not in st.session_state or st.session_state.current_project is None: | |
st.info("يرجى اختيار مشروع من تبويب تحليل المخاطر أولاً.") | |
return | |
project = st.session_state.current_project | |
if 'risks' not in project or not project['risks']: | |
st.info("لا توجد مخاطر مسجلة لهذا المشروع. يمكنك إضافة مخاطر من تبويب تحليل المخاطر.") | |
return | |
# إنشاء ضبط مصفوفة المخاطر (3×3) | |
impact_values = {"منخفض": 1, "متوسط": 2, "عالي": 3} | |
probability_values = {"غير محتمل": 1, "محتمل": 2, "مؤكد": 3} | |
# إنشاء DataFrame لتمثيل مصفوفة المخاطر | |
matrix_data = [] | |
for p in probability_values.keys(): | |
for i in impact_values.keys(): | |
p_value = probability_values[p] | |
i_value = impact_values[i] | |
risk_score = p_value * i_value | |
# تحديد اللون حسب درجة المخاطرة | |
if risk_score <= 2: | |
color = 'green' # منخفضة | |
elif risk_score <= 6: | |
color = 'orange' # متوسطة | |
else: | |
color = 'red' # عالية | |
# استخراج المخاطر التي تقع في هذه الخلية | |
cell_risks = [r for r in project['risks'] if r.get('impact') == i and r.get('probability') == p] | |
# إضافة بيانات الخلية | |
matrix_data.append({ | |
'احتمالية': p, | |
'تأثير': i, | |
'درجة_المخاطرة': risk_score, | |
'عدد_المخاطر': len(cell_risks), | |
'المخاطر': [r.get('risk_code') for r in cell_risks], | |
'لون': color | |
}) | |
# تحويل إلى DataFrame | |
matrix_df = pd.DataFrame(matrix_data) | |
# رسم مصفوفة المخاطر باستخدام Plotly | |
fig = go.Figure() | |
for index, row in matrix_df.iterrows(): | |
# إنشاء نص الخلية | |
if row['عدد_المخاطر'] > 0: | |
cell_text = f"{', '.join(row['المخاطر'])}<br>({row['عدد_المخاطر']} مخاطر)" | |
else: | |
cell_text = '' | |
# إنشاء خلية المصفوفة | |
fig.add_trace(go.Scatter( | |
x=[row['تأثير']], | |
y=[row['احتمالية']], | |
mode='markers+text', | |
marker=dict( | |
color=row['لون'], | |
size=20 + (row['عدد_المخاطر'] * 5), | |
opacity=0.8 | |
), | |
text=cell_text, | |
textposition="middle center", | |
name=f"{row['احتمالية']} - {row['تأثير']}" | |
)) | |
# تكوين المحاور | |
fig.update_layout( | |
title="مصفوفة المخاطر (الاحتمالية × التأثير)", | |
xaxis=dict( | |
title="التأثير", | |
tickmode='array', | |
tickvals=[1, 2, 3], | |
ticktext=["منخفض", "متوسط", "عالي"], | |
gridcolor='lightgray' | |
), | |
yaxis=dict( | |
title="الاحتمالية", | |
tickmode='array', | |
tickvals=[1, 2, 3], | |
ticktext=["غير محتمل", "محتمل", "مؤكد"], | |
gridcolor='lightgray' | |
), | |
height=600 | |
) | |
# عرض المصفوفة | |
st.plotly_chart(fig, use_container_width=True) | |
# عرض توزيع المخاطر حسب الفئة | |
st.markdown("#### توزيع المخاطر حسب الفئة") | |
# حساب عدد المخاطر في كل فئة | |
category_counts = {} | |
for r in project['risks']: | |
category = r.get('category', 'أخرى') | |
category_counts[category] = category_counts.get(category, 0) + 1 | |
# إنشاء DataFrame | |
category_df = pd.DataFrame({ | |
'الفئة': list(category_counts.keys()), | |
'عدد المخاطر': list(category_counts.values()) | |
}) | |
# رسم مخطط دائري | |
fig = px.pie( | |
category_df, | |
values='عدد المخاطر', | |
names='الفئة', | |
title='توزيع المخاطر حسب الفئة', | |
hole=0.4 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
def _render_risk_response_tab(self): | |
"""عرض تبويب خطة الاستجابة للمخاطر""" | |
st.markdown("### خطة الاستجابة للمخاطر") | |
# التحقق من وجود مشروع حالي | |
if 'current_project' not in st.session_state or st.session_state.current_project is None: | |
st.info("يرجى اختيار مشروع من تبويب تحليل المخاطر أولاً.") | |
return | |
project = st.session_state.current_project | |
if 'risks' not in project or not project['risks']: | |
st.info("لا توجد مخاطر مسجلة لهذا المشروع. يمكنك إضافة مخاطر من تبويب تحليل المخاطر.") | |
return | |
# ترتيب المخاطر حسب درجة المخاطرة (من الأعلى إلى الأقل) | |
sorted_risks = sorted(project['risks'], key=lambda x: x.get('risk_score', 0), reverse=True) | |
# عرض خطة الاستجابة للمخاطر | |
for i, risk in enumerate(sorted_risks): | |
with st.expander(f"{risk.get('risk_code', '')}: {risk.get('description', 'بدون وصف')}", expanded=(i < 3)): | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.markdown(f"**الفئة**: {risk.get('category', 'غير محدد')}") | |
st.markdown(f"**التأثير**: {risk.get('impact', 'غير محدد')}") | |
with col2: | |
st.markdown(f"**الاحتمالية**: {risk.get('probability', 'غير محدد')}") | |
st.markdown(f"**درجة المخاطرة**: {risk.get('risk_score', 'غير محدد')}") | |
with col3: | |
st.markdown(f"**الحالة**: {risk.get('status', 'نشط')}") | |
risk_owner = risk.get('risk_owner', 'غير محدد') | |
st.markdown(f"**مسؤول المخاطرة**: {risk_owner}") | |
st.markdown("---") | |
st.markdown("#### استراتيجية الاستجابة") | |
current_strategy = risk.get('response_strategy', '') | |
new_strategy = st.text_area(f"استراتيجية الاستجابة للمخاطرة {risk.get('risk_code', '')}", | |
value=current_strategy, | |
height=100, | |
key=f"strategy_{risk.get('risk_code', '')}") | |
# تحديث استراتيجية الاستجابة إذا تم تغييرها | |
if new_strategy != current_strategy: | |
risk['response_strategy'] = new_strategy | |
st.markdown("#### إجراءات التحكم") | |
control_measures = risk.get('control_measures', []) | |
if control_measures: | |
for j, measure in enumerate(control_measures): | |
st.markdown(f"{j+1}. {measure}") | |
else: | |
st.info("لم يتم تعريف إجراءات تحكم لهذه المخاطرة.") | |
# إضافة إجراء تحكم جديد | |
new_measure = st.text_input(f"إجراء تحكم جديد للمخاطرة {risk.get('risk_code', '')}", | |
key=f"measure_{risk.get('risk_code', '')}") | |
if st.button(f"إضافة إجراء", key=f"add_measure_{risk.get('risk_code', '')}"): | |
if new_measure: | |
if 'control_measures' not in risk: | |
risk['control_measures'] = [] | |
risk['control_measures'].append(new_measure) | |
st.success(f"تم إضافة إجراء التحكم بنجاح!") | |
st.experimental_rerun() | |
else: | |
st.error("يرجى إدخال إجراء التحكم.") | |
# زر تصدير خطة الاستجابة للمخاطر | |
if st.button("تصدير خطة الاستجابة للمخاطر"): | |
st.success("تم تصدير خطة الاستجابة للمخاطر بنجاح!") | |
def _calculate_risk_score(self, impact, probability): | |
"""حساب درجة المخاطرة بناءً على التأثير والاحتمالية""" | |
impact_values = {"منخفض": 1, "متوسط": 2, "عالي": 3} | |
probability_values = {"غير محتمل": 1, "محتمل": 2, "مؤكد": 3} | |
impact_value = impact_values.get(impact, 1) | |
probability_value = probability_values.get(probability, 1) | |
return impact_value * probability_value | |
def _generate_automated_risks(self, project): | |
"""توليد مخاطر تلقائية بناءً على خصائص المشروع""" | |
# قائمة المخاطر الشائعة في مشاريع المقاولات | |
common_risks = [ | |
{ | |
'risk_code': 'RF01', | |
'description': 'غرامة تأخير مرتفعة (10% من قيمة العقد)', | |
'category': 'مخاطر مالية', | |
'impact': 'عالي', | |
'probability': 'محتمل', | |
'response_strategy': 'تخصيص مبلغ احتياطي للغرامات المحتملة ووضع خطة لإدارة الجدول الزمني بشكل فعال', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RF02', | |
'description': 'متطلبات ضمان بنكي مرتفعة (15% من قيمة العقد)', | |
'category': 'مخاطر مالية', | |
'impact': 'متوسط', | |
'probability': 'مؤكد', | |
'response_strategy': 'التفاوض مع العميل لتخفيض نسبة الضمان البنكي أو تقسيمه على مراحل المشروع', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RF03', | |
'description': 'شروط دفع متأخرة (60 يوم)', | |
'category': 'مخاطر مالية', | |
'impact': 'متوسط', | |
'probability': 'مؤكد', | |
'response_strategy': 'التخطيط للتدفق النقدي مع الأخذ بالاعتبار تأخر الدفعات وتأمين خط ائتمان احتياطي', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RT01', | |
'description': 'مدة تنفيذ قصيرة (12 شهر)', | |
'category': 'مخاطر زمنية', | |
'impact': 'عالي', | |
'probability': 'محتمل', | |
'response_strategy': 'زيادة فريق العمل واستخدام موارد إضافية مع وضع خطة عمل تفصيلية ومراقبتها أسبوعياً', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RT02', | |
'description': 'احتمالية تأخر توريد المواد الرئيسية', | |
'category': 'مخاطر زمنية', | |
'impact': 'عالي', | |
'probability': 'محتمل', | |
'response_strategy': 'تحديد المواد ذات فترات التوريد الطويلة وطلبها مبكراً مع التعاقد مع موردين بدلاء', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RTE01', | |
'description': 'غموض في بعض المواصفات الفنية', | |
'category': 'مخاطر فنية', | |
'impact': 'متوسط', | |
'probability': 'محتمل', | |
'response_strategy': 'طلب توضيح من العميل قبل البدء بالتنفيذ وتوثيق جميع الردود والتوضيحات', | |
'status': 'نشط', | |
'risk_score': 4 | |
}, | |
{ | |
'risk_code': 'RTE02', | |
'description': 'تضارب بين المخططات والمواصفات', | |
'category': 'مخاطر فنية', | |
'impact': 'متوسط', | |
'probability': 'محتمل', | |
'response_strategy': 'مراجعة شاملة للمستندات وتوثيق التضاربات وطلب توضيح من العميل', | |
'status': 'نشط', | |
'risk_score': 4 | |
}, | |
{ | |
'risk_code': 'RM01', | |
'description': 'عدم وضوح آلية استلام الأعمال', | |
'category': 'مخاطر إدارية', | |
'impact': 'منخفض', | |
'probability': 'محتمل', | |
'response_strategy': 'طلب توضيح آلية الاستلام من العميل ووضع إجراءات داخلية للتحقق من جودة الأعمال قبل التقديم للاستلام', | |
'status': 'نشط', | |
'risk_score': 2 | |
}, | |
{ | |
'risk_code': 'RR01', | |
'description': 'شروط تعجيزية للمحتوى المحلي', | |
'category': 'مخاطر تنظيمية', | |
'impact': 'عالي', | |
'probability': 'محتمل', | |
'response_strategy': 'دراسة متطلبات المحتوى المحلي بدقة ووضع خطة لتحقيقها مع الاحتفاظ بسجلات التوثيق اللازمة', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RM01', | |
'description': 'خطر التغييرات في أسعار المواد', | |
'category': 'مخاطر سوقية', | |
'impact': 'عالي', | |
'probability': 'محتمل', | |
'response_strategy': 'تثبيت أسعار المواد الرئيسية مع الموردين وإدراج بند تعديل الأسعار في العقد', | |
'status': 'نشط', | |
'risk_score': 6 | |
}, | |
{ | |
'risk_code': 'RC01', | |
'description': 'عدم وضوح بعض بنود العقد', | |
'category': 'مخاطر تعاقدية', | |
'impact': 'متوسط', | |
'probability': 'محتمل', | |
'response_strategy': 'مراجعة العقد من قبل مستشار قانوني متخصص وطلب توضيح للبنود الغامضة قبل التوقيع', | |
'status': 'نشط', | |
'risk_score': 4 | |
} | |
] | |
# إضافة المخاطر الشائعة إلى المشروع | |
existing_risk_codes = [r['risk_code'] for r in project['risks']] | |
for risk in common_risks: | |
# تجنب تكرار المخاطر | |
if risk['risk_code'] not in existing_risk_codes: | |
risk['id'] = len(project['risks']) + 1 | |
risk['created_at'] = datetime.now().strftime('%Y-%m-%d') | |
project['risks'].append(risk) | |
def _get_risks_from_documents(self): | |
"""استيراد المخاطر من تحليل المستندات""" | |
# محاكاة لاستيراد المخاطر من تحليل المستندات | |
# في التطبيق الفعلي، يجب استدعاء الوظيفة المناسبة من وحدة تحليل المستندات | |
document_risks = [ | |
{ | |
'risk_code': 'RD01', | |
'description': 'غرامة تأخير مرتفعة تصل إلى 20% من قيمة العقد', | |
'category': 'مخاطر مالية', | |
'impact': 'عالي', | |
'probability': 'مؤكد', | |
'response_strategy': 'التفاوض على تخفيض الغرامة أو تقسيمها حسب مراحل المشروع مع وضع خطة محكمة للجدول الزمني', | |
'status': 'نشط', | |
'risk_score': 9, | |
'created_at': datetime.now().strftime('%Y-%m-%d') | |
}, | |
{ | |
'risk_code': 'RD02', | |
'description': 'يحق للمالك إيقاف المشروع لمدة تصل إلى 90 يوم دون تعويض', | |
'category': 'مخاطر تعاقدية', | |
'impact': 'عالي', | |
'probability': 'محتمل', | |
'response_strategy': 'طلب إضافة بند للتعويض عن التكاليف الإضافية الناتجة عن الإيقاف لفترات طويلة', | |
'status': 'نشط', | |
'risk_score': 6, | |
'created_at': datetime.now().strftime('%Y-%m-%d') | |
}, | |
{ | |
'risk_code': 'RD03', | |
'description': 'تحمل المقاول مسؤولية استخراج جميع التصاريح الحكومية', | |
'category': 'مخاطر تنظيمية', | |
'impact': 'متوسط', | |
'probability': 'مؤكد', | |
'response_strategy': 'حصر جميع التصاريح المطلوبة والبدء في إجراءات استخراجها مبكراً مع تخصيص فريق لمتابعتها', | |
'status': 'نشط', | |
'risk_score': 6, | |
'created_at': datetime.now().strftime('%Y-%m-%d') | |
}, | |
{ | |
'risk_code': 'RD04', | |
'description': 'شروط الدفعة المقدمة مقيدة بضمان بنكي بقيمة 120% من قيمة الدفعة', | |
'category': 'مخاطر مالية', | |
'impact': 'متوسط', | |
'probability': 'مؤكد', | |
'response_strategy': 'التفاوض على خفض نسبة الضمان البنكي أو تقديم ضمان شركة بدلاً من الضمان البنكي', | |
'status': 'نشط', | |
'risk_score': 6, | |
'created_at': datetime.now().strftime('%Y-%m-%d') | |
} | |
] | |
return document_risks | |
def _show_risk_summary(self, risks): | |
"""عرض ملخص المخاطر""" | |
st.markdown("#### ملخص المخاطر") | |
# حساب إحصائيات المخاطر | |
total_risks = len(risks) | |
risk_levels = { | |
'عالية': len([r for r in risks if r.get('risk_score', 0) >= 6]), | |
'متوسطة': len([r for r in risks if 3 <= r.get('risk_score', 0) < 6]), | |
'منخفضة': len([r for r in risks if r.get('risk_score', 0) < 3]) | |
} | |
# عرض الإحصائيات | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
st.metric("إجمالي المخاطر", total_risks) | |
with col2: | |
st.metric("المخاطر العالية", risk_levels['عالية'], delta=f"{risk_levels['عالية']/total_risks*100:.1f}%", delta_color="inverse") | |
with col3: | |
st.metric("المخاطر المتوسطة", risk_levels['متوسطة'], delta=f"{risk_levels['متوسطة']/total_risks*100:.1f}%", delta_color="off") | |
with col4: | |
st.metric("المخاطر المنخفضة", risk_levels['منخفضة'], delta=f"{risk_levels['منخفضة']/total_risks*100:.1f}%", delta_color="normal") | |
# عرض الرسم البياني للمخاطر | |
risk_level_df = pd.DataFrame({ | |
'مستوى المخاطرة': list(risk_levels.keys()), | |
'عدد المخاطر': list(risk_levels.values()) | |
}) | |
fig = px.bar( | |
risk_level_df, | |
x='مستوى المخاطرة', | |
y='عدد المخاطر', | |
color='مستوى المخاطرة', | |
color_discrete_map={ | |
'عالية': 'red', | |
'متوسطة': 'orange', | |
'منخفضة': 'green' | |
}, | |
title='توزيع المخاطر حسب المستوى' | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
# عرض أعلى 5 مخاطر من حيث درجة المخاطرة | |
st.markdown("#### أعلى 5 مخاطر") | |
# ترتيب المخاطر حسب درجة المخاطرة | |
sorted_risks = sorted(risks, key=lambda x: x.get('risk_score', 0), reverse=True) | |
top_risks = sorted_risks[:5] | |
# إنشاء DataFrame للعرض | |
if top_risks: | |
top_risks_data = [] | |
for r in top_risks: | |
top_risks_data.append({ | |
'رمز المخاطرة': r.get('risk_code', ''), | |
'وصف المخاطرة': r.get('description', ''), | |
'الفئة': r.get('category', ''), | |
'التأثير': r.get('impact', ''), | |
'الاحتمالية': r.get('probability', ''), | |
'درجة المخاطرة': r.get('risk_score', 0) | |
}) | |
top_risks_df = pd.DataFrame(top_risks_data) | |
st.dataframe(top_risks_df, use_container_width=True, hide_index=True) | |