Enhanced xlsxwriter scripts with comprehensive store_name replacement and ArrayFormula support
- Added create_excel_xlsxwriter.py and update_excel_xlsxwriter.py - Implemented comprehensive store_name replacement across all formula types - Added special handling for ArrayFormula objects (H25-AG27 range) - Added external link breaking to prevent Excel security warnings - Enhanced sheet renaming with formula reference preservation - Updated server.js to use new xlsxwriter scripts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,34 @@ def update_excel_variables(excel_path):
|
||||
print(f"Opening Excel file: {excel_path}")
|
||||
wb = openpyxl.load_workbook(excel_path)
|
||||
|
||||
# Break any external links to prevent unsafe external sources error
|
||||
print("Breaking any external links...")
|
||||
try:
|
||||
# Clear external links if they exist
|
||||
if hasattr(wb, '_external_links'):
|
||||
wb._external_links.clear()
|
||||
if hasattr(wb, 'external_links'):
|
||||
wb.external_links.clear()
|
||||
|
||||
# Remove any defined names that might contain external references
|
||||
names_to_remove = []
|
||||
for name in wb.defined_names:
|
||||
if name.value and ('[' in str(name.value) or 'store_name' in str(name.value)):
|
||||
names_to_remove.append(name.name)
|
||||
print(f"Removing potentially problematic defined name: {name.name}")
|
||||
|
||||
for name_to_remove in names_to_remove:
|
||||
del wb.defined_names[name_to_remove]
|
||||
|
||||
# Set calculation mode to manual to prevent external link issues
|
||||
if hasattr(wb, 'calculation') and hasattr(wb.calculation, 'calcMode'):
|
||||
wb.calculation.calcMode = 'manual'
|
||||
print("Set calculation mode to manual")
|
||||
|
||||
print("External links handling completed")
|
||||
except Exception as e:
|
||||
print(f"Warning during external links handling: {e}")
|
||||
|
||||
# Try to access the Variables sheet
|
||||
try:
|
||||
# First try by name
|
||||
@@ -128,6 +156,169 @@ def update_excel_variables(excel_path):
|
||||
print("Saving workbook with updated variables...")
|
||||
wb.save(excel_path)
|
||||
|
||||
# Rename sheets containing store_name placeholder with actual store name
|
||||
store_name = user_data.get('store_name', 'Your Store')
|
||||
if store_name and store_name.strip():
|
||||
print(f"Renaming sheets with store_name placeholder to: {store_name}")
|
||||
|
||||
# Create a list of sheets to rename to avoid modifying during iteration
|
||||
sheets_to_rename = []
|
||||
for sheet_name in wb.sheetnames:
|
||||
if 'store_name' in sheet_name:
|
||||
new_sheet_name = sheet_name.replace('store_name', store_name)
|
||||
sheets_to_rename.append((sheet_name, new_sheet_name))
|
||||
|
||||
# Update all formulas and references before renaming sheets
|
||||
if sheets_to_rename:
|
||||
print("Updating formulas to prevent external link errors...")
|
||||
|
||||
# Go through all sheets and update any formulas that reference sheets with store_name
|
||||
for ws in wb.worksheets:
|
||||
for row in ws.iter_rows():
|
||||
for cell in row:
|
||||
if cell.value and isinstance(cell.value, str) and cell.value.startswith('='):
|
||||
original_formula = cell.value
|
||||
updated_formula = original_formula
|
||||
|
||||
# Replace sheet references in formulas
|
||||
for old_name, new_name in sheets_to_rename:
|
||||
# Handle different formula reference patterns
|
||||
patterns_to_replace = [
|
||||
f"'{old_name}'!", # 'Sheet Name'!
|
||||
f"{old_name}!", # SheetName! (if no spaces)
|
||||
f"'{old_name}'.", # 'Sheet Name'. (alternative reference)
|
||||
]
|
||||
|
||||
for pattern in patterns_to_replace:
|
||||
if pattern in updated_formula:
|
||||
new_pattern = pattern.replace(old_name, new_name)
|
||||
updated_formula = updated_formula.replace(pattern, new_pattern)
|
||||
|
||||
# Also replace ALL store_name placeholder variations within formula content
|
||||
store_name_variations = ['store_name', '{store_name}', 'store_name}', '{store_name']
|
||||
for variation in store_name_variations:
|
||||
if variation in updated_formula:
|
||||
updated_formula = updated_formula.replace(variation, store_name)
|
||||
|
||||
# Update the cell if formula changed
|
||||
if updated_formula != original_formula:
|
||||
try:
|
||||
cell.value = updated_formula
|
||||
print(f"Updated formula in {ws.title}!{cell.coordinate}")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not update formula in {ws.title}!{cell.coordinate}: {e}")
|
||||
|
||||
# Also check for store_name in regular cell values (non-formula)
|
||||
elif cell.value and isinstance(cell.value, str) and 'store_name' in cell.value:
|
||||
try:
|
||||
original_value = cell.value
|
||||
updated_value = original_value.replace('store_name', store_name)
|
||||
cell.value = updated_value
|
||||
print(f"Updated cell value in {ws.title}!{cell.coordinate}: '{original_value}' -> '{updated_value}'")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not update cell value in {ws.title}!{cell.coordinate}: {e}")
|
||||
|
||||
# Now safely rename each sheet
|
||||
for old_name, new_name in sheets_to_rename:
|
||||
try:
|
||||
sheet_obj = wb[old_name]
|
||||
sheet_obj.title = new_name
|
||||
print(f"Renamed sheet '{old_name}' to '{new_name}'")
|
||||
except Exception as e:
|
||||
print(f"Error renaming sheet '{old_name}' to '{new_name}': {e}")
|
||||
|
||||
# COMPREHENSIVE pass: Replace store_name in ALL cells throughout the workbook
|
||||
print("=== COMPREHENSIVE SCAN: Checking all sheets for store_name placeholders ===")
|
||||
total_formulas_updated = 0
|
||||
total_text_updated = 0
|
||||
|
||||
for ws in wb.worksheets:
|
||||
print(f"Scanning sheet: {ws.title}")
|
||||
sheet_formulas_updated = 0
|
||||
sheet_text_updated = 0
|
||||
|
||||
for row in ws.iter_rows():
|
||||
for cell in row:
|
||||
if cell.value:
|
||||
# Handle ArrayFormula objects specially
|
||||
if hasattr(cell.value, 'text'): # ArrayFormula
|
||||
formula_text = cell.value.text
|
||||
store_name_variations = ['store_name', '{store_name}', 'store_name}', '{store_name']
|
||||
has_store_name = any(variation in formula_text for variation in store_name_variations)
|
||||
|
||||
if has_store_name:
|
||||
original_formula = formula_text
|
||||
updated_formula = original_formula
|
||||
|
||||
# Replace all variations
|
||||
for variation in store_name_variations:
|
||||
if variation in updated_formula:
|
||||
updated_formula = updated_formula.replace(variation, store_name)
|
||||
|
||||
if updated_formula != original_formula:
|
||||
try:
|
||||
cell.value.text = updated_formula
|
||||
sheet_formulas_updated += 1
|
||||
total_formulas_updated += 1
|
||||
print(f" ✓ Updated store_name in ArrayFormula {cell.coordinate}: {original_formula}")
|
||||
except Exception as e:
|
||||
print(f" ✗ Could not update ArrayFormula {cell.coordinate}: {e}")
|
||||
|
||||
elif isinstance(cell.value, str):
|
||||
# Handle regular string cells
|
||||
store_name_variations = ['store_name', '{store_name}', 'store_name}', '{store_name']
|
||||
has_store_name = any(variation in cell.value for variation in store_name_variations)
|
||||
|
||||
if has_store_name:
|
||||
if cell.value.startswith('='):
|
||||
# Formula with store_name variations
|
||||
original_formula = cell.value
|
||||
updated_formula = original_formula
|
||||
|
||||
# Replace all variations
|
||||
for variation in store_name_variations:
|
||||
if variation in updated_formula:
|
||||
updated_formula = updated_formula.replace(variation, store_name)
|
||||
|
||||
if updated_formula != original_formula:
|
||||
try:
|
||||
cell.value = updated_formula
|
||||
sheet_formulas_updated += 1
|
||||
total_formulas_updated += 1
|
||||
print(f" ✓ Updated store_name in formula {cell.coordinate}: {original_formula[:50]}...")
|
||||
except Exception as e:
|
||||
print(f" ✗ Could not update formula {cell.coordinate}: {e}")
|
||||
else:
|
||||
# Regular text with store_name variations
|
||||
original_value = cell.value
|
||||
updated_value = original_value
|
||||
|
||||
# Replace all variations
|
||||
for variation in store_name_variations:
|
||||
if variation in updated_value:
|
||||
updated_value = updated_value.replace(variation, store_name)
|
||||
|
||||
if updated_value != original_value:
|
||||
try:
|
||||
cell.value = updated_value
|
||||
sheet_text_updated += 1
|
||||
total_text_updated += 1
|
||||
print(f" ✓ Updated store_name in text {cell.coordinate}: '{original_value}' -> '{updated_value}'")
|
||||
except Exception as e:
|
||||
print(f" ✗ Could not update text {cell.coordinate}: {e}")
|
||||
|
||||
if sheet_formulas_updated > 0 or sheet_text_updated > 0:
|
||||
print(f" → Sheet {ws.title}: {sheet_formulas_updated} formulas, {sheet_text_updated} text cells updated")
|
||||
else:
|
||||
print(f" → Sheet {ws.title}: No store_name placeholders found")
|
||||
|
||||
print(f"=== TOTAL UPDATES: {total_formulas_updated} formulas, {total_text_updated} text cells ===")
|
||||
|
||||
# Save after sheet renaming and formula updates
|
||||
if sheets_to_rename:
|
||||
print("Saving workbook after sheet renaming and formula updates...")
|
||||
wb.save(excel_path)
|
||||
|
||||
# Get the calculated years array from config
|
||||
starting_date = user_data.get('starting_date', '')
|
||||
duration = user_data.get('duration', 36)
|
||||
|
||||
Reference in New Issue
Block a user