# Capability: seo

SEO orientado a datos sobre Google Search Console (GSC): autenticar contra la Search Console
API con una cuenta de servicio, extraer Search Analytics (impresiones, clicks, CTR, posición
por query y página) y aterrizarlo en DuckDB (verdad acumulada) + Postgres (espejo para
Metabase). Es la cadena de ingesta del proyecto `seo_analytics`.

La tesis del grupo: el SEO deja de hacerse a ciegas y se convierte en un problema de datos
con loop medible — el dashboard señala la oportunidad (striking distance, CTR bajo, content
decay), se aplica el cambio y se mide el impacto en la siguiente ingesta.

## Funciones

| ID | Firma | Qué hace |
|---|---|---|
| `gsc_auth_py_infra` | `gsc_auth(credentials_path="", subject="") -> service` | Autentica contra la Search Console API v1 con una service account JSON (scope `webmasters.readonly`). Fallback a env `GSC_SA_JSON`. Devuelve el `service` de googleapiclient listo para consultar. |
| `pull_gsc_search_analytics_py_datascience` | `pull_gsc_search_analytics(service, site_url, start_date, end_date, dimensions=None, row_limit=25000, max_total_rows=0, search_type="web") -> list[dict]` | Extrae Search Analytics paginando (startRow) hasta agotar. Aplana cada fila (keys → nombres de dimensión + clicks/impressions/ctr/position). `dimensions` por defecto `["query","page"]`. |
| `ingest_gsc_search_analytics_py_pipelines` | `ingest_gsc_search_analytics(site_url="", duckdb_path="", pg_dsn="", start_date="", end_date="", lookback_days=5, credentials_path="") -> dict` | Pipeline: auth → pull (dims date,query,page) → upsert idempotente en DuckDB → espejo a Postgres (`mode=replace`). Resuelve defaults de env (`GSC_SITE_URL`, `SEO_DSN`, `GSC_SA_JSON`). Lo invoca el DAG `seo-gsc-daily`. |

## Ejemplo canónico (end-to-end)

```bash
# Greenfield: ver projects/seo_analytics/docs/SETUP.md para crear la service account,
# verificar la propiedad en Search Console y darle acceso a la SA.

# 1. Variables (el .env del proyecto las agrupa)
export GSC_SITE_URL="sc-domain:ejemplo.com"
export SEO_DSN="postgresql://captacion:PASS@localhost:5433/seo"
export GSC_SA_JSON="$HOME/.config/seo/gsc-sa.json"

# 2. Ingesta diaria (auth + pull + DuckDB + espejo Postgres) — la corre el DAG seo-gsc-daily
python/.venv/bin/python3 python/functions/pipelines/ingest_gsc_search_analytics.py

# 3. Dashboards en Metabase (una vez): añade la DB seo + 4 cards + dashboard
SEO_PG_PASS=... METABASE_USER=... METABASE_PASS=... \
  python/.venv/bin/python3 projects/seo_analytics/setup_metabase.py
```

Uso desde Python, componiendo las tres:

```python
import sys; sys.path.insert(0, "python/functions")
from infra import gsc_auth
from datascience import pull_gsc_search_analytics

svc = gsc_auth()  # lee GSC_SA_JSON
rows = pull_gsc_search_analytics(svc, "sc-domain:ejemplo.com",
                                 "2026-05-01", "2026-05-28",
                                 dimensions=["date", "query", "page"])
print(len(rows), rows[0])
```

## Fronteras

- **NO hace keyword research ni rank tracking externo**. GSC dice por qué keywords ya apareces
  en Google; descubrir keywords nuevas o medir SERP de competidores es otro trabajo (scrapers).
- **NO escribe los dashboards**. Las cards/dashboard de Metabase los construye el script del
  proyecto `setup_metabase.py` componiendo el grupo `metabase`. Este grupo solo ingiere datos.
- **NO gestiona el scheduling**. Eso es `dag_engine` (DAG `seo-gsc-daily`, grupo `scheduler`).
- **NO cubre Bing/otros buscadores**. Solo Google Search Console.

## Gotchas del grupo

- Los datos de GSC llegan con **~2-3 días de lag**. El pipeline pide hasta hoy menos 3 días.
- Google **anonimiza queries de baja frecuencia** (privacy threshold): la suma por query no
  cuadra con el total del sitio. Es esperado, no un bug.
- El formato de `site_url` importa: `sc-domain:ejemplo.com` (propiedad de dominio) vs URL
  completa con esquema (propiedad de prefijo).
- La service account accede porque su email está **añadido como usuario en Search Console**
  (Settings > Users), no por domain-wide delegation. El JSON de la SA es un secreto.
- **DuckDB es la verdad** (upsert idempotente, acumula histórico); **Postgres es un espejo**
  que se regenera por `replace` en cada sync. No acumular en Postgres directamente.

## Prerequisitos

- Sitio verificado en Search Console + service account con acceso (ver SETUP.md del proyecto).
- Stack Postgres + Metabase del proyecto `captacion_clientes` (contenedores `captacion-postgres`
  :5433 y `captacion-metabase` :3030), con la DB `seo` creada.
- Deps Python `google-api-python-client` + `google-auth` (ya en el venv del registry).
