Spaces:
Sleeping
Sleeping
File size: 6,179 Bytes
e573ff3 2f60b4d e573ff3 a7d00db 2626b66 a7d00db 2d5d3c0 a7d00db e8b72fe a7d00db 2626b66 a7d00db 2626b66 a7d00db cab1211 a7d00db 2626b66 a7d00db 2626b66 a7d00db 2626b66 a7d00db 2d5d3c0 a7d00db 2d5d3c0 1ecc1c6 a7d00db 1ecc1c6 b25d7a1 6c74bda b25d7a1 6c74bda 1ecc1c6 a7d00db 30f3a3c a7d00db 1ecc1c6 a7d00db 30f3a3c 1ecc1c6 30f3a3c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | import streamlit as st
import pandas as pd
import altair as alt
st.set_page_config(page_title="Shipment Monitoring Dashboard", layout="wide")
st.title("Shipment Monitoring Dashboard")
tabs = st.tabs(["Main Dashboard", "Last Mile Summary"])
with tabs[0]:
uploaded_file = st.file_uploader("Upload your Excel file", type=["xlsx"], key="main_upload")
if uploaded_file:
df = pd.read_excel(uploaded_file, sheet_name=0)
# Rename columns
df.columns = ['MAWB', 'MAWB Serial Number', 'Injection Gateway', 'Clearance Port', 'Latest Status',
'Cartons', 'Packages', 'Weights', 'Not online packages count', 'ETD', 'ETA', 'ATD', 'ATA',
'Last Mile Carrier', 'Readynotice has been notified', 'Vehicle has been arranged',
'Pick-upcompletedfrom', 'WH-IN Scan', 'Cargo Ready', 'Ship Out', 'Note',
'Exception Note', 'Exception Note Description']
# Add status column
def determine_status(row):
if pd.isna(row['WH-IN Scan']):
return "Pending"
elif pd.isna(row['Cargo Ready']):
return "WH-IN"
elif pd.isna(row['Ship Out']):
return "Cargo Ready"
else:
return "Ship Out"
df['Shipment Status'] = df.apply(determine_status, axis=1)
df['ETA'] = pd.to_datetime(df['ETA'])
df['ATA'] = pd.to_datetime(df['ATA'])
# Fallback date: ATA if available, otherwise ETA
df['Final Arrival Date'] = df['ATA'].fillna(df['ETA'])
df['Final Arrival Date'] = pd.to_datetime(df['Final Arrival Date']).dt.date
# Filter for Injection Gateway
injection_gateways = df['Injection Gateway'].dropna().unique().tolist()
selected_gateway = st.selectbox("Filter by Injection Gateway", options=["All"] + injection_gateways)
if selected_gateway != "All":
df = df[df['Injection Gateway'] == selected_gateway]
# Filter by ETA date range
st.subheader("Filter by ETA Date Range")
min_date, max_date = df['ETA'].min(), df['ETA'].max()
start_date, end_date = st.date_input("Select ETA date range:", [min_date, max_date], min_value=min_date, max_value=max_date)
df = df[(df['ETA'] >= pd.to_datetime(start_date)) & (df['ETA'] <= pd.to_datetime(end_date))]
# Shipment status distribution
st.subheader("Shipment Status Distribution")
status_order = ["Pending", "WH-IN", "Cargo Ready", "Ship Out"]
status_counts = df['Shipment Status'].value_counts().reindex(status_order).reset_index()
status_counts.columns = ['Shipment Status', 'Count']
status_counts = status_counts.dropna()
chart = alt.Chart(status_counts).mark_bar().encode(
x=alt.X('Shipment Status', sort=status_order),
y='Count',
color='Shipment Status',
tooltip=['Shipment Status', 'Count']
).properties(height=300)
st.altair_chart(chart, use_container_width=True)
# Volume by final arrival date (ATA preferred, fallback to ETA)
st.subheader("Shipment Volume by ETA")
volume_by_arrival = df.groupby('Final Arrival Date').agg({
'MAWB': 'count',
'Cartons': 'sum',
'Packages': 'sum',
'Weights': 'sum'
}).reset_index().rename(columns={'MAWB': 'Shipment Count'})
st.dataframe(volume_by_arrival, use_container_width=True)
line_chart = alt.Chart(volume_by_arrival).mark_line(point=True).encode(
x='Final Arrival Date:T',
y='Cartons:Q',
tooltip=['Final Arrival Date:T', 'Shipment Count:Q', 'Cartons:Q', 'Packages:Q', 'Weights:Q']
).properties(height=400)
st.altair_chart(line_chart, use_container_width=True)
# Status filter before detail view
st.subheader("Shipment Detail View by ETA")
status_options = ["All"] + status_order
selected_status = st.radio("Select a Shipment Status to filter the details below:", options=status_options, index=0)
if selected_status == "All":
filtered_df = df.copy()
else:
filtered_df = df[df['Shipment Status'] == selected_status]
sorted_df = filtered_df[['MAWB', 'ETA', 'ATA', 'Shipment Status', 'Cartons', 'Last Mile Carrier']].sort_values(by='ETA')
st.dataframe(sorted_df, use_container_width=True)
# Add download as Excel button
import io
excel_buffer = io.BytesIO()
with pd.ExcelWriter(excel_buffer, engine='xlsxwriter') as writer:
sorted_df.to_excel(writer, index=False, sheet_name='Shipment Details')
excel_data = excel_buffer.getvalue()
st.download_button(
label="📥 Download Shipment Details as Excel",
data=excel_data,
file_name="shipment_details.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
with tabs[1]:
st.header("Upload CTN File for Last Mile Summary")
ctn_files = st.file_uploader("Upload one or more Excel files with Last Mile Service",
type=["xlsx"], key="ctn_upload", accept_multiple_files=True)
if ctn_files:
df_list = []
for file in ctn_files:
df_part = pd.read_excel(file, sheet_name=0)
df_list.append(df_part)
df_ctn = pd.concat(df_list, ignore_index=True)
# Columns of interest
carton_col = "No. Large box"
last_mile_col = "Last mile service"
# ✅ Count unique cartons by last mile service
st.subheader("Unique Cartons by Last Mile Service")
if carton_col in df_ctn.columns and last_mile_col in df_ctn.columns:
unique_summary = (
df_ctn.groupby(last_mile_col)[carton_col]
.nunique()
.reset_index(name="Unique Carton Count")
)
st.dataframe(unique_summary, use_container_width=True)
else:
st.warning(f"Columns '{carton_col}' or '{last_mile_col}' not found in uploaded file(s).")
|