davinci/davinci/caldav/models.py
2020-11-11 17:02:11 +01:00

118 lines
3.8 KiB
Python

from typing import List, Tuple, Optional
import caldav
from caldav import Calendar, vcal
from django.db import models
# Create your models here.
from ics import Event
class CalDAVServer(models.Model):
name = models.CharField(max_length=191, verbose_name="Server name", unique=True)
url = models.URLField(verbose_name="CalDAV server URL")
username = models.CharField(max_length=191, verbose_name="CalDAV username")
password = models.CharField(max_length=191, verbose_name="CalDAV password")
ssl_verify = models.BooleanField(verbose_name="Verify SSL", default=True)
class Meta:
verbose_name = "CalDAV Server"
verbose_name_plural = "CalDAV Servers"
def __str__(self):
return self.name
def get_client(self):
return caldav.DAVClient(url=self.url, username=self.username, password=self.password,
ssl_verify_cert=self.ssl_verify)
def test_connection(self) -> Tuple[bool, str]:
try:
self.get_client().principal().calendars()
return True, ""
except Exception as e:
return False, str(e)
def get_calendars(self) -> List[caldav.Calendar]:
return self.get_client().principal().calendars()
def get_calendar_name(self, url):
cals = self.get_client().principal().calendars()
for cal in cals:
if cal.url == url:
return cal.name
return None
class CalDAVCalendar(models.Model):
server = models.ForeignKey(to='CalDAVServer', on_delete=models.CASCADE, verbose_name="CalDAV server")
calendar_url = models.CharField(max_length=191, verbose_name="Calendar URL")
name = models.CharField(max_length=191, verbose_name="Calendar Name")
class Meta:
verbose_name = "CalDAV Calendar"
verbose_name_plural = "CalDAV Calendars"
unique_together = ['server', 'calendar_url']
def __str__(self):
return f"{self.name} on {self.server}"
def get_calendar(self) -> Optional[Calendar]:
cals = self.server.get_client().principal().calendars()
for cal in cals:
if cal.url == self.calendar_url:
return cal
return None
def upload_events(self, events: List[Event], purge: bool = True) -> Tuple[int, int, int]:
"""
Upload events to this calendar. If purge is True, all old events will be removed.
:param events: The events to upload
:type events: List[Event]
:param purge: If old events in the calendar should be purged or not.
:type purge: bool
:return Amount of events added, updated, and deleted
:rtype Tuple[int, int, int]
"""
calendar = self.get_calendar()
if calendar is None:
raise ValueError(f"No calendar found on url '{self.calendar_url}'")
# Keep track of which events were already in the calendar
purge_uids = []
if purge:
purge_uids = [e.instance.vevent.uid.value for e in calendar.events()]
updated = 0
added = 0
# Import events
for event in events:
ev = f"BEGIN:VCALENDAR\n" \
f"VERSION:2.0\n" \
f"CALSCALE:GREGORIAN\n" \
f"PRODID:DAVinci.ical_utils\n" \
f"{event}\n" \
f"END:VCALENDAR"
if event.uid in purge_uids:
# Need to overwrite
cevent = calendar.event_by_uid(event.uid)
cevent.data = vcal.fix(ev)
cevent.save()
purge_uids.remove(event.uid)
updated += 1
else:
calendar.save_event(ev)
added += 1
# Purge old events
deleted = len(purge_uids)
for uid in purge_uids:
calendar.event_by_uid(uid).delete()
return added, updated, deleted