Fix formula update in Excel sheets to handle sheet name changes
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
"company_name": "Footprints AI",
|
||||
"email": "denisa@example.com",
|
||||
"phone": "+40 712 345 678",
|
||||
"store_name": "Profi Romania",
|
||||
"store_name": "Carrefour Romania",
|
||||
"country": "Romania",
|
||||
"starting_date": "2025-02-01",
|
||||
"starting_date": "01.01.2026",
|
||||
"duration": 36,
|
||||
"store_types": ["convenience", "supermarket", "hypermarket"],
|
||||
"open_days_per_month": 26,
|
||||
|
||||
166
update_excel.py
166
update_excel.py
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import openpyxl
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
@@ -107,101 +108,100 @@ def update_excel_variables(excel_path):
|
||||
# Update sheet names - replace {store_name} with actual store name
|
||||
store_name = user_data.get('store_name', '')
|
||||
if store_name:
|
||||
# Dictionary to store old sheet name to new sheet name mappings
|
||||
sheet_name_mapping = {}
|
||||
|
||||
# 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)
|
||||
# Get the sheet by its old name
|
||||
sheet = wb[sheet_name]
|
||||
# Set the new title
|
||||
sheet.title = new_sheet_name
|
||||
# Store the mapping
|
||||
sheet_name_mapping[sheet_name] = new_sheet_name
|
||||
print(f"Renamed sheet '{sheet_name}' to '{new_sheet_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 ""
|
||||
# Update formulas in the Graphics sheet to reference the new sheet names
|
||||
if sheet_name_mapping and 'Graphics' in wb.sheetnames:
|
||||
print("Updating formulas in Graphics sheet...")
|
||||
update_formulas_in_graphics_sheet(wb, sheet_name_mapping)
|
||||
|
||||
# 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
|
||||
# Save the workbook
|
||||
wb.save(excel_path)
|
||||
print(f"Excel file updated successfully: {excel_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error updating Excel file: {e}")
|
||||
return False
|
||||
|
||||
def update_formulas_in_graphics_sheet(workbook, sheet_name_mapping):
|
||||
"""
|
||||
Update formulas in the Graphics sheet to reference the new sheet names
|
||||
|
||||
Args:
|
||||
workbook: The openpyxl workbook object
|
||||
sheet_name_mapping: Dictionary mapping old sheet names to new sheet names
|
||||
"""
|
||||
try:
|
||||
graphics_sheet = workbook['Graphics']
|
||||
print("Found Graphics sheet, updating formulas...")
|
||||
|
||||
# Iterate through all cells in the Graphics sheet
|
||||
for row in graphics_sheet.iter_rows():
|
||||
for cell in row:
|
||||
# Check if the cell contains a formula
|
||||
if cell.data_type == 'f' and cell.value:
|
||||
try:
|
||||
# Get the formula as a string
|
||||
formula = cell.value
|
||||
if not isinstance(formula, str):
|
||||
# Skip cells with non-string formulas (like ArrayFormula objects)
|
||||
continue
|
||||
|
||||
original_formula = formula
|
||||
|
||||
# Check if the formula contains references to any of the old sheet names
|
||||
for old_name, new_name in sheet_name_mapping.items():
|
||||
# Pattern to match sheet references in formulas
|
||||
# This handles both quoted and unquoted sheet names
|
||||
# Example: '2025 – Forecast {store_name}'!$B$10 or [2025 – Forecast {store_name}]!$B$10
|
||||
|
||||
# Handle quoted sheet names: 'Sheet Name'!
|
||||
pattern1 = f"'({re.escape(old_name)})'"
|
||||
replacement1 = f"'{new_name}'"
|
||||
formula = re.sub(pattern1, replacement1, formula)
|
||||
|
||||
# Handle unquoted sheet names: SheetName!
|
||||
pattern2 = f"([^']|^)({re.escape(old_name)})!"
|
||||
replacement2 = f"\\1{new_name}!"
|
||||
formula = re.sub(pattern2, replacement2, formula)
|
||||
|
||||
# Handle sheet names in square brackets: [Sheet Name]
|
||||
pattern3 = f"\\[({re.escape(old_name)})\\]"
|
||||
replacement3 = f"[{new_name}]"
|
||||
formula = re.sub(pattern3, replacement3, formula)
|
||||
|
||||
# If the formula was changed, update the cell
|
||||
if formula != original_formula:
|
||||
try:
|
||||
cell.value = formula
|
||||
print(f"Updated formula in cell {cell.coordinate}: {original_formula} -> {formula}")
|
||||
except Exception as e:
|
||||
print(f"Error updating formula in cell {cell.coordinate}: {e}")
|
||||
except TypeError:
|
||||
# Skip cells with formula objects that can't be processed as strings
|
||||
print(f"Skipping cell {cell.coordinate} with non-string formula type: {type(cell.value)}")
|
||||
|
||||
print("Finished updating formulas in Graphics sheet")
|
||||
except KeyError:
|
||||
print("Graphics sheet not found, skipping formula updates")
|
||||
except Exception as e:
|
||||
print(f"Error updating formulas in Graphics sheet: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# For testing purposes
|
||||
import sys
|
||||
|
||||
Reference in New Issue
Block a user