Implement Excel file generation with variable injection from config.json

This commit is contained in:
denisacirstea
2025-09-12 10:51:02 +03:00
parent 1b9cb50961
commit 9c0435ddd8
7 changed files with 143 additions and 511 deletions

12
.gitignore vendored
View File

@@ -21,8 +21,12 @@ Thumbs.db
# Log files
logs
*.log
*.log
# Generated files
output/
*.xlsx
# Python
__pycache__/
*.py[cod]
*$py.class
# Output directory
output/

View File

@@ -5,7 +5,7 @@
"company_name": "Footprints AI",
"email": "denisa@example.com",
"phone": "+40 712 345 678",
"store_name": "Media Romania",
"store_name": "Profi Romania",
"country": "Romania",
"starting_date": "2025-02-01",
"duration": 36,

View File

@@ -6,7 +6,7 @@ import datetime
import re
from pathlib import Path
from dateutil.relativedelta import relativedelta
import openpyxl
from update_excel import update_excel_variables
def create_excel_from_template():
"""
@@ -63,8 +63,14 @@ def create_excel_from_template():
shutil.copy2(template_path, output_path)
print(f"Excel file created successfully: {output_path}")
# Now inject variables from config.json into the Variables sheet
inject_variables(output_path, config)
# Update the Excel file with variables from config.json
print("Updating Excel file with variables from config.json...")
update_result = update_excel_variables(output_path)
if update_result:
print("Excel file updated successfully with variables from config.json")
else:
print("Warning: Failed to update Excel file with variables from config.json")
return True
except Exception as e:
@@ -125,125 +131,5 @@ def calculate_years(starting_date, duration):
print(f"Error calculating years: {e}")
return default_years
def inject_variables(excel_path, config):
"""
Inject variables from config.json into the Variables sheet of the Excel file.
Args:
excel_path (str): Path to the Excel file
config (dict): Configuration data from config.json
"""
try:
# Load the workbook
workbook = openpyxl.load_workbook(excel_path)
# Try to find the Variables sheet
sheet_names = workbook.sheetnames
variables_sheet = None
# Print all sheet names for debugging
print("Available sheets:", sheet_names)
# Look for the Variables sheet by name (case-insensitive)
for sheet_name in sheet_names:
if "variable" in sheet_name.lower():
variables_sheet = workbook[sheet_name]
print(f"Found Variables sheet: '{sheet_name}'")
break
# If Variables sheet not found by name, try the last sheet
if variables_sheet is None and sheet_names:
last_sheet_name = sheet_names[-1]
variables_sheet = workbook[last_sheet_name]
print(f"Using last sheet '{last_sheet_name}' as Variables sheet")
# If still not found, try all sheets and look for specific cell patterns
if variables_sheet is None:
for sheet_name in sheet_names:
sheet = workbook[sheet_name]
# Check if this sheet has a cell B2 with a value
if sheet["B2"].value is not None:
variables_sheet = sheet
print(f"Using sheet '{sheet_name}' as it has data in cell B2")
break
if variables_sheet is None:
print("Warning: Variables sheet not found. No variables were injected.")
return
# Get user data from config
user_data = config.get("user_data", {})
# Map cell references to config values based on the image
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),
# Website, App, Loyalty
"B43": user_data.get("website_visitors", 0),
"B44": user_data.get("app_users", 0),
"B45": user_data.get("loyalty_users", 0),
# Social Media
"B49": user_data.get("facebook_followers", 0),
"B50": user_data.get("instagram_followers", 0),
"B51": user_data.get("google_views", 0)
}
# Inject values into the Variables sheet
print(f"Injecting variables into sheet: {variables_sheet.title}")
for cell_ref, value in cell_mappings.items():
try:
# Check if cell exists
if cell_ref in variables_sheet:
variables_sheet[cell_ref] = value
print(f"Set {cell_ref} = {value}")
else:
print(f"Warning: Cell {cell_ref} not found in sheet")
except Exception as e:
print(f"Warning: Could not set value for cell {cell_ref}: {e}")
# Save the workbook
workbook.save(excel_path)
print(f"Variables successfully injected into {excel_path}")
except Exception as e:
print(f"Error injecting variables: {e}")
if __name__ == "__main__":
create_excel_from_template()

View File

@@ -1,381 +0,0 @@
#!/usr/bin/env python3
import json
import os
import shutil
import datetime
import re
import traceback
from pathlib import Path
from dateutil.relativedelta import relativedelta
import sys
import unicodedata
from openpyxl import load_workbook
import zipfile
from xml.etree import ElementTree as ET
def create_excel_from_template():
"""
Create a copy of the Excel template, replacing {store_name} with the value from config.json
and save it to the output folder.
"""
# Define paths
script_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(script_dir, 'config.json')
template_path = os.path.join(script_dir, 'template', 'Footprints AI for {store_name} - Retail Media Business Case Calculations.xlsx')
output_dir = os.path.join(script_dir, 'output')
print(f"[DEBUG] script_dir={script_dir}")
print(f"[DEBUG] config_path={config_path}")
print(f"[DEBUG] template_path={template_path}")
print(f"[DEBUG] output_dir={output_dir}")
print(f"[DEBUG] cwd={os.getcwd()}")
# Ensure output directory exists
os.makedirs(output_dir, exist_ok=True)
if not os.path.exists(config_path):
print(f"[ERROR] config.json not found at: {config_path}")
return False
# Read config.json to get store_name, starting_date, and duration
try:
with open(config_path, 'r') as f:
config = json.load(f)
user_data = config.get('user_data', {})
store_name = user_data.get('store_name', '')
starting_date = user_data.get('starting_date', '')
duration = user_data.get('duration', 36)
# If store_name is empty, use a default value
if not store_name:
store_name = "Your Store"
# Calculate years array based on starting_date and duration
years = calculate_years(starting_date, duration)
print(f"Years in the period: {years}")
except Exception as e:
print(f"Error reading config file: {e}")
print(traceback.format_exc())
return False
# Use first and last years from the array in the filename
year_range = ""
if years and len(years) > 0:
if len(years) == 1:
year_range = f"{years[0]}"
else:
year_range = f"{years[0]}-{years[-1]}"
else:
# Fallback to current year if years array is empty
current_year = datetime.datetime.now().year
year_range = f"{current_year}"
# Create output filename with store_name and year range
output_filename = f"Footprints AI for {store_name} - Retail Media Business Case Calculations {year_range}.xlsx"
output_path = os.path.join(output_dir, output_filename)
print(f"[DEBUG] output_path={output_path}")
if not os.path.exists(template_path):
print(f"[ERROR] Template not found at: {template_path}")
return False
# Copy the template to the output directory with the new name
try:
shutil.copy2(template_path, output_path)
if not os.path.exists(output_path):
print(f"[ERROR] Copy reported success but file missing: {output_path}")
return False
print(f"Excel file created successfully: {output_path}")
# Rename any sheets that contain the {store_name} token
try:
renamed_count = rename_store_placeholders(output_path, store_name)
print(f"[RENAME] Sheets renamed: {renamed_count}")
except Exception as e:
print(f"[RENAME] Unexpected error while renaming sheets: {e}")
# Now inject variables from config.json into the Variables sheet
ok = inject_variables(output_path, config)
if not ok:
print("[ERROR] inject_variables failed.")
return False
return True
except Exception as e:
print(f"Error creating Excel file: {e}")
print(traceback.format_exc())
return False
def calculate_years(starting_date, duration):
"""
Calculate an array of years that appear in the period from starting_date for duration months.
Args:
starting_date (str): Date in format dd/mm/yyyy, dd.mm.yyyy, or yyyy-mm-dd
duration (int): Number of months, including the starting month
Returns:
list: Array of years in the period [year1, year2, ...]
"""
# Default result if we can't parse the date
default_years = [datetime.datetime.now().year]
# If starting_date is empty, return current year
if not starting_date:
return default_years
try:
# Try to parse the date, supporting multiple formats
if '/' in starting_date:
day, month, year = map(int, starting_date.split('/'))
elif '.' in starting_date:
day, month, year = map(int, starting_date.split('.'))
elif '-' in starting_date:
# Handle yyyy-mm-dd format (from HTML date input)
parts = starting_date.split('-')
if len(parts) == 3:
year, month, day = map(int, parts)
else:
return default_years
else:
# If format is not recognized, return default
return default_years
# Create datetime object for starting date
start_date = datetime.datetime(year, month, day)
# Calculate end date (starting date + duration months - 1 day)
end_date = start_date + relativedelta(months=duration-1)
# Create a set of years (to avoid duplicates)
years_set = set()
# Add starting year
years_set.add(start_date.year)
# Add ending year
years_set.add(end_date.year)
# If there are years in between, add those too
for y in range(start_date.year + 1, end_date.year):
years_set.add(y)
# Convert set to sorted list
return sorted(list(years_set))
except Exception as e:
print(f"Error calculating years: {e}")
return default_years
def _normalize_name(s: str) -> str:
"""Normalize sheet names to avoid issues with en-dash/nbsp/casing."""
if s is None:
return ""
s = unicodedata.normalize("NFKC", s)
return s.replace("\u2013", "-").replace("\u00A0", " ").strip().lower()
def _diagnose_xlsx(path: str):
"""Inspect the XLSX container to list sheets and their types when openpyxl sees none."""
try:
with zipfile.ZipFile(path, 'r') as z:
print("[DIAG] ZIP entries:", len(z.namelist()))
# Workbook relationships and workbook xml
if 'xl/workbook.xml' in z.namelist():
xml = z.read('xl/workbook.xml')
root = ET.fromstring(xml)
ns = {'ns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
sheets = root.findall('.//ns:sheets/ns:sheet', ns)
if not sheets:
print("[DIAG] No <sheet> nodes found in xl/workbook.xml")
for s in sheets:
print(f"[DIAG] sheet name={s.get('name')!r} id={s.get('sheetId')} r:id={s.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}id')}")
# Check for 'fileVersion' and workbookPr flags that sometimes confuse parsers
wbpr = root.find('.//ns:workbookPr', ns)
if wbpr is not None:
print("[DIAG] workbookPr attrs:", wbpr.attrib)
else:
print("[DIAG] Missing xl/workbook.xml (file may be corrupted or not an xlsx).")
# Look for worksheet vs chartsheet parts
worksheet_parts = [n for n in z.namelist() if n.startswith('xl/worksheets/sheet') and n.endswith('.xml')]
chartsheet_parts = [n for n in z.namelist() if n.startswith('xl/chartsheets/sheet') and n.endswith('.xml')]
dialogsheets = [n for n in z.namelist() if n.startswith('xl/dialogsheets/') and n.endswith('.xml')]
print(f"[DIAG] worksheets={len(worksheet_parts)}, chartsheets={len(chartsheet_parts)}, dialogsheets={len(dialogsheets)}")
if chartsheet_parts and not worksheet_parts:
print("[DIAG] This workbook appears to contain only chart sheets (no worksheets). openpyxl will show zero sheetnames.")
except Exception as e:
print(f"[DIAG] Failed to inspect xlsx: {e}")
print(traceback.format_exc())
def _sanitize_sheet_title(title: str) -> str:
"""
Make a worksheet title Excel-safe:
- Replace invalid characters : \ / ? * [ ]
- Trim to 31 chars
"""
invalid = r'[:\\/\?\*\[\]]'
safe = re.sub(invalid, ' ', title).strip()
if len(safe) > 31:
safe = safe[:31]
return safe
def rename_store_placeholders(excel_path: str, store_name: str) -> int:
"""
Rename any worksheet whose title contains '{store_name}' by replacing the token
with the provided store_name, enforcing Excel naming rules and uniqueness.
Returns the number of sheets renamed.
"""
try:
wb = load_workbook(excel_path, data_only=False)
except Exception as e:
print(f"[RENAME] Could not open workbook for renaming: {e}")
return 0
renamed = 0
existing = set(ws.title for ws in wb.worksheets)
for ws in wb.worksheets:
old = ws.title
if "{store_name}" not in old:
continue
new_title_raw = old.replace("{store_name}", store_name or "Your Store")
new_title = _sanitize_sheet_title(new_title_raw)
# Ensure uniqueness by appending (2), (3), ...
candidate = new_title
suffix = 2
while candidate in existing and candidate != old:
base = new_title
# leave room for " (nn)"
max_base = 31 - (len(str(suffix)) + 3)
if len(base) > max_base:
base = base[:max_base]
candidate = f"{base} ({suffix})"
suffix += 1
if candidate != old:
try:
ws.title = candidate
existing.discard(old)
existing.add(candidate)
renamed += 1
print(f"[RENAME] '{old}''{candidate}'")
except Exception as e:
print(f"[RENAME] Failed to rename '{old}' to '{candidate}': {e}")
if renamed > 0:
try:
wb.save(excel_path)
print(f"[RENAME] Saved workbook after renaming {renamed} sheet(s).")
except Exception as e:
print(f"[RENAME] Failed to save workbook after renames: {e}")
else:
print("[RENAME] No sheets contained '{store_name}'.")
return renamed
def inject_variables(excel_path, config):
"""
Inject variables from config.json into the Variables sheet of the Excel file.
Linux-only path: uses openpyxl (no Excel required). This reads/writes .xlsx safely; .xlsm VBA projects are not preserved if you re-save them.
"""
user_data = config.get("user_data", {})
# Map cell references to config values based on the image
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),
"H37": user_data.get("convenience_store_type", {}).get("stores_number", 0),
"C37": user_data.get("convenience_store_type", {}).get("monthly_transactions", 0),
"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),
"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),
"H38": user_data.get("supermarket_store_type", {}).get("stores_number", 0),
"C38": user_data.get("supermarket_store_type", {}).get("monthly_transactions", 0),
"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),
"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),
"H39": user_data.get("hypermarket_store_type", {}).get("stores_number", 0),
"C39": user_data.get("hypermarket_store_type", {}).get("monthly_transactions", 0),
"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),
"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),
"B43": user_data.get("website_visitors", 0),
"B44": user_data.get("app_users", 0),
"B45": user_data.get("loyalty_users", 0),
"B49": user_data.get("facebook_followers", 0),
"B50": user_data.get("instagram_followers", 0),
"B51": user_data.get("google_views", 0),
"B53": user_data.get("sms_users", 0)
}
# Warn if trying to process a macro-enabled workbook: openpyxl will not preserve VBA
if excel_path.lower().endswith(".xlsm"):
print("Warning: .xlsm detected. openpyxl cannot preserve VBA projects; consider switching to a .xlsx template or running this step on Windows/Excel.")
# ---- openpyxl fallback (works on Linux, no Excel required) ----
try:
wb = load_workbook(excel_path, data_only=False)
if not wb.sheetnames:
print("[WARN] openpyxl reports no worksheets. Running container diagnostics…")
_diagnose_xlsx(excel_path)
print("Available sheets (openpyxl):", [repr(s) for s in wb.sheetnames])
# Find Variables sheet (case-insensitive, normalized)
target_idx = None
for idx, name in enumerate(wb.sheetnames):
if "variable" in _normalize_name(name):
target_idx = idx
break
if target_idx is None:
target_idx = len(wb.sheetnames) - 1 if wb.sheetnames else None
if target_idx is not None:
print(f"Variables sheet not found by name; using last sheet: {wb.sheetnames[target_idx]}")
else:
print("Suggestion: Ensure the template has at least one normal worksheet (not only chartsheets). Open and 'Save As' a regular .xlsx in Excel.")
if target_idx is None:
print("Warning: Workbook has no sheets. No variables were injected.")
return False
ws = wb[wb.sheetnames[target_idx]]
# Write values
for cell_ref, value in cell_mappings.items():
try:
ws[cell_ref].value = value
print(f"[openpyxl] Set {cell_ref} = {value}")
except Exception as e:
print(f"Warning: Could not set value for cell {cell_ref}: {e}")
# Ensure we're saving to .xlsx path to avoid accidental macro loss if template was .xlsm
save_path = excel_path
if save_path.lower().endswith(".xlsm"):
save_path = save_path[:-5] + ".xlsx"
print(f"Saving as {save_path} to avoid stripping VBA from .xlsm.")
wb.save(save_path)
print(f"Variables successfully injected into {save_path} using openpyxl")
return True
except Exception as e:
print(f"Error in openpyxl fallback: {e}")
print(traceback.format_exc())
return False
if __name__ == "__main__":
try:
ok = create_excel_from_template()
sys.exit(0 if ok else 1)
except Exception as e:
print(f"[FATAL] Unhandled exception: {e}")
print(traceback.format_exc())
sys.exit(2)

View File

@@ -35,8 +35,8 @@ app.post('/calculate', async (req, res) => {
await updateConfig(formData);
console.log('Config file updated successfully');
// Run Python script to create Excel file with variables injection
exec('python3 create_excel_xlwings.py', (error, stdout, stderr) => {
// Run Python script to create Excel file
exec('python3 create_excel.py', (error, stdout, stderr) => {
if (error) {
console.error(`Error executing Python script: ${error}`);
console.error(`stderr: ${stderr}`);

123
update_excel.py Executable file
View File

@@ -0,0 +1,123 @@
#!/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}")
# 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
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")