213 lines
10 KiB
Python
Executable File
213 lines
10 KiB
Python
Executable File
#!/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")
|