Jeluva/eventscope
EventScope
Descubrí lo que pasa cerca, antes de que pase.
Agregador inteligente de eventos locales para la Zona Sur del GBA (piloto: Adrogué / Partido de Almirante Brown, ~15km de radio) con pipeline de normalización vía LLM, deduplicación y API geoespacial.
Estado del MVP
Quickstart (offline)
python -m venv .venv
.venv\Scripts\pip install -e ".[dev]" # Windows
pytest # 39+ tests, 0 credenciales, 0 red
eventscope init-db # crear tablas (SQLite por defecto)
eventscope scrapers # listar fuentes registradas
eventscope serve --port 8080
# → http://127.0.0.1:8080/docsCorrer el pipeline real
# Copiar config y agregar credenciales opcionales
cp .env.example .env
# Ingestar todos los scrapers discovery (sin credenciales: brown, lomas, quilmes)
eventscope ingest
# Un scraper específico
eventscope ingest --source lomas_agenda
# Probar sin escribir a DB
eventscope ingest --dry-run
# Resolver permalinks de Instagram cosechados (requiere IG_ACCESS_TOKEN)
eventscope enrich
# Cancelar eventos vencidos
eventscope purge --days 7
# Ver estadísticas
eventscope statusArquitectura del pipeline
ScrapedItem(s)
→ raw_events (landing inmutable, idempotente por source+external_id)
→ extracción LLM (campos normalizados)
→ validación (título + fecha futura)
→ dedup clustering (embeddings + similitud coseno + fecha/geo)
→ events (normalizado) → API REST → Mapa / Calendario / AgendaGeocoding (limitación conocida Fase 1): fuentes sin coordenadas propias (gobierno, Instagram) pasan por el hook Geocoder. Por defecto es no-op. Eventos sin coords aparecen en /events/range pero no en /events/nearby. Activar con EVENTSCOPE_GEOCODER_PROVIDER=nominatim.
Fuentes de datos
Instagram — enriquecimiento, no discovery
oEmbed no puede descubrir ni listar posts de un perfil; solo resuelve un permalink conocido. Los scrapers cosechan links de IG de las páginas que scrapeean y los guardan en raw_events.payload["instagram_permalinks"]. eventscope enrich los resuelve vía oEmbed en una segunda pasada. Es idempotente: saltea permalinks ya resueltos.
Desde abril 2025 el endpoint sin autenticar fue removido. Se necesita token de app Meta con oembed_read. Sin token: fallback a iframe embed público.
API endpoints
GET /health
GET /events/nearby?lat=&lng=&radius_km=5&category=musica&free_only=false
GET /events/range?start=2026-06-15T00:00:00Z&end=2026-06-22T00:00:00Z
GET /events/{id}
GET /admin/statusConfiguración de producción
# Levantar Postgres + PostGIS
docker compose up -d db
# .env mínimo para producción
EVENTSCOPE_DATABASE_URL=postgresql+psycopg://eventscope:eventscope@localhost:5432/eventscope
EVENTSCOPE_LLM_PROVIDER=gemini
EVENTSCOPE_LLM_API_KEY=AIza...
EVENTSCOPE_GEOCODER_PROVIDER=nominatimVer .env.example para todas las opciones.
Layout
src/eventscope/
config.py settings (env, todo OFF por defecto)
models.py db.py ORM + sesión
geo.py repository.py
scrapers/ base + registro + 7 fuentes
extraction/ schema + extractor (stub/Gemini/OpenAI)
dedup/ embeddings + deduplicador
geocoding.py Geocoder ABC (Null / Static / Nominatim)
pipeline.py run_items() + enrich_from_instagram() + purge_stale_events()
api/ FastAPI (nearby / range / detail / admin/status)
cli.py ingest / enrich / purge / status / serve
airflow/dags/ DAG de ejemplo (Airflow opcional)
db/schema.sql DDL PostGIS (producción)
tests/ fixtures + suite offline
CLAUDE.md contexto para sesiones de Claude Code