---
name: add_event_dav
kind: pipeline
lang: py
domain: pipelines
version: "1.0.0"
purity: impure
signature: "def add_event_dav(summary: str, start: str, end: str = '', *, location: str = '', description: str = '', all_day: bool = False, rrule: str = '', alarm_minutes: int = 0, uid: str = '', base_url: str = DEFAULT_BASE_URL, username: str = DEFAULT_USERNAME, collection_path: str = DEFAULT_COLLECTION, secret_path: str = 'dav/xandikos-enmanuel', timeout_s: float = 20.0, verify_tls: bool = True) -> dict"
description: "One-shot que anade UN evento al calendario CalDAV de Enmanuel (Xandikos self-hosted) en una sola llamada. Compone build_vevent (componer el VCALENDAR), extract_or_make_uid (UID si falta), pass_get_secret (resolver la contrasena DAV desde pass) y caldav_put_event (HTTP PUT). Impuro: escritura remota real. Idempotente por UID. La contrasena nunca se logea ni aparece en el resultado. Defaults apuntan al calendario de Enmanuel."
tags: [dav, caldav, calendar, event, pipelines]
params:
  - name: summary
    desc: "titulo del evento (-> SUMMARY). Obligatorio."
  - name: start
    desc: "fecha/hora de inicio, p.ej. '2026-06-20T17:00' (naive local), con sufijo 'Z' para UTC, o '2026-06-20' para all_day. Obligatorio."
  - name: end
    desc: "fecha/hora de fin. Si vacio y no es all_day, se deriva +1h del start; si all_day, el dia siguiente."
  - name: location
    desc: "lugar del evento (-> LOCATION)."
  - name: description
    desc: "descripcion del evento (-> DESCRIPTION)."
  - name: all_day
    desc: "bool. Si True, evento de dia completo (DTSTART;VALUE=DATE)."
  - name: rrule
    desc: "regla de recurrencia RRULE, p.ej. 'FREQ=WEEKLY;BYDAY=MO'."
  - name: alarm_minutes
    desc: "int. Si > 0, anade un recordatorio (VALARM display) N minutos antes."
  - name: uid
    desc: "UID explicito del evento. Si vacio, se sintetiza determinista del VCALENDAR (re-subir el mismo evento sobrescribe = idempotente)."
  - name: base_url
    desc: "URL base del servidor DAV. Default = Xandikos de Enmanuel."
  - name: username
    desc: "usuario para HTTP Basic auth. Default 'enmanuel'."
  - name: collection_path
    desc: "ruta de la coleccion CalDAV destino. Default '/enmanuel/calendars/calendar/'."
  - name: secret_path
    desc: "ruta del secreto en pass con la contrasena DAV. Default 'dav/xandikos-enmanuel'."
  - name: timeout_s
    desc: "timeout del PUT en segundos. Default 20.0."
  - name: verify_tls
    desc: "si True (default) verifica el certificado TLS. No desactivar salvo entornos de prueba."
output: "dict. En exito: {status: 'ok', http_status: int, uid: str, url: str}. En error (sin lanzar): {status: 'error', error: str, uid: str|None, http_status: int|None}. La contrasena nunca aparece en el resultado."
uses_functions: [build_vevent_py_core, extract_or_make_uid_py_infra, pass_get_secret_py_infra, caldav_put_event_py_infra]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [os, sys, argparse, json]
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/pipelines/add_event_dav.py"
---

## Ejemplo

```bash
# Anadir un evento con hora, lugar y recordatorio (UID sintetico determinista):
./fn run add_event_dav --summary "Cita dentista" --start 2026-06-20T17:00 \
    --end 2026-06-20T18:00 --location "Clinica" --alarm-minutes 30
# {"status": "ok", "http_status": 201, "uid": "evt-<md5>", "url": "https://dav-.../enmanuel/calendars/calendar/evt-<md5>.ics"}

# Evento de dia completo recurrente:
./fn run add_event_dav --summary "Cumpleanos" --start 2026-06-20 --all-day \
    --rrule "FREQ=YEARLY"
```

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from pipelines.add_event_dav import add_event_dav

res = add_event_dav(
    "Reunion equipo", "2026-06-22T09:00", "2026-06-22T10:00",
    location="Sala A", description="Sprint review", alarm_minutes=15,
)
print(res["status"], res["uid"])  # 'ok' evt-...
```

## Cuando usarla

Cuando quieras anadir un evento al calendario de Enmanuel sin orquestar a mano
los pasos (componer el iCal, resolver el secreto, hacer el PUT). Es la operacion
one-shot del grupo `dav` para CalDAV. Para subir un `.ics` entero con N eventos
usa `import_ics_to_caldav_py_pipelines`; para un solo evento parametrizado, esta.
Pasa `uid` explicito si quieres controlar/actualizar un evento concreto; dejalo
vacio para crear uno nuevo con UID derivado del contenido.

## Gotchas

- **Accion con efecto real (impura)**: hace un HTTP PUT que escribe en el
  calendario remoto de Enmanuel. No es un dry-run. Verifica `start`/`end` antes
  de lanzar; un PUT con datos erroneos crea el evento igualmente.
- **Idempotente por UID**: el nombre del recurso es `<uid>.ics`. Re-subir el
  mismo UID SOBRESCRIBE el evento existente (no duplica). Con `uid` vacio el UID
  es determinista (md5 de summary+start): re-lanzar el mismo evento exacto pisa
  el anterior; cambiar summary o start crea un recurso nuevo.
- **Secreto desde `pass`, nunca hardcode**: la contrasena se resuelve con
  `pass_get_secret('dav/xandikos-enmanuel')` y NUNCA se logea ni se incluye en el
  dict de retorno. Si `pass` no esta instalado o la entry no existe, devuelve
  `{status:'error', error:'pass: ...'}` sin lanzar y sin hacer el PUT.
- **`verify_tls=True` por defecto**: no uses `--no-verify-tls` salvo en pruebas
  controladas. El servidor de Enmanuel tiene certificado valido.
- **ValueError de build_vevent**: si falta `summary` o `start`, el pipeline lo
  captura y devuelve `{status:'error'}` (no propaga la excepcion).
