763e06c127
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
169 lines
6.0 KiB
Python
169 lines
6.0 KiB
Python
"""Pipeline: anade UN evento al calendario CalDAV de Enmanuel en una llamada.
|
|
|
|
Compone funciones del registry:
|
|
- build_vevent (core): compone el dict de evento -> texto VCALENDAR.
|
|
- extract_or_make_uid (infra): resuelve/sintetiza el UID si no se da.
|
|
- pass_get_secret (infra): resuelve la contrasena DAV desde `pass`.
|
|
- caldav_put_event (infra): hace el HTTP PUT a la coleccion CalDAV.
|
|
|
|
Impuro (red + lectura de secreto via subproceso). La contrasena NUNCA se logea
|
|
ni aparece en el resultado. Solo stdlib + funciones del registry.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
|
from core.build_vevent import build_vevent
|
|
from infra.extract_or_make_uid import extract_or_make_uid
|
|
from infra.pass_get_secret import pass_get_secret
|
|
from infra.caldav_put_event import caldav_put_event
|
|
|
|
DEFAULT_BASE_URL = "https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com"
|
|
DEFAULT_USERNAME = "enmanuel"
|
|
DEFAULT_COLLECTION = "/enmanuel/calendars/calendar/"
|
|
|
|
|
|
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:
|
|
"""Anade un evento al calendario CalDAV de Enmanuel en un solo paso.
|
|
|
|
Args:
|
|
summary: titulo del evento (-> SUMMARY). Obligatorio.
|
|
start: fecha/hora de inicio (p.ej. '2026-06-20T17:00'). Obligatorio.
|
|
end: fecha/hora de fin. Si vacio y no es all_day, build_vevent deriva +1h.
|
|
location: lugar del evento (-> LOCATION).
|
|
description: descripcion (-> DESCRIPTION).
|
|
all_day: si True, evento de dia completo (DTSTART;VALUE=DATE).
|
|
rrule: regla de recurrencia (p.ej. 'FREQ=WEEKLY;BYDAY=MO').
|
|
alarm_minutes: si > 0, anade un recordatorio N minutos antes (VALARM).
|
|
uid: UID explicito del evento. Si vacio, se sintetiza determinista a
|
|
partir del VCALENDAR generado (idempotente: re-subir sobrescribe).
|
|
base_url: URL base del servidor DAV. Default = Xandikos de Enmanuel.
|
|
username: usuario para HTTP Basic auth. Default 'enmanuel'.
|
|
collection_path: ruta de la coleccion CalDAV destino.
|
|
secret_path: ruta del secreto en `pass` con la contrasena DAV.
|
|
timeout_s: timeout del PUT en segundos.
|
|
verify_tls: si True (default) verifica el certificado TLS.
|
|
|
|
Returns:
|
|
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.
|
|
"""
|
|
event = {
|
|
"summary": summary,
|
|
"start": start,
|
|
"end": end or None,
|
|
"location": location or None,
|
|
"description": description or None,
|
|
"all_day": all_day,
|
|
"rrule": rrule or None,
|
|
"alarm_minutes": alarm_minutes or None,
|
|
"uid": uid or None,
|
|
}
|
|
|
|
try:
|
|
vcalendar = build_vevent(event)
|
|
except ValueError as e:
|
|
return {"status": "error", "error": str(e), "uid": None, "http_status": None}
|
|
|
|
# UID definitivo: el explicito si vino, o el (sintetico) del VCALENDAR.
|
|
final_uid = uid.strip() if uid else extract_or_make_uid(vcalendar, prefix="evt-")
|
|
|
|
secret = pass_get_secret(secret_path)
|
|
if secret.get("status") != "ok":
|
|
return {
|
|
"status": "error",
|
|
"error": "pass: %s" % secret.get("error", "unknown"),
|
|
"uid": final_uid,
|
|
"http_status": None,
|
|
}
|
|
password = secret["value"]
|
|
|
|
res = caldav_put_event(
|
|
base_url,
|
|
username,
|
|
password,
|
|
collection_path,
|
|
final_uid,
|
|
vcalendar,
|
|
timeout_s=timeout_s,
|
|
verify_tls=verify_tls,
|
|
)
|
|
# Reusa el dict de caldav_put_event y le anade el uid usado.
|
|
out = dict(res)
|
|
out["uid"] = final_uid
|
|
return out
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
import json
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Anade un evento al calendario CalDAV de Enmanuel."
|
|
)
|
|
parser.add_argument("--summary", required=True, help="Titulo del evento.")
|
|
parser.add_argument(
|
|
"--start", required=True, help="Inicio, p.ej. 2026-06-20T17:00."
|
|
)
|
|
parser.add_argument("--end", default="", help="Fin, p.ej. 2026-06-20T18:00.")
|
|
parser.add_argument("--location", default="", help="Lugar.")
|
|
parser.add_argument("--description", default="", help="Descripcion.")
|
|
parser.add_argument(
|
|
"--all-day", action="store_true", help="Evento de dia completo."
|
|
)
|
|
parser.add_argument(
|
|
"--rrule", default="", help="Recurrencia, p.ej. FREQ=WEEKLY;BYDAY=MO."
|
|
)
|
|
parser.add_argument(
|
|
"--alarm-minutes", type=int, default=0, help="Recordatorio N min antes."
|
|
)
|
|
parser.add_argument("--uid", default="", help="UID explicito (opcional).")
|
|
parser.add_argument("--base-url", default=DEFAULT_BASE_URL)
|
|
parser.add_argument("--username", default=DEFAULT_USERNAME)
|
|
parser.add_argument("--collection-path", default=DEFAULT_COLLECTION)
|
|
parser.add_argument("--secret-path", default="dav/xandikos-enmanuel")
|
|
parser.add_argument("--timeout-s", type=float, default=20.0)
|
|
parser.add_argument(
|
|
"--no-verify-tls", action="store_true", help="Desactiva verificacion TLS."
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
result = add_event_dav(
|
|
args.summary,
|
|
args.start,
|
|
args.end,
|
|
location=args.location,
|
|
description=args.description,
|
|
all_day=args.all_day,
|
|
rrule=args.rrule,
|
|
alarm_minutes=args.alarm_minutes,
|
|
uid=args.uid,
|
|
base_url=args.base_url,
|
|
username=args.username,
|
|
collection_path=args.collection_path,
|
|
secret_path=args.secret_path,
|
|
timeout_s=args.timeout_s,
|
|
verify_tls=not args.no_verify_tls,
|
|
)
|
|
print(json.dumps(result))
|