EGYADMIN commited on
Commit
3e1c4f2
·
verified ·
1 Parent(s): dff2d03

Create web/pages/tender_analysis.py

Browse files
Files changed (1) hide show
  1. web/pages/tender_analysis.py +290 -0
web/pages/tender_analysis.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.express as px
5
+ from datetime import datetime
6
+ import os
7
+
8
+ def show_tender_analysis():
9
+ """
10
+ عرض صفحة تحليل المناقصات
11
+ """
12
+ st.subheader("تحليل المناقصات")
13
+
14
+ # تقسيم الشاشة إلى جزئين
15
+ col1, col2 = st.columns([1, 2])
16
+
17
+ with col1:
18
+ # قسم رفع الملفات
19
+ st.markdown("### رفع ملفات المناقصة")
20
+
21
+ uploaded_files = st.file_uploader(
22
+ "قم برفع ملفات المناقصة (PDF, DOCX, XLSX)",
23
+ type=["pdf", "docx", "xlsx", "csv", "json"],
24
+ accept_multiple_files=True
25
+ )
26
+
27
+ if uploaded_files:
28
+ st.session_state.uploaded_files = uploaded_files
29
+ st.success(f"تم رفع {len(uploaded_files)} ملفات بنجاح")
30
+
31
+ # عرض قائمة الملفات
32
+ st.markdown("### الملفات المرفوعة")
33
+ for file in uploaded_files:
34
+ st.markdown(f"- {file.name} ({file.size / 1024:.1f} KB)")
35
+
36
+ # خيارات التحليل
37
+ st.markdown("### خيارات التحليل")
38
+
39
+ analysis_options = st.multiselect(
40
+ "اختر أنواع التحليل",
41
+ [
42
+ "استخراج المتطلبات الرئيسية",
43
+ "تحليل التكاليف التقديرية",
44
+ "تحليل المخاطر",
45
+ "تحليل المحتوى المحلي",
46
+ "تحليل سلاسل الإمداد",
47
+ "التحليل الزمني",
48
+ "توقع احتمالية النجاح"
49
+ ],
50
+ default=["استخراج المتطلبات الرئيسية", "تحليل التكاليف التقديرية"]
51
+ )
52
+
53
+ # زر بدء التحليل
54
+ if st.button("بدء التحليل"):
55
+ if not uploaded_files:
56
+ st.error("يرجى رفع ملفات المناقصة أولاً")
57
+ elif not analysis_options:
58
+ st.error("يرجى اختيار نوع التحليل المطلوب")
59
+ else:
60
+ # هنا سيتم استدعاء عمليات التحليل الفعلية
61
+ # نستخدم هنا بيانات توضيحية للعرض فقط
62
+ with st.spinner("جاري تحليل المناقصة... قد تستغرق العملية بضع دقائق..."):
63
+ # محاكاة وقت المعالجة
64
+ import time
65
+ time.sleep(2)
66
+
67
+ # تخزين نتائج التحليل في حالة الجلسة
68
+ st.session_state.analysis_results = {
69
+ "tender_id": "T-2025-" + str(np.random.randint(1000, 9999)),
70
+ "analyzed_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
71
+ "requirements": [
72
+ "توريد وتركيب معدات البنية التحتية",
73
+ "صيانة الشبكات لمدة سنتين",
74
+ "تدريب الموظفين على الأنظمة الجديدة",
75
+ "توفير قطع الغيار اللازمة",
76
+ "الالتزام بمعايير الجودة ISO 9001"
77
+ ],
78
+ "cost_estimate": {
79
+ "total": np.random.uniform(80, 150, 1)[0].round(2),
80
+ "breakdown": {
81
+ "مواد": np.random.uniform(30, 60, 1)[0].round(2),
82
+ "عمالة": np.random.uniform(20, 40, 1)[0].round(2),
83
+ "معدات": np.random.uniform(10, 30, 1)[0].round(2),
84
+ "إدارة": np.random.uniform(5, 15, 1)[0].round(2),
85
+ "أخرى": np.random.uniform(5, 10, 1)[0].round(2)
86
+ }
87
+ },
88
+ "risks": [
89
+ {"name": "تأخر التوريدات", "probability": 0.4, "impact": 0.7, "score": 0.28},
90
+ {"name": "تغيير المواصفات", "probability": 0.3, "impact": 0.6, "score": 0.18},
91
+ {"name": "نقص العمالة الماهرة", "probability": 0.5, "impact": 0.5, "score": 0.25},
92
+ {"name": "تقلبات أسعار المواد", "probability": 0.6, "impact": 0.4, "score": 0.24},
93
+ {"name": "ظروف جوية غير مناسبة", "probability": 0.2, "impact": 0.3, "score": 0.06}
94
+ ],
95
+ "local_content": {
96
+ "estimated_percentage": np.random.uniform(50, 80, 1)[0].round(2),
97
+ "required_percentage": np.random.uniform(40, 60, 1)[0].round(2),
98
+ "breakdown": {
99
+ "عمالة محلية": np.random.uniform(60, 90, 1)[0].round(2),
100
+ "مواد محلية": np.random.uniform(40, 70, 1)[0].round(2),
101
+ "خدمات محلية": np.random.uniform(50, 80, 1)[0].round(2),
102
+ "تدريب وتطوير": np.random.uniform(30, 60, 1)[0].round(2)
103
+ }
104
+ },
105
+ "success_probability": np.random.uniform(60, 90, 1)[0].round(2)
106
+ }
107
+
108
+ st.success("تم الانتهاء من تحليل المناقصة بنجاح!")
109
+
110
+ with col2:
111
+ # عرض نتائج التحليل إذا كانت متوفرة
112
+ if "analysis_results" in st.session_state and st.session_state.analysis_results:
113
+ results = st.session_state.analysis_results
114
+
115
+ # عرض معلومات المناقصة
116
+ st.markdown("### معلومات المناقصة")
117
+ st.markdown(f"**رقم المناقصة:** {results['tender_id']}")
118
+ st.markdown(f"**تاريخ التحليل:** {results['analyzed_at']}")
119
+
120
+ # تبويب لعرض مختلف أنواع التحليل
121
+ tabs = st.tabs([
122
+ "المتطلبات",
123
+ "التكاليف",
124
+ "المخاطر",
125
+ "المحتوى المحلي",
126
+ "احتمالية النجاح"
127
+ ])
128
+
129
+ # تبويب المتطلبات
130
+ with tabs[0]:
131
+ st.markdown("### المتطلبات الرئيسية للمناقصة")
132
+ for i, req in enumerate(results["requirements"]):
133
+ st.markdown(f"{i+1}. {req}")
134
+
135
+ # تبويب التكاليف
136
+ with tabs[1]:
137
+ st.markdown("### تحليل التكاليف التقديرية")
138
+
139
+ # إجمالي التكلفة
140
+ st.markdown(f"**إجمالي التكلفة التقديرية:** {results['cost_estimate']['total']} مليون ريال")
141
+
142
+ # رسم بياني لتوزيع التكاليف
143
+ cost_data = {
144
+ "الفئة": list(results["cost_estimate"]["breakdown"].keys()),
145
+ "القيمة (مليون ريال)": list(results["cost_estimate"]["breakdown"].values())
146
+ }
147
+
148
+ cost_df = pd.DataFrame(cost_data)
149
+
150
+ fig = px.pie(
151
+ cost_df,
152
+ values="القيمة (مليون ريال)",
153
+ names="الفئة",
154
+ title="توزيع التكاليف التقديرية",
155
+ color_discrete_sequence=px.colors.qualitative.Bold
156
+ )
157
+
158
+ st.plotly_chart(fig, use_container_width=True)
159
+
160
+ # تبويب المخاطر
161
+ with tabs[2]:
162
+ st.markdown("### تحليل المخاطر")
163
+
164
+ # جدول المخاطر
165
+ risk_data = pd.DataFrame(results["risks"])
166
+ risk_data.columns = ["المخاطرة", "الاحتمالية", "التأثير", "الدرجة"]
167
+
168
+ st.table(risk_data.style.format({
169
+ "الاحتمالية": "{:.1%}",
170
+ "التأثير": "{:.1%}",
171
+ "الدرجة": "{:.1%}"
172
+ }))
173
+
174
+ # مصفوفة المخاطر
175
+ st.markdown("### مصفوفة المخاطر")
176
+
177
+ fig = px.scatter(
178
+ risk_data,
179
+ x="الاحتمالية",
180
+ y="التأثير",
181
+ size="الدرجة",
182
+ text="المخاطرة",
183
+ size_max=60,
184
+ color="الدرجة",
185
+ color_continuous_scale=px.colors.sequential.Reds,
186
+ title="مصفوفة المخاطر",
187
+ range_x=[0, 1],
188
+ range_y=[0, 1]
189
+ )
190
+
191
+ fig.update_traces(textposition="top center")
192
+ fig.update_layout(
193
+ xaxis_title="احتمالية الحدوث",
194
+ yaxis_title="مستوى التأثير"
195
+ )
196
+
197
+ st.plotly_chart(fig, use_container_width=True)
198
+
199
+ # تبويب المحتوى المحلي
200
+ with tabs[3]:
201
+ st.markdown("### تحليل المحتوى المحلي")
202
+
203
+ # نسب المحتوى المحلي
204
+ est_pct = results["local_content"]["estimated_percentage"]
205
+ req_pct = results["local_content"]["required_percentage"]
206
+
207
+ col1, col2 = st.columns(2)
208
+
209
+ with col1:
210
+ # متطلبات المحتوى المحلي
211
+ st.markdown(f"**نسبة المحتوى المحلي المطلوبة:** {req_pct}%")
212
+ st.progress(req_pct / 100)
213
+
214
+ with col2:
215
+ # النسبة المتوقعة
216
+ st.markdown(f"**نسبة المحتوى المحلي المتوقعة:** {est_pct}%")
217
+ st.progress(est_pct / 100)
218
+
219
+ # حالة المحتوى المحلي (هل يلبي المتطلبات)
220
+ if est_pct >= req_pct:
221
+ st.success(f"المحتوى المحلي المتوقع يتجاوز المتطلبات بنسبة {est_pct - req_pct:.1f}%")
222
+ else:
223
+ st.error(f"المحتوى المحلي المتوقع أقل من المتطلبات بنسبة {req_pct - est_pct:.1f}%")
224
+
225
+ # رسم بياني لمكونات المحتوى المحلي
226
+ local_data = {
227
+ "الفئة": list(results["local_content"]["breakdown"].keys()),
228
+ "النسبة (%)": list(results["local_content"]["breakdown"].values())
229
+ }
230
+
231
+ local_df = pd.DataFrame(local_data)
232
+
233
+ fig = px.bar(
234
+ local_df,
235
+ x="الفئة",
236
+ y="النسبة (%)",
237
+ color="النسبة (%)",
238
+ color_continuous_scale=px.colors.sequential.Viridis,
239
+ title="مكونات المحتوى المحلي حسب الفئة"
240
+ )
241
+
242
+ st.plotly_chart(fig, use_container_width=True)
243
+
244
+ # تبويب احتمالية النجاح
245
+ with tabs[4]:
246
+ st.markdown("### توقع احتمالية النجاح")
247
+
248
+ # عرض احتمالية النجاح
249
+ success_prob = results["success_probability"]
250
+
251
+ # اختيار اللون حسب النسبة
252
+ color = "green" if success_prob >= 80 else "orange" if success_prob >= 60 else "red"
253
+
254
+ st.markdown(f"<h1 style='text-align: center; color: {color};'>{success_prob}%</h1>", unsafe_allow_html=True)
255
+ st.progress(success_prob / 100)
256
+
257
+ # نصائح لتحسين الاحتمالية
258
+ st.markdown("### توصيات لتحسين فرص النجاح")
259
+
260
+ recommendations = [
261
+ "زيادة نسبة المحتوى المحلي بنسبة 5-10%",
262
+ "تعزيز فريق المشروع بخبرات في مجال التقنية",
263
+ "البحث عن موردين محليين بديلين للمواد الرئيسية",
264
+ "وضع خطة واضحة للتعامل مع المخاطر ذات التأثير العالي",
265
+ "تقديم حلول مبتكرة في المجالات التقنية"
266
+ ]
267
+
268
+ for rec in recommendations:
269
+ st.markdown(f"- {rec}")
270
+
271
+ # زر لحفظ التقرير
272
+ if st.button("حفظ تقرير التحليل"):
273
+ st.session_state.latest_analysis = results
274
+ st.success("تم حفظ تقرير التحليل بنجاح!")
275
+ else:
276
+ # توجيهات للمستخدم
277
+ st.info("قم برفع ملفات المناقصة واختر خيارات التحليل المطلوبة، ثم اضغط على زر 'بدء التحليل' لعرض النتائج هنا.")
278
+
279
+ # عرض مثال توضيحي
280
+ st.markdown("### مثال توضيحي لنتائج التحليل")
281
+ st.image("https://via.placeholder.com/800x500?text=مثال+لنتائج+تحليل+المناقصة", caption="مثال لنتائج تحليل المناقصة")
282
+
283
+ # اختبار مستقل للصفحة
284
+ if __name__ == "__main__":
285
+ st.set_page_config(
286
+ page_title="نظام تحليل المناقصات - تحليل المناقصات",
287
+ page_icon="📊",
288
+ layout="wide",
289
+ )
290
+ show_tender_analysis()