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
Select Year for Analysis
2024
2025
Compare Both Years
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
Branch Delivery Trends
Delivery Performance Analysis
Compare delivery counts and amounts between 2024 and 2025 for all branches.
Delivery Counts Comparison
Branch
January
February
March
April
May
June
July
August
September
October
November
December
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
2024
2025
Change
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)