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 | |
# إعداد الصفحة | |
st.set_page_config( | |
page_title="عرض التسعير غير المتوازن", | |
page_icon="📊", | |
layout="wide" | |
) | |
st.title("عرض تحسينات واجهة المستخدم") | |
# بيانات تجريبية للعرض | |
def get_sample_data(): | |
items = pd.DataFrame({ | |
'رقم البند': ['UB1', 'UB2', 'UB3', 'UB4', 'UB5'], | |
'وصف البند': ['حفر أساسات', 'صب خرسانة مسلحة', 'أعمال طوب', 'أعمال تشطيبات', 'أعمال كهرباء'], | |
'الوحدة': ['م3', 'م3', 'م2', 'م2', 'نقطة'], | |
'الكمية': [350.0, 120.0, 500.0, 800.0, 150.0], | |
'سعر الوحدة': [80.0, 950.0, 45.0, 120.0, 90.0], | |
'الإجمالي': [28000.0, 114000.0, 22500.0, 96000.0, 13500.0], | |
'إستراتيجية التسعير': ['نقص', 'زيادة', 'متوازن', 'زيادة', 'نقص'] | |
}) | |
return items | |
items = get_sample_data() | |
# 1. عرض الجدول مع تنسيق محسن | |
st.markdown("<h3 style='color: #1F7A8C; background: linear-gradient(to right, #f0f9ff, #ffffff); padding: 10px; border-radius: 5px;'>بنود التسعير غير المتوازن</h3>", unsafe_allow_html=True) | |
# تعيين ألوان للإستراتيجيات وتنسيق الجدول بشكل متقدم | |
def highlight_row(row): | |
strategy = row['إستراتيجية التسعير'] | |
styles = [''] * len(row) | |
# تطبيق لون خلفية لكل صف حسب الإستراتيجية | |
if strategy == 'زيادة': | |
background = 'linear-gradient(90deg, rgba(168, 230, 207, 0.3), rgba(168, 230, 207, 0.1))' | |
text_color = '#1F7A8C' | |
elif strategy == 'نقص': | |
background = 'linear-gradient(90deg, rgba(255, 154, 162, 0.3), rgba(255, 154, 162, 0.1))' | |
text_color = '#9D2A45' | |
else: | |
background = 'linear-gradient(90deg, rgba(220, 237, 255, 0.3), rgba(220, 237, 255, 0.1))' | |
text_color = '#555555' | |
# تطبيق النمط على جميع الخلايا في الصف | |
for i in range(len(styles)): | |
styles[i] = f'background: {background}; color: {text_color}; border-bottom: 1px solid #ddd;' | |
# تطبيق نمط خاص على خلية الإستراتيجية | |
if strategy == 'زيادة': | |
styles[list(row.index).index('إستراتيجية التسعير')] = 'background-color: #a8e6cf; color: #007263; font-weight: bold; border-radius: 5px; text-align: center;' | |
elif strategy == 'نقص': | |
styles[list(row.index).index('إستراتيجية التسعير')] = 'background-color: #ff9aa2; color: #9D2A45; font-weight: bold; border-radius: 5px; text-align: center;' | |
else: | |
styles[list(row.index).index('إستراتيجية التسعير')] = 'background-color: #dceeff; color: #555555; font-weight: bold; border-radius: 5px; text-align: center;' | |
# تنسيق عمود السعر | |
price_idx = list(row.index).index('سعر الوحدة') | |
styles[price_idx] = styles[price_idx] + 'font-weight: bold;' | |
# تنسيق عمود الإجمالي | |
total_idx = list(row.index).index('الإجمالي') | |
styles[total_idx] = styles[total_idx] + 'font-weight: bold;' | |
return styles | |
# تطبيق التنسيق على الجدول | |
styled_items = items.style.apply(highlight_row, axis=1) | |
# تنسيق تنسيق الأرقام | |
styled_items = styled_items.format({ | |
'الكمية': '{:,.2f}', | |
'سعر الوحدة': '{:,.2f}', | |
'الإجمالي': '{:,.2f}' | |
}) | |
st.dataframe(styled_items, use_container_width=True, height=None) | |
# 2. عرض المقارنة مع تصميم محسن | |
st.markdown("<h3 style='color: #1F7A8C; background: linear-gradient(to right, #f0f9ff, #ffffff); padding: 10px; border-radius: 5px;'>مقارنة التسعير المتوازن وغير المتوازن</h3>", unsafe_allow_html=True) | |
# بيانات المقارنة | |
original_items = items.copy() | |
original_items['سعر الوحدة'] = [70.0, 820.0, 45.0, 100.0, 110.0] | |
original_items['الإجمالي'] = original_items['الكمية'] * original_items['سعر الوحدة'] | |
original_total = original_items['الإجمالي'].sum() | |
unbalanced_total = items['الإجمالي'].sum() | |
# عرض بطاقات المقارنة بتصميم متقدم | |
st.markdown(""" | |
<style> | |
.metric-container { | |
background: linear-gradient(to right, #f1f8ff, #ffffff); | |
border-radius: 10px; | |
padding: 15px; | |
box-shadow: 0 2px 5px rgba(0,0,0,0.05); | |
text-align: center; | |
border: 1px solid #e6f2ff; | |
} | |
.metric-title { | |
color: #555; | |
font-size: 0.9em; | |
margin-bottom: 5px; | |
} | |
.metric-value { | |
color: #1F7A8C; | |
font-size: 1.8em; | |
font-weight: bold; | |
margin: 5px 0; | |
} | |
.metric-delta { | |
font-size: 0.9em; | |
font-weight: bold; | |
padding: 3px 8px; | |
border-radius: 10px; | |
display: inline-block; | |
margin-top: 5px; | |
} | |
.positive-delta { | |
background-color: rgba(40, 167, 69, 0.1); | |
color: #28a745; | |
} | |
.negative-delta { | |
background-color: rgba(220, 53, 69, 0.1); | |
color: #dc3545; | |
} | |
.neutral-delta { | |
background-color: rgba(108, 117, 125, 0.1); | |
color: #6c757d; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.markdown(""" | |
<div class="metric-container"> | |
<div class="metric-title">إجمالي التسعير المتوازن</div> | |
<div class="metric-value">{:,.2f} ريال</div> | |
<div class="metric-delta neutral-delta">التسعير الأصلي</div> | |
</div> | |
""".format(original_total), unsafe_allow_html=True) | |
with col2: | |
st.markdown(""" | |
<div class="metric-container"> | |
<div class="metric-title">إجمالي التسعير غير المتوازن</div> | |
<div class="metric-value">{:,.2f} ريال</div> | |
<div class="metric-delta {}">بعد إعادة توزيع الأسعار</div> | |
</div> | |
""".format( | |
unbalanced_total, | |
"positive-delta" if unbalanced_total > original_total else "negative-delta" if unbalanced_total < original_total else "neutral-delta" | |
), unsafe_allow_html=True) | |
with col3: | |
diff = unbalanced_total - original_total | |
delta_percent = diff/original_total*100 if original_total > 0 else 0 | |
st.markdown(""" | |
<div class="metric-container"> | |
<div class="metric-title">الفرق بين التسعيرين</div> | |
<div class="metric-value">{:,.2f} ريال</div> | |
<div class="metric-delta {}">نسبة الفرق: {:+.1f}%</div> | |
</div> | |
""".format( | |
diff, | |
"positive-delta" if diff > 0 else "negative-delta" if diff < 0 else "neutral-delta", | |
delta_percent | |
), unsafe_allow_html=True) | |
# 3. رسم بياني للمقارنة | |
st.markdown("<h3 style='color: #1F7A8C; background: linear-gradient(to right, #f0f9ff, #ffffff); padding: 10px; border-radius: 5px;'>تحليل بصري للتسعير غير المتوازن</h3>", unsafe_allow_html=True) | |
# إعداد البيانات للرسم البياني | |
chart_data = pd.DataFrame({ | |
'وصف البند': original_items['وصف البند'], | |
'التسعير المتوازن': original_items['الإجمالي'], | |
'التسعير غير المتوازن': items['الإجمالي'] | |
}) | |
# إضافة عمود للنسبة المئوية للتغيير | |
chart_data['نسبة التغيير'] = (chart_data['التسعير غير المتوازن'] - chart_data['التسعير المتوازن']) / chart_data['التسعير المتوازن'] * 100 | |
# تحديد لون الأعمدة بناءً على نسبة التغيير | |
bar_colors = [] | |
for change in chart_data['نسبة التغيير']: | |
if change > 5: # زيادة كبيرة | |
bar_colors.append('#1F7A8C') # أزرق مخضر | |
elif change > 0: # زيادة صغيرة | |
bar_colors.append('#81B29A') # أخضر فاتح | |
elif change > -5: # نقص صغير | |
bar_colors.append('#F2CC8F') # أصفر | |
else: # نقص كبير | |
bar_colors.append('#E07A5F') # أحمر | |
# التبويب بين مخططات مختلفة للمقارنة | |
chart_tabs = st.tabs(["مخطط شريطي", "مخطط مقارنة", "مخطط نسبة التغيير"]) | |
with chart_tabs[0]: # رسم بياني شريطي | |
# رسم بياني شريطي للمقارنة | |
fig = go.Figure() | |
fig.add_trace(go.Bar( | |
x=chart_data['وصف البند'], | |
y=chart_data['التسعير المتوازن'], | |
name='التسعير المتوازن', | |
marker_color='rgba(55, 83, 109, 0.7)' | |
)) | |
fig.add_trace(go.Bar( | |
x=chart_data['وصف البند'], | |
y=chart_data['التسعير غير المتوازن'], | |
name='التسعير غير المتوازن', | |
marker_color=bar_colors | |
)) | |
fig.update_layout( | |
title='مقارنة بين التسعير المتوازن وغير المتوازن', | |
xaxis_tickfont_size=14, | |
yaxis=dict( | |
title='الإجمالي (ريال)', | |
titlefont_size=16, | |
tickfont_size=14, | |
), | |
legend=dict( | |
x=0.01, | |
y=0.99, | |
bgcolor='rgba(255, 255, 255, 0.8)', | |
bordercolor='rgba(0, 0, 0, 0.1)', | |
borderwidth=1 | |
), | |
barmode='group', | |
bargap=0.15, | |
bargroupgap=0.1, | |
plot_bgcolor='rgba(240, 249, 255, 0.5)', | |
margin=dict(t=50, b=50, l=20, r=20) | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with chart_tabs[1]: # رسم مقارنة | |
# رسم مقارنة بين التسعيرين | |
fig = go.Figure() | |
# إضافة خط للتسعير المتوازن | |
fig.add_trace(go.Scatter( | |
x=chart_data['وصف البند'], | |
y=chart_data['التسعير المتوازن'], | |
name='التسعير المتوازن', | |
mode='lines+markers', | |
line=dict(color='rgb(55, 83, 109)', width=3), | |
marker=dict(size=10, color='rgb(55, 83, 109)') | |
)) | |
# إضافة نقاط للتسعير غير المتوازن | |
fig.add_trace(go.Scatter( | |
x=chart_data['وصف البند'], | |
y=chart_data['التسعير غير المتوازن'], | |
name='التسعير غير المتوازن', | |
mode='lines+markers', | |
line=dict(color='rgb(26, 118, 255)', width=3), | |
marker=dict( | |
size=12, | |
color=bar_colors, | |
line=dict(width=2, color='white') | |
) | |
)) | |
# تحديثات التخطيط | |
fig.update_layout( | |
title='مقارنة مرئية بين استراتيجيات التسعير', | |
xaxis_tickfont_size=14, | |
yaxis=dict( | |
title='القيمة الإجمالية (ريال)', | |
titlefont_size=16, | |
tickfont_size=14, | |
gridcolor='rgba(200, 200, 200, 0.2)' | |
), | |
legend=dict( | |
x=0.01, | |
y=0.99, | |
bgcolor='rgba(255, 255, 255, 0.8)', | |
bordercolor='rgba(0, 0, 0, 0.1)', | |
borderwidth=1 | |
), | |
plot_bgcolor='rgba(240, 249, 255, 0.5)', | |
margin=dict(t=50, b=50, l=20, r=20) | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with chart_tabs[2]: # مخطط نسبة التغيير | |
# مخطط للنسبة المئوية للتغيير | |
fig = go.Figure() | |
# إضافة أعمدة لنسبة التغيير مع ألوان مختلفة حسب القيمة | |
fig.add_trace(go.Bar( | |
x=chart_data['وصف البند'], | |
y=chart_data['نسبة التغيير'], | |
name='نسبة التغيير', | |
marker_color=bar_colors, | |
text=[f"{val:.1f}%" for val in chart_data['نسبة التغيير']], | |
textposition='auto' | |
)) | |
# إضافة خط أفقي عند الصفر | |
fig.add_shape( | |
type="line", | |
x0=-0.5, | |
y0=0, | |
x1=len(chart_data['وصف البند'])-0.5, | |
y1=0, | |
line=dict( | |
color="black", | |
width=2, | |
dash="dash", | |
) | |
) | |
# تحديثات التخطيط | |
fig.update_layout( | |
title='نسبة التغيير في أسعار البنود (%)', | |
xaxis_tickfont_size=14, | |
yaxis=dict( | |
title='نسبة التغيير (%)', | |
titlefont_size=16, | |
tickfont_size=14, | |
gridcolor='rgba(200, 200, 200, 0.2)', | |
zeroline=True, | |
zerolinecolor='black', | |
zerolinewidth=2 | |
), | |
plot_bgcolor='rgba(240, 249, 255, 0.5)', | |
margin=dict(t=50, b=50, l=20, r=20) | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
# إضافة جدول مع نسب التغيير | |
st.markdown("#### جدول مفصل بنسب التغيير") | |
# إعداد بيانات الجدول | |
table_data = chart_data[['وصف البند', 'التسعير المتوازن', 'التسعير غير المتوازن', 'نسبة التغيير']] | |
# تنسيق الجدول | |
def highlight_change(row): | |
change = row['نسبة التغيير'] | |
if change > 5: | |
return ['', '', '', 'background-color: rgba(31, 122, 140, 0.3); color: #1F7A8C; font-weight: bold;'] | |
elif change > 0: | |
return ['', '', '', 'background-color: rgba(129, 178, 154, 0.3); color: #2A9D8F; font-weight: bold;'] | |
elif change > -5: | |
return ['', '', '', 'background-color: rgba(242, 204, 143, 0.3); color: #BC6C25; font-weight: bold;'] | |
else: | |
return ['', '', '', 'background-color: rgba(224, 122, 95, 0.3); color: #AE2012; font-weight: bold;'] | |
# تطبيق التنسيق | |
styled_table = table_data.style.apply(highlight_change, axis=1).format({ | |
'التسعير المتوازن': '{:,.2f} ريال', | |
'التسعير غير المتوازن': '{:,.2f} ريال', | |
'نسبة التغيير': '{:+.1f}%' | |
}) | |
st.dataframe(styled_table, use_container_width=True) | |
# 4. أزرار الحفظ والتصدير مع تصميم محسن | |
st.markdown("<hr style='margin-top: 30px; margin-bottom: 20px; border-top: 1px solid #ddd;'>", unsafe_allow_html=True) | |
st.markdown("<h3 style='color: #1F7A8C; background: linear-gradient(to right, #f0f9ff, #ffffff); padding: 10px; border-radius: 5px;'>حفظ وتصدير البيانات</h3>", unsafe_allow_html=True) | |
st.markdown(""" | |
<style> | |
.action-card { | |
background: linear-gradient(135deg, #f8f9fa, #e9ecef); | |
border-radius: 10px; | |
padding: 20px; | |
box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
border-left: 5px solid #1F7A8C; | |
transition: all 0.3s ease; | |
} | |
.action-card:hover { | |
box-shadow: 0 5px 15px rgba(0,0,0,0.1); | |
transform: translateY(-2px); | |
} | |
.action-icon { | |
color: #1F7A8C; | |
font-size: 24px; | |
margin-bottom: 10px; | |
} | |
.action-title { | |
color: #333; | |
font-size: 18px; | |
font-weight: bold; | |
margin-bottom: 10px; | |
} | |
.action-desc { | |
color: #666; | |
font-size: 14px; | |
margin-bottom: 15px; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
col1, col2 = st.columns(2) | |
with col1: | |
# بطاقة حفظ التسعير | |
st.markdown(""" | |
<div class="action-card"> | |
<div class="action-icon">💾</div> | |
<div class="action-title">حفظ التسعير غير المتوازن</div> | |
<div class="action-desc">قم بحفظ التسعير الحالي في المشروع لاستخدامه لاحقاً في التقارير وفي إجمالي التسعير.</div> | |
</div> | |
""", unsafe_allow_html=True) | |
# زر حفظ التسعير غير المتوازن | |
if st.button("حفظ التسعير غير المتوازن", type="primary", use_container_width=True): | |
st.success("تم حفظ التسعير غير المتوازن بنجاح!") | |
st.balloons() # إضافة تأثير احتفالي عند الحفظ | |
with col2: | |
# بطاقة تصدير التسعير | |
st.markdown(""" | |
<div class="action-card"> | |
<div class="action-icon">📊</div> | |
<div class="action-title">تصدير البيانات</div> | |
<div class="action-desc">قم بتصدير جدول التسعير الحالي بصيغة CSV لاستخدامه في برامج أخرى مثل Excel.</div> | |
</div> | |
""", unsafe_allow_html=True) | |
# زر تصدير التسعير | |
export_button = st.button("تجهيز ملف للتصدير", use_container_width=True) | |
if export_button: | |
# تحويل البيانات إلى CSV | |
csv = items.to_csv(index=False) | |
st.success("تم تجهيز ملف التصدير بنجاح! يمكنك تنزيله الآن.") | |
# تقديم البيانات للتنزيل | |
st.download_button( | |
label="تنزيل ملف CSV", | |
data=csv, | |
file_name="unbalanced_pricing.csv", | |
mime="text/csv", | |
use_container_width=True | |
) |