"""Setup wizard for Family Assistant. Creates family.yaml and .env template in ~/.config/family-assistant/. """ import os import sys from pathlib import Path import yaml CONFIG_DIR = Path.home() / ".config" / "family-assistant" FAMILY_YAML_PATH = CONFIG_DIR / "family.yaml" ENV_TEMPLATE_PATH = CONFIG_DIR / ".env" ENV_TEMPLATE = """# Family Assistant configuration # Copy and fill in your values, then source this file or export the vars. # Gmail account for reading appointment emails GMAIL_USER= GMAIL_APP_PASSWORD= # Google Calendar (must match the calendar the service account has access to) GCAL_CALENDAR_ID= GCAL_SERVICE_ACCOUNT=gcal-service-account.json # LLM endpoint (default: local Ollama) # LLM_URL=http://localhost:11434/v1/chat/completions # LLM_MODEL=qwen2.5-coder:7b # Family config path (optional — defaults to CWD/family.yaml or ~/.config/family-assistant/family.yaml) # FAMILY_CONFIG_PATH= """ def _input(prompt, default=""): """Read input with a default value.""" if default: result = input(f"{prompt} [{default}]: ").strip() else: result = input(f"{prompt}: ").strip() return result or default def _yes_no(prompt, default=False): """Read a yes/no answer.""" suffix = "[Y/n]" if default else "[y/N]" result = input(f"{prompt} {suffix}: ").strip().lower() if not result: return default return result in ("y", "yes") def run_setup(non_interactive=False): """Run the setup wizard. If non_interactive=True, creates template files without prompting. """ CONFIG_DIR.mkdir(parents=True, exist_ok=True) if non_interactive: _create_templates() return # Interactive setup print("=" * 50) print("Family Assistant Setup Wizard") print("=" * 50) print() # Family members members = [] print("Let's add your family members.") print("Press Enter with no name to finish.\n") while True: name = _input(f" Member #{len(members)+1} name") if not name: break role = _input(" Role (e.g. dad, mom, son, daughter, dog)", "member") pronouns = _input(" Pronouns (e.g. he/him, she/her)", "") needs_adult = _yes_no(" Needs adult to transport/accompany?", False) nicknames = [] nick_str = _input(" Nicknames (comma-separated, or Enter for none)", "") if nick_str: nicknames = [n.strip() for n in nick_str.split(",") if n.strip()] member = {"name": name, "role": role} if pronouns: member["pronouns"] = pronouns if nicknames: member["nicknames"] = nicknames if needs_adult: member["needs_adult"] = True members.append(member) print() if not members: print("No members added. Creating a template family.yaml instead.") _create_templates() return # Write family.yaml family_data = {"family": {"members": members}} with open(FAMILY_YAML_PATH, "w") as f: yaml.dump(family_data, f, default_flow_style=False, sort_keys=False) print(f"✅ Created {FAMILY_YAML_PATH}") # Write .env template with open(ENV_TEMPLATE_PATH, "w") as f: f.write(ENV_TEMPLATE) print(f"✅ Created {ENV_TEMPLATE_PATH}") # Maintenance items maint_items = [] print() print("=" * 50) print("Maintenance Tracking") print("=" * 50) print() print("Track recurring household tasks (HVAC filters, pet meds, etc.).") print("Add items now, or edit maintenance.yaml later.") print() add_maint = _yes_no("Add maintenance items now?", True) if add_maint: categories = ["pet", "home", "seasonal", "vehicle", "health", "other"] print(f"Categories: {', '.join(categories)}") print("Press Enter with no name to finish.\n") while True: name = _input(f" Item #{len(maint_items)+1} name") if not name: break cat = _input(" Category", "home") who = _input(" Who/what is this for?", "House") interval = _input(" Interval (e.g. '30 days', '3 months', '12 months')", "3 months") last_done = _input(" Last done date (YYYY-MM-DD)", "") notify = _input(" Remind how many days before due?", "7") due_month = _input(" Due month only? (1-12, or Enter for anytime)", "") item = { "name": name, "category": cat, "who": who, "interval": interval, "last_done": last_done or "", "notify_days_before": int(notify) if notify.isdigit() else 7, } if due_month.isdigit(): item["due_month"] = int(due_month) maint_items.append(item) print() if maint_items: maint_data = {"items": maint_items} with open(CONFIG_DIR / "maintenance.yaml", "w") as f: yaml.dump(maint_data, f, default_flow_style=False, sort_keys=False) print(f"✅ Created {CONFIG_DIR / 'maintenance.yaml'} with {len(maint_items)} items") elif add_maint: # User said yes but didn't add any — create from example _copy_maintenance_template() else: _copy_maintenance_template() # Print next steps print() print("=" * 50) print("Next Steps") print("=" * 50) print(""" 1. Set up Gmail App Password: - Go to https://myaccount.google.com/apppasswords - Create an app password for "Mail" on your device - Set GMAIL_APP_PASSWORD in your .env file 2. Set up Google Calendar Service Account: - Go to https://console.cloud.google.com - Create a project and enable the Google Calendar API - Create a service account and download the JSON key - Place the key file as gcal-service-account.json (or set GCAL_SERVICE_ACCOUNT) - Share your Google Calendar with the service account email 3. Configure environment variables: - Edit {env_path} with your actual values - Source it: source {env_path} - Or export vars directly 4. Review maintenance items: - Edit ~/.config/family-assistant/maintenance.yaml - Update last_done dates with actual completion dates - Add/remove items to match your household 5. Test the setup: family-assistant upcoming family-assistant process --dry-run family-assistant maintenance """.format(env_path=ENV_TEMPLATE_PATH)) def _copy_maintenance_template(): """Create maintenance.yaml from the example template.""" import shutil pkg_dir = Path(__file__).parent example = pkg_dir / "maintenance.yaml.example" dest = CONFIG_DIR / "maintenance.yaml" if example.exists(): shutil.copy2(example, dest) print(f"✅ Created {dest} from template — edit with your actual items") else: # Fallback: create minimal template maint_data = {"items": []} with open(dest, "w") as f: yaml.dump(maint_data, f, default_flow_style=False, sort_keys=False) print(f"✅ Created empty {dest} — add items later") def _create_templates(): """Create template family.yaml and .env without prompting.""" # Template family.yaml with example data template_members = [ {"name": "Example", "role": "parent", "pronouns": "they/them", "nicknames": ["Ex"], "needs_adult": False}, ] family_data = {"family": {"members": template_members}} with open(FAMILY_YAML_PATH, "w") as f: yaml.dump(family_data, f, default_flow_style=False, sort_keys=False) print(f"✅ Created template {FAMILY_YAML_PATH}") # Template maintenance.yaml _copy_maintenance_template() with open(ENV_TEMPLATE_PATH, "w") as f: f.write(ENV_TEMPLATE) print(f"✅ Created template {ENV_TEMPLATE_PATH}") print() print("Edit both files with your actual values, then run:") print(" family-assistant upcoming") if __name__ == "__main__": non_interactive = "--non-interactive" in sys.argv run_setup(non_interactive=non_interactive)