Initial commit: sales analysis template
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
240
setup_wizard.py
Normal file
240
setup_wizard.py
Normal file
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Interactive setup wizard for configuring the sales analysis template
|
||||
Asks clarifying questions to configure config.py for your specific company and data
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def print_header(text):
|
||||
"""Print a formatted header"""
|
||||
print("\n" + "="*70)
|
||||
print(f" {text}")
|
||||
print("="*70 + "\n")
|
||||
|
||||
def ask_question(prompt, default=None, validator=None):
|
||||
"""
|
||||
Ask a question and return the answer
|
||||
|
||||
Args:
|
||||
prompt: Question to ask
|
||||
default: Default value if user just presses Enter
|
||||
validator: Optional function to validate input
|
||||
|
||||
Returns:
|
||||
User's answer (or default)
|
||||
"""
|
||||
if default:
|
||||
full_prompt = f"{prompt} [{default}]: "
|
||||
else:
|
||||
full_prompt = f"{prompt}: "
|
||||
|
||||
while True:
|
||||
answer = input(full_prompt).strip()
|
||||
if not answer and default:
|
||||
return default
|
||||
elif not answer:
|
||||
print(" Please provide an answer.")
|
||||
continue
|
||||
|
||||
if validator:
|
||||
try:
|
||||
return validator(answer)
|
||||
except Exception as e:
|
||||
print(f" Invalid input: {e}")
|
||||
continue
|
||||
|
||||
return answer
|
||||
|
||||
def validate_yes_no(answer):
|
||||
"""Validate yes/no answer"""
|
||||
answer_lower = answer.lower()
|
||||
if answer_lower in ['y', 'yes', 'true', '1']:
|
||||
return True
|
||||
elif answer_lower in ['n', 'no', 'false', '0']:
|
||||
return False
|
||||
else:
|
||||
raise ValueError("Please answer 'yes' or 'no'")
|
||||
|
||||
def validate_int(answer):
|
||||
"""Validate integer answer"""
|
||||
return int(answer)
|
||||
|
||||
def validate_file_exists(answer):
|
||||
"""Validate that file exists"""
|
||||
if not Path(answer).exists():
|
||||
raise ValueError(f"File not found: {answer}")
|
||||
return answer
|
||||
|
||||
def main():
|
||||
"""Run the setup wizard"""
|
||||
print_header("Sales Analysis Template - Setup Wizard")
|
||||
print("This wizard will help you configure the template for your company's data.")
|
||||
print("You can press Enter to accept defaults (shown in brackets).\n")
|
||||
|
||||
responses = {}
|
||||
|
||||
# Company Information
|
||||
print_header("Company Information")
|
||||
responses['company_name'] = ask_question("Company Name", default="Your Company Name")
|
||||
responses['analysis_date'] = ask_question("Analysis Date (YYYY-MM-DD)", default="2026-01-12")
|
||||
|
||||
# Data File
|
||||
print_header("Data File Configuration")
|
||||
print("Where is your sales data CSV file located?")
|
||||
data_file = ask_question("Data file name (e.g., sales_data.csv)", default="sales_data.csv")
|
||||
|
||||
# Check if file exists
|
||||
if Path(data_file).exists():
|
||||
print(f" ✓ Found: {data_file}")
|
||||
else:
|
||||
print(f" ⚠ Warning: {data_file} not found. Make sure to place it in the template directory.")
|
||||
|
||||
responses['data_file'] = data_file
|
||||
|
||||
# Column Mapping
|
||||
print_header("Column Mapping")
|
||||
print("What are the column names in your CSV file?")
|
||||
print("(Press Enter to accept defaults if your columns match common names)\n")
|
||||
|
||||
responses['revenue_column'] = ask_question("Revenue/Amount column name", default="USD")
|
||||
responses['date_column'] = ask_question("Primary date column name", default="InvoiceDate")
|
||||
|
||||
has_fallback = ask_question("Do you have fallback date columns (Month, Year)?", default="yes", validator=validate_yes_no)
|
||||
if has_fallback:
|
||||
fallback_str = ask_question("Fallback date columns (comma-separated)", default="Month, Year")
|
||||
responses['date_fallback'] = [col.strip() for col in fallback_str.split(',')]
|
||||
else:
|
||||
responses['date_fallback'] = []
|
||||
|
||||
responses['customer_column'] = ask_question("Customer/Account column name", default="Customer")
|
||||
responses['item_column'] = ask_question("Item/Product column name", default="Item")
|
||||
|
||||
has_quantity = ask_question("Do you have a Quantity column?", default="yes", validator=validate_yes_no)
|
||||
if has_quantity:
|
||||
responses['quantity_column'] = ask_question("Quantity column name", default="Quantity")
|
||||
else:
|
||||
responses['quantity_column'] = None
|
||||
|
||||
# Date Range
|
||||
print_header("Date Range Configuration")
|
||||
responses['min_year'] = ask_question("Minimum year to include in analysis", default="2021", validator=validate_int)
|
||||
responses['max_date'] = ask_question("Maximum date (YYYY-MM-DD)", default="2025-09-30")
|
||||
|
||||
years_str = ask_question("Analysis years (comma-separated, e.g., 2021,2022,2023,2024,2025)", default="2021,2022,2023,2024,2025")
|
||||
responses['analysis_years'] = [int(y.strip()) for y in years_str.split(',')]
|
||||
|
||||
# LTM Configuration
|
||||
print_header("LTM (Last Twelve Months) Configuration")
|
||||
print("LTM is used for the most recent partial year to enable apples-to-apples comparison.")
|
||||
print("Example: If your latest data is through September 2025, use Oct 2024 - Sep 2025.\n")
|
||||
|
||||
use_ltm = ask_question("Do you need LTM for the most recent year?", default="yes", validator=validate_yes_no)
|
||||
responses['ltm_enabled'] = use_ltm
|
||||
|
||||
if use_ltm:
|
||||
responses['ltm_start_month'] = ask_question("LTM start month (1-12)", default="10", validator=validate_int)
|
||||
responses['ltm_start_year'] = ask_question("LTM start year", default="2024", validator=validate_int)
|
||||
responses['ltm_end_month'] = ask_question("LTM end month (1-12)", default="9", validator=validate_int)
|
||||
responses['ltm_end_year'] = ask_question("LTM end year", default="2025", validator=validate_int)
|
||||
else:
|
||||
responses['ltm_start_month'] = 10
|
||||
responses['ltm_start_year'] = 2024
|
||||
responses['ltm_end_month'] = 9
|
||||
responses['ltm_end_year'] = 2025
|
||||
|
||||
# Exclusion Filters
|
||||
print_header("Exclusion Filters (Optional)")
|
||||
use_exclusions = ask_question("Do you need to exclude specific segments (e.g., test accounts, business units)?", default="no", validator=validate_yes_no)
|
||||
responses['exclusions_enabled'] = use_exclusions
|
||||
|
||||
if use_exclusions:
|
||||
responses['exclude_column'] = ask_question("Column name to filter on", default="Country")
|
||||
exclude_values_str = ask_question("Values to exclude (comma-separated)", default="")
|
||||
responses['exclude_values'] = [v.strip() for v in exclude_values_str.split(',') if v.strip()]
|
||||
else:
|
||||
responses['exclude_column'] = None
|
||||
responses['exclude_values'] = []
|
||||
|
||||
# Generate config.py
|
||||
print_header("Generating Configuration")
|
||||
print("Updating config.py with your settings...")
|
||||
|
||||
# Read current config.py
|
||||
config_path = Path('config.py')
|
||||
if not config_path.exists():
|
||||
print("ERROR: config.py not found!")
|
||||
return
|
||||
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config_content = f.read()
|
||||
|
||||
# Replace values
|
||||
replacements = {
|
||||
"COMPANY_NAME = \"Your Company Name\"": f"COMPANY_NAME = \"{responses['company_name']}\"",
|
||||
"ANALYSIS_DATE = \"2026-01-12\"": f"ANALYSIS_DATE = \"{responses['analysis_date']}\"",
|
||||
"DATA_FILE = 'sales_data.csv'": f"DATA_FILE = '{responses['data_file']}'",
|
||||
"REVENUE_COLUMN = 'USD'": f"REVENUE_COLUMN = '{responses['revenue_column']}'",
|
||||
"DATE_COLUMN = 'InvoiceDate'": f"DATE_COLUMN = '{responses['date_column']}'",
|
||||
"DATE_FALLBACK_COLUMNS = ['Month', 'Year']": f"DATE_FALLBACK_COLUMNS = {responses['date_fallback']}",
|
||||
"CUSTOMER_COLUMN = 'Customer'": f"CUSTOMER_COLUMN = '{responses['customer_column']}'",
|
||||
"ITEM_COLUMN = 'Item'": f"ITEM_COLUMN = '{responses['item_column']}'",
|
||||
"QUANTITY_COLUMN = 'Quantity'": f"QUANTITY_COLUMN = '{responses['quantity_column']}'" if responses['quantity_column'] else "QUANTITY_COLUMN = None",
|
||||
"MIN_YEAR = 2021": f"MIN_YEAR = {responses['min_year']}",
|
||||
"MAX_DATE = pd.Timestamp('2025-09-30')": f"MAX_DATE = pd.Timestamp('{responses['max_date']}')",
|
||||
"ANALYSIS_YEARS = [2021, 2022, 2023, 2024, 2025]": f"ANALYSIS_YEARS = {responses['analysis_years']}",
|
||||
"LTM_ENABLED = True": f"LTM_ENABLED = {responses['ltm_enabled']}",
|
||||
"LTM_START_MONTH = 10": f"LTM_START_MONTH = {responses['ltm_start_month']}",
|
||||
"LTM_START_YEAR = 2024": f"LTM_START_YEAR = {responses['ltm_start_year']}",
|
||||
"LTM_END_MONTH = 9": f"LTM_END_MONTH = {responses['ltm_end_month']}",
|
||||
"LTM_END_YEAR = 2025": f"LTM_END_YEAR = {responses['ltm_end_year']}",
|
||||
}
|
||||
|
||||
# Handle exclusions
|
||||
if responses['exclusions_enabled']:
|
||||
exclusions_config = f"""EXCLUSION_FILTERS = {{
|
||||
'enabled': True,
|
||||
'exclude_by_column': '{responses['exclude_column']}',
|
||||
'exclude_values': {responses['exclude_values']}
|
||||
}}"""
|
||||
# Replace the exclusion filters section
|
||||
import re
|
||||
pattern = r"EXCLUSION_FILTERS = \{.*?\}"
|
||||
config_content = re.sub(pattern, exclusions_config, config_content, flags=re.DOTALL)
|
||||
else:
|
||||
exclusions_config = """EXCLUSION_FILTERS = {
|
||||
'enabled': False,
|
||||
'exclude_by_column': None,
|
||||
'exclude_values': []
|
||||
}"""
|
||||
import re
|
||||
pattern = r"EXCLUSION_FILTERS = \{.*?\}"
|
||||
config_content = re.sub(pattern, exclusions_config, config_content, flags=re.DOTALL)
|
||||
|
||||
# Apply replacements
|
||||
for old, new in replacements.items():
|
||||
if old in config_content:
|
||||
config_content = config_content.replace(old, new)
|
||||
|
||||
# Write updated config
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
f.write(config_content)
|
||||
|
||||
print(" ✓ Configuration updated successfully!")
|
||||
|
||||
# Summary
|
||||
print_header("Setup Complete")
|
||||
print("Your configuration has been saved to config.py")
|
||||
print("\nNext steps:")
|
||||
print("1. Place your data file in the template directory (if not already there)")
|
||||
print("2. Test data loading: python -c \"from data_loader import load_sales_data; from config import get_data_path; df = load_sales_data(get_data_path()); print(f'Loaded {len(df):,} rows')\"")
|
||||
print("3. Review config.py and adjust any settings as needed")
|
||||
print("4. Start creating your analysis scripts using analysis_template.py")
|
||||
print("\nFor help, see README.md")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nSetup cancelled by user.")
|
||||
sys.exit(0)
|
||||
Reference in New Issue
Block a user