#!/usr/bin/env python3 import json import os import openpyxl from openpyxl.utils import get_column_letter def update_excel_variables(excel_path): """ Update the Variables sheet in the Excel file with values from config.json Args: excel_path (str): Path to the Excel file to update Returns: bool: True if successful, False otherwise """ # Define paths script_dir = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(script_dir, 'config.json') try: # Load config.json with open(config_path, 'r') as f: config = json.load(f) user_data = config.get('user_data', {}) # Load Excel workbook print(f"Opening Excel file: {excel_path}") wb = openpyxl.load_workbook(excel_path) # Try to access the Variables sheet try: # First try by name sheet = wb['Variables'] except KeyError: # If not found by name, try to access the last sheet sheet_names = wb.sheetnames if sheet_names: print(f"Variables sheet not found by name. Using last sheet: {sheet_names[-1]}") sheet = wb[sheet_names[-1]] else: print("No sheets found in the workbook") return False # Map config variables to Excel cells based on the provided mapping cell_mappings = { 'B2': user_data.get('store_name', ''), 'B31': user_data.get('starting_date', ''), 'B32': user_data.get('duration', 36), 'B37': user_data.get('open_days_per_month', 0), # Convenience store type 'H37': user_data.get('convenience_store_type', {}).get('stores_number', 0), 'C37': user_data.get('convenience_store_type', {}).get('monthly_transactions', 0), # Convert boolean to 1/0 for has_digital_screens 'I37': 1 if user_data.get('convenience_store_type', {}).get('has_digital_screens', False) else 0, 'J37': user_data.get('convenience_store_type', {}).get('screen_count', 0), 'K37': user_data.get('convenience_store_type', {}).get('screen_percentage', 0), # Convert boolean to 1/0 for has_in_store_radio 'M37': 1 if user_data.get('convenience_store_type', {}).get('has_in_store_radio', False) else 0, 'N37': user_data.get('convenience_store_type', {}).get('radio_percentage', 0), # Supermarket store type 'H38': user_data.get('supermarket_store_type', {}).get('stores_number', 0), 'C38': user_data.get('supermarket_store_type', {}).get('monthly_transactions', 0), # Convert boolean to 1/0 for has_digital_screens 'I38': 1 if user_data.get('supermarket_store_type', {}).get('has_digital_screens', False) else 0, 'J38': user_data.get('supermarket_store_type', {}).get('screen_count', 0), 'K38': user_data.get('supermarket_store_type', {}).get('screen_percentage', 0), # Convert boolean to 1/0 for has_in_store_radio 'M38': 1 if user_data.get('supermarket_store_type', {}).get('has_in_store_radio', False) else 0, 'N38': user_data.get('supermarket_store_type', {}).get('radio_percentage', 0), # Hypermarket store type 'H39': user_data.get('hypermarket_store_type', {}).get('stores_number', 0), 'C39': user_data.get('hypermarket_store_type', {}).get('monthly_transactions', 0), # Convert boolean to 1/0 for has_digital_screens 'I39': 1 if user_data.get('hypermarket_store_type', {}).get('has_digital_screens', False) else 0, 'J39': user_data.get('hypermarket_store_type', {}).get('screen_count', 0), 'K39': user_data.get('hypermarket_store_type', {}).get('screen_percentage', 0), # Convert boolean to 1/0 for has_in_store_radio 'M39': 1 if user_data.get('hypermarket_store_type', {}).get('has_in_store_radio', False) else 0, 'N39': user_data.get('hypermarket_store_type', {}).get('radio_percentage', 0), # On-site channels 'B43': user_data.get('website_visitors', 0), 'B44': user_data.get('app_users', 0), 'B45': user_data.get('loyalty_users', 0), # Off-site channels 'B49': user_data.get('facebook_followers', 0), 'B50': user_data.get('instagram_followers', 0), 'B51': user_data.get('google_views', 0), 'B52': user_data.get('email_subscribers', 0), 'B53': user_data.get('sms_users', 0), 'B54': user_data.get('whatsapp_contacts', 0) } # Update the cells for cell_ref, value in cell_mappings.items(): try: sheet[cell_ref] = value print(f"Updated {cell_ref} with value: {value}") except Exception as e: print(f"Error updating cell {cell_ref}: {e}") # Update sheet names - replace {store_name} with actual store name store_name = user_data.get('store_name', '') if store_name: # Make a copy of the sheet names to avoid modifying during iteration sheet_names = wb.sheetnames.copy() # Keep track of used sheet names to avoid duplicates used_sheet_names = set() for sheet_name in sheet_names: if '{store_name}' in sheet_name: # Replace the placeholder with the store name new_sheet_name = sheet_name.replace('{store_name}', store_name) # Excel has a 31-character limit for sheet names if len(new_sheet_name) > 31: # Extract parts of the sheet name (assuming format like "2025 – Forecast {store_name}") parts = sheet_name.split('{store_name}') prefix = parts[0] if parts else "" suffix = parts[1] if len(parts) > 1 else "" # Calculate how much space we have for the store name available_chars = 31 - len(prefix) - len(suffix) # If we have space for at least part of the store name if available_chars > 0: # Use as much of the store name as possible truncated_store_name = store_name[:available_chars] new_sheet_name = prefix + truncated_store_name + suffix else: # If no space for store name, use a more aggressive approach year_part = sheet_name.split('–')[0].strip() if '–' in sheet_name else "" # Create a shorter name using just the year and abbreviated store name abbrev_store = store_name[:15] if len(store_name) > 15 else store_name new_sheet_name = f"{year_part} {abbrev_store}"[:31] # Remove any invalid characters for Excel sheet names invalid_chars = [':', '\\', '/', '?', '*', '[', ']'] for char in invalid_chars: new_sheet_name = new_sheet_name.replace(char, '_') # Ensure the name is unique base_name = new_sheet_name counter = 1 while new_sheet_name in used_sheet_names: suffix = f" ({counter})" new_sheet_name = f"{base_name[:31-len(suffix)]}{suffix}" counter += 1 used_sheet_names.add(new_sheet_name) try: # Get the sheet by its old name sheet = wb[sheet_name] # Set the new title sheet.title = new_sheet_name print(f"Renamed sheet '{sheet_name}' to '{new_sheet_name}'") except Exception as e: print(f"Error renaming sheet '{sheet_name}': {e}") # Save the workbook with error handling try: # First try saving with normal mode wb.save(excel_path) print(f"Excel file updated successfully: {excel_path}") return True except Exception as save_error: print(f"Warning: Error saving Excel file: {save_error}") try: # Try with a different approach - save to a new file and then replace temp_path = excel_path + ".temp" wb.save(temp_path) # Close any potential file handles wb.close() # If the original file exists, try to remove it if os.path.exists(excel_path): try: os.remove(excel_path) except Exception as remove_error: print(f"Warning: Could not remove original file: {remove_error}") # If we can't remove it, use a new filename excel_path = excel_path.replace(".xlsx", f"_new_{int(datetime.datetime.now().timestamp())}.xlsx") # Rename the temp file to the target file os.rename(temp_path, excel_path) print(f"Excel file saved with alternative method: {excel_path}") return True except Exception as alt_save_error: print(f"Error: Failed to save Excel file with alternative method: {alt_save_error}") return False except Exception as e: print(f"Error updating Excel file: {e}") return False if __name__ == "__main__": # For testing purposes import sys if len(sys.argv) > 1: excel_path = sys.argv[1] update_excel_variables(excel_path) else: print("Please provide the path to the Excel file as an argument")