Files
bussines_case_automation/update_excel.py
2025-09-12 11:10:43 +03:00

213 lines
10 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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")