| | import pandas as pd |
| | import plotly.express as px |
| | import plotly.graph_objects as go |
| | import streamlit as st |
| | from typing import Dict, Optional, List |
| |
|
| | from config import get_chart_theme, DESIGN_SYSTEM, get_translation |
| |
|
| | @st.cache_data |
| | def get_material_stats(df: pd.DataFrame) -> Dict: |
| | stats = {} |
| | total = df['weight_kg'].sum() |
| | total_work_days = df['date'].nunique() |
| | |
| | for material in df['material_type'].unique(): |
| | data = df[df['material_type'] == material] |
| | work_days = data['date'].nunique() |
| | daily_avg = data.groupby('date')['weight_kg'].sum().mean() |
| | |
| | stats[material] = { |
| | 'total': data['weight_kg'].sum(), |
| | 'percentage': (data['weight_kg'].sum() / total) * 100, |
| | 'daily_avg': daily_avg, |
| | 'work_days': work_days, |
| | 'records': len(data) |
| | } |
| | |
| | stats['_total_'] = { |
| | 'total': total, |
| | 'percentage': 100.0, |
| | 'daily_avg': df.groupby('date')['weight_kg'].sum().mean(), |
| | 'work_days': total_work_days, |
| | 'records': len(df) |
| | } |
| | |
| | return stats |
| |
|
| | @st.cache_data |
| | def detect_outliers(df: pd.DataFrame) -> Dict: |
| | outliers = {} |
| | |
| | for material in df['material_type'].unique(): |
| | material_data = df[df['material_type'] == material] |
| | data = material_data['weight_kg'] |
| | |
| | Q1, Q3 = data.quantile(0.25), data.quantile(0.75) |
| | IQR = Q3 - Q1 |
| | lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR |
| | |
| | outlier_mask = (data < lower) | (data > upper) |
| | outlier_dates = material_data[outlier_mask]['date'].dt.strftime('%Y-%m-%d').tolist() |
| | |
| | outliers[material] = { |
| | 'count': len(outlier_dates), |
| | 'range': f"{lower:.0f} - {upper:.0f} kg", |
| | 'dates': outlier_dates |
| | } |
| | |
| | return outliers |
| |
|
| | def create_total_production_chart(df: pd.DataFrame, time_period: str = 'daily', lang: str = 'English'): |
| | t = get_translation(lang) |
| | |
| | if time_period == 'daily': |
| | grouped = df.groupby('date')['weight_kg'].sum().reset_index() |
| | fig = px.line(grouped, x='date', y='weight_kg', |
| | title=t.get('chart_total_production', 'Total Production Trend'), |
| | labels={'weight_kg': t.get('label_weight', 'Weight (kg)'), 'date': t.get('label_date', 'Date')}) |
| | elif time_period == 'weekly': |
| | df_copy = df.copy() |
| | df_copy['week'] = df_copy['date'].dt.isocalendar().week |
| | df_copy['year'] = df_copy['date'].dt.year |
| | grouped = df_copy.groupby(['year', 'week'])['weight_kg'].sum().reset_index() |
| | grouped['week_label'] = grouped['year'].astype(str) + '-W' + grouped['week'].astype(str) |
| | fig = px.bar(grouped, x='week_label', y='weight_kg', |
| | title=t.get('chart_total_production_weekly', 'Total Production Trend (Weekly)'), |
| | labels={'weight_kg': t.get('label_weight', 'Weight (kg)'), 'week_label': t.get('label_week', 'Week')}) |
| | else: |
| | df_copy = df.copy() |
| | df_copy['month'] = df_copy['date'].dt.to_period('M') |
| | grouped = df_copy.groupby('month')['weight_kg'].sum().reset_index() |
| | grouped['month'] = grouped['month'].astype(str) |
| | fig = px.bar(grouped, x='month', y='weight_kg', |
| | title=t.get('chart_total_production_monthly', 'Total Production Trend (Monthly)'), |
| | labels={'weight_kg': t.get('label_weight', 'Weight (kg)'), 'month': t.get('label_month', 'Month')}) |
| | |
| | fig.update_layout(**get_chart_theme()['layout'], height=400, showlegend=False) |
| | return fig |
| |
|
| | def create_materials_trend_chart(df: pd.DataFrame, time_period: str = 'daily', |
| | selected_materials: Optional[List[str]] = None, lang: str = 'English'): |
| | df_copy = df.copy() |
| | t = get_translation(lang) |
| | |
| | if selected_materials: |
| | df_copy = df_copy[df_copy['material_type'].isin(selected_materials)] |
| | |
| | if time_period == 'daily': |
| | grouped = df_copy.groupby(['date', 'material_type'])['weight_kg'].sum().reset_index() |
| | fig = px.line(grouped, x='date', y='weight_kg', color='material_type', |
| | title=t.get('chart_materials_trends', 'Materials Production Trends'), |
| | labels={'weight_kg': t.get('label_weight', 'Weight (kg)'), |
| | 'date': t.get('label_date', 'Date'), |
| | 'material_type': t.get('label_material', 'Material')}) |
| | elif time_period == 'weekly': |
| | df_copy['week'] = df_copy['date'].dt.isocalendar().week |
| | df_copy['year'] = df_copy['date'].dt.year |
| | grouped = df_copy.groupby(['year', 'week', 'material_type'])['weight_kg'].sum().reset_index() |
| | grouped['week_label'] = grouped['year'].astype(str) + '-W' + grouped['week'].astype(str) |
| | fig = px.bar(grouped, x='week_label', y='weight_kg', color='material_type', |
| | title=t.get('chart_materials_trends_weekly', 'Materials Production Trends (Weekly)'), |
| | labels={'weight_kg': t.get('label_weight', 'Weight (kg)'), |
| | 'week_label': t.get('label_week', 'Week'), |
| | 'material_type': t.get('label_material', 'Material')}) |
| | else: |
| | df_copy['month'] = df_copy['date'].dt.to_period('M') |
| | grouped = df_copy.groupby(['month', 'material_type'])['weight_kg'].sum().reset_index() |
| | grouped['month'] = grouped['month'].astype(str) |
| | fig = px.bar(grouped, x='month', y='weight_kg', color='material_type', |
| | title=t.get('chart_materials_trends_monthly', 'Materials Production Trends (Monthly)'), |
| | labels={'weight_kg': t.get('label_weight', 'Weight (kg)'), |
| | 'month': t.get('label_month', 'Month'), |
| | 'material_type': t.get('label_material', 'Material')}) |
| | |
| | fig.update_layout(**get_chart_theme()['layout'], height=400) |
| | return fig |
| |
|
| | def create_shift_trend_chart(df: pd.DataFrame, time_period: str = 'daily', lang: str = 'English'): |
| | theme = get_chart_theme() |
| | t = get_translation(lang) |
| | |
| | if time_period == 'daily': |
| | grouped = df.groupby(['date', 'shift'])['weight_kg'].sum().reset_index() |
| | pivot_data = grouped.pivot(index='date', columns='shift', values='weight_kg').fillna(0) |
| | |
| | fig = go.Figure() |
| | |
| | if 'day' in pivot_data.columns: |
| | fig.add_trace(go.Bar( |
| | x=pivot_data.index, |
| | y=pivot_data['day'], |
| | name=t.get('label_day_shift', 'Day Shift'), |
| | marker_color=DESIGN_SYSTEM['colors']['warning'], |
| | text=pivot_data['day'].round(0), |
| | textposition='inside' |
| | )) |
| | |
| | if 'night' in pivot_data.columns: |
| | fig.add_trace(go.Bar( |
| | x=pivot_data.index, |
| | y=pivot_data['night'], |
| | name=t.get('label_night_shift', 'Night Shift'), |
| | marker_color=DESIGN_SYSTEM['colors']['primary'], |
| | base=pivot_data['day'] if 'day' in pivot_data.columns else 0, |
| | text=pivot_data['night'].round(0), |
| | textposition='inside' |
| | )) |
| | |
| | fig.update_layout( |
| | **theme['layout'], |
| | title=t.get('chart_shift_trends', 'Daily Shift Production Trends (Stacked)'), |
| | xaxis_title=t.get('label_date', 'Date'), |
| | yaxis_title=t.get('label_weight', 'Weight (kg)'), |
| | barmode='stack', |
| | height=400, |
| | showlegend=True |
| | ) |
| | else: |
| | grouped = df.groupby(['date', 'shift'])['weight_kg'].sum().reset_index() |
| | fig = px.bar(grouped, x='date', y='weight_kg', color='shift', |
| | title=t.get('chart_shift_trends_period', f'{time_period.title()} Shift Production Trends'), |
| | barmode='stack') |
| | fig.update_layout(**theme['layout'], height=400) |
| | |
| | return fig |