def update_calendar(client: DAVClient, calendar_name: str, event: Event) -> None: """ Create or update an event in a calendar. """ principal = client.principal() try: calendar = principal.calendar(name=calendar_name) except caldav.error.NotFoundError: calendar = principal.make_calendar(name=calendar_name) # delete existing event, identified by URL if "url" in event: # pad search, otherwise it will return nothing existing_events = calendar.date_search( start=event["dtstart"].dt.date(), end=event["dtend"].dt.date() + timedelta(days=1), expand=False, ) for existing_event in existing_events: vevent = existing_event.vobject_instance.vevent if hasattr(vevent, "url") and vevent.url.value == event["url"]: _logger.info("Found existing event, deleting") existing_event.delete() # wrap in BEGIN:VCALENDAR component = Calendar() component.add("prodid", "-//Nefelibata Corp.//CalDAV Client//EN") component.add("version", "2.0") component.add("x-wr-calname", calendar_name) component.add_component(event) _logger.info("Creating event") calendar.save_event(component.to_ical())
def build(): try: cal = Calendar() dav = DAVClient(url) for calendar in dav.principal().calendars(): for event in calendar.events(): evcal = Calendar.from_ical(event.data) for e in evcal.walk(): if isinstance(e, Event): cal.add_component(e) return cal.to_ical().decode('utf-8') except OSError as e: print("caldav:{}: {}".format(name, e)) return None
def main(): url = getenv(URL_ENV) username = getenv(USER_ENV) password = getenv(PASS_ENV) if url is None: url = input('URL: ') if username is None: username = input('Username: '******'Password: '******'Invalid URL or credentials.') exit(1) start = getenv(START_ENV) end = getenv(END_ENV) try: start = datetime.fromisoformat(start) end = datetime.fromisoformat(end) except ValueError: print(f'Invalid {START_ENV} or {END_ENV} value.') exit(1) if start is None: default_start = datetime(date.today().year, 1, 1) start = timeinput( f'Start time [{default_start.strftime("%Y-%m-%d %H:%M")}]: ', default_start) if end is None: default_end = datetime(date.today().year + 1, 1, 1) end = timeinput( f'End time [{default_end.strftime("%Y-%m-%d %H:%M")}]: ', default_end) for cal in principal.calendars(): for ev in cal.date_search(start=start, end=end, expand=True): # expand recurring events write_event(cal.name, ev) with open(join(calendar_folder(cal.name), CAL_NAME_FILE), 'w') as f: # for importing f.write(cal.name) print(f'Exported events from calendar \'{cal.name}\'.')
def main(): url = getenv(URL_ENV) username = getenv(USER_ENV) password = getenv(PASS_ENV) if url is None: url = input('URL: ') if username is None: username = input('Username: '******'Password: '******'Invalid URL or credentials.') exit(1) parent_folder = getcwd() for path in listdir(): if not (path.startswith(EXPORT_FOLDER_PREFIX) and isdir(path)): continue chdir(join(parent_folder, path)) try: with open(CAL_NAME_FILE, 'r') as f: cal_name = f.readline().strip() except FileNotFoundError: print( f'Found directory {path} which matches the export folder schema but does not contain a \ {CAL_NAME_FILE} file.') continue print(f'Importing calendar \'{cal_name}\'...') calendar = available_calendars.get(cal_name, principal.make_calendar(cal_name)) count = len(listdir()) for i, path in enumerate(listdir()): if path.endswith('.ics'): with open(path, 'r') as f: calendar.save_event(''.join(f.readlines())) print(f'\t{i}/{count}', end='\r')
def main(): """Command line tool to download from CalDAV to Remind""" parser = ArgumentParser(description='Command line tool to download from CalDAV to Remind') parser.add_argument('-d', '--delete', type=bool, default=False, help='Delete old events') parser.add_argument('-r', '--davurl', required=True, help='The URL of the calDAV server') parser.add_argument('-u', '--davuser', help='The username for the calDAV server') parser.add_argument('-p', '--davpass', help='The password for the calDAV server', default=None) parser.add_argument('remfile', nargs='?', default=expanduser('~/.reminders'), help='The Remind file to process (default: ~/.reminders)') args = parser.parse_args() rem = Remind(args.remfile) ldict = set(rem.get_uids()) try: (user, _, passwd) = netrc().authenticators(urlparse(args.davurl).netloc) except (IOError, TypeError): if not args.davuser: print "dav2rem: error: argument -u/--davuser is required" return 2 user = args.davuser if args.davpass: passwd = args.davpass else: passwd = getpass() client = DAVClient(args.davurl, username=user, password=passwd) principal = client.principal() calendar = principal.calendars()[0] rdict = {splitext(basename(event.canonical_url))[0].replace('%40', '@'): event for event in calendar.events()} if args.delete: local = ldict - rdict.viewkeys() for uid in local: rem.remove(uid) remote = rdict.viewkeys() - ldict for uid in remote: vevent = rdict[uid] vevent.load() rem.append(vevent.data)
class Client: def __init__(self, calendar=None, url=None): if not config.CALENDAR_ACTIVE: return self.url = url if url is not None else config.CALENDAR_URL self.client = DAVClient(self.url) self.principal = None for _ in range(config.CALENDAR_MAX_REQUESTS): try: self.principal = self.client.principal() break except Exception as exc: print("Got exception {} from caldav, retrying".format( str(exc))) if self.principal is None: raise CalendarException( "Got {} CalDAV-error from the CalDAV server.".format( config.CALENDAR_MAX_REQUESTS)) if calendar is not None: self.calendar = self.get_calendar(calendar) else: self.calendar = calendar def get_calendars(self): if not config.CALENDAR_ACTIVE: return for _ in range(config.CALENDAR_MAX_REQUESTS): try: return [ calendar.name for calendar in self.principal.calendars() ] except Exception as exc: print("Got exception {} from caldav, retrying".format( str(exc))) raise CalendarException( "Got {} CalDAV Errors from the CalDAV server.".format( config.CALENDAR_MAX_REQUESTS)) def get_calendar(self, calendar_name): candidates = self.principal.calendars() for calendar in candidates: if calendar.name == calendar_name: return calendar raise CalendarException("No calendar named {}.".format(calendar_name)) def set_event_at(self, begin, name, description): if not config.CALENDAR_ACTIVE: return candidates = [ Event.from_raw_event(raw_event) for raw_event in self.calendar.date_search( begin, begin + timedelta(hours=1)) ] candidates = [event for event in candidates if event.name == name] event = None if len(candidates) == 0: event = Event( None, name, description, begin, begin + timedelta(hours=config.CALENDAR_DEFAULT_DURATION)) vevent = self.calendar.add_event(event.to_vcal()) event.vevent = vevent else: event = candidates[0] event.set_description(description) event.vevent.save()
def main(): """Command line tool to upload a Remind file to CalDAV""" parser = ArgumentParser(description="Command line tool to upload a Remind file to CalDAV") parser.add_argument( "-z", "--zone", default="Europe/Berlin", help="Timezone of Remind file (default: Europe/Berlin)" ) parser.add_argument( "-s", "--startdate", type=lambda s: parse(s).date(), default=date.today() - timedelta(weeks=12), help="Start offset for remind call (default: -12 weeks)", ) parser.add_argument( "-m", "--month", type=int, default=15, help="Number of month to generate calendar beginning wit stadtdate (default: 15)", ) parser.add_argument("-d", "--delete", action="store_true", help="Delete old events") parser.add_argument("-r", "--davurl", required=True, help="The URL of the calDAV server") parser.add_argument("-u", "--davuser", help="The username for the calDAV server") parser.add_argument("-p", "--davpass", help="The password for the calDAV server", default=None) parser.add_argument( "infile", nargs="?", default=expanduser("~/.reminders"), help="The Remind file to process (default: ~/.reminders)", ) parser.add_argument( "-o", "--old", default=None, help="The old reference Remind file (entries not in the current one will be deleted from dav)", ) args = parser.parse_args() zone = gettz(args.zone) # Manually set timezone name to generate correct ical files # (python-vobject tests for the zone attribute) zone.zone = args.zone if args.infile == "-": remind = Remind(args.infile, zone, args.startdate, args.month) vobject = remind.stdin_to_vobject(stdin.read().decode("utf-8")) else: remind = Remind(args.infile, zone, args.startdate, args.month) vobject = remind.to_vobject() if hasattr(vobject, "vevent_list"): ldict = {event.uid.value: event for event in vobject.vevent_list} else: ldict = {} try: (user, _, passwd) = netrc().authenticators(urlparse(args.davurl).netloc) except (IOError, TypeError): if not args.davuser: print "rem2dav: error: argument -u/--davuser is required" return 2 user = args.davuser if args.davpass: passwd = args.davpass else: passwd = getpass() client = DAVClient(args.davurl, username=user, password=passwd) principal = client.principal() calendar = principal.calendars()[0] rdict = {splitext(basename(event.canonical_url))[0].replace("%40", "@"): event for event in calendar.events()} if args.old: old = Remind(args.old, zone, args.startdate, args.month) old_vobject = old.to_vobject() if hasattr(old_vobject, "vevent_list"): odict = {event.uid.value: event for event in old_vobject.vevent_list} intersect = rdict.viewkeys() & odict.viewkeys() rdict = {key: rdict[key] for key in intersect} else: rdict = {} local = ldict.viewkeys() - rdict.viewkeys() for uid in local: ncal = iCalendar() ncal.add(ldict[uid]) calendar.add_event(ncal.serialize()) if args.delete or args.old: remote = rdict.viewkeys() - ldict.viewkeys() for uid in remote: rdict[uid].delete()