Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
@@ -603,250 +603,147 @@ def home_page():
|
|
603 |
legend_title_font_size=16
|
604 |
)
|
605 |
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
606 |
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
)
|
627 |
-
st.plotly_chart(fig, use_container_width=True)
|
628 |
|
629 |
with tab2:
|
|
|
|
|
|
|
|
|
630 |
col1, col2 = st.columns(2)
|
631 |
|
632 |
with col1:
|
633 |
-
|
634 |
-
|
635 |
-
st.session_state.contracts_df,
|
636 |
-
x='القيمة',
|
637 |
-
nbins=10,
|
638 |
-
title='توزيع قيم العقود',
|
639 |
-
color_discrete_sequence=['#3498db']
|
640 |
-
)
|
641 |
-
fig.update_layout(
|
642 |
-
title_font_size=20,
|
643 |
-
title_font_family="Tajawal",
|
644 |
-
title_x=0.5,
|
645 |
-
xaxis_title="القيمة (ريال)",
|
646 |
-
yaxis_title="عدد العقود"
|
647 |
-
)
|
648 |
-
st.plotly_chart(fig, use_container_width=True)
|
649 |
|
650 |
with col2:
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
}
|
665 |
-
)
|
666 |
-
fig.update_layout(
|
667 |
-
title_font_size=20,
|
668 |
-
title_font_family="Tajawal",
|
669 |
-
title_x=0.5,
|
670 |
-
xaxis_title="القيمة (ريال)",
|
671 |
-
yaxis_title="نسبة المحتوى المحلي (%)"
|
672 |
-
)
|
673 |
-
st.plotly_chart(fig, use_container_width=True)
|
674 |
|
675 |
with tab3:
|
676 |
-
|
677 |
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
}
|
701 |
-
|
702 |
-
# تحضير بيانات المصفوفة
|
703 |
-
impact_order = ['منخفض', 'متوسط', 'عالي', 'حرج']
|
704 |
-
probability_order = ['منخفضة', 'متوسطة', 'عالية']
|
705 |
-
|
706 |
-
# إنشاء مصفوفة حرارية
|
707 |
-
heat_values = []
|
708 |
-
annotations = []
|
709 |
-
|
710 |
-
for impact in impact_order:
|
711 |
-
heat_row = []
|
712 |
-
for prob in probability_order:
|
713 |
-
try:
|
714 |
-
value = risk_matrix.loc[impact, prob]
|
715 |
-
except:
|
716 |
-
value = 0
|
717 |
-
heat_row.append(risk_severity.get((impact, prob), 0))
|
718 |
-
annotations.append(dict(
|
719 |
-
text=str(value),
|
720 |
-
x=prob,
|
721 |
-
y=impact,
|
722 |
-
showarrow=False
|
723 |
-
))
|
724 |
-
heat_values.append(heat_row)
|
725 |
-
|
726 |
-
# إنشاء الرسم البياني
|
727 |
-
fig = go.Figure(data=go.Heatmap(
|
728 |
-
z=heat_values,
|
729 |
-
x=probability_order,
|
730 |
-
y=impact_order,
|
731 |
-
colorscale=[
|
732 |
-
[0.0, "#2ecc71"], # أخضر للمخاطر المنخفضة
|
733 |
-
[0.33, "#f1c40f"], # أصفر للمخاطر المتوسطة
|
734 |
-
[0.66, "#e67e22"], # برتقالي للمخاطر العالية
|
735 |
-
[1.0, "#e74c3c"] # أحمر للمخاطر الحرجة
|
736 |
-
],
|
737 |
-
showscale=False
|
738 |
-
))
|
739 |
-
|
740 |
-
# إضافة النصوص
|
741 |
-
for annotation in annotations:
|
742 |
-
fig.add_annotation(annotation)
|
743 |
-
|
744 |
-
fig.update_layout(
|
745 |
-
title='مصفوفة المخاطر',
|
746 |
-
title_font_size=20,
|
747 |
-
title_font_family="Tajawal",
|
748 |
-
title_x=0.5,
|
749 |
-
xaxis_title="الاحتمالية",
|
750 |
-
yaxis_title="التأثير"
|
751 |
)
|
|
|
752 |
|
753 |
-
st.
|
754 |
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
treatment_counts,
|
762 |
-
x='المعالجة',
|
763 |
-
y='العدد',
|
764 |
-
title='استراتيجيات معالجة المخاطر',
|
765 |
-
color='المعالجة',
|
766 |
-
color_discrete_sequence=px.colors.qualitative.Pastel
|
767 |
-
)
|
768 |
-
fig.update_layout(
|
769 |
-
title_font_size=20,
|
770 |
-
title_font_family="Tajawal",
|
771 |
-
title_x=0.5,
|
772 |
-
xaxis_title="استراتيجية المعالجة",
|
773 |
-
yaxis_title="عدد المخاطر"
|
774 |
-
)
|
775 |
-
st.plotly_chart(fig, use_container_width=True)
|
776 |
|
777 |
-
|
778 |
-
|
779 |
-
upcoming_tenders = st.session_state.tenders_df[
|
780 |
-
(pd.to_datetime(st.session_state.tenders_df['تاريخ الإغلاق']) > datetime.now()) &
|
781 |
-
(pd.to_datetime(st.session_state.tenders_df['تاريخ الإغلاق']) <= datetime.now() + timedelta(days=14))
|
782 |
-
]
|
783 |
-
upcoming_tenders = upcoming_tenders.sort_values('تاريخ الإغلاق')
|
784 |
|
785 |
-
|
786 |
-
# عرض المناقصات في جدول
|
787 |
-
upcoming_display = upcoming_tenders[['رقم المناقصة', 'العنوان', 'تاريخ الإغلاق', 'القيمة التقديرية', 'نسبة النجاح المتوقعة']]
|
788 |
-
upcoming_display['أيام متبقية'] = (pd.to_datetime(upcoming_display['تاريخ الإغلاق']) - datetime.now()).dt.days
|
789 |
-
upcoming_display['القيمة التقديرية'] = upcoming_display['القيمة التقديرية'].apply(lambda x: f"{x:,.0f} ريال")
|
790 |
-
upcoming_display['نسبة النجاح المتوقعة'] = upcoming_display['نسبة النجاح المتوقعة'].apply(lambda x: f"{x}%")
|
791 |
-
|
792 |
-
st.dataframe(
|
793 |
-
upcoming_display,
|
794 |
-
hide_index=True,
|
795 |
-
use_container_width=True
|
796 |
-
)
|
797 |
-
else:
|
798 |
-
st.info("لا توجد مناقصات ستغلق خلال الأسبوعين القادمين")
|
799 |
|
800 |
-
|
801 |
-
|
802 |
|
803 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
804 |
|
805 |
with col1:
|
806 |
-
st.
|
807 |
-
st.markdown("""
|
808 |
-
تستخدم خوارزميات الذكاء الاصطناعي لتحليل البيانات التاريخية وتحديد العوامل المؤثرة في نجاح المناقصات.
|
809 |
-
يمكن للنظام التنبؤ بنسب النجاح المتوقعة للمناقصات الجديدة بناءً على خصائصها ومتطلباتها.
|
810 |
-
""")
|
811 |
-
|
812 |
-
if st.button("تحليل مناقصة جديدة", key="analyze_new_tender"):
|
813 |
-
with st.spinner("جاري تحليل المناقصة..."):
|
814 |
-
time.sleep(2) # محاكاة وقت المعالجة
|
815 |
-
st.success("تم التحليل بنجاح!")
|
816 |
-
st.info("نسبة النجاح المتوقعة للمناقصة: 78.5%")
|
817 |
-
st.markdown("""
|
818 |
-
**العوامل المؤثرة في النتيجة:**
|
819 |
-
- قدرة عالية على تلبية المتطلبات الفنية (+15%)
|
820 |
-
- توافق المنتج مع المعايير المطلوبة (+12%)
|
821 |
-
- خبرة سابقة مع نفس الجهة (+10%)
|
822 |
-
- المنافسة المتوقعة قوية (-7%)
|
823 |
-
""")
|
824 |
|
825 |
with col2:
|
826 |
-
st.
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
""")
|
831 |
|
832 |
-
uploaded_file = st.file_uploader("رفع ملف متطلبات للتحليل", type=['pdf', 'docx', 'txt'])
|
833 |
-
if uploaded_file is not None:
|
834 |
-
with st.spinner("جاري تحليل المتطلبات..."):
|
835 |
-
time.sleep(3) # محاكاة وقت المعالجة
|
836 |
-
st.success("تم تحليل المتطلبات بنجاح!")
|
837 |
-
|
838 |
-
# عرض نتائج التحليل
|
839 |
-
col1, col2 = st.columns(2)
|
840 |
-
with col1:
|
841 |
-
st.metric("عدد المتطلبات", "27")
|
842 |
-
st.metric("المتطلبات الوظيفية", "19")
|
843 |
-
st.metric("المتطلبات غير الوظيفية", "8")
|
844 |
-
|
845 |
-
with col2:
|
846 |
-
st.metric("درجة التعقيد", "متوسطة")
|
847 |
-
st.metric("المخاطر المحتملة", "4")
|
848 |
-
st.metric("البنود الغامضة", "3")
|
849 |
-
|
850 |
def dashboard_page():
|
851 |
"""صفحة لوحة المؤشرات"""
|
852 |
st.markdown("# 📊 لوحة المؤشرات")
|
@@ -1003,6 +900,86 @@ def dashboard_page():
|
|
1003 |
st.dataframe(upcoming_display, hide_index=True, use_container_width=True)
|
1004 |
else:
|
1005 |
st.info("لا توجد مناقصات قادمة خلال الثلاثين يوماً القادمة.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1006 |
|
1007 |
def procurement_page():
|
1008 |
"""صفحة المناقصات والعقود"""
|
|
|
603 |
legend_title_font_size=16
|
604 |
)
|
605 |
st.plotly_chart(fig, use_container_width=True)
|
606 |
+
|
607 |
+
def settings_page():
|
608 |
+
"""صفحة الإعدادات"""
|
609 |
+
st.markdown("# ⚙️ الإعدادات")
|
610 |
+
|
611 |
+
st.markdown("""
|
612 |
+
## إعدادات النظام
|
613 |
+
|
614 |
+
يمكنك من خلال هذه الصفحة تخصيص إعدادات النظام بما يتناسب مع احتياجاتك.
|
615 |
+
""")
|
616 |
+
|
617 |
+
tab1, tab2, tab3 = st.tabs(["إعدادات عامة", "إعدادات الحساب", "الدعم والمساعدة"])
|
618 |
+
|
619 |
+
with tab1:
|
620 |
+
st.markdown("### الإعدادات العامة")
|
621 |
|
622 |
+
st.markdown("#### إعدادات اللغة")
|
623 |
+
lang = st.radio(
|
624 |
+
"اللغة الافتراضية للنظام:",
|
625 |
+
["العربية", "English"],
|
626 |
+
index=0
|
627 |
+
)
|
628 |
+
|
629 |
+
st.markdown("#### إعدادات العرض")
|
630 |
+
theme = st.selectbox(
|
631 |
+
"السمة:",
|
632 |
+
["السمة الافتراضية", "الوضع الداكن", "الوضع الفاتح"],
|
633 |
+
index=0
|
634 |
+
)
|
635 |
+
|
636 |
+
st.markdown("#### إعدادات الإشعارات")
|
637 |
+
st.checkbox("تفعيل الإشعارات داخل النظام", value=True)
|
638 |
+
st.checkbox("تفعيل الإشعارات عبر البريد الإلكتروني", value=True)
|
639 |
+
|
640 |
+
if st.button("حفظ الإعدادات العامة"):
|
641 |
+
st.success("تم حفظ الإعدادات بنجاح")
|
|
|
642 |
|
643 |
with tab2:
|
644 |
+
st.markdown("### إعدادات الحساب")
|
645 |
+
|
646 |
+
st.markdown("#### البيانات الشخصية")
|
647 |
+
|
648 |
col1, col2 = st.columns(2)
|
649 |
|
650 |
with col1:
|
651 |
+
st.text_input("الاسم:", value="محمد أحمد")
|
652 |
+
st.text_input("البريد الإلكتروني:", value="mohammed@example.com")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
653 |
|
654 |
with col2:
|
655 |
+
st.text_input("المسمى الوظيفي:", value="مدير مشاريع")
|
656 |
+
st.text_input("رقم الهاتف:", value="0555555555")
|
657 |
+
|
658 |
+
st.markdown("#### تغيير كلمة المرور")
|
659 |
+
st.password_input("كلمة المرور الحالية:")
|
660 |
+
st.password_input("كلمة المرور الجديدة:")
|
661 |
+
st.password_input("تأكيد كلمة المرور الجديدة:")
|
662 |
+
|
663 |
+
if st.button("تحديث البيانات"):
|
664 |
+
st.success("تم تحديث البيانات بنجاح")
|
665 |
+
|
666 |
+
st.markdown("#### إعدادات الأمان")
|
667 |
+
st.checkbox("تفعيل المصادقة الثنائية", value=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
668 |
|
669 |
with tab3:
|
670 |
+
st.markdown("### الدعم والمساعدة")
|
671 |
|
672 |
+
st.markdown("""
|
673 |
+
#### كيفية استخدام النظام
|
674 |
+
|
675 |
+
يوفر النظام مجموعة من الأدوات والتحليلات التي تساعدك في إدارة المناقصات والعقود وتحليل المخاطر.
|
676 |
+
يمكنك الاطلاع على دليل الاستخدام للحصول على شرح تفصيلي لكافة الوظائف.
|
677 |
+
""")
|
678 |
+
|
679 |
+
st.download_button(
|
680 |
+
"تحميل دليل الاستخدام",
|
681 |
+
data="محتوى دليل الاستخدام",
|
682 |
+
file_name="user_guide.pdf",
|
683 |
+
mime="application/pdf"
|
684 |
+
)
|
685 |
+
|
686 |
+
st.markdown("#### التواصل مع الدعم الفني")
|
687 |
+
|
688 |
+
with st.form("support_form"):
|
689 |
+
st.text_input("العنوان:")
|
690 |
+
st.text_area("الرسالة:")
|
691 |
+
st.selectbox(
|
692 |
+
"نوع المشكلة:",
|
693 |
+
["استفسار عام", "مشكلة فنية", "اقتراح تحسين", "أخرى"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
694 |
)
|
695 |
+
st.file_uploader("إرفاق ملف (اختياري):")
|
696 |
|
697 |
+
submit_support = st.form_submit_button("إرسال")
|
698 |
|
699 |
+
if submit_support:
|
700 |
+
st.success("تم إرسال رسالتك بنجاح، سيتم التواصل معك قريباً")
|
701 |
+
|
702 |
+
def about_page():
|
703 |
+
"""صفحة حول النظام"""
|
704 |
+
st.markdown("# ℹ️ حول النظام")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
705 |
|
706 |
+
st.markdown("""
|
707 |
+
## نظام إدارة المناقصات والعقود
|
|
|
|
|
|
|
|
|
|
|
708 |
|
709 |
+
### نبذة عن النظام
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
710 |
|
711 |
+
نظام متكامل لإدارة المناقصات والعقود يعتمد على أحدث التقنيات والذكاء الاصطناعي لتحسين كفاءة العمليات وزيادة فرص النجاح.
|
712 |
+
يوفر النظام مجموعة من الأدوات والتحليلات التي تساعد في اتخاذ القرارات وإدارة المخاطر وتحسين المحتوى المحلي.
|
713 |
|
714 |
+
### المميزات الرئيسية
|
715 |
+
|
716 |
+
* إدارة المناقصات والفرص التجارية
|
717 |
+
* تحليل وإدارة المخاطر
|
718 |
+
* قياس وتحسين المحتوى المحلي
|
719 |
+
* توقع احتمالية النجاح في المناقصات
|
720 |
+
* إدارة وتحليل العقود
|
721 |
+
* تخطيط سلسلة التوريد
|
722 |
+
* تحليل المتطلبات
|
723 |
+
|
724 |
+
### فريق التطوير
|
725 |
+
|
726 |
+
تم تطوير هذا النظام بواسطة فريق من الخبراء المتخصصين في مجالات البرمجة والذكاء الاصطناعي وإدارة المشاريع والعقود.
|
727 |
+
|
728 |
+
### التواصل
|
729 |
+
|
730 |
+
للمزيد من المعلومات أو الاستفسارات، يرجى التواصل عبر:
|
731 |
+
|
732 |
+
* البريد الإلكتروني: [email protected]
|
733 |
+
* رقم الهاتف: 0123456789
|
734 |
+
""")
|
735 |
+
|
736 |
+
col1, col2, col3 = st.columns(3)
|
737 |
|
738 |
with col1:
|
739 |
+
st.info("إصدار النظام: 1.0.0")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
740 |
|
741 |
with col2:
|
742 |
+
st.info("تاريخ الإطلاق: 2024/03/15")
|
743 |
+
|
744 |
+
with col3:
|
745 |
+
st.info("آخر تحديث: 2024/03/18")
|
|
|
746 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
747 |
def dashboard_page():
|
748 |
"""صفحة لوحة المؤشرات"""
|
749 |
st.markdown("# 📊 لوحة المؤشرات")
|
|
|
900 |
st.dataframe(upcoming_display, hide_index=True, use_container_width=True)
|
901 |
else:
|
902 |
st.info("لا توجد مناقصات قادمة خلال الثلاثين يوماً القادمة.")
|
903 |
+
|
904 |
+
def procurement_page():
|
905 |
+
"""صفحة المناقصات والعقود"""
|
906 |
+
st.markdown("# 📑 المناقصات والعقود")
|
907 |
+
|
908 |
+
tab1, tab2 = st.tabs(["المناقصات", "العقود"])
|
909 |
+
|
910 |
+
with tab1:
|
911 |
+
st.markdown("## إدارة المناقصات")
|
912 |
+
|
913 |
+
# أزرار الإجراءات
|
914 |
+
col1, col2, col3, col4 = st.columns(4)
|
915 |
+
with col1:
|
916 |
+
if st.button("🆕 إضافة مناقصة جديدة"):
|
917 |
+
st.session_state.show_add_tender = True
|
918 |
+
|
919 |
+
with col2:
|
920 |
+
st.button("🔍 بحث متقدم")
|
921 |
+
|
922 |
+
with col3:
|
923 |
+
st.button("📊 تصدير التقرير")
|
924 |
+
|
925 |
+
with col4:
|
926 |
+
st.button("🔄 تحديث البيانات")
|
927 |
+
|
928 |
+
# عرض جدول المناقصات
|
929 |
+
st.markdown("### قائمة المناقصات")
|
930 |
+
|
931 |
+
# فلاتر
|
932 |
+
col1, col2, col3 = st.columns(3)
|
933 |
+
with col1:
|
934 |
+
status_filter = st.multiselect(
|
935 |
+
"الحالة:",
|
936 |
+
["الكل"] + list(st.session_state.tenders_df['الحالة'].unique()),
|
937 |
+
default=["الكل"]
|
938 |
+
)
|
939 |
+
|
940 |
+
with col2:
|
941 |
+
date_range = st.date_input(
|
942 |
+
"تاريخ الإعلان:",
|
943 |
+
value=(
|
944 |
+
(datetime.now() - timedelta(days=60)).date(),
|
945 |
+
datetime.now().date()
|
946 |
+
)
|
947 |
+
)
|
948 |
+
|
949 |
+
with col3:
|
950 |
+
min_value, max_value = st.slider(
|
951 |
+
"القيمة التقديرية (ريال):",
|
952 |
+
0,
|
953 |
+
int(st.session_state.tenders_df['القيمة التقديرية'].max()),
|
954 |
+
(0, int(st.session_state.tenders_df['القيمة التقديرية'].max())),
|
955 |
+
step=100000
|
956 |
+
)
|
957 |
+
|
958 |
+
# تطبيق الفلاتر
|
959 |
+
filtered_df = st.session_state.tenders_df.copy()
|
960 |
+
|
961 |
+
# فلتر الحالة
|
962 |
+
if "الكل" not in status_filter:
|
963 |
+
filtered_df = filtered_df[filtered_df['الحالة'].isin(status_filter)]
|
964 |
+
|
965 |
+
# فلتر التاريخ
|
966 |
+
filtered_df = filtered_df[
|
967 |
+
(pd.to_datetime(filtered_df['تاريخ الإعلان']) >= pd.Timestamp(date_range[0])) &
|
968 |
+
(pd.to_datetime(filtered_df['تاريخ الإعلان']) <= pd.Timestamp(date_range[1]))
|
969 |
+
]
|
970 |
+
|
971 |
+
# فلتر القيمة
|
972 |
+
filtered_df = filtered_df[
|
973 |
+
(filtered_df['القيمة التقد��رية'] >= min_value) &
|
974 |
+
(filtered_df['القيمة التقديرية'] <= max_value)
|
975 |
+
]
|
976 |
+
|
977 |
+
# عرض الجدول
|
978 |
+
st.dataframe(
|
979 |
+
filtered_df,
|
980 |
+
use_container_width=True,
|
981 |
+
hide_index=True
|
982 |
+
)
|
983 |
|
984 |
def procurement_page():
|
985 |
"""صفحة المناقصات والعقود"""
|