PurchaseAgent2 / src /streamlit_app.py
PD03's picture
Update src/streamlit_app.py
a8d5e0a verified
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
@st.cache_data
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()