---
name: import_ics_to_caldav
kind: pipeline
lang: py
domain: pipelines
version: "1.0.0"
purity: impure
signature: "def import_ics_to_caldav(ics_path: str, base_url: str, username: str, password: str, collection_path: str, *, timeout_s: float = 20.0, verify_tls: bool = True, uid_prefix: str = 'goog-') -> dict"
description: "Pipeline que importa un archivo .ics completo a una coleccion CalDAV. Lee el .ics del disco, parte el VCALENDAR en N VCALENDARs independientes con un VEVENT cada uno (split_vevents_to_vcalendars, replicando las VTIMEZONE), por cada uno extrae o sintetiza el UID (extract_or_make_uid) y lo sube por HTTP PUT (caldav_put_event). Devuelve {ok, fail, total, errors}. Idempotente por UID: re-importar el mismo .ics sobrescribe en vez de duplicar. Formaliza el heredoc ad-hoc usado para migrar 98 eventos de Google Calendar a Xandikos. Solo stdlib."
tags: [dav, caldav, ical, ics, vevent, import, calendar, migration, pipelines]
uses_functions: [split_vevents_to_vcalendars_py_infra, extract_or_make_uid_py_infra, caldav_put_event_py_infra]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [os, sys, json]
params:
  - name: ics_path
    desc: "ruta en disco del archivo .ics a importar (export de Google Calendar: un VCALENDAR con N VEVENT)."
  - name: base_url
    desc: "URL base del servidor DAV (p.ej. 'https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com')."
  - name: username
    desc: "usuario para HTTP Basic auth (p.ej. 'enmanuel')."
  - name: password
    desc: "contrasena para HTTP Basic auth. Resolver desde pass con pass_get_secret, nunca hardcodear."
  - name: collection_path
    desc: "ruta de la coleccion CalDAV destino (p.ej. '/enmanuel/calendars/calendar/')."
  - name: timeout_s
    desc: "timeout por PUT individual en segundos. Default 20.0."
  - name: verify_tls
    desc: "si True (default) verifica el certificado TLS. No desactivar salvo entorno de prueba."
  - name: uid_prefix
    desc: "prefijo del UID sintetico para eventos sin UID. Default 'goog-'."
output: "dict: {ok:int, fail:int, total:int, errors:[{uid, error}, ...]}. ok=eventos subidos con exito, fail=eventos que fallaron, total=VEVENT en el .ics, errors=detalle de los fallos (uid + mensaje, sin datos sensibles)."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/pipelines/import_ics_to_caldav.py"
---

## Ejemplo

```python
import sys
sys.path.insert(0, "python/functions")
from infra.pass_get_secret import pass_get_secret
from pipelines.import_ics_to_caldav import import_ics_to_caldav

pw = pass_get_secret("dav/xandikos-enmanuel")["value"]  # NO logear

summary = import_ics_to_caldav(
    ics_path="/home/enmanuel/Descargas/calendar.ics",
    base_url="https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com",
    username="enmanuel",
    password=pw,
    collection_path="/enmanuel/calendars/calendar/",
)
print(summary["ok"], summary["fail"], summary["total"])  # 98 0 98
```

Desde la CLI del registry (resuelve la pass tu mismo y pasala como arg):

```bash
PW=$(pass show dav/xandikos-enmanuel | head -n1)
./fn run import_ics_to_caldav /home/enmanuel/Descargas/calendar.ics \
  https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com \
  enmanuel "$PW" /enmanuel/calendars/calendar/
```

## Cuando usarla

Cuando tienes un `.ics` exportado (Google Calendar, otro CalDAV) con todos los
eventos en un unico VCALENDAR y quieres volcarlo entero a Xandikos en una sola
llamada. Reemplaza el flujo ad-hoc hecho a mano en la migracion. Re-ejecutable:
por idempotencia de UID, correrlo dos veces no duplica eventos.

## Gotchas

- Solo importa VEVENT. Si el .ics trae VTODO o VJOURNAL, esos componentes se
  ignoran (split_vevents_to_vcalendars solo extrae VEVENT).
- Las VTIMEZONE del original se replican en CADA evento subido (conservador):
  garantiza que cualquier TZID referenciado este definido.
- Escritura remota masiva secuencial: una request por evento. Cada PUT respeta
  `timeout_s`.
- Password por HTTP Basic sobre TLS; leela de `pass`, no la hardcodees ni la
  dejes en el historial del shell sin cuidado.
- `errors` lista solo uid + mensaje, nunca el contenido del evento.
