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() | |