Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| from streamlit_option_menu import option_menu | |
| import numpy as np | |
| from datetime import datetime, timedelta | |
| # Import our modules | |
| from data.synthetic_data import SAPDataGenerator | |
| from agents.procurement_agent import ProcurementAgent | |
| from utils.charts import ProcurementCharts | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="π SAP S/4HANA Procurement AI", | |
| page_icon="π", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Custom CSS for beautiful UI | |
| st.markdown(""" | |
| <style> | |
| .main-header { | |
| font-size: 3rem; | |
| font-weight: bold; | |
| text-align: center; | |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| margin-bottom: 2rem; | |
| } | |
| .metric-card { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| padding: 1rem; | |
| border-radius: 10px; | |
| color: white; | |
| text-align: center; | |
| margin: 0.5rem 0; | |
| } | |
| .insight-box { | |
| background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); | |
| padding: 1.5rem; | |
| border-radius: 15px; | |
| color: white; | |
| margin: 1rem 0; | |
| } | |
| .recommendation-box { | |
| background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); | |
| padding: 1rem; | |
| border-radius: 10px; | |
| color: white; | |
| margin: 0.5rem 0; | |
| } | |
| .sidebar .sidebar-content { | |
| background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Initialize session state | |
| if 'data_loaded' not in st.session_state: | |
| st.session_state.data_loaded = False | |
| st.session_state.po_data = None | |
| st.session_state.supplier_data = None | |
| st.session_state.spend_data = None | |
| def load_synthetic_data(): | |
| """Load and cache synthetic data""" | |
| generator = SAPDataGenerator() | |
| po_data = generator.generate_purchase_orders(1000) | |
| supplier_data = generator.generate_supplier_performance() | |
| spend_data = generator.generate_spend_analysis() | |
| return po_data, supplier_data, spend_data | |
| def main(): | |
| # Main header | |
| st.markdown('<h1 class="main-header">π SAP S/4HANA Procurement AI Assistant</h1>', unsafe_allow_html=True) | |
| st.markdown('<p style="text-align: center; font-size: 1.2rem; color: #666;">Intelligent Procurement Analytics with AI-Powered Insights</p>', unsafe_allow_html=True) | |
| # Load data | |
| if not st.session_state.data_loaded: | |
| with st.spinner("π Loading SAP S/4HANA Data..."): | |
| po_data, supplier_data, spend_data = load_synthetic_data() | |
| st.session_state.po_data = po_data | |
| st.session_state.supplier_data = supplier_data | |
| st.session_state.spend_data = spend_data | |
| st.session_state.data_loaded = True | |
| # Sidebar navigation | |
| with st.sidebar: | |
| st.image("https://via.placeholder.com/200x80/667eea/white?text=SAP+S/4HANA", width=200) | |
| selected = option_menu( | |
| menu_title="Navigation", | |
| options=["π Dashboard", "π Analytics", "π€ AI Insights", "π Deep Dive", "βοΈ Settings"], | |
| icons=["house", "graph-up", "robot", "search", "gear"], | |
| menu_icon="cast", | |
| default_index=0, | |
| styles={ | |
| "container": {"padding": "0!important", "background-color": "#fafafa"}, | |
| "icon": {"color": "#667eea", "font-size": "18px"}, | |
| "nav-link": {"font-size": "16px", "text-align": "left", "margin": "0px", "--hover-color": "#eee"}, | |
| "nav-link-selected": {"background-color": "#667eea"}, | |
| } | |
| ) | |
| # Initialize AI agent | |
| agent = ProcurementAgent() | |
| charts = ProcurementCharts() | |
| # Main content based on selection | |
| if selected == "π Dashboard": | |
| show_dashboard(st.session_state.po_data, st.session_state.supplier_data, charts) | |
| elif selected == "π Analytics": | |
| show_analytics(st.session_state.po_data, st.session_state.spend_data, charts) | |
| elif selected == "π€ AI Insights": | |
| show_ai_insights(st.session_state.po_data, st.session_state.supplier_data, agent) | |
| elif selected == "π Deep Dive": | |
| show_deep_dive(st.session_state.po_data, st.session_state.supplier_data) | |
| elif selected == "βοΈ Settings": | |
| show_settings() | |
| def show_dashboard(po_data, supplier_data, charts): | |
| st.subheader("π Executive Dashboard") | |
| # KPI Metrics | |
| col1, col2, col3, col4 = st.columns(4) | |
| total_spend = po_data['Total_Value'].sum() | |
| total_pos = len(po_data) | |
| avg_delivery = po_data['Delivery_Performance'].mean() | |
| top_supplier = po_data.groupby('Supplier')['Total_Value'].sum().idxmax() | |
| with col1: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>π° Total Spend</h3> | |
| <h2>${total_spend:,.0f}</h2> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>π Purchase Orders</h3> | |
| <h2>{total_pos:,}</h2> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col3: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>π― Avg Delivery</h3> | |
| <h2>{avg_delivery:.1f}%</h2> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col4: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>π Top Supplier</h3> | |
| <h2>{top_supplier}</h2> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.markdown("---") | |
| # Charts | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| fig_trend = charts.create_spend_trend_chart(po_data) | |
| st.plotly_chart(fig_trend, use_container_width=True) | |
| with col2: | |
| fig_category = charts.create_category_pie_chart(po_data) | |
| st.plotly_chart(fig_category, use_container_width=True) | |
| # Status and Performance | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| fig_status = charts.create_status_donut_chart(po_data) | |
| st.plotly_chart(fig_status, use_container_width=True) | |
| with col2: | |
| fig_supplier = charts.create_supplier_performance_chart(po_data) | |
| st.plotly_chart(fig_supplier, use_container_width=True) | |
| def show_analytics(po_data, spend_data, charts): | |
| st.subheader("π Advanced Analytics") | |
| # Filter controls | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| selected_suppliers = st.multiselect( | |
| "Select Suppliers:", | |
| options=po_data['Supplier'].unique(), | |
| default=po_data['Supplier'].unique()[:5] | |
| ) | |
| with col2: | |
| selected_categories = st.multiselect( | |
| "Select Categories:", | |
| options=po_data['Category'].unique(), | |
| default=po_data['Category'].unique()[:5] | |
| ) | |
| with col3: | |
| date_range = st.date_input( | |
| "Date Range:", | |
| value=(po_data['PO_Date'].min(), po_data['PO_Date'].max()), | |
| min_value=po_data['PO_Date'].min(), | |
| max_value=po_data['PO_Date'].max() | |
| ) | |
| # Filter data | |
| filtered_data = po_data[ | |
| (po_data['Supplier'].isin(selected_suppliers)) & | |
| (po_data['Category'].isin(selected_categories)) | |
| ] | |
| st.markdown("---") | |
| # Advanced Charts | |
| tab1, tab2, tab3 = st.tabs(["π Trends", "π’ Suppliers", "π¦ Categories"]) | |
| with tab1: | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Monthly trend | |
| fig_trend = charts.create_spend_trend_chart(filtered_data) | |
| st.plotly_chart(fig_trend, use_container_width=True) | |
| with col2: | |
| # Delivery performance over time | |
| monthly_delivery = filtered_data.groupby(filtered_data['PO_Date'].dt.to_period('M'))['Delivery_Performance'].mean().reset_index() | |
| monthly_delivery['PO_Date'] = monthly_delivery['PO_Date'].astype(str) | |
| fig = px.bar(monthly_delivery, x='PO_Date', y='Delivery_Performance', | |
| title='π Monthly Delivery Performance', | |
| color='Delivery_Performance', | |
| color_continuous_scale='RdYlGn') | |
| fig.update_layout(height=400, plot_bgcolor='rgba(0,0,0,0)') | |
| st.plotly_chart(fig, use_container_width=True) | |
| with tab2: | |
| # Supplier analysis | |
| supplier_summary = filtered_data.groupby('Supplier').agg({ | |
| 'Total_Value': ['sum', 'mean', 'count'], | |
| 'Delivery_Performance': 'mean' | |
| }).round(2) | |
| supplier_summary.columns = ['Total Spend', 'Avg PO Value', 'PO Count', 'Delivery %'] | |
| supplier_summary = supplier_summary.reset_index() | |
| st.dataframe( | |
| supplier_summary.style.highlight_max(axis=0), | |
| use_container_width=True, | |
| height=400 | |
| ) | |
| with tab3: | |
| # Category deep dive | |
| category_analysis = filtered_data.groupby('Category').agg({ | |
| 'Total_Value': 'sum', | |
| 'Quantity': 'sum', | |
| 'Unit_Price': 'mean', | |
| 'Delivery_Performance': 'mean' | |
| }).round(2) | |
| st.dataframe( | |
| category_analysis.style.highlight_max(axis=0), | |
| use_container_width=True, | |
| height=400 | |
| ) | |
| def show_ai_insights(po_data, supplier_data, agent): | |
| st.subheader("π€ AI-Powered Procurement Insights") | |
| # Generate insights | |
| with st.spinner("π§ AI Agent is analyzing your procurement data..."): | |
| insights = agent.generate_insights(po_data, supplier_data) | |
| # Executive Summary | |
| st.markdown(f""" | |
| <div class="insight-box"> | |
| <h3>π Executive Summary</h3> | |
| {insights['summary']} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Tabs for different insights | |
| tab1, tab2, tab3 = st.tabs(["π° Spend Analysis", "π’ Supplier Intelligence", "β οΈ Risk Alerts"]) | |
| with tab1: | |
| spend_insights = insights['spend_analysis'] | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.metric("Total Spend", f"${spend_insights.get('total_spend', 0):,.2f}") | |
| st.metric("Avg PO Value", f"${spend_insights.get('avg_po_value', 0):,.2f}") | |
| with col2: | |
| st.metric("Spending Trend", spend_insights.get('monthly_trend', 'N/A').title()) | |
| st.metric("Top Category", spend_insights.get('top_category', 'N/A')) | |
| st.subheader("π― AI Recommendations") | |
| for recommendation in spend_insights.get('recommendations', []): | |
| st.markdown(f""" | |
| <div class="recommendation-box"> | |
| {recommendation} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with tab2: | |
| supplier_insights = insights['supplier_analysis'] | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| best = supplier_insights.get('best_performer', {}) | |
| st.success(f"π Best Performer: {best.get('name', 'N/A')} ({best.get('performance', 0):.1f}%)") | |
| with col2: | |
| worst = supplier_insights.get('worst_performer', {}) | |
| st.error(f"β οΈ Needs Improvement: {worst.get('name', 'N/A')} ({worst.get('performance', 0):.1f}%)") | |
| st.subheader("π Supplier Recommendations") | |
| for recommendation in supplier_insights.get('recommendations', []): | |
| st.markdown(f""" | |
| <div class="recommendation-box"> | |
| {recommendation} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with tab3: | |
| anomalies = insights['anomalies'] | |
| if anomalies: | |
| st.subheader(f"π¨ {len(anomalies)} Critical Issues Detected") | |
| for anomaly in anomalies: | |
| risk_color = {"High": "π΄", "Medium": "π‘", "Low": "π’"} | |
| st.markdown(f""" | |
| <div class="recommendation-box"> | |
| <strong>{risk_color.get(anomaly.get('risk_level', 'Medium'), 'π‘')} {anomaly['type']}</strong><br> | |
| PO: {anomaly.get('po_number', 'N/A')} | Supplier: {anomaly.get('supplier', 'N/A')}<br> | |
| Risk Level: {anomaly.get('risk_level', 'Unknown')} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| else: | |
| st.success("π No critical issues detected in your procurement data!") | |
| def show_deep_dive(po_data, supplier_data): | |
| st.subheader("π Deep Dive Analysis") | |
| # Data explorer | |
| st.subheader("π Purchase Orders Data Explorer") | |
| # Search and filter | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| search_po = st.text_input("π Search PO Number:") | |
| with col2: | |
| filter_status = st.selectbox("Filter by Status:", ['All'] + list(po_data['Status'].unique())) | |
| with col3: | |
| min_value = st.number_input("Min PO Value:", min_value=0, value=0) | |
| # Apply filters | |
| filtered_po = po_data.copy() | |
| if search_po: | |
| filtered_po = filtered_po[filtered_po['PO_Number'].str.contains(search_po, case=False)] | |
| if filter_status != 'All': | |
| filtered_po = filtered_po[filtered_po['Status'] == filter_status] | |
| if min_value > 0: | |
| filtered_po = filtered_po[filtered_po['Total_Value'] >= min_value] | |
| # Display filtered data | |
| st.dataframe( | |
| filtered_po.style.highlight_max(axis=0), | |
| use_container_width=True, | |
| height=400 | |
| ) | |
| # Download data | |
| csv = filtered_po.to_csv(index=False) | |
| st.download_button( | |
| label="π₯ Download Filtered Data", | |
| data=csv, | |
| file_name=f"procurement_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", | |
| mime="text/csv" | |
| ) | |
| def show_settings(): | |
| st.subheader("βοΈ Application Settings") | |
| st.info("π **Demo Configuration**") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("### π Data Settings") | |
| data_refresh = st.button("π Refresh Synthetic Data") | |
| if data_refresh: | |
| st.session_state.data_loaded = False | |
| st.rerun() | |
| st.markdown("### π¨ Theme Settings") | |
| theme = st.selectbox("Choose Theme:", ["Default", "Dark", "Light"]) | |
| with col2: | |
| st.markdown("### π€ AI Settings") | |
| ai_model = st.selectbox("AI Model:", ["GPT-4", "Claude", "Local Model"]) | |
| confidence = st.slider("Confidence Threshold:", 0.0, 1.0, 0.8) | |
| st.markdown("### π Chart Settings") | |
| chart_style = st.selectbox("Chart Style:", ["Modern", "Classic", "Minimal"]) | |
| st.markdown("---") | |
| st.markdown("### π Application Info") | |
| st.json({ | |
| "version": "1.0.0", | |
| "framework": "Streamlit", | |
| "data_source": "Synthetic SAP S/4HANA", | |
| "ai_agent": "Custom Procurement Agent", | |
| "last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| }) | |
| if __name__ == "__main__": | |
| main() | |