import logging import sys import random import pandas as pd import json from datetime import datetime # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('dashboard.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) # -------------------------- # --- REALISTIC DATA SETUP --- # -------------------------- # Define branch names and payment types branch_names = ["Al Rigga", "Al Barsha", "Hamdan", "Khalidiya", "Muroor", "Satwa", "Shabiya", "Electra"] payment_types = [ "Receipts", "Cash", "NOON CARD", "Master card", "Careem CARD", "Visa Card", "SMILES CARD", "Delivery CARD", "Delivery Cash", "ONLINE CASH", "Keeta CARD", "DELIVERO CARD", "ONLINE CARD" ] # Base values for 2023 data base_values = { "All Outlets": {"Total": 4447282.47, "Count": 157481}, "Al Rigga": {"Total": 76198.20, "Count": 3680}, "Al Barsha": {"Total": 629719.75, "Count": 23756}, "Hamdan": {"Total": 722126.83, "Count": 24619}, "Khalidiya": {"Total": 571512.20, "Count": 19137}, "Muroor": {"Total": 493890.77, "Count": 16509}, "Satwa": {"Total": 557244.49, "Count": 19546}, "Shabiya": {"Total": 669003.30, "Count": 21870}, "Electra": {"Total": 727586.93, "Count": 28364} } # Payment method distribution payment_distribution = { "Cash": 0.248, "NOON CARD": 0.213, "Master card": 0.128, "Careem CARD": 0.125, "Visa Card": 0.119, "SMILES CARD": 0.051, "Delivery CARD": 0.040, "Delivery Cash": 0.037, "ONLINE CASH": 0.014, "Keeta CARD": 0.008, "DELIVERO CARD": 0.008, "ONLINE CARD": 0.008 } # Validate base data def validate_base_data(): if not branch_names or not payment_types: logger.error("Branch names or payment types list is empty.") raise ValueError("Branch names and payment types must not be empty.") if not base_values or not payment_distribution: logger.error("Base values or payment distribution is empty.") raise ValueError("Base values and payment distribution must not be empty.") for branch in branch_names + ["All Outlets"]: if branch not in base_values: logger.error(f"Missing base values for branch: {branch}") raise ValueError(f"Base values missing for branch: {branch}") if not all(key in base_values[branch] for key in ["Total", "Count"]): logger.error(f"Invalid base values format for branch: {branch}") raise ValueError(f"Base values for {branch} must include Total and Count.") if abs(sum(payment_distribution.values()) - 1.0) > 0.01: logger.error("Payment distribution percentages do not sum to approximately 1.") raise ValueError("Payment distribution percentages must sum to approximately 1.") # Generate realistic 2023 data def generate_realistic_data(branch_name, branch_base_values, payment_distribution): try: payment_rows = [] total_total = 0 total_count = 0 for payment_type, percentage in payment_distribution.items(): variation_total = random.uniform(0.95, 1.05) variation_count = random.uniform(0.9, 1.1) base_total = branch_base_values["Total"] * percentage base_count = branch_base_values["Count"] * percentage total = base_total * variation_total count = int(base_count * variation_count) average = total / count if count > 0 else 0 payment_rows.append({ "Type": payment_type, "Total": total, "Pct of Total": "", "Count": count, "Average": average }) total_total += total total_count += count data = [{ "Type": "Receipts", "Total": total_total, "Pct of Total": "100%", "Count": total_count, "Average": total_total / total_count if total_count > 0 else 0 }] for row in payment_rows: row["Pct of Total"] = f"{(row['Total'] / total_total * 100):.2f}%" if total_total > 0 else "0.00%" data.append(row) return pd.DataFrame(data) except Exception as e: logger.error(f"Error generating data for branch {branch_name}: {str(e)}") raise # Generate data for all branches try: validate_base_data() branches_2023 = {} for branch in branch_names + ["All Outlets"]: branches_2023[branch] = generate_realistic_data(branch, base_values[branch], payment_distribution) all_years_data = {2023: branches_2023} except Exception as e: logger.error(f"Failed to initialize 2023 data: {str(e)}") sys.exit(1) # -------------------------- # --- HELPER FUNCTIONS --- # -------------------------- def generate_year_data(base_branches, growth_rate=0.1, months=12): try: if not base_branches: logger.error("Base branches data is empty.") raise ValueError("Base branches data cannot be empty.") if months <= 0 or months > 12: logger.error(f"Invalid number of months: {months}") raise ValueError("Months must be between 1 and 12.") if not isinstance(growth_rate, (int, float)): logger.error(f"Invalid growth rate: {growth_rate}") raise ValueError("Growth rate must be a number.") year_data = {} for branch_name, branch_2023 in base_branches.items(): branch_year = branch_2023.copy() branch_year['Total'] = branch_2023['Total'] * (1 + growth_rate) * (months/12) branch_year['Count'] = branch_2023['Count'] * (1 + growth_rate) * (months/12) branch_year['Average'] = branch_year['Total'] / branch_year['Count'] if branch_year['Count'].sum() > 0 else 0 receipts_total = branch_year[branch_year['Type'] == 'Receipts']['Total'].values[0] if not branch_year[branch_year['Type'] == 'Receipts'].empty else 0 branch_year['Pct of Total'] = branch_year.apply( lambda row: "100%" if row['Type'] == 'Receipts' else f"{(row['Total'] / receipts_total * 100):.2f}%" if receipts_total > 0 else "0.00%", axis=1 ) year_data[branch_name] = branch_year return year_data except Exception as e: logger.error(f"Error generating year data: {str(e)}") raise def generate_monthly_data(annual_sales, annual_orders, months=12, growth_rate=0.0): try: if annual_sales < 0 or annual_orders < 0: logger.error("Annual sales or orders cannot be negative.") raise ValueError("Annual sales and orders must be non-negative.") if months <= 0 or months > 12: logger.error(f"Invalid number of months: {months}") raise ValueError("Months must be between 1 and 12.") monthly_sales = [] monthly_orders = [] base_monthly = annual_sales / months if months > 0 else 0 base_monthly_orders = annual_orders / months if months > 0 else 0 for i in range(months): season = 1.2 if i in [5, 6, 7] else 0.8 if i in [0, 11] else 1.0 growth = 1 + growth_rate * (i / months) monthly_sales.append(int(base_monthly * season * growth)) monthly_orders.append(int(base_monthly_orders * season * growth)) return monthly_sales, monthly_orders except Exception as e: logger.error(f"Error generating monthly data: {str(e)}") raise def calculate_delivery_metrics(branch, year, data): try: df = data[f'data{year}']['payment_methods'].get(branch) if df is None or df.empty: logger.warning(f"No payment method data for branch {branch} in year {year}") return { 'delivery_sales': 0, 'delivery_orders': 0, 'delivery_percentage': 0, 'avg_delivery_value': 0 } delivery_sales = df[df['Type'].str.contains('Delivery|ONLINE', case=False)]['Total'].sum() total_sales = df[df['Type'] == 'Receipts']['Total'].values[0] if not df[df['Type'] == 'Receipts'].empty else 0 delivery_orders = df[df['Type'].str.contains('Delivery|ONLINE', case=False)]['Count'].sum() total_orders = df[df['Type'] == 'Receipts']['Count'].values[0] if not df[df['Type'] == 'Receipts'].empty else 0 return { 'delivery_sales': delivery_sales, 'delivery_orders': delivery_orders, 'delivery_percentage': (delivery_sales / total_sales * 100) if total_sales > 0 else 0, 'avg_delivery_value': (delivery_sales / delivery_orders) if delivery_orders > 0 else 0 } except Exception as e: logger.error(f"Error calculating delivery metrics for {branch} in {year}: {str(e)}") return { 'delivery_sales': 0, 'delivery_orders': 0, 'delivery_percentage': 0, 'avg_delivery_value': 0 } def filter_delivery_online(all_years_data): try: filtered = {} for year, branches_data in all_years_data.items(): filtered_year = {} for branch, df in branches_data.items(): receipts_row = df[df['Type'] == 'Receipts'] delivery_online = df[df['Type'].str.contains('Delivery|ONLINE', case=False)] if not delivery_online.empty: filtered_year[branch] = pd.concat([receipts_row, delivery_online]) if filtered_year: filtered[year] = filtered_year return filtered except Exception as e: logger.error(f"Error filtering delivery/online data: {str(e)}") return {} def filter_platforms(all_years_data): try: platform_mapping = { 'NOON': ['NOON CARD'], 'Careem': ['Careem CARD'], 'Mastercard': ['Master card'], 'Visa': ['Visa Card'], 'Smiles': ['SMILES CARD'], 'Keeta': ['Keeta CARD'], 'Deliveroo': ['DELIVERO CARD'] } filtered = {} for year, branches_data in all_years_data.items(): filtered_year = {} for branch, df in branches_data.items(): platform_data = {} for platform, types_list in platform_mapping.items(): df_platform = df[df['Type'].isin(types_list)] if not df_platform.empty: platform_data[platform] = df_platform cash_df = df[df['Type'] == 'Cash'] if not cash_df.empty: platform_data['Cash'] = cash_df if platform_data: filtered_year[branch] = platform_data if filtered_year: filtered[year] = filtered_year return filtered except Exception as e: logger.error(f"Error filtering platforms: {str(e)}") return {} # -------------------------- # --- DASHBOARD HTML REPORT --- # -------------------------- def generate_dashboard_html(output_file="Crispy_Chicken_Dashboard.html"): try: # Generate 2024 & 2025 data year_2024 = generate_year_data(branches_2023, growth_rate=0.10) year_2025 = generate_year_data(branches_2023, growth_rate=0.15, months=9) all_years_data[2024] = year_2024 all_years_data[2025] = year_2025 # Calculate key metrics total_revenue_2024 = sum(year_2024[branch].loc[0, 'Total'] for branch in branch_names if branch in year_2024) total_revenue_2025 = sum(year_2025[branch].loc[0, 'Total'] for branch in branch_names if branch in year_2025) growth_rate = ((total_revenue_2025 - total_revenue_2024) / total_revenue_2024 * 100) if total_revenue_2024 > 0 else 0 # Create dashboard data structure dashboard_data = { 'branches': branch_names, 'branchNames': {name: f"Crispy Chicken - {name}" for name in branch_names}, 'months': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 'monthNames': {month: month for month in ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']}, 'data2024': {'sales': {}, 'orders': {}, 'payment_methods': year_2024}, 'data2025': {'sales': {}, 'orders': {}, 'payment_methods': year_2025}, 'userEntries': [] } # Generate monthly sales and orders for branch in branch_names: try: annual_sales_2024 = year_2024.get(branch, pd.DataFrame()).loc[0, 'Total'] if not year_2024.get(branch, pd.DataFrame()).empty else 0 annual_orders_2024 = year_2024.get(branch, pd.DataFrame()).loc[0, 'Count'] if not year_2024.get(branch, pd.DataFrame()).empty else 0 monthly_sales_2024, monthly_orders_2024 = generate_monthly_data(annual_sales_2024, annual_orders_2024, months=12, growth_rate=0.10) dashboard_data['data2024']['sales'][branch] = monthly_sales_2024 dashboard_data['data2024']['orders'][branch] = monthly_orders_2024 annual_sales_2025 = year_2025.get(branch, pd.DataFrame()).loc[0, 'Total'] if not year_2025.get(branch, pd.DataFrame()).empty else 0 annual_orders_2025 = year_2025.get(branch, pd.DataFrame()).loc[0, 'Count'] if not year_2025.get(branch, pd.DataFrame()).empty else 0 monthly_sales_2025, monthly_orders_2025 = generate_monthly_data(annual_sales_2025, annual_orders_2025, months=9, growth_rate=0.15) monthly_sales_2025 += [0] * 3 monthly_orders_2025 += [0] * 3 dashboard_data['data2025']['sales'][branch] = monthly_sales_2025 dashboard_data['data2025']['orders'][branch] = monthly_orders_2025 except Exception as e: logger.warning(f"Error generating monthly data for {branch}: {str(e)}") dashboard_data['data2024']['sales'][branch] = [0] * 12 dashboard_data['data2024']['orders'][branch] = [0] * 12 dashboard_data['data2025']['sales'][branch] = [0] * 12 dashboard_data['data2025']['orders'][branch] = [0] * 12 # Calculate delivery metrics delivery_metrics_2024 = {} delivery_metrics_2025 = {} for branch in branch_names: delivery_metrics_2024[branch] = calculate_delivery_metrics(branch, 2024, dashboard_data) delivery_metrics_2025[branch] = calculate_delivery_metrics(branch, 2025, dashboard_data) # Add monthly delivery orders for year, delivery_metrics in [(2024, delivery_metrics_2024), (2025, delivery_metrics_2025)]: dashboard_data[f'data{year}']['delivery_orders'] = {} for branch in branch_names: try: monthly_orders = dashboard_data[f'data{year}']['orders'][branch] annual_total_orders = sum(monthly_orders) annual_delivery_orders = delivery_metrics[branch]['delivery_orders'] ratio = annual_delivery_orders / annual_total_orders if annual_total_orders > 0 else 0 monthly_delivery_orders = [int(mo * ratio) for mo in monthly_orders] dashboard_data[f'data{year}']['delivery_orders'][branch] = monthly_delivery_orders except Exception as e: logger.warning(f"Error calculating delivery orders for {branch} in {year}: {str(e)}") dashboard_data[f'data{year}']['delivery_orders'][branch] = [0] * 12 # Calculate year metrics def calculate_year_metrics(year): try: data_year = dashboard_data[f'data{year}'] total_sales = sum(sum(data_year['sales'][branch]) for branch in branch_names if branch in data_year['sales']) total_orders = sum(sum(data_year['orders'][branch]) for branch in branch_names if branch in data_year['orders']) avg_order_value = total_sales / total_orders if total_orders > 0 else 0 total_delivery_sales = sum(delivery_metrics_2024[branch]['delivery_sales'] if year == 2024 else delivery_metrics_2025[branch]['delivery_sales'] for branch in branch_names) total_delivery_orders = sum(delivery_metrics_2024[branch]['delivery_orders'] if year == 2024 else delivery_metrics_2025[branch]['delivery_orders'] for branch in branch_names) delivery_percentage = (total_delivery_sales / total_sales * 100) if total_sales > 0 else 0 avg_delivery_value = total_delivery_sales / total_delivery_orders if total_delivery_orders > 0 else 0 return { "totalSales": total_sales, "totalOrders": total_orders, "averageOrderValue": avg_order_value, "totalDeliverySales": total_delivery_sales, "totalDeliveryOrders": total_delivery_orders, "averageDeliveryValue": avg_delivery_value, "deliveryPercentage": delivery_percentage } except Exception as e: logger.error(f"Error calculating year metrics for {year}: {str(e)}") return { "totalSales": 0, "totalOrders": 0, "averageOrderValue": 0, "totalDeliverySales": 0, "totalDeliveryOrders": 0, "averageDeliveryValue": 0, "deliveryPercentage": 0 } metrics2024 = calculate_year_metrics(2024) metrics2025 = calculate_year_metrics(2025) # Compute additional insights try: max_branch_2024 = max(branch_names, key=lambda b: year_2024[b].loc[0, 'Total'] if b in year_2024 else 0) max_sales_2024 = year_2024[max_branch_2024].loc[0, 'Total'] if max_branch_2024 in year_2024 else 0 except Exception as e: logger.warning(f"Error finding top branch for 2024: {str(e)}") max_branch_2024 = "Unknown" max_sales_2024 = 0 order_diff = metrics2024['totalOrders'] - metrics2025['totalOrders'] opp_text = f"Potential to increase order volume by {order_diff:,.0f} orders by improving delivery efficiency." if order_diff > 0 else f"Order volume exceeded 2024 by {abs(order_diff):,.0f} orders." imp_text = f"Sales declined by {abs(growth_rate):.1f}% in 2025, requiring strategic interventions." if growth_rate < 0 else f"Sales increased by {growth_rate:.1f}% in 2025, continue the momentum." avg_order_change = ((metrics2025['averageOrderValue'] - metrics2024['averageOrderValue']) / metrics2024['averageOrderValue'] * 100) if metrics2024['averageOrderValue'] > 0 else 0 delivery_perc_change = ((metrics2025['deliveryPercentage'] - metrics2024['deliveryPercentage']) / metrics2024['deliveryPercentage'] * 100) if metrics2024['deliveryPercentage'] > 0 else 0 summer_share = (sum(sum(dashboard_data['data2024']['sales'][branch][5:8]) for branch in branch_names if branch in dashboard_data['data2024']['sales']) / metrics2024['totalSales'] * 100) if metrics2024['totalSales'] > 0 else 0 # Generate HTML content html_content = f""" Crispy Chicken Dashboard
Real-time Updates

Crispy Chicken Dashboard

Interactive Comparison Between 2024 and 2025 Performance

Select Year for Analysis

Performance Overview

Network Performance Summary

Compare total sales and performance metrics between 2024 and 2025 across all branches.

2024 Performance

${metrics2024['totalSales']:,}
Total Transactions: {metrics2024['totalOrders']:,}
Delivery Sales: ${metrics2024['totalDeliverySales']:,}
Average Value: ${metrics2024['averageOrderValue']:.2f}

2025 Performance

${metrics2025['totalSales']:,}
Total Transactions: {metrics2025['totalOrders']:,}
Delivery Sales: ${metrics2025['totalDeliverySales']:,}
Average Value: ${metrics2025['averageOrderValue']:.2f}

Comparison Summary

{growth_rate:.1f}%
Sales Growth: ${metrics2025['totalSales'] - metrics2024['totalSales']:,}
Transaction Change: {((metrics2025['totalOrders'] - metrics2024['totalOrders']) / metrics2024['totalOrders'] * 100 if metrics2024['totalOrders'] > 0 else 0):.1f}%
Data Status: 2025 partial data

Year-over-Year Comparison

Key Insight

{'Sales increased by ' + str(growth_rate) + '% in 2025 compared to 2024. Average order value increased by ' + str(avg_order_change) + '%.' if growth_rate > 0 else 'Sales decreased by ' + str(abs(growth_rate)) + '% in 2025 compared to 2024.'}

Branch Performance Analysis

Branch Sales Comparison

Monthly Performance Trends

Monthly Sales Comparison

Monthly Transaction Volume

Key Insights & Analysis

Performance Insights

Comprehensive analysis of business performance between 2024 and 2025.

Top Performing Branch (2024)

{max_branch_2024} branch led with ${max_sales_2024:,.2f} in sales.

${max_sales_2024:,.2f}
Total Sales

Sales Trend Analysis

Average order value changed by {avg_order_change:.1f}%.

{avg_order_change:.1f}%
Avg Value Change

Channel Performance

Delivery channel market share changed from {metrics2024['deliveryPercentage']:.1f}% to {metrics2025['deliveryPercentage']:.1f}%.

{delivery_perc_change:.1f}%
Delivery Channel Change

Seasonal Patterns

Summer months (June-August) account for {summer_share:.1f}% of total sales.

{summer_share:.1f}%
Summer Sales Share

Growth Opportunities

{opp_text}

{abs(order_diff):,.0f}
Order Volume {'Increase Potential' if order_diff > 0 else 'Exceeded'}

Areas for Improvement

{imp_text}

{abs(growth_rate):.1f}%
{'Sales Decline' if growth_rate < 0 else 'Sales Increase'}
""" # Write the HTML to file with open(output_file, 'w') as f: f.write(html_content) logger.info(f"Dashboard generated successfully: {output_file}") return output_file except Exception as e: logger.error(f"Error generating dashboard: {str(e)}") raise # Generate the dashboard if __name__ == "__main__": try: dashboard_file = generate_dashboard_html() print(f"Dashboard created successfully: {dashboard_file}") except Exception as e: print(f"Error creating dashboard: {str(e)}") sys.exit(1)