feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
---
|
||||
name: ingest_gsc_search_analytics
|
||||
kind: pipeline
|
||||
lang: py
|
||||
domain: pipelines
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def ingest_gsc_search_analytics(site_url: str = '', duckdb_path: str = '', pg_dsn: str = '', start_date: str = '', end_date: str = '', lookback_days: int = 5, credentials_path: str = '') -> dict"
|
||||
description: "Pipeline de ingesta diaria de Google Search Console (Search Analytics): GSC -> DuckDB -> PostgreSQL. Autentica con una service account (gsc_auth), extrae las filas de Search Analytics por las dimensiones date/query/page (pull_gsc_search_analytics), crea la tabla DuckDB si no existe con una restriccion UNIQUE (duckdb_execute), transforma cada fila renombrando 'date'->'data_date' y rellenando defaults estables (country='', device='', search_type='web') para las dimensiones no pedidas, hace upsert idempotente en DuckDB (duckdb_upsert) y espeja la tabla completa a PostgreSQL en modo replace para que Metabase la lea (duckdb_to_postgres). DuckDB es la verdad acumulada (historico append idempotente); PostgreSQL es un espejo regenerado por completo cada corrida. Resuelve defaults de site_url/pg_dsn/duckdb_path desde env (GSC_SITE_URL, SEO_DSN, SEO_DUCKDB con fallback ~/.fn_seo/seo.duckdb). Resuelve fechas teniendo en cuenta el lag de ~3 dias de la API: end=hoy-3, start=hoy-(3+lookback_days), re-pulleando los ultimos dias para que el upsert corrija lo que GSC ajusta a posteriori. Devuelve un dict sin lanzar: {status:'ok', site_url, start_date, end_date, rows_pulled, duckdb, postgres} en exito, {status:'error', error} en fallo."
|
||||
tags: [seo, gsc, search-console, pipelines, duckdb]
|
||||
uses_functions:
|
||||
- gsc_auth_py_infra
|
||||
- pull_gsc_search_analytics_py_datascience
|
||||
- duckdb_execute_py_infra
|
||||
- duckdb_upsert_py_infra
|
||||
- duckdb_to_postgres_py_pipelines
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [os, datetime]
|
||||
params:
|
||||
- name: site_url
|
||||
desc: "propiedad de Search Console: 'sc-domain:ejemplo.com' (propiedad de dominio) o la URL de prefijo 'https://ejemplo.com/'. Si esta vacio se lee de la env var GSC_SITE_URL. Obligatorio: ValueError si falta."
|
||||
- name: duckdb_path
|
||||
desc: "ruta al archivo DuckDB de la fuente de verdad acumulada. Si esta vacio se lee de la env var SEO_DUCKDB y, en su defecto, ~/.fn_seo/seo.duckdb. El directorio padre se crea (os.makedirs exist_ok=True)."
|
||||
- name: pg_dsn
|
||||
desc: "cadena de conexion PostgreSQL del espejo BI, p.ej. 'postgresql://user:pass@host:5432/db'. Si esta vacio se lee de la env var SEO_DSN. Obligatorio: ValueError si falta."
|
||||
- name: start_date
|
||||
desc: "fecha inicial inclusiva 'YYYY-MM-DD'. Si esta vacia se calcula como hoy-(3+lookback_days)."
|
||||
- name: end_date
|
||||
desc: "fecha final inclusiva 'YYYY-MM-DD'. Si esta vacia se calcula como hoy-3 (lag de la API de GSC)."
|
||||
- name: lookback_days
|
||||
desc: "numero de dias extra hacia atras que se re-pullean para que el upsert idempotente corrija los datos que GSC ajusta a posteriori (hasta ~3 dias). Default 5."
|
||||
- name: credentials_path
|
||||
desc: "ruta al JSON de la service account. Se pasa tal cual a gsc_auth, que ya hace su propio fallback a la env var GSC_SA_JSON."
|
||||
output: "dict. En exito: {status:'ok', site_url:str, start_date:str, end_date:str, rows_pulled:int, duckdb:dict (resultado de duckdb_upsert), postgres:dict (resultado de duckdb_to_postgres)}. En error (sin lanzar): {status:'error', error:str}."
|
||||
tested: true
|
||||
tests:
|
||||
- "test_renombra_date_a_data_date_y_persiste_en_duckdb"
|
||||
- "test_resolucion_fechas_por_defecto"
|
||||
- "test_upsert_idempotente_no_duplica"
|
||||
- "test_falta_site_url_da_value_error"
|
||||
- "test_falta_pg_dsn_da_value_error"
|
||||
test_file_path: "python/functions/pipelines/ingest_gsc_search_analytics_test.py"
|
||||
file_path: "python/functions/pipelines/ingest_gsc_search_analytics.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Con las 3 env seteadas, una sola corrida hace el snapshot diario completo:
|
||||
export GSC_SITE_URL="sc-domain:ejemplo.com"
|
||||
export SEO_DSN="postgresql://seo:****@127.0.0.1:5432/seo"
|
||||
export GSC_SA_JSON="$HOME/.fn_seo/service_account.json"
|
||||
# (SEO_DUCKDB opcional; por defecto ~/.fn_seo/seo.duckdb)
|
||||
./fn run ingest_gsc_search_analytics
|
||||
# -> {"status": "ok", "site_url": "sc-domain:ejemplo.com",
|
||||
# "start_date": "2026-06-09", "end_date": "2026-06-17",
|
||||
# "rows_pulled": 1280, "duckdb": {...}, "postgres": {...}}
|
||||
```
|
||||
|
||||
```python
|
||||
import sys
|
||||
sys.path.insert(0, "python/functions")
|
||||
from pipelines.ingest_gsc_search_analytics import ingest_gsc_search_analytics
|
||||
|
||||
# Variante explicita: rango de fechas fijo y rutas pasadas como args.
|
||||
res = ingest_gsc_search_analytics(
|
||||
site_url="sc-domain:ejemplo.com",
|
||||
duckdb_path="/home/me/.fn_seo/seo.duckdb",
|
||||
pg_dsn="postgresql://seo:****@127.0.0.1:5432/seo",
|
||||
start_date="2026-06-01",
|
||||
end_date="2026-06-17",
|
||||
credentials_path="/home/me/.fn_seo/service_account.json",
|
||||
)
|
||||
print(res["rows_pulled"], res["status"]) # 4210 ok
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando quieras un snapshot diario de Google Search Console acumulado y consultable
|
||||
desde Metabase: cada corrida añade/actualiza los datos del rango en DuckDB y
|
||||
regenera el espejo PostgreSQL. La invoca el DAG `seo-gsc-daily` de dag_engine una
|
||||
vez al dia (no uses cron ni systemd timers: usa dag_engine). Para un re-pull manual
|
||||
puntual de un rango concreto, pásale `start_date`/`end_date` a mano.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Lag de ~3 dias**: la API de GSC no consolida datos hasta ~3 dias despues. Por
|
||||
eso `end_date` por defecto es hoy-3 y `start_date` retrocede `lookback_days` extra.
|
||||
Pedir hasta hoy devolveria filas vacias o incompletas.
|
||||
- **Re-pull idempotente**: se re-piden a proposito los ultimos `lookback_days` dias.
|
||||
La restriccion `UNIQUE (site_url, data_date, query, page, country, device,
|
||||
search_type)` + `duckdb_upsert` actualizan esas filas sin duplicarlas, recogiendo
|
||||
las correcciones que GSC aplica a posteriori. El `snapshot_date` se sobrescribe al
|
||||
valor de la ultima corrida.
|
||||
- **DuckDB es la verdad; PostgreSQL es un espejo**: la ingesta acumula histórico solo
|
||||
en DuckDB. El espejo a Postgres usa `mode='replace'` -> hace DROP + CREATE + INSERT
|
||||
de la tabla completa cada vez. NO escribas en la tabla Postgres ni esperes acumular
|
||||
alli: se borra y reescribe en cada corrida. Si quieres histórico, leelo de DuckDB.
|
||||
- **Dimensiones**: este pull pide solo `date`/`query`/`page`. `country` y `device`
|
||||
quedan vacios y `search_type='web'` como defaults estables para que la tupla UNIQUE
|
||||
sea consistente. Si necesitas desglose por pais/dispositivo, es otro pull/tabla.
|
||||
- **Requisitos de entorno**: necesita las 3 env (`GSC_SITE_URL`, `SEO_DSN`,
|
||||
`GSC_SA_JSON`) o sus args equivalentes, y la service account debe estar añadida como
|
||||
usuario con permiso sobre la propiedad en Search Console. Faltar `site_url` o
|
||||
`pg_dsn` devuelve `{status:'error'}` (ValueError capturado, no crash).
|
||||
Reference in New Issue
Block a user