def test_extract_vtodo(self): event1 = Calendar.from_ical(EXAMPLE_VEVENT1) todo1 = Calendar.from_ical(EXAMPLE_VTODO1) self.assertEqual([todo1.subcomponents[0]], list( filters.extract_vtodos([('foo.ics', event1), ('bar.ics', todo1)])))
def create_or_update_calendar_item(collection_url, component_name, uid): """Create or update a calendar item by UID. :param collection_url: URL of collection to search in :param component_name: Component name (VTODO, VEVENT, etc) :param uid: UID value :return: (href, etag, old, new), where old and new are Calendar items New items will have the first three elements set to None """ try: (href, etag, old) = get_by_uid(collection_url, component_name, uid) except KeyError: etag = None props = {'UID': uid} todo = component_factory[component_name](**props) old = None href = None new = Calendar() new.add_component(todo) else: new = Calendar.from_ical(old.to_ical()) for component in old.subcomponents: if component.name == component_name: todo = component break return (old, new, href, etag, todo)
def test_repr(self): """Test correct class representation. """ from icalendar.cal import Component, Calendar, Event component = Component() component['key1'] = 'value1' self.assertTrue( re.match(r"Component\({u?'KEY1': 'value1'}\)", str(component))) calendar = Calendar() calendar['key1'] = 'value1' self.assertTrue( re.match(r"VCALENDAR\({u?'KEY1': 'value1'}\)", str(calendar))) event = Event() event['key1'] = 'value1' self.assertTrue(re.match(r"VEVENT\({u?'KEY1': 'value1'}\)", str(event))) # Representation of nested Components nested = Component(key1='VALUE1') nested.add_component(component) calendar.add_component(event) nested.add_component(calendar) self.assertTrue( re.match( r"Component\({u?'KEY1': 'VALUE1'}, Component\({u?'KEY1': 'value1'}\), VCALENDAR\({u?'KEY1': 'value1'}, VEVENT\({u?'KEY1': 'value1'}\)\)\)", # nopep8 str(nested)))
async def report(self, environ, body, resources_by_hrefs, properties, base_href, base_resource, depth): requested = None for el in body: if el.tag == '{urn:ietf:params:xml:ns:caldav}time-range': requested = el else: raise AssertionError("unexpected XML element") tz = get_calendar_timezone(base_resource) def tzify(dt): return as_tz_aware_ts(dt, tz).astimezone(pytz.utc) (start, end) = _parse_time_range(requested) assert start.tzinfo assert end.tzinfo ret = ICalendar() ret['VERSION'] = '2.0' ret['PRODID'] = PRODID fb = FreeBusy() fb['DTSTAMP'] = vDDDTypes(tzify(datetime.datetime.now())) fb['DTSTART'] = vDDDTypes(start) fb['DTEND'] = vDDDTypes(end) fb['FREEBUSY'] = [item async for item in iter_freebusy( webdav.traverse_resource(base_resource, base_href, depth), start, end, tzify)] ret.add_component(fb) return webdav.Response(status='200 OK', body=[ret.to_ical()])
def extractEqual(self, incal_str, outcal_str): incal = ICalendar.from_ical(incal_str) expected_outcal = ICalendar.from_ical(outcal_str) outcal = ICalendar() caldav.extract_from_calendar(incal, outcal, self.requested) self.assertEqual(expected_outcal.to_ical().decode(), outcal.to_ical().decode(), ET.tostring(self.requested))
def extractEqual(self, incal_str, outcal_str): incal = ICalendar.from_ical(incal_str) expected_outcal = ICalendar.from_ical(outcal_str) outcal = ICalendar() outcal = caldav.extract_from_calendar(incal, self.requested) self.maxDiff = None self.assertMultiLineEqual( expected_outcal.to_ical().decode(), outcal.to_ical().decode(), ET.tostring(self.requested), )
def get_value_ext(self, base_href, resource, el, environ, requested): if len(requested) == 0: serialized_cal = b''.join(resource.get_body()) else: c = ICalendar() calendar = calendar_from_resource(resource) if calendar is None: raise KeyError extract_from_calendar(calendar, c, requested) serialized_cal = c.to_ical() # TODO(jelmer): Don't hardcode encoding el.text = serialized_cal.decode('utf-8')
def ls(bdt, pve=7, cmt=False, fp=os.path.expanduser('~/.cal.dat'), comm=False, exp=True, eli=0, evo=False): entries = Entries(bdt=bdt, fp=fp, comm=comm, exp=exp) pvs = "" if pve > 0: if isinstance(fp, StringIO): fp.seek(0) pvd = bdt[0] + dt.timedelta(days=-(bdt[0].day - 2) + 30) pvs = repr(Entries(bdt=(None, pvd), fp=fp, exp=False)) if pvs: pvs = "%s%s" % (pvd.strftime("\n %B --\n"), '\n'.join( pvs.split('\n')[:pve])) else: pvs = "" if evo: return repr(entries) + pvs cal = Calendar(bdt[0], bdt if len(bdt) > 1 else (bdt[0], ), entries) if eli: entries.limit(eli) return nextTo(cal, repr(entries) + pvs)
def getEvent(self, title): cal = Calendar.from_ical(self.getCalendarText()) countdownEvent = None for component in cal.subcomponents: if "SUMMARY" in component.keys() and component["SUMMARY"] == title: countdownEvent = component return countdownEvent
def calendar(self): if self._calendar is None: try: self._calendar = Calendar.from_ical(b''.join(self.content)) except ValueError: raise InvalidFileContents(self.content_type, self.content) return self._calendar
def get_by_uid(url, component, uid, depth='1'): uidprop = ET.Element('{urn:ietf:params:xml:ns:caldav}calendar-data') uidprop.set('name', 'UID') dataprop = ET.Element('{urn:ietf:params:xml:ns:caldav}calendar-data') ret = caldav.calendar_query( url, props=[uidprop, dataprop, '{DAV:}getetag'], depth=depth, filter=caldav.comp_filter( "VCALENDAR", caldav.comp_filter( component, caldav.prop_filter( "UID", caldav.text_match(text=uid, collation="i;octet"))))) for (href, status, propstat) in ret: if status == 'HTTP/1.1 404 Not Found': raise KeyError(uid) etag = None data = None for prop, prop_status in propstat: if prop.tag == '{urn:ietf:params:xml:ns:caldav}calendar-data': data = prop.text if prop.tag == '{DAV:}getetag': etag = prop.text assert data is not None, "data missing for %r" % href return (href, etag, Calendar.from_ical(data)) raise KeyError(uid)
def StripStamps(c): if c is None: return None c = Calendar.from_ical(c.to_ical()) for sc in c.subcomponents: if 'DTSTAMP' in sc: del sc['DTSTAMP'] return c.to_ical()
def getEvent(self, title): calendarText = self.__getCalendarText() cal = Calendar.from_ical(calendarText) countdownEvent = None for component in cal.subcomponents: if "SUMMARY" in component.keys() and component["SUMMARY"] == title: countdownEvent = component return countdownEvent
def generate_ics(showings, location): calendar = Calendar() caplocation = location.capitalize() calendar.add('prodid', '-//BFiCal %s Calendar//bfical.com//' % caplocation) calendar.add('version', '2.0') for showing in showings: if showing.master_location == location: calevent = CalEvent() if showing.ident: calevent.add('uid', '*****@*****.**' % showing.ident) else: calevent.add('uid', '*****@*****.**' % int(time.time())) calevent.add('summary', showing.parent().name) calevent.add('description', showing.parent().precis) calevent.add('location', '%s, BFI %s, London' % (showing.location, caplocation)) calevent.add('dtstart', showing.start.replace(tzinfo=GB_TZ).astimezone(pytz.utc)) calevent.add('dtend', showing.end.replace(tzinfo=GB_TZ).astimezone(pytz.utc)) calevent.add('url', showing.parent().src_url) calevent.add('sequence', int(time.time())) # TODO - fix #calevent.add('dtstamp', datetime.datetime.now()) calendar.add_component(calevent) return calendar
def events(): cal = Calendar.from_ical(requests.get('http://api.meetup.com/noisebridge/upcoming.ical').text) events = [] for event in cal.walk('vevent'): events.append({ 'start': event.decoded('dtstart'), 'title': event.decoded('summary') }) return jsonify(events=events)
def do_import(username, password, calendar, server, ics_url): base_url = CALDAVURL % (server, username, calendar) # fetch events from target cal target_fetch_url = '%s?export' % base_url r = requests.get(target_fetch_url, auth=(username, password)) r.raise_for_status() try: target_cal = Calendar.from_ical(r.text) except ValueError as e: print('Warning: Could not parse iCal (%s)' % target_fetch_url, file=sys.stderr) print(e, file=sys.stderr) return existing_uids = [e['UID'].to_ical() for e in target_cal.walk('VEVENT')] # fetch webcal sourceRequest = requests.get(ics_url) sourceRequest.encoding = 'utf-8' sourceContent = sourceRequest.text c = Calendar.from_ical(sourceContent) # import webcal imported_uids = [] for e in c.walk('VEVENT'): uid = bytes.decode(e['UID'].to_ical()) uid.replace('\'', '') uid.replace('/', 'slash') cal = Calendar() cal.add_component(e) r = requests.put( '%s/%s.ics' % (base_url, uid), data=cal.to_ical(), auth=(username, password), headers={'content-type': 'text/calendar; charset=UTF-8'}) if r.status_code == 500 and r'Sabre\VObject\Recur\NoInstancesException' in r.text: # ignore the NoInstancesException print('Warning: No valid instances: %s' % uid, file=sys.stdout) elif r.status_code == 201 or r.status_code == 204: print('Imported: %s (%d)' % (uid, r.status_code), file=sys.stdout) imported_uids.append(uid) else: r.raise_for_status() # remove events not in webcal for uid in existing_uids: if not uid in imported_uids: r = requests.delete('%s/%s.ics' % (base_url, uid), auth=(username, password)) if r.status_code == 204: print('Deleted %s' % uid, file=sys.stdout) # ignore 404 - not found (seems to be a manually created event) elif r.status_code == 404: pass else: r.raise_for_status()
def get_all_calendars(url, depth=None, filter=None): for (href, status, propstat) in caldav.calendar_query( url, ['{DAV:}getetag', '{urn:ietf:params:xml:ns:caldav}calendar-data'], filter): data = None for prop, prop_status in propstat: if prop.tag == '{urn:ietf:params:xml:ns:caldav}calendar-data': data = prop.text assert data is not None, "data missing for %r" % href yield href, Calendar.from_ical(data)
def test_repr(self): """Test correct class representation. """ from icalendar.cal import Component, Calendar, Event component = Component() component['key1'] = 'value1' self.assertTrue( re.match(r"Component\({u?'KEY1': u?'value1'}\)", str(component)) ) calendar = Calendar() calendar['key1'] = 'value1' self.assertTrue( re.match(r"VCALENDAR\({u?'KEY1': u?'value1'}\)", str(calendar)) ) event = Event() event['key1'] = 'value1' self.assertTrue( re.match(r"VEVENT\({u?'KEY1': u?'value1'}\)", str(event)) ) # Representation of nested Components nested = Component(key1='VALUE1') nested.add_component(component) calendar.add_component(event) nested.add_component(calendar) self.assertTrue( re.match( r"Component\({u?'KEY1': u?'VALUE1'}, " r"Component\({u?'KEY1': u?'value1'}\), " r"VCALENDAR\({u?'KEY1': u?'value1'}, " r"VEVENT\({u?'KEY1': u?'value1'}\)\)\)", str(nested) ) )
def do_import(username, password, calendar, server, ics_url): base_url = CALDAVURL % (server, username, calendar) # fetch events from target cal target_fetch_url = '%s?export' % base_url r = requests.get(target_fetch_url, auth=(username, password)) r.raise_for_status() try: target_cal = Calendar.from_ical(r.text) except ValueError as e: print('Warning: Could not parse iCal (%s)' % target_fetch_url, file=sys.stderr) print(e, file=sys.stderr) return existing_uids = [] for e in target_cal.walk('VEVENT'): uid = bytes.decode(e['UID'].to_ical()).replace('\'', '').replace('/', 'slash') existing_uids.append(uid) # fetch webcal sourceRequest = requests.get(ics_url) sourceRequest.encoding = 'utf-8' sourceContent = sourceRequest.text c = Calendar.from_ical(sourceContent) # import webcal imported_uids = [] for e in c.walk('VEVENT'): uid = bytes.decode(e['UID'].to_ical()) uid.replace('\'','') uid.replace('/','slash') cal = Calendar() cal.add_component(e) r = requests.put('%s/%s.ics' % (base_url, uid), data=cal.to_ical(), auth=(username, password), headers={'content-type':'text/calendar; charset=UTF-8'} ) if r.status_code == 500 and r'Sabre\VObject\Recur\NoInstancesException' in r.text: # ignore the NoInstancesException print('Warning: No valid instances: %s' % uid, file=sys.stdout) elif r.status_code == 201 or r.status_code == 204: print('Imported: %s (%d)' % (uid, r.status_code), file=sys.stdout) imported_uids.append(uid) else: r.raise_for_status() # remove events not in webcal for uid in existing_uids: if not uid in imported_uids: r = requests.delete('%s/%s.ics' % (base_url, uid), auth=(username, password)) if r.status_code == 204: print('Deleted %s' % uid, file=sys.stdout) # ignore 404 - not found (seems to be a manually created event) elif r.status_code == 404: pass else: r.raise_for_status()
def setUp(self): self.icalReader = ICalReader() self.calendarData = DBCalendarData() self.calendarData.ownerId = 'dummyOwner' self.calendarData.ownerType = STUDENT_OWNER_TYPE self.calendarData.accessPermissions = PUBLIC_PERMISSION self.calendarData.calendarData = Calendar().to_ical() self.dummyTask = DBTask() self.dummyTask.displayName = "dummyTaskDisplayName" self.dummyTask.name = "dummyTaskName" self.dummyTask.taskId = "dummyTaskId"
def gather_icalendars(dirs): """Find all the ics files in a directory, yield components. :param dirs: List of directories to browse :return: Iterator over components found """ for bp in dirs: for n in os.listdir(bp): p = os.path.join(bp, n) if not p.endswith(".ics"): continue yield Calendar.from_ical(open(p, 'r').read())
def do_import(username, password, calendar, server, ics_url): base_url = CALDAVURL % (server, username, calendar) # fetch events from target cal target_fetch_url = '%s?export' % base_url r = requests.get(target_fetch_url, auth=(username, password)) r.raise_for_status() try: target_cal = Calendar.from_ical(r.text) except ValueError as e: print('Warning: Could not parse iCal (%s)' % target_fetch_url, file=sys.stderr) print(e, file=sys.stderr) return existing_uids = [e['UID'].to_ical() for e in target_cal.walk('VEVENT')] # replace markers today = date.today() ics_url = ics_url.replace('###YEAR###', today.strftime("%Y")) ics_url = ics_url.replace('###YEAR+1###', date(today.year + 1, today.month, today.day).strftime("%Y")) print('Used ics_url: %s' % (ics_url)) # fetch webcal c = Calendar.from_ical(requests.get(ics_url).text) # import webcal imported_uids = [] for e in c.walk('VEVENT'): uid = e['UID'].to_ical() cal = Calendar() cal.add_component(e) r = requests.put('%s/%s.ics' % (base_url, uid), data=cal.to_ical(), auth=(username, password), headers={'content-type':'text/calendar; charset=UTF-8'} ) if r.status_code == 500 and 'Sabre\VObject\Recur\NoInstancesException' in r.text: # ignore the NoInstancesException print('Warning: No valid instances: %s' % uid, file=sys.stdout) elif r.status_code == 201 or r.status_code == 204: print('Imported: %s (%d)' % (uid, r.status_code), file=sys.stdout) imported_uids.append(uid) else: r.raise_for_status() # remove events not in webcal for uid in existing_uids: if not uid in imported_uids: r = requests.delete('%s/%s.ics' % (base_url, uid), auth=(username, password)) if r.status_code == 204: print('Deleted %s' % uid, file=sys.stdout) # ignore 404 - not found (seems to be a manually created event) elif r.status_code == 404: pass else: r.raise_for_status()
def do_import(username, password, calendar, server, ics_url, ics_username, ics_password): logging.info(' Working with calendar %s...' % calendar) base_url = CALDAVURL % (server, username, calendar) target_fetch_url = '%s?export' % base_url r = requests.get(target_fetch_url, auth=(username, password)) r.raise_for_status() try: target_cal = Calendar.from_ical(r.text) except ValueError as e: logging.error(' Warning: Could not parse iCal (%s)' % target_fetch_url) logging.error(e) return existing_uids = [bytes.decode(e['UID'].to_ical()).replace('\'', '').replace('/', 'slash') for e in target_cal.walk('VEVENT')] sourceRequest = requests.get(ics_url, auth=(ics_username, ics_password)) sourceRequest.encoding = 'utf-8' sourceContent = sourceRequest.text c = Calendar.from_ical(sourceContent) distant_uids = [bytes.decode(e['UID'].to_ical()).replace('\'', '').replace('/', 'slash') for e in c.walk('VEVENT')] imported_uids = [] for e in c.walk('VEVENT'): uid = bytes.decode(e['UID'].to_ical()).replace('\'', '').replace('/', 'slash') name = bytes.decode(e['SUMMARY'].to_ical()) if uid not in existing_uids: cal = Calendar() cal.add_component(e) r = requests.put('%s/%s.ics' % (base_url, uid), data=cal.to_ical(), auth=(username, password), headers={'content-type': 'text/calendar; charset=UTF-8'} ) if r.status_code == 500 and r'Sabre\VObject\Recur\NoInstancesException' in r.text: logging.warning(' No valid instances: %s (%s)' % (uid, name)) elif r.status_code == 201 or r.status_code == 204: logging.info(' Imported: %s (%s)' % (uid, name)) imported_uids.append(uid) else: r.raise_for_status() for euid in existing_uids: if not euid in distant_uids: r = requests.delete('%s/%s.ics' % (base_url, euid), auth=(username, password)) if r.status_code == 204: logging.info('Deleted %s' % euid) elif r.status_code == 404: pass else: r.raise_for_status() logging.info(' Done.')
def import_events(self, source): Event.objects.filter(source=source).delete() iCalEvent.ignore_exceptions = False Alarm.ignore_exceptions = True with source.sourcefile.file as feedfile: cal = Calendar.from_ical(feedfile.read()) # Some icalendar feed choose to use X-WR-TIMEZONE to denote the timezone of the creator of the feed. # Although there is some argument out there, [1] this should be used to define the timezone of any # events that don't are not UTC and don't have a TZID reference. The icalendar library does not # Use this property but since this is a shared calendar system we have to use it at the point of # sharing, ie when the date is unpacked from the feed. # # 1 http://blog.jonudell.net/2011/10/17/x-wr-timezone-considered-harmful/ try: default_timezone = pytz.timezone(cal.get('X-WR-TIMEZONE')) except: default_timezone = None # Unless we put TZ support into the UI to allow users to set their timezone, this is the best we can do. metadata = source.metadata for k, _ in cal.iteritems(): metadata[k.lower()] = self._get_value(cal, k, default_timezone) events = [] for e in cal.walk('VEVENT'): dstart = e.decoded('DTSTART') dend = e.decoded('DTEND') starttz = default_timezone if dstart.tzinfo is None else dstart.tzinfo endtz = default_timezone if dstart.tzinfo is None else dstart.tzinfo event = Event(start=DateConverter.to_datetime( dstart, defaultzone=default_timezone), end=DateConverter.to_datetime( dend, defaultzone=default_timezone), starttz=starttz, endtz=endtz, location=self._safe_get(e, "LOCATION", ""), title=self._safe_get(e, "SUMMARY", ""), uid=self._safe_get(e, "UID", ""), source=source) metadata = event.metadata for k, _ in e.iteritems(): metadata[k.lower()] = self._get_value( e, k, default_timezone) metadata['x-allday'] = DateConverter.is_date( e.decoded('DTSTART')) events.append(event) source.save() Event.objects.bulk_create(events) Event.after_bulk_operation() return len(events)
def generate_calendar(request): """http://codespeak.net/icalendar/""" cal = Calendar() cal.add("prodid", "-//Club Connect//ericleong.me//") cal.add("version", "2.0") posts = Post.objects.order_by("-created") cal["X-WR-CALNAME"] = "Club Connect Events" cal["X-PUBLISH-TTL"] = "PT12H" cal["CALSCALE"] = "GREGORIAN" cal["METHOD"] = "PUBLISH" # TODO: separate out private events using a private URL? # TODO: switch to using EDT for post in posts: if post.start_time: # Make sure we have a time event = Event() event.add("summary", vText(post.title)) event.add("dtstart", post.start_time) event.add("dtend", post.end_time if post.end_time else post.start_time) # event.add('dtstamp', datetime(2005,4,4,0,10,0,tzinfo=UTC)) event["uid"] = vText(post.id) event["organizer"] = vText(post.author.username) event["description"] = vText(post.description) event["url"] = vUri(post.get_absolute_url()) if post.location: if post.location.room: event["location"] = vText("%s (%s)" % (post.location.name, post.location.room)) else: event["location"] = vText(post.location.name) for commit in post.committed.all(): attendee = vCalAddress("MAILTO:" + commit.email) name = ( ([commit.first_name, commit.last_name]) if (commit.first_name and commit.last_name) else commit.username ) attendee.params["cn"] = vText(name) event.add("attendee", attendee, encode=0) cal.add_component(event) return HttpResponse(cal.to_ical().replace("\n", "\r\n"), content_type="text/calendar")
def sync_events(sender, instance, created, **kwargs): ''' Synchronizes events for the saved calendar. ''' for gevent in instance.get_events(): # get or create this event in the DB event, created = Event.objects.get_or_create(uri=gevent.id.text) # update title and description event.title = gevent.title.text event.description = gevent.content.text if gevent.where: event.where = gevent.where[0].value_string event.save() stale_occurrences = [] if gevent.recurrence and gevent.recurrence.text: # manage recurrences (if any) recurrence_ical_data = 'BEGIN:VCALENDAR\n%s\nEND:VCALENDAR' % gevent.recurrence.text parsed_recurrence = Calendar.from_ical(recurrence_ical_data) start_time = parsed_recurrence['DTSTART'].dt end_time = parsed_recurrence['DTEND'].dt rrules = dict(parsed_recurrence['RRULE']) # add a default count to rrule to bound it unless a bound is specified if not (('COUNT' in rrules) or ('UNTIL' in rrules)): rrules['count'] = 52 occurrence_pks = event.add_occurrences(start_time, end_time, **rrules) stale_occurrences = event.occurrence_set.exclude( pk__in=occurrence_pks) elif gevent.when: # otherwise just do a single-occurrence event occurrence_pks = event.add_occurrences(gevent.when[0].start_time, gevent.when[0].end_time) stale_occurrences = event.occurrence_set.exclude( pk__in=occurrence_pks) # Clear out any stale occurrences. # Note: only occurrences with end times in the future will be removed, # since past occurrences may have data already associated with them. for occurrence in stale_occurrences: if occurrence.end_time > timezone.now(): occurrence.delete()
def sync_events(sender, instance, created, **kwargs): ''' Synchronizes events for the saved calendar. ''' for gevent in instance.get_events(): # get or create this event in the DB event, created = Event.objects.get_or_create(uri=gevent.id.text) # update title and description event.title = gevent.title.text event.description = gevent.content.text if gevent.where: event.where = gevent.where[0].value_string event.save() stale_occurrences = [] if gevent.recurrence and gevent.recurrence.text: # manage recurrences (if any) recurrence_ical_data = 'BEGIN:VCALENDAR\n%s\nEND:VCALENDAR' % gevent.recurrence.text parsed_recurrence = Calendar.from_ical(recurrence_ical_data) start_time = parsed_recurrence['DTSTART'].dt end_time = parsed_recurrence['DTEND'].dt rrules = dict(parsed_recurrence['RRULE']) # add a default count to rrule to bound it unless a bound is specified if not (('COUNT' in rrules) or ('UNTIL' in rrules)): rrules['count'] = 52 occurrence_pks = event.add_occurrences(start_time, end_time, **rrules) stale_occurrences = event.occurrence_set.exclude(pk__in=occurrence_pks) elif gevent.when: # otherwise just do a single-occurrence event occurrence_pks = event.add_occurrences(gevent.when[0].start_time, gevent.when[0].end_time) stale_occurrences = event.occurrence_set.exclude(pk__in=occurrence_pks) # Clear out any stale occurrences. # Note: only occurrences with end times in the future will be removed, # since past occurrences may have data already associated with them. for occurrence in stale_occurrences: if occurrence.end_time > timezone.now(): occurrence.delete()
def icsout(inp): from icalendar.cal import Calendar, Event from hashlib import sha1 inp = os.path.expanduser(inp) cal = Calendar() cal.add('prodid', '-//ccal.py 0.5//niij.org//') cal.add('version', '2.0') entries = Entries(every=True, comm=True) for entry in entries: event = Event() event.add('summary', entry.desc) event.add('dtstart', entry.dt) event.add('dtend', entry.dt + dt.timedelta(days=1)) event.add('dtstamp', dt.datetime.now()) uid = "%s%s%s %s" % (entry.dt.year, entry.dt.month, entry.dt.day, entry.desc) event.add('uid', sha1(uid.encode('utf-8')).hexdigest()) if (entry.comm): event.add('description', entry.comm.strip()) cal.add_component(event) print(cal.to_ical().decode('utf-8')) sys.exit(0)
def fetch_calendar(self, force_xml=False, force_ics=False): "Fetches the calendar data from an XML/.ics resource in preperation for parsing." cal_data = None if self.xml_url: cal_data = urlopen(self.xml_url) elif self.ics_url: cal_data = urlopen(self.ics_url) elif self.xml_file: cal_data = open(self.xml_file, "rb") elif self.ics_file: cal_data = open(self.ics_file, "rb") else: raise UnboundLocalError("No calendar url or file path has been set.") cal_str = cal_data.read() cal_data.close() if (self.xml_url or self.xml_file) and not force_ics: self.calendar = BeautifulStoneSoup(_normalize(cal_str, True)) elif (self.ics_url or self.ics_file) and not force_xml: self.calendar = Calendar.from_ical(cal_str) return self.calendar
def fetch_calendar(self, force_xml=False, force_ics=False): "Fetches the calendar data from an XML/.ics resource in preperation for parsing." cal_data = None if self.xml_url: cal_data = urlopen(self.xml_url) elif self.ics_url: cal_data = urlopen(self.ics_url) elif self.xml_file: cal_data = open(self.xml_file, "rb") elif self.ics_file: cal_data = open(self.ics_file, "rb") else: raise UnboundLocalError("No calendar url or file path has been set.") cal_str = cal_data.read() cal_data.close() if (self.xml_url or self.xml_file) and not force_ics: self.calendar = BeautifulStoneSoup(_normalize(cal_str, True)) elif (self.ics_url or self.ics_file) and not force_xml: self.calendar = Calendar.from_string(cal_str) return self.calendar
def expand_calendar_rrule(incal, start, end): outcal = Calendar() if incal.name != 'VCALENDAR': raise AssertionError( 'called on file with root component %s' % incal.name) for field in incal: outcal[field] = incal[field] known = {} for insub in incal.subcomponents: if 'RECURRENCE-ID' in insub: ts = insub['RECURRENCE-ID'].dt utcts = asutc(ts) known[utcts] = insub for insub in incal.subcomponents: if insub.name == 'VTIMEZONE': continue if 'RECURRENCE-ID' in insub: continue if 'RRULE' in insub: for outsub in _expand_rrule_component(insub, start, end, known): outcal.add_component(outsub) else: outcal.add_component(insub) return outcal
def add(request): def submit_form(form): return render_to_response("add.html", {"form": form}, context_instance=RequestContext(request)) if request.method == "GET": if not request.user.is_authenticated(): pass # Ask the user if the want to sign on data = {} if "url" in request.GET: data.update({"url": request.GET["url"]}) day = datetime.today() if "day" in request.GET: if request.GET["day"] != "": day = request.GET["day"] # Javascript hands you Tue May 20 1990 data.update({"date": day}) else: data.update({"date": day.strftime("%a %b %d %Y")}) else: data.update({"date": day.strftime("%a %b %d %Y")}) start_time = datetime.today() start_time = start_time.strftime("%H:%M") if "start_time" in request.GET: if request.GET["start_time"] != "": start_time = request.GET["start_time"] data.update({"start_time": start_time}) if "end_time" in request.GET: end_time = request.GET["end_time"] if end_time != "null": data.update({"end_time": end_time}) data.update({"mail": "outlook"}) form = EventForm(data) return submit_form(form) # Form was returned with data if request.method == "POST": form = EventForm(request.POST) if not form.is_valid(): return submit_form(form) title = form.cleaned_data["title"] date = form.cleaned_data["date"] start_time = form.cleaned_data["start_time"] end_time = form.cleaned_data["end_time"] url = form.cleaned_data["url"] describe = form.cleaned_data["describe"] address = form.cleaned_data["address"] mail = form.cleaned_data["mail"] latitude = None longitude = None if address != u"": local = geocode(address) if local != None: if "address" in local: address = local["address"] if "latitude" in local and "longitude" in local: latitude = local["latitude"] longitude = local["longitude"] # If they move the pointer to be more specific override address """ if form.data['lati'] and form.data['lngi']: latitude = form.data['lati'] longitude = form.data['lngi'] """ event = EventModel( title=title, date=date, start_time=start_time, end_time=end_time, address=address, longitude=longitude, latitude=latitude, description=describe, url=url, ) # Save this event event.save() # Make sure you save the event before connecting it to a user if request.user.is_authenticated(): event.connect(request.user) # Ical or Outlook iCal file if mail == "outlook" or mail == "ical": # Create the iCal file cal = Calendar() cal.add("version", "2.0") cal.add("prodid", "-//Microsoft Corporation//Windows Calendar 1.0//EN") cal.add("method", "PUBLISH") event = Event() event.add("summary", describe) if start_time != None: dt = datetime.combine(date, start_time) else: dt = date event.add("dtstart", dt) event.add("dtstamp", dt) if end_time != None: de = datetime.combine(date, end_time) event.add("dtend", de) g = (latitude, latitude) event.add("geo", g) event.add("location", address) uid = date.isoformat() + "@wenzit.net" event.add("UID", uid) event.add("url", url) cal.add_component(event) f = open("schedule.ics", "wb") f.write(cal.as_string()) f.close() response = HttpResponse(cal.as_string(), mimetype="text/calendar") response["Content-Disposition"] = "attachment; filename=schedule.ics" return response # Send the event to google elif mail == "google": response = "http://www.google.com/calendar/event?action=TEMPLATE" response += "&text=" + urllib.quote_plus(title) if start_time != None: ds = datetime.combine(date, start_time) else: ds = date if end_time != None: de = datetime.combine(date, end_time) response += "&dates=" + vDatetime(ds).ical() + "/" + vDatetime(de).ical() else: response += "&dates=" + vDatetime(ds).ical() response += "&details=" + urllib.quote_plus(title) response += "&location=" + urllib.quote_plus(address) response += "&sprop=" + urllib.quote_plus(url) return HttpResponseRedirect(response) # Send the event to Yahoo if mail == "yahoo": response = "http://calendar.yahoo.com/?v=60" response += "&TITLE=" + urllib.quote_plus(title) ds = datetime.combine(date, start_time) if end_time: de = datetime.combine(date, end_time) dur = de - ds hrs, left = divmod(dur.seconds, 3600) mins, secs = divmod(left, 60) dur = "%02d%02d" % (hrs, mins) else: dur = "" response += "&ST=" + vDatetime(ds).ical() response += "&DUR=" + dur response += "&in_loc=" + urllib.quote_plus(address) response += "&DESC=" + urllib.quote_plus(title) response += "&URL=" + urllib.quote_plus(url) return HttpResponseRedirect(response)
def get_pytz_from_text(tztext): tzid = extract_tzid(ICalendar.from_ical(tztext)) return pytz.timezone(tzid)
if status is not None: props['status'] = status if location is not None: props['location'] = vText(location) if opts.url: props['url'] = vUri(opts.url) if description is not None: props['summary'] = vText(description) if dtend is not None: props['dtend'] = vDate(dtend.date()) if duration is not None: props['duration'] = vDuration(duration) ev = Event(**props) c = Calendar() c.add_component(ev) md5 = hashlib.md5() md5.update(c.to_ical()) uid = md5.hexdigest() props['UID'] = uid fname = uid + '.ics' path = os.path.join(opts.outputdir, fname) porcelain.add(opts.outputdir, path) porcelain.commit(opts.outputdir, 'Add %s.' % description) with open(path, 'w') as f:
def test_cal_Component(self): from icalendar.cal import Component, Calendar, Event from icalendar import prop # A component is like a dictionary with extra methods and attributes. c = Component() c.name = 'VCALENDAR' # Every key defines a property.A property can consist of either a # single item. This can be set with a single value... c['prodid'] = '-//max m//icalendar.mxm.dk/' self.assertEqual(c, Calendar({'PRODID': '-//max m//icalendar.mxm.dk/'})) # or with a list c['ATTENDEE'] = ['Max M', 'Rasmussen'] self.assertEqual( c, Calendar({ 'ATTENDEE': ['Max M', 'Rasmussen'], 'PRODID': '-//max m//icalendar.mxm.dk/' })) ### ADD MULTIPLE VALUES TO A PROPERTY # if you use the add method you don't have to considder if a value is # a list or not. c = Component() c.name = 'VEVENT' # add multiple values at once c.add('attendee', ['*****@*****.**', '*****@*****.**']) # or add one per line c.add('attendee', '*****@*****.**') c.add('attendee', '*****@*****.**') # add again multiple values at once to very concatenaton of lists c.add('attendee', ['*****@*****.**', '*****@*****.**']) self.assertEqual( c, Event({ 'ATTENDEE': [ prop.vCalAddress('*****@*****.**'), prop.vCalAddress('*****@*****.**'), prop.vCalAddress('*****@*****.**'), prop.vCalAddress('*****@*****.**'), prop.vCalAddress('*****@*****.**'), prop.vCalAddress('*****@*****.**') ] })) ### # You can get the values back directly ... c.add('prodid', '-//my product//') self.assertEqual(c['prodid'], prop.vText(u'-//my product//')) # ... or decoded to a python type self.assertEqual(c.decoded('prodid'), b'-//my product//') # With default values for non existing properties self.assertEqual(c.decoded('version', 'No Version'), 'No Version') c.add('rdate', [datetime(2013, 3, 28), datetime(2013, 3, 27)]) self.assertTrue(isinstance(c.decoded('rdate'), prop.vDDDLists)) # The component can render itself in the RFC 2445 format. c = Component() c.name = 'VCALENDAR' c.add('attendee', 'Max M') self.assertEqual( c.to_ical(), b'BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n') # Components can be nested, so You can add a subcompont. Eg a calendar # holds events. e = Component(summary='A brief history of time') e.name = 'VEVENT' e.add('dtend', '20000102T000000', encode=0) e.add('dtstart', '20000101T000000', encode=0) self.assertEqual( e.to_ical(), b'BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n' + b'DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r' + b'\nEND:VEVENT\r\n') c.add_component(e) self.assertEqual(c.subcomponents, [ Event({ 'DTEND': '20000102T000000', 'DTSTART': '20000101T000000', 'SUMMARY': 'A brief history of time' }) ]) # We can walk over nested componentes with the walk method. self.assertEqual([i.name for i in c.walk()], ['VCALENDAR', 'VEVENT']) # We can also just walk over specific component types, by filtering # them on their name. self.assertEqual([i.name for i in c.walk('VEVENT')], ['VEVENT']) self.assertEqual([i['dtstart'] for i in c.walk('VEVENT')], ['20000101T000000']) # We can enumerate property items recursively with the property_items # method. self.assertEqual(c.property_items(), [('BEGIN', b'VCALENDAR'), ('ATTENDEE', prop.vCalAddress('Max M')), ('BEGIN', b'VEVENT'), ('DTEND', '20000102T000000'), ('DTSTART', '20000101T000000'), ('SUMMARY', 'A brief history of time'), ('END', b'VEVENT'), ('END', b'VCALENDAR')]) # We can also enumerate property items just under the component. self.assertEqual(c.property_items(recursive=False), [('BEGIN', b'VCALENDAR'), ('ATTENDEE', prop.vCalAddress('Max M')), ('END', b'VCALENDAR')]) sc = c.subcomponents[0] self.assertEqual(sc.property_items(recursive=False), [('BEGIN', b'VEVENT'), ('DTEND', '20000102T000000'), ('DTSTART', '20000101T000000'), ('SUMMARY', 'A brief history of time'), ('END', b'VEVENT')]) # Text fields which span multiple mulitple lines require proper # indenting c = Calendar() c['description'] = u'Paragraph one\n\nParagraph two' self.assertEqual( c.to_ical(), b'BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two' + b'\r\nEND:VCALENDAR\r\n') # INLINE properties have their values on one property line. Note the # double quoting of the value with a colon in it. c = Calendar() c['resources'] = 'Chair, Table, "Room: 42"' self.assertEqual(c, Calendar({'RESOURCES': 'Chair, Table, "Room: 42"'})) self.assertEqual( c.to_ical(), b'BEGIN:VCALENDAR\r\nRESOURCES:Chair\\, Table\\, "Room: 42"\r\n' + b'END:VCALENDAR\r\n') # The inline values must be handled by the get_inline() and # set_inline() methods. self.assertEqual(c.get_inline('resources', decode=0), [u'Chair', u'Table', u'Room: 42']) # These can also be decoded self.assertEqual(c.get_inline('resources', decode=1), [b'Chair', b'Table', b'Room: 42']) # You can set them directly ... c.set_inline('resources', ['A', 'List', 'of', 'some, recources'], encode=1) self.assertEqual(c['resources'], 'A,List,of,"some, recources"') # ... and back again self.assertEqual(c.get_inline('resources', decode=0), ['A', 'List', 'of', 'some, recources']) c['freebusy'] = '19970308T160000Z/PT3H,19970308T200000Z/PT1H,'\ + '19970308T230000Z/19970309T000000Z' self.assertEqual(c.get_inline('freebusy', decode=0), [ '19970308T160000Z/PT3H', '19970308T200000Z/PT1H', '19970308T230000Z/19970309T000000Z' ]) freebusy = c.get_inline('freebusy', decode=1) self.assertTrue(isinstance(freebusy[0][0], datetime)) self.assertTrue(isinstance(freebusy[0][1], timedelta))
def add(request): def submit_form(form): return render_to_response('add.html', {'form': form}, context_instance=RequestContext(request)) if request.method == 'GET': if not request.user.is_authenticated(): pass # Ask the user if the want to sign on data = {} if 'url' in request.GET: data.update({'url': request.GET['url']}) day = datetime.today() if 'day' in request.GET: if request.GET['day'] != "": day = request.GET[ 'day'] # Javascript hands you Tue May 20 1990 data.update({'date': day}) else: data.update({'date': day.strftime('%a %b %d %Y')}) else: data.update({'date': day.strftime('%a %b %d %Y')}) start_time = datetime.today() start_time = start_time.strftime('%H:%M') if 'start_time' in request.GET: if request.GET['start_time'] != '': start_time = request.GET['start_time'] data.update({'start_time': start_time}) if 'end_time' in request.GET: end_time = request.GET['end_time'] if end_time != 'null': data.update({'end_time': end_time}) data.update({'mail': 'outlook'}) form = EventForm(data) return submit_form(form) # Form was returned with data if request.method == 'POST': form = EventForm(request.POST) if not form.is_valid(): return submit_form(form) title = form.cleaned_data['title'] date = form.cleaned_data['date'] start_time = form.cleaned_data['start_time'] end_time = form.cleaned_data['end_time'] url = form.cleaned_data['url'] describe = form.cleaned_data['describe'] address = form.cleaned_data['address'] mail = form.cleaned_data['mail'] latitude = None longitude = None if address != u'': local = geocode(address) if local != None: if 'address' in local: address = local['address'] if 'latitude' in local and 'longitude' in local: latitude = local['latitude'] longitude = local['longitude'] # If they move the pointer to be more specific override address """ if form.data['lati'] and form.data['lngi']: latitude = form.data['lati'] longitude = form.data['lngi'] """ event = EventModel(title=title, date=date, start_time=start_time, end_time=end_time, address=address, longitude=longitude, latitude=latitude, description=describe, url=url) # Save this event event.save() # Make sure you save the event before connecting it to a user if request.user.is_authenticated(): event.connect(request.user) # Ical or Outlook iCal file if mail == 'outlook' or mail == 'ical': # Create the iCal file cal = Calendar() cal.add('version', '2.0') cal.add('prodid', '-//Microsoft Corporation//Windows Calendar 1.0//EN') cal.add('method', 'PUBLISH') event = Event() event.add('summary', describe) if start_time != None: dt = datetime.combine(date, start_time) else: dt = date event.add('dtstart', dt) event.add('dtstamp', dt) if end_time != None: de = datetime.combine(date, end_time) event.add('dtend', de) g = (latitude, latitude) event.add('geo', g) event.add('location', address) uid = date.isoformat() + '@wenzit.net' event.add('UID', uid) event.add('url', url) cal.add_component(event) f = open('schedule.ics', 'wb') f.write(cal.as_string()) f.close() response = HttpResponse(cal.as_string(), mimetype='text/calendar') response[ 'Content-Disposition'] = 'attachment; filename=schedule.ics' return response # Send the event to google elif mail == 'google': response = "http://www.google.com/calendar/event?action=TEMPLATE" response += "&text=" + urllib.quote_plus(title) if start_time != None: ds = datetime.combine(date, start_time) else: ds = date if end_time != None: de = datetime.combine(date, end_time) response += "&dates=" + vDatetime(ds).ical()+ \ '/'+vDatetime(de).ical() else: response += "&dates=" + vDatetime(ds).ical() response += "&details=" + urllib.quote_plus(title) response += "&location=" + urllib.quote_plus(address) response += "&sprop=" + urllib.quote_plus(url) return HttpResponseRedirect(response) # Send the event to Yahoo if mail == 'yahoo': response = 'http://calendar.yahoo.com/?v=60' response += '&TITLE=' + urllib.quote_plus(title) ds = datetime.combine(date, start_time) if end_time: de = datetime.combine(date, end_time) dur = de - ds hrs, left = divmod(dur.seconds, 3600) mins, secs = divmod(left, 60) dur = '%02d%02d' % (hrs, mins) else: dur = '' response += '&ST=' + vDatetime(ds).ical() response += '&DUR=' + dur response += '&in_loc=' + urllib.quote_plus(address) response += '&DESC=' + urllib.quote_plus(title) response += '&URL=' + urllib.quote_plus(url) return HttpResponseRedirect(response)
logger = logging.getLogger() logger.setLevel(logging.DEBUG) ch = logging.StreamHandler(sys.stderr) ch.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s: %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) parser = optparse.OptionParser("fix-songkick") opts, args = parser.parse_args() url = args[0] orig = Calendar.from_ical(urllib.urlopen(url).read()) TRACKING_PREFIX = u"You’re tracking this event.\n\n" GOING_PREFIX = u"You’re going.\n\n" def fix_vevent(vevent): status = None if unicode(vevent['DESCRIPTION']).startswith(TRACKING_PREFIX): vevent['DESCRIPTION'] = vevent['DESCRIPTION'][len(TRACKING_PREFIX):] if unicode(vevent['DESCRIPTION']).startswith(GOING_PREFIX): vevent['STATUS'] = 'CONFIRMED' vevent['DESCRIPTION'] = vevent['DESCRIPTION'][len(GOING_PREFIX):] if unicode(vevent['DESCRIPTION']).startswith(unicode(vevent['URL'])): vevent['DESCRIPTION'] = vevent['DESCRIPTION'][len(unicode(vevent['URL'])):] if not vevent['DESCRIPTION']: del vevent['DESCRIPTION']
logging.info('Inbox URL: %s', inbox_url) if opts.invite: target_collection_url = inbox_url else: target_collection_url = opts.url try: import_url = args[0] except IndexError: f = sys.stdin.buffer import_url = None else: f = urllib.request.urlopen(import_url) orig = Calendar.from_ical(f.read()) other = [] items = {} for component in orig.subcomponents: if component.name in ('VEVENT', 'VTODO'): try: items[component['UID']] = component except KeyError: raise KeyError('missing UID for %s in %s' % (component.name, url)) else: other.append(component) seen = 0 changed = 0 added = 0
duration = datetime.timedelta(1) props = { 'categories': opts.categories.split(','), 'dtstart': vDate(dtstart.date()), 'created': vDatetime(datetime.datetime.now()), 'class': 'PUBLIC', } if status is not None: props['status'] = status if location is not None: props['location'] = vText(location) if opts.event_url: props['url'] = vUri(opts.event_url) if description is not None: props['summary'] = vText(description) if dtend is not None: props['dtend'] = vDate(dtend.date()) if duration is not None: props['duration'] = vDuration(duration) uid = str(uuid.uuid1()) props['UID'] = uid ev = Event(**props) c = Calendar() c.add_component(ev) utils.add_member(opts.url, 'text/calendar', c.to_ical())
'start': start, 'duration': duration, 'label': '' } events.append(event) return events def global_label(events, label): for event in events: event['label'] = label events = [] with open('calendar.ics', 'r') as cal_file: cal = Calendar.from_ical(cal_file.read()) new_events = calendar_to_events(cal) global_label(new_events, 'personal') events.extend(new_events) with open('school.ics', 'r') as cal_file: cal = Calendar.from_ical(cal_file.read()) new_events = calendar_to_events(cal) global_label(new_events, 'school') events.extend(new_events) with open('sleep.ics', 'r') as cal_file: cal = Calendar.from_ical(cal_file.read()) new_events = calendar_to_events(cal) global_label(new_events, 'sleep') events.extend(new_events)
formatter = logging.Formatter('%(levelname)s: %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) parser = optparse.OptionParser("fix-songkick") opts, args = parser.parse_args() try: url = args[0] except IndexError: f = sys.stdin.buffer else: f = urllib.request.urlopen(url) orig = Calendar.from_ical(f.read()) TRACKING_PREFIX = "You’re tracking this event.\n\n" GOING_PREFIX = "You’re going.\n\n" def fix_vevent(vevent): status = None if str(vevent['DESCRIPTION']).startswith(TRACKING_PREFIX): vevent['DESCRIPTION'] = vevent['DESCRIPTION'][len(TRACKING_PREFIX):] vevent['STATUS'] = 'TENTATIVE' if str(vevent['DESCRIPTION']).startswith(GOING_PREFIX): vevent['STATUS'] = 'CONFIRMED' vevent['DESCRIPTION'] = vevent['DESCRIPTION'][len(GOING_PREFIX):] if str(vevent['DESCRIPTION']).startswith(str(vevent['URL'])): vevent['DESCRIPTION'] = vevent['DESCRIPTION'][len(str(vevent['URL'])):]
def do_import(username, password, calendar, server, ics_url): base_url = CALDAVURL % (server, username, calendar) # fetch events from target cal target_fetch_url = '%s?export' % base_url r = requests.get(target_fetch_url, auth=(username, password)) r.raise_for_status() try: target_cal = Calendar.from_ical(r.text) except ValueError as e: print('Warning: Could not parse iCal (%s)' % target_fetch_url, file=sys.stderr) print(e, file=sys.stderr) return existing_uids = [e['UID'].to_ical() for e in target_cal.walk('VEVENT')] # fetch webcal if ics_url.startswith('file://'): # Special handling for file:// URLs: substring to strip the file:// prefix, # then read it using normal file-reading functionality. sourceFile = open(ics_url[7:], 'r') sourceContent = sourceFile.read() sourceFile.close() else: sourceRequest = requests.get(ics_url) sourceRequest.encoding = 'utf-8' sourceContent = sourceRequest.text c = Calendar.from_ical(sourceContent) # import webcal imported_uids = [] for e in c.walk('VEVENT'): uid = e['UID'].to_ical() cal = Calendar() cal.add_component(e) r = requests.put('%s/%s.ics' % (base_url, uid), data=cal.to_ical(), auth=(username, password), headers={'content-type':'text/calendar; charset=UTF-8'} ) if r.status_code == 500 and 'Sabre\VObject\Recur\NoInstancesException' in r.text: # ignore the NoInstancesException print('Warning: No valid instances: %s' % uid, file=sys.stdout) elif r.status_code == 201 or r.status_code == 204: print('Imported: %s (%d)' % (uid, r.status_code), file=sys.stdout) imported_uids.append(uid) else: r.raise_for_status() # remove events not in webcal for uid in existing_uids: if not uid in imported_uids: r = requests.delete('%s/%s.ics' % (base_url, uid), auth=(username, password)) if r.status_code == 204: print('Deleted %s' % uid, file=sys.stdout) # ignore 404 - not found (seems to be a manually created event) elif r.status_code == 404: pass else: r.raise_for_status()
ch.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s: %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) parser = optparse.OptionParser("split") parser.add_option("--prefix", dest="prefix", default="unknown", help="Filename prefix") parser.add_option("--outdir", dest="outdir", default=".", help="Output directory path") parser.add_option('--category', dest='category', default=None, help="Category to add.") parser.add_option('--status', dest='status', type="choice", choices=["", "tentative", "confirmed"], default=None, help="Status to set.") opts, args = parser.parse_args() url = args[0] orig = Calendar.from_ical(urllib.urlopen(url).read()) other = [] items = {} for component in orig.subcomponents: try: uid = component['UID'] except KeyError: md5 = hashlib.md5() md5.update(component.to_ical()) component['UID'] = uid = md5.hexdigest() if component.name in ('VEVENT', 'VTODO'): items[uid] = component else: other.append(component)
#!/usr/bin/python from icalendar.cal import Calendar, FreeBusy import optparse import os import sys sys.path.insert(0, os.path.dirname(__file__)) from dystros import utils parser = optparse.OptionParser("travel") collection_set_options = utils.CollectionSetOptionGroup(parser) parser.add_option_group(collection_set_options) opts, args = parser.parse_args() collections = utils.CollectionSet.from_options(opts) vevents = collections.iter_vevents() out = Calendar() freebusy = FreeBusy() for vevent in vevents: freebusy['UID'] = vevent['UID'] freebusy['DTSTART'] = vevent['DTSTART'] freebusy['DTEND'] = vevent['DTEND'] out.add_component(freebusy) print out.to_ical()
def create_cal_from_classes(dfs_list, class_list: List[str], semester_selector: str): assert (semester_selector[0:2] == "WS" and len(semester_selector) == 7) or (semester_selector[0:2] == "SS" and len(semester_selector) == 4) academic_year_url = academic_year_base_url.replace( "%year%", f"20{int(semester_selector[2:4]) - 1}-{int(semester_selector[2:4])}" if semester_selector[0:2] == "SS" else f"20{semester_selector[2:7].replace('/', '-')}") fp = urllib.request.urlopen(academic_year_url) mybytes = fp.read() html_doc2 = mybytes.decode("utf8") fp.close() academic_year = Calendar.from_ical(html_doc2) semester_date_spans = [] recess_date_spans = [] for component in academic_year.walk(): if component.name == 'VEVENT': if component['summary'].startswith( expand_semester(semester_selector)): semester_date_spans.append( (component['DTSTART'].dt, component['DTEND'].dt)) if any(r.lower() in component['summary'].lower() for r in recess): recess_date_spans.append( (component['DTSTART'].dt, component['DTEND'].dt)) weekdates_semester = [] for start_date, end_date in semester_date_spans: delta = end_date - start_date for i in range(delta.days + 1): day = start_date + timedelta(days=i) if not day.weekday() == SATURDAY and not day.weekday() == SUNDAY: weekdates_semester.append(day) recess_dates_semester = [] for start_date, end_date in recess_date_spans: delta = end_date - start_date for i in range(delta.days + 1): day = start_date + timedelta(days=i) if not day.weekday() == SATURDAY and not day.weekday() == SUNDAY: recess_dates_semester.append(day) cal = Calendar() cal.add("version", 2.0) cal.add("prodid", "-//flmann.de//timetable//DE") cal.add('x-wr-calname', "Stundenplan") cal.add('x-wr-caldesc', "Stundenplan TU Dresden ET") cal.add('x-wr-timezone', "Europe/Berlin") tzc = Timezone() tzc.add('tzid', 'Europe/Berlin') tzc.add('x-lic-location', 'Europe/Berlin') tzs = TimezoneStandard() tzs.add('tzname', 'CET') tzs.add('dtstart', datetime(1970, 10, 25, 3, 0, 0)) tzs.add('rrule', {'freq': 'yearly', 'bymonth': 10, 'byday': '-1su'}) tzs.add('TZOFFSETFROM', timedelta(hours=2)) tzs.add('TZOFFSETTO', timedelta(hours=1)) tzd = TimezoneDaylight() tzd.add('tzname', 'CEST') tzd.add('dtstart', datetime(1970, 3, 29, 2, 0, 0)) tzs.add('rrule', {'freq': 'yearly', 'bymonth': 3, 'byday': '-1su'}) tzd.add('TZOFFSETFROM', timedelta(hours=1)) tzd.add('TZOFFSETTO', timedelta(hours=2)) tzc.add_component(tzs) tzc.add_component(tzd) cal.add_component(tzc) dates_added = set() for dfs in dfs_list: timetable = dfs[1].values.tolist() index = 2 for i, row in enumerate(timetable): if i == 0: continue for j, cell in enumerate(row): if j == 0 or isinstance(cell, float): continue time_info = timetable[i][0] weekday_tt = timetable[0][j] classes = dfs[index].values.tolist() for c in classes: lecturer, name, room, *_ = c[0].split(seperator) odd_even, time_start, time_end = time_info.split(seperator) print(f"{name=} {name in class_list}") if not name in class_list: continue time_start_hour, time_start_minutes = [ int(x) for x in time_start.split(COLON) ] time_end_hour, time_end_minutes = [ int(x) for x in time_end.split(COLON) ] if odd_even == "1.WO": week_mod = 1 elif odd_even == "2.WO": week_mod = 0 else: print(f"Error. Invalid odd_even identifier {odd_even}") exit() id = f"{c[0]}{time_info}" if id in dates_added: continue dates_added.add(id) for day in weekdates_semester: _, weeknumber, weekday = day.isocalendar() if not weekday - 1 == WEEKDAYS[weekday_tt.lower()]: continue if not weeknumber % 2 == week_mod: continue if any([ d.day == day.day and d.month == day.day and d.year == day.year for d in recess_dates_semester ]): continue e = Event() e.add('summary', name) e.add( 'dtstart', datetime(day.year, day.month, day.day, time_start_hour, time_start_minutes, 0, tzinfo=pytz.timezone("Europe/Berlin"))) e.add( 'dtend', datetime(day.year, day.month, day.day, time_end_hour, time_end_minutes, 0, tzinfo=pytz.timezone("Europe/Berlin"))) e.add('dtstamp', datetime.now()) e.add('uid', uuid.uuid4()) e.add('location', room) e.add('description', f"Dozent: {flip_name(lecturer)}") cal.add_component(e) index += 1 return cal.to_ical().decode('utf-8').replace('\n\n', '\n').replace('\r\n', '\n')
def main(): semester_selector = "WS20/21" # semester_selector = "SS20" group_selector = "EuiDE-9-NT1" assert (semester_selector[0:2] == "WS" and len(semester_selector) == 7) or (semester_selector[0:2] == "SS" and len(semester_selector) == 4) timetable_url = timetable_base_url.replace("%semester%", semester_selector).replace( "%group%", group_selector) academic_year_url = academic_year_base_url.replace( "%year%", f"20{int(semester_selector[2:4]) - 1}-{int(semester_selector[2:4])}" if semester_selector[0:2] == "SS" else f"20{semester_selector[2:7].replace('/', '-')}") fp = urllib.request.urlopen(timetable_url) mybytes = fp.read() html_doc = mybytes.decode("utf-8").replace("<BR>", seperator) fp.close() fp = urllib.request.urlopen(academic_year_url) mybytes = fp.read() html_doc2 = mybytes.decode("utf8") fp.close() academic_year = Calendar.from_ical(html_doc2) semester_date_spans = [] recess_date_spans = [] for component in academic_year.walk(): if component.name == 'VEVENT': if component['summary'].startswith( expand_semester(semester_selector)): semester_date_spans.append( (component['DTSTART'].dt, component['DTEND'].dt)) if any(r.lower() in component['summary'].lower() for r in recess): recess_date_spans.append( (component['DTSTART'].dt, component['DTEND'].dt)) weekdates_semester = [] for start_date, end_date in semester_date_spans: delta = end_date - start_date for i in range(delta.days + 1): day = start_date + timedelta(days=i) if not day.weekday() == SATURDAY and not day.weekday() == SUNDAY: weekdates_semester.append(day) recess_dates_semester = [] for start_date, end_date in recess_date_spans: delta = end_date - start_date for i in range(delta.days + 1): day = start_date + timedelta(days=i) if not day.weekday() == SATURDAY and not day.weekday() == SUNDAY: recess_dates_semester.append(day) dfs = pd.read_html(html_doc) dfs = [ df.applymap(lambda x: x.replace(u"\xa0", u" ") if isinstance(x, str) else x) for df in dfs ] timetable = dfs[1].values.tolist() cal = Calendar() index = 2 class_list = [] for i, row in enumerate(timetable): if i == 0: continue for j, cell in enumerate(row): if j == 0 or isinstance(cell, float): continue classes = dfs[index].values.tolist() for c in classes: print(c[0]) lecturer, name, room, *_ = c[0].split(seperator) if not name in class_list: class_list.append(name) index += 1 print("[") for c in class_list: print(f" \"{c}\",") print("]") index = 2 for i, row in enumerate(timetable): if i == 0: continue for j, cell in enumerate(row): if j == 0 or isinstance(cell, float): continue time_info = timetable[i][0] weekday_tt = timetable[0][j] classes = dfs[index].values.tolist() for c in classes: lecturer, name, room, *_ = c[0].split(seperator) odd_even, time_start, time_end = time_info.split(seperator) time_start_hour, time_start_minutes = [ int(x) for x in time_start.split(COLON) ] time_end_hour, time_end_minutes = [ int(x) for x in time_end.split(COLON) ] if odd_even == "1.WO": week_mod = 1 elif odd_even == "2.WO": week_mod = 0 else: print(f"Error. Invalid odd_even identifier {odd_even}") exit() for day in weekdates_semester: _, weeknumber, weekday = day.isocalendar() if not weekday - 1 == WEEKDAYS[weekday_tt.lower()]: continue if not weeknumber % 2 == week_mod: continue if any([ d.day == day.day and d.month == day.day and d.year == day.year for d in recess_dates_semester ]): continue e = Event() e.add('summary', name) e.add( 'dtstart', datetime(day.year, day.month, day.day, time_start_hour, time_start_minutes, 0, tzinfo=pytz.timezone("Europe/Berlin"))) e.add( 'dtstart', datetime(day.year, day.month, day.day, time_end_hour, time_end_minutes, 0, tzinfo=pytz.timezone("Europe/Berlin"))) e.add('location', room) e.add('description', f"Dozent: {flip_name(lecturer)}") cal.add_component(e) index += 1 print(cal.to_ical().decode('utf-8')) with open('my.ics', 'w', encoding='utf-8') as f: f.write(cal.to_ical().decode('utf-8'))