Files
odoo-expertos.com/directory/cli.py
T
2026-08-28 10:11:11 -03:00

226 lines
6.7 KiB
Python

"""
Odoo Directory CLI - Main entry point.
A Python CLI tool that generates static XML/HTML pages for Odoo partners,
integrating with the existing odoo-expertos.com static website.
Usage:
python -m directory.cli setup
python -m directory.cli plan --country DE --cities berlin muenchen
python -m directory.cli execute --plan <plan-id>
python -m directory.cli export --country DE
python -m directory.cli deploy
"""
import logging
from typing import List, Optional
import typer
from rich.console import Console
from .config import get_settings
from .utils.logger import setup_logging
from .commands.setup import setup_command, print_schema
from .commands.plan import plan_command, list_plans, show_plan
from .commands.execute import execute_command
from .commands.export import export_command
from .commands.deploy import deploy_command, status_command
# Create CLI app
app = typer.Typer(
name="odoo-directory",
help="Generate static pages for Odoo partner directory.",
add_completion=False,
)
console = Console()
# ==================== Setup Commands ====================
@app.command()
def setup(
seed_tags: bool = typer.Option(True, help="Seed default tags"),
seed_germany: bool = typer.Option(True, help="Seed Germany cities"),
schema: bool = typer.Option(False, "--schema", help="Print SQL schema only"),
):
"""
Initialize database with schema and seed data.
Run this once when setting up a new installation.
"""
setup_logging()
if schema:
print_schema()
return
setup_command(seed_tags=seed_tags, seed_germany=seed_germany)
# ==================== Plan Commands ====================
@app.command()
def plan(
country: str = typer.Option(..., "--country", "-c", help="Country code (e.g., DE)"),
cities: Optional[List[str]] = typer.Option(None, "--cities", help="City slugs to include"),
name: Optional[str] = typer.Option(None, "--name", "-n", help="Plan name"),
):
"""
Create an execution plan for a country.
Specify which cities to process with --cities or process all cities
in the country by default.
"""
setup_logging()
plan_command(country=country, cities=cities, name=name)
@app.command("plans")
def plans_list():
"""List all execution plans."""
setup_logging()
list_plans()
@app.command("plan-show")
def plan_show(
plan_id: str = typer.Argument(..., help="Plan UUID"),
):
"""Show details of a specific plan."""
setup_logging()
show_plan(plan_id)
# ==================== Execute Commands ====================
@app.command()
def execute(
plan_id: str = typer.Option(..., "--plan", "-p", help="Plan UUID"),
step: Optional[str] = typer.Option(None, "--step", "-s", help="Specific step: scrape, enrich, content"),
city: Optional[str] = typer.Option(None, "--city", help="Specific city slug"),
dry_run: bool = typer.Option(False, "--dry-run", help="Don't make actual API calls"),
):
"""
Execute a plan or specific step.
Runs the pipeline: scrape -> enrich -> content generation.
"""
setup_logging()
execute_command(plan_id=plan_id, step=step, city=city, dry_run=dry_run)
# ==================== Export Commands ====================
@app.command()
def export(
country: str = typer.Option(..., "--country", "-c", help="Country code"),
city: Optional[str] = typer.Option(None, "--city", help="Specific city slug"),
output: Optional[str] = typer.Option(None, "--output", "-o", help="Output directory"),
):
"""
Export static files for a country.
Generates HTML pages, sitemaps, and tag JSON files.
"""
setup_logging()
export_command(country=country, city=city, output_dir=output)
# ==================== Deploy Commands ====================
@app.command()
def deploy(
message: Optional[str] = typer.Option(None, "--message", "-m", help="Commit message"),
no_push: bool = typer.Option(False, "--no-push", help="Don't push to remote"),
):
"""
Deploy changes via Git.
Commits exported files and pushes to trigger Vercel deployment.
"""
setup_logging()
deploy_command(message=message, push=not no_push)
@app.command()
def status():
"""Show git status of the project."""
setup_logging()
status_command()
# ==================== Utility Commands ====================
@app.command()
def info():
"""Show configuration and environment info."""
setup_logging()
console.print("\n[bold blue]Odoo Directory CLI[/bold blue]\n")
try:
settings = get_settings()
console.print("[bold]Configuration:[/bold]")
console.print(f" Export directory: {settings.export_dir}")
console.print(f" Images directory: {settings.images_dir}")
console.print(f" Templates directory: {settings.templates_dir}")
console.print(f" Log level: {settings.log_level}")
console.print(f" Batch size: {settings.batch_size}")
console.print("\n[bold]API Status:[/bold]")
console.print(f" Supabase URL: {settings.supabase_url[:30]}...")
console.print(f" Supabase Key: {'*' * 10}... (configured)")
console.print(f" Apify Token: {'*' * 10}... (configured)")
console.print(f" Firecrawl Key: {'*' * 10}... (configured)")
console.print(f" Anthropic Key: {'*' * 10}... (configured)")
except Exception as e:
console.print(f"\n[red]Configuration error: {e}[/red]")
console.print("\nMake sure you have a .env file with all required variables.")
console.print("Copy .env.example to .env and fill in your API keys.")
@app.command()
def test_connection():
"""Test connections to all external services."""
setup_logging()
console.print("\n[bold blue]Testing Connections[/bold blue]\n")
# Test Supabase
console.print("Testing Supabase...")
try:
from .services.supabase import SupabaseService
db = SupabaseService()
regions = db.list_regions()
console.print(f" [green]Connected! Found {len(regions)} regions.[/green]")
except Exception as e:
console.print(f" [red]Failed: {e}[/red]")
# Test Anthropic
console.print("Testing Anthropic...")
try:
from anthropic import Anthropic
settings = get_settings()
client = Anthropic(api_key=settings.anthropic_api_key)
# Just verify the key format
console.print(" [green]API key configured.[/green]")
except Exception as e:
console.print(f" [red]Failed: {e}[/red]")
# Note about other services
console.print("\nNote: Apify and Firecrawl are tested during actual execution.")
# ==================== Main Entry ====================
def main():
"""Main entry point for the CLI."""
app()
if __name__ == "__main__":
main()