feat(dav,obsidian): grupo dav completo (CardDAV/CalDAV client + split vcf/ics + import pipelines) + build_obsidian_graph + dav_list_calendars
Funciones reutilizables creadas esta sesion para el sistema self-hosted de contactos/calendario (Xandikos) y la app osint_web: - grupo dav (infra): split_vcards, split_vevents_to_vcalendars, extract_or_make_uid, carddav_put_vcard, caldav_put_event, dav_list_resources, dav_get_resource, dav_list_calendars - pipelines: import_vcf_to_carddav, import_ics_to_caldav - obsidian: build_obsidian_graph (grafo agregado del vault)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
"""Divide un .ics (un VCALENDAR con N VEVENT) en N VCALENDARs independientes.
|
||||
|
||||
Funcion pura: sin I/O, sin estado, determinista. Cada salida es un VCALENDAR
|
||||
completo y autonomo con un unico VEVENT, listo para subir como recurso CalDAV
|
||||
individual. Las VTIMEZONE del original se incluyen en cada salida (un VEVENT
|
||||
puede referenciar un TZID definido en el VCALENDAR padre). Solo usa stdlib (re).
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
_VEVENT_RE = re.compile(r"BEGIN:VEVENT.*?END:VEVENT", re.DOTALL)
|
||||
_VTIMEZONE_RE = re.compile(r"BEGIN:VTIMEZONE.*?END:VTIMEZONE", re.DOTALL)
|
||||
|
||||
_DEFAULT_PRODID = "-//xandikos-migracion//google-export//EN"
|
||||
|
||||
|
||||
def _to_crlf(text: str) -> str:
|
||||
"""Normaliza saltos de linea a CRLF (RFC 5545)."""
|
||||
return text.strip().replace("\r\n", "\n").replace("\n", "\r\n")
|
||||
|
||||
|
||||
def split_vevents_to_vcalendars(ics_text: str, prodid: str = _DEFAULT_PRODID) -> list:
|
||||
"""Divide un VCALENDAR con N VEVENT en N VCALENDARs independientes.
|
||||
|
||||
Args:
|
||||
ics_text: contenido completo del .ics (un VCALENDAR con uno o varios
|
||||
VEVENT). Tolera LF o CRLF.
|
||||
prodid: valor del campo PRODID a usar en el header de cada VCALENDAR de
|
||||
salida. Default: identifica la migracion a Xandikos.
|
||||
|
||||
Returns:
|
||||
Lista de strings. Cada elemento es un VCALENDAR completo y autonomo
|
||||
('BEGIN:VCALENDAR'..'END:VCALENDAR' terminado en CRLF) con header
|
||||
VERSION/PRODID/CALSCALE, las VTIMEZONE del original (si las habia) y un
|
||||
unico VEVENT. Lista vacia si no hay ningun VEVENT.
|
||||
"""
|
||||
if not ics_text:
|
||||
return []
|
||||
events = _VEVENT_RE.findall(ics_text)
|
||||
timezones = _VTIMEZONE_RE.findall(ics_text)
|
||||
tz_block = ""
|
||||
for tz in timezones:
|
||||
tz_block += _to_crlf(tz) + "\r\n"
|
||||
|
||||
header = (
|
||||
"BEGIN:VCALENDAR\r\n"
|
||||
"VERSION:2.0\r\n"
|
||||
"PRODID:%s\r\n"
|
||||
"CALSCALE:GREGORIAN\r\n" % prodid
|
||||
)
|
||||
|
||||
out = []
|
||||
for ev in events:
|
||||
body = header + tz_block + _to_crlf(ev) + "\r\nEND:VCALENDAR\r\n"
|
||||
out.append(body)
|
||||
return out
|
||||
Reference in New Issue
Block a user