def test_compare_simple(): first = iCalendar() first.add('vevent') second = iCalendar() second.add('vevent') second_out = iCalendar() compare(first, second, second_out) assert len(first.contents) == 0 assert len(second_out.contents) == 0
def test_compare_summary_diff2(): first = iCalendar() first_vevent = first.add('vevent') first_vevent.add('summary').value = "Foo" second = iCalendar() second.add('vevent') second_out = iCalendar() compare(first, second, second_out) assert len(first.contents) == 1 assert len(second_out.contents) == 1
def test_compare_dtend_duration_diff(): first = iCalendar() first_vevent = first.add('vevent') first_vevent.add('dtstart').value = datetime(2001, 1, 1, 10, 0) first_vevent.add('dtend').value = datetime(2001, 1, 1, 11, 0) second = iCalendar() second_vevent = second.add('vevent') second_vevent.add('dtstart').value = datetime(2001, 1, 1, 10, 0) second_vevent.add('duration').value = timedelta(hours=2) second_out = iCalendar() compare(first, second, second_out) assert len(first.contents) == 1 assert len(second_out.contents) == 1
def test_compare_dtend(): first = iCalendar() first_vevent = first.add('vevent') first_vevent.add('dtstart').value = datetime(2001, 1, 1, 10, 0) first_vevent.add('dtend').value = datetime(2001, 1, 1, 11, 0) second = iCalendar() second_vevent = second.add('vevent') second_vevent.add('dtstart').value = datetime(2001, 1, 1, 10, 0) second_vevent.add('dtend').value = datetime(2001, 1, 1, 11, 0) second_out = iCalendar() compare(first, second, second_out) assert len(first.contents) == 0 assert len(second_out.contents) == 0
def events_calendar(request, public=True): cache_key = 'calendar_%s' % ('public' if public else 'private') cached = cache.get(cache_key) if cached: return cached cal = vobject.iCalendar() cal.add('X-WR-CALNAME').value = ('Air Mozilla Public Events' if public else 'Air Mozilla Private Events') now = datetime.datetime.utcnow().replace(tzinfo=utc) events = list(Event.objects.approved() .filter(start_time__lt=now, public=public) .order_by('-start_time')[:settings.CALENDAR_SIZE]) events += list(Event.objects.approved() .filter(start_time__gte=now, public=public) .order_by('start_time')[:settings.CALENDAR_SIZE]) base_url = '%s://%s/' % (request.is_secure() and 'https' or 'http', RequestSite(request).domain) for event in events: vevent = cal.add('vevent') vevent.add('summary').value = event.title vevent.add('dtstart').value = event.start_time vevent.add('dtend').value = (event.start_time + datetime.timedelta(hours=1)) vevent.add('description').value = event.description if event.location: vevent.add('location').value = event.location.name vevent.add('url').value = base_url + event.slug + '/' icalstream = cal.serialize() response = http.HttpResponse(icalstream, mimetype='text/calendar; charset=utf-8') filename = 'AirMozillaEvents%s.ics' % ('Public' if public else 'Private') response['Content-Disposition'] = ( 'inline; filename=%s' % filename) cache.set(cache_key, response) return response
def all_dates(request, extra_filter=None, filename="Sheriff Duty.ics", summary=None): # by giving it a verbose filnema like 'Sheriff Duty.css' means it's # going to appear very nicely automatically on peoples iCal. cal = vobject.iCalendar() # always start on the first of this month today = datetime.date.today() first = datetime.date(today.year, today.month, 1) slots = (Slot.objects.filter(date__gte=first) .order_by('date') .select_related('user')) if extra_filter: slots = slots.filter(**extra_filter) base_url = '%s://%s' % (request.is_secure() and 'https' or 'http', RequestSite(request).domain) home_url = base_url + reverse('cal.home') for slot in slots[:31]: event = cal.add('vevent') event.add('summary').value = (summary and summary or get_user_name(slot.user)) event.add('dtstart').value = slot.date event.add('dtend').value = slot.date url = (home_url + '?cal_y=%d&cal_m=%d' % (slot.date.year, slot.date.month)) event.add('url').value = url event.add('description').value = ('Sheriff Duty on %s' % slot.date.strftime(settings.DEFAULT_DATE_FORMAT)) resp = http.HttpResponse(cal.serialize(), mimetype='text/calendar;charset=utf-8') resp['Content-Disposition'] = 'inline; filename="%s"' % filename return resp
def generateIcal(startDate, endDate, data): fileName = 'timetable_%s-%s.ics' % (startDate, endDate) startDate = datetime.date(int(startDate[6:]), int(startDate[3:5]), int(startDate[:2])) cal = vobject.iCalendar() local_tz = gettz("Europe/Ljubljana") for key in data: if len(data[key]) > 0: for lecture in data[key]: delta = datetime.timedelta(days = key - 1) date = startDate + delta event = cal.add('vevent') event.add('summary').value = '%s (%s)' % (lecture[0], lecture[1]) event.add('location').value = 'FERI - %s' % (lecture[2]) event.add('dtstart').value = datetime.datetime(date.year, date.month, date.day, int(hours[lecture[3]][:2]), int(hours[lecture[3]][3:]), 0, tzinfo = local_tz) event.add('dtend').value = datetime.datetime(date.year, date.month, date.day, int(hours[lecture[4]][:2]), int(hours[lecture[4]][3:]), tzinfo = local_tz) icalstream = cal.serialize() with open(fileName, 'w') as file: file.write(icalstream) return fileName
def create_ical(request, slug): """ Creates an ical .ics file for an event using vobject. """ event = get_object_or_404(Event, slug=slug) # convert dates to datetimes. # when we change code to datetimes, we won't have to do this. start = event.start_date start = datetime.datetime(start.year, start.month, start.day) if event.end_date: end = event.end_date end = datetime.datetime(end.year, end.month, end.day) else: end = start cal = vobject.iCalendar() cal.add('method').value = 'PUBLISH' vevent = cal.add('vevent') vevent.add('dtstart').value = start vevent.add('dtend').value = end vevent.add('dtstamp').value = datetime.datetime.now() vevent.add('summary').value = event.name response = HttpResponse(cal.serialize(), content_type='text/calendar') response['Filename'] = 'filename.ics' response['Content-Disposition'] = 'attachment; filename=filename.ics' return response
def __init__(self, list): if not list: raise RuntimeError("You must define at least one GitHubIssue \ object") self.cal = vobject.iCalendar() for issues in list: if type(issues) != GitHubIssues: raise RuntimeError("All arguments have to be GitHubIssues \ objects") for issue in issues.get(): todo = self.cal.add('vtodo') todo.add('summary').value = issue['title'] todo.add('description').value = issue['body'] # FIXME: repo name as a location? any better ideas? todo.add('location').value = issues.repo created = date(issue['created_at']) todo.add('dtstart').value = \ todo.add('dtstamp').value = \ todo.add('created').value = created todo.add('last-modified').value = date(issue['updated_at']) # if the issue has a milestone with due on date if issue['milestone']: if not issue['milestone']['due_on']: continue todo.add('due').value = date(issue['milestone']['due_on'])
def get(self, request, *args, **kwargs): cal = vobject.iCalendar() cal.add('x-wr-calname').value = 'Studentenportal Events' cal.add('x-wr-timezone').value = 'Europe/Zurich' for event in models.Event.objects.all(): vevent = cal.add('vevent') vevent.add('summary').value = event.summary vevent.add('description').value = event.description if event.start_time: dtstart = datetime.datetime.combine(event.start_date, event.start_time) else: dtstart = event.start_date vevent.add('dtstart').value = dtstart if event.end_date or event.end_time: if not event.end_date: dtend = datetime.datetime.combine(event.start_date, event.end_time) elif event.end_time: dtend = datetime.datetime.combine(event.end_date, event.end_time) else: dtend = datetime.datetime.combine( event.end_date, datetime.time(23, 59, 59)) vevent.add('dtend').value = dtend if event.author: vevent.add( 'comment').value = 'Erfasst von %s' % event.author.name() return HttpResponse(cal.serialize(), content_type='text/calendar')
def test0050create_event_attendee(self): 'Create event with attendee' ical = vobject.iCalendar() vevent = ical.add('vevent') vevent.add('summary') vevent.summary.value = 'Test event with attendee' vevent.add('dtstart') vevent.dtstart.value = datetime.datetime.now() + relativedelta(days=10) vevent.add('dtend') vevent.dtend.value = datetime.datetime.now() + relativedelta(days=10, hours=4) vevent.add('organizer') vevent.organizer.value = '*****@*****.**' % user attendees = [] for name in ('foo', 'bar'): attendee = vobject.base.ContentLine('ATTENDEE', [], '') attendee.partstat_param = 'TENTATIVE' attendee.value = 'MAILTO:%[email protected]' % name attendees.append(attendee) vevent.attendee_list = attendees caldav.Event(self.client, data=ical.serialize(), parent=self.calendar).save() Event = Model.get('calendar.event') owner_event, = Event.find([ ('calendar.owner.email', '=', '*****@*****.**' % user), ('summary', '=', vevent.summary.value), ]) attendee_event, = Event.find([ ('calendar.owner.email', '=', '*****@*****.**'), ]) self.assertEqual(attendee_event.uuid, owner_event.uuid)
def upcomingical(request): ''' upcomingical(request) Returns the calendar of conferences as an ical file. ''' try: import vobject except ImportError: return HttpResponse('import vobject failed in confereces/views.py:upcomingical') def _add_event(summary, start, end=None): if end is None: end = start rep = cal.add('vevent') rep.add('summary').value = summary rep.add('dtstart').value = start rep.add('dtend').value = end events = Conference.objects.filter(start__gte=datetime.datetime.now()).order_by('start') cal = vobject.iCalendar() for ev in events: _add_event(summary=ev.summary(), start=ev.start, end=ev.end) if ev.submission_deadline: _add_event(summary=('%s deadline' % ev.short_name), start=ev.submission_deadline) _add_event(summary=('%s deadline in 21 days' % ev.short_name), start=(ev.submission_deadline - datetime.timedelta(days=21))) response = HttpResponse(cal.serialize()) response['Content-Type'] = 'text/calendar' return response
def icalFeed(request, tag): cal = vobject.iCalendar() cal.add('CALSCALE').value = 'GREGORIAN' cal.add('METHOD').value = 'PUBLISH' cal.add('X-WR-CALNAME').value = tag + ' Events' cal.add('X-WR-TIMEZONE').value = 'America/New_York' cal.add('X-WR-CALDESC').value = 'Calendar of campus events. Filter:'+tag filteredEvents = Event.objects.filter(event_date_time_end__lte=datetime.now()).order_by('event_date_time_start') if not tag == "All": filteredEvents = filteredEvents.filter(event_cluster__cluster_tags__category_name=tag) for event in filteredEvents[0:200]: vevent = cal.add('VEVENT') vevent.add('SUMMARY').value = smart_unicode(str(event)) vevent.add('DTSTART').value = event.event_date_time_start vevent.add('DTEND').value = event.event_date_time_end vevent.add('LOCATION').value = unicode(event.getGCalLocation()) vevent.add('DESCRIPTION').value = unicode(event.getGCalClusterDes() + "\n\n" + event.getGCalEventDes()) icalstream = cal.serialize() response = HttpResponse(icalstream, mimetype='text/calendar') response['Content-Type'] = 'text/calendar; charset=utf-8' # response['Transfer-Encoding'] = 'chunked' response['Connection'] = 'close' response['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' response['Pragma'] = 'no-cache' return response
def action_ical(request,username): user = get_object_or_404(User,username=username) todos = user.actionitem_todo.filter(done=False) filename = "JK_Gestor_ActionItems.ics" import vobject import datetime cal = vobject.iCalendar() cal.add('method').value = 'PUBLISH' # IE/Outlook needs this for actionitem in todos: vtodo = cal.add('vtodo') vtodo.add('summary').value = actionitem.title # TODO: escaping this stuff # vtodo.add('description').value = actionitem.description vtodo.add('due;value=date').value = actionitem.due_date.strftime("%Y%m%d") vtodo.add('priority').value = "0" icalstream = cal.serialize() response = HttpResponse(icalstream, mimetype='text/calendar') response['Filename'] = filename # IE needs this response['Content-Disposition'] = 'attachment; filename='+filename return response
def initFromAttributes(self, rrule, dtstart, dtend = None, isAllDay = False): """ Initialize a recurrence from the information chunks """ self._rrule = rrule self._dtstart = dtstart self._dtend = dtend self._allDay = isAllDay data = self._rrule.serialize() + self._dtstart.serialize() if self._dtend: data += dtend.serialize() if type(self._dtstart.value) == datetime.date or self._dtstart.serialize().strip().endswith("Z"): # special handling for all day recurrences and UTCs frame = vobject.iCalendar() frame.add("standard") frame.standard = vobject.icalendar.TimezoneComponent(UTC()) data += frame.standard.serialize() # file = open("/tmp/pisi-ics.data", "w") # file.write("from attributes") # file.write(data) # file.close() # import os # os.system("gedit /tmp/pisi-ics.data") self._data = data
def ical_feed2(request): ''' Generates an ical sync of all events in the future ''' cal = vobject.iCalendar() tz = timezone(TIME_ZONE) utc = timezone('GMT') # IE/Outlook needs this: cal.add('method').value = 'PUBLISH' # Only publish events in the future for event in filter(lambda e: e.is_in_future(),Event.objects.order_by('start').exclude(displayFrom__gte=datetime.now())): total = str(event.signup_total()) if event.signup_total() else u'\u221E' signups = u' [%i/%s]' % (event.signup_count(),total) if event.has_signups() else u'' vevent = cal.add('vevent') vevent.add('summary').value = event.type.name + (' - ' + event.shortDescription if event.shortDescription else event.type.name) + signups vevent.add('location').value = str(event.location) vevent.add('dtstart').value = tz.localize(event.start).astimezone(utc) vevent.add('dtend').value = tz.localize(event.finish).astimezone(utc) vevent.add('dtstamp').value = tz.localize(event.creation_time()).astimezone(utc) # again, for Outlook vevent.add('description').value = event.longDescription vevent.add('sequence').value = str(event.update_count()) # for updates vevent.add('categories').value = [event.type.get_target_display()] url = "http://%s/events/details/%i/" % (Site.objects.get_current() , event.id) vevent.add('uid').value = url vevent.add('url').value = url response = HttpResponse(cal.serialize(), mimetype='text/calendar; charset=UTF-8') response['Filename'] = 'compsoc.ics' # IE needs this response['Content-Disposition'] = 'attachment; filename=compsoc.ics' return response
def create_collection(cls, href, collection=None, tag=None): folder = os.path.expanduser( cls.configuration.get("storage", "filesystem_folder")) path = path_to_filesystem(folder, href) if not os.path.exists(path): os.makedirs(path) if not tag and collection: tag = collection[0].name self = cls(href) if tag == "VCALENDAR": self.set_meta("tag", "VCALENDAR") if collection: collection, = collection for content in ("vevent", "vtodo", "vjournal"): if content in collection.contents: for item in getattr(collection, "%s_list" % content): new_collection = vobject.iCalendar() new_collection.add(item) self.upload(uuid4().hex, new_collection) elif tag == "VCARD": self.set_meta("tag", "VADDRESSBOOK") if collection: for card in collection: self.upload(uuid4().hex, card) return self
def response_as_ical(request, occurrences): ical = iCalendar() cal_name = settings.ICAL_CALNAME # If multiple occurrences with one event, name the calendar after the event if hasattr(occurrences, "__iter__"): events = list(set([o.event for o in occurrences])) if len(events) == 1: cal_name = unicode(events[0]) # If a single occurrence with an event elif getattr(occurrences, "event", None): cal_name = unicode(occurrences.event) ical.add("X-WR-CALNAME").value = cal_name ical.add("X-WR-CALDESC").value = settings.ICAL_CALDESC ical.add("method").value = "PUBLISH" # IE/Outlook needs this if hasattr(occurrences, "__iter__"): for occ in occurrences: ical = occ.as_icalendar(ical, request) else: ical = occurrences.as_icalendar(ical, request) icalstream = ical.serialize() response = HttpResponse(icalstream, mimetype="text/calendar") response["Filename"] = "events.ics" # IE needs this response["Content-Disposition"] = "attachment; filename=events.ics" return response
def get_ical(self, obj, request): """ Returns a populated iCalendar instance. """ cal = vobject.iCalendar() cal.add('method').value = 'PUBLISH' # IE/Outlook needs this items = self.__get_dynamic_attr("items", obj) cal_name = self.__get_dynamic_attr("cal_name", obj) cal_desc = self.__get_dynamic_attr("cal_desc", obj) # Add calendar name and description if set if cal_name: cal.add('x-wr-calname').value = cal_name if cal_desc: cal.add('x-wr-caldesc').value = cal_desc if get_current_site: current_site = get_current_site(request) else: current_site = None for item in items: event = cal.add('vevent') for vkey, key in EVENT_ITEMS: value = self.__get_dynamic_attr(key, item) if value: if vkey == 'rruleset': event.rruleset = value else: if vkey == 'url' and current_site: value = add_domain( current_site.domain, value, request.is_secure(), ) event.add(vkey).value = value return cal
def calendar_ics(self,**kwargs): #Build list of courses. courses = [] for i in range(1,MAX_COURSES +1): if kwargs['course%dname' % i] == '': continue courses.append({ 'name':kwargs['course%dname' %i], 'location':kwargs['course%dlocation' %i], 'slot':kwargs['course%dslot' %i], 'tutlocation':kwargs['tutorial%dlocation' %i], 'tutslot':kwargs['tutorial%dslot' %i] }) for course in courses: if course['slot'] not in self.slots: try: self.parseTimes(course['slot']) except: return self.invalidSlot(course) if course['tutslot'] != '' and course['tutslot'] not in self.slots: try: self.parseTimes(course['tutslot']) except: return self.invalidSlot(course) cal = vobject.iCalendar() for course in courses: self.addToCalendar(cal,course) cherrypy.response.headers['Content-Type']= 'text/calendar' return cal.serialize()
def make_ical(data, sources): calweek_regex = re.compile(r'^(\d+)\. KW$') time_regex = re.compile(r'^(\d+)\.(\d+) - (\d+)\.(\d+)$') room_regex = re.compile(r'^(.*) - (.*)$') times = {} for time in data[0]['order']: matches = time_regex.match(time) if not matches: raise CannotParseTime("String was: %s" % time) newtime = {'start': rd.relativedelta(hour=int(matches.group(1)), minute=int(matches.group(2))), 'end': rd.relativedelta(hour=int(matches.group(3)), minute=int(matches.group(4)))} times[time] = newtime calendar = vobject.iCalendar() cat_map = {u"V": u"Vorlesung", u"Ü": u"Übung", u"P": u"Praktikum"} begin_date = None for week in data: if not begin_date: calweek = calweek_regex.match(week['week']) if not calweek: raise CannotParseCalweek("String was: %s" % week['week']) calweek = int(calweek.group(1)) begin_date = datetime.now() + rd.relativedelta(month=1, day=4, weekday=rd.MO(-1), weeks=+(calweek - 1), hour=0, minute=0, second=0, microsecond=0) else: begin_date = begin_date + rd.relativedelta(weeks=+1) for day in range(0,5): day_data = week['data'][day] day_date = begin_date + rd.relativedelta(days=+day) for time in day_data: for entry in day_data[time]: event = calendar.add('vevent') event.add('dtstart').value = day_date + times[time]["start"] event.add('dtend').value = day_date + times[time]["end"] cat = "" if entry["typ"][0] in cat_map: event.add('categories').value = ["UNI:" + cat_map[entry["typ"][0]]] cat = " (%s)" % cat_map[entry["typ"][0]] teacher = entry["room"] room_match = room_regex.match(entry["room"]) if room_match: event.add('location').value = room_match.group(1).strip() teacher = room_match.group(2) event.add('summary').value = "%s%s" % (entry['name'], cat) event.add('description').value = u"Kürzel: %s\nDozent: %s\nVeranstaltungsdyp: %s\nQuelle:%s" % (entry["short"], teacher, entry["typ"], sources[entry['source']].string) uid = uuid.uuid3(uuid.NAMESPACE_DNS, '%s %s' % (str(event.location.value), str(event.dtstart.value))) event.add("uid").value = str(uid) return calendar.serialize()
def events_calendar(request, public=True): cache_key = "calendar_%s" % ("public" if public else "private") cached = cache.get(cache_key) if cached: return cached cal = vobject.iCalendar() cal.add("X-WR-CALNAME").value = "Air Mozilla Public Events" if public else "Air Mozilla Private Events" now = datetime.datetime.utcnow().replace(tzinfo=utc) events = list( Event.objects.approved() .filter(start_time__lt=now, public=public) .order_by("-start_time")[: settings.CALENDAR_SIZE] ) events += list( Event.objects.approved() .filter(start_time__gte=now, public=public) .order_by("start_time")[: settings.CALENDAR_SIZE] ) base_url = "%s://%s/" % (request.is_secure() and "https" or "http", RequestSite(request).domain) for event in events: vevent = cal.add("vevent") vevent.add("summary").value = event.title vevent.add("dtstart").value = event.start_time vevent.add("dtend").value = event.start_time + datetime.timedelta(hours=1) vevent.add("description").value = unhtml(short_desc(event)) if event.location: vevent.add("location").value = event.location.name vevent.add("url").value = base_url + event.slug + "/" icalstream = cal.serialize() response = http.HttpResponse(icalstream, mimetype="text/calendar; charset=utf-8") filename = "AirMozillaEvents%s.ics" % ("Public" if public else "Private") response["Content-Disposition"] = "inline; filename=%s" % filename cache.set(cache_key, response) return response
def handler(request): """Handler which outputs an iCal feed.""" cal = vobject.iCalendar() # /ical/<key> try: # If a key is passed, return just that Event unused_ical, key = request.path_info.split('/') event = shortcuts.get_object_or_404(models.Event, pk=key) self.add_event(request.get_host(), event, cal) except IndexError: # else whole calendar. future_events = event_lists.get_future_events() current_events = event_lists.get_current_events() for event, _, _ in current_events.events: self.add_event(request.get_host(), event, cal) for event, _, _ in future_events.events: self.add_event(event, cal) response = http.HttpResponse() response['Content-Type'] = 'text/x-vCalendar' response.write(cal.serialize()) return response
def event_ical(request): event_list = Event.objects.filter(date__gte=datetime.now()-timedelta(weeks=4)).order_by('date') cal = vobject.iCalendar() cal.add('method').value = 'PUBLISH' # IE/Outlook needs this cal.add('x-wr-calname').value = "Subterrarium intern" for event in event_list: url = settings.SITE_URL + reverse('event', args=[event.id]) date = event.date - settings.EVENT_TIMEZONE.utcoffset(event.date) vevent = cal.add('vevent') if event.date.hour == 0 and event.date.minute == 0: vevent.add('X-FUNAMBOL-ALLDAY').value = '1' vevent.add('dtstart').value = datetime.date(event.date) vevent.add('dtend').value = datetime.date(event.date) + timedelta(days=1) else: vevent.add('dtstart').value = date vevent.add('dtend').value = date + timedelta(hours = 2) vevent.add('dtstamp').value = datetime.now() vevent.add('summary').value = event.title vevent.add('url').value = url vevent.add('uid').value = "%s-%s" % (event.id, settings.EVENT_REMINDER_FROM) vevent.add('description').value = url + "\n" + event.info icalstream = cal.serialize() response = HttpResponse(icalstream, mimetype='text/calendar; charset=utf-8') response['Filename'] = 'subkoordinator.ics' # IE needs this response['Content-Disposition'] = 'attachment; filename=subkoordinator.ics' return response
def __call__(self, *args, **kwargs): response = HttpResponse() cal = vobject.iCalendar() cal.add('X-WR-CALNAME').value = getattr(self, "name")() cal.add('X-WR-CALDESC').value = getattr(self, "description")() cal.add('X-WR-TIMEZONE').value = getattr(self, "timezone")() cal.add('CALSCALE').value = "GREGORIAN" cal.add('METHOD').value = "PUBLISH" for item in self.items(): event = cal.add('vevent') for vkey, key in EVENT_ITEMS: value = getattr(self, 'item_' + key)(item) if isinstance(value, str): value = smart_unicode(value) if value: event.add(vkey).value = value locale.setlocale(locale.LC_ALL, 'fr_CA.UTF8') response = HttpResponse(cal.serialize()) response['Content-Type'] = 'application/octet-stream; charset=utf-8' return response
def __call__(self, *args, **kwargs): self.args = args self.kwargs = kwargs cal = vobject.iCalendar() tz = pytz.timezone(settings.TIME_ZONE) for item in self.items(): event = cal.add('vevent') for vkey, key in EVENT_ITEMS: value = getattr(self, 'item_' + key)(item) if value: event.add(vkey).value = value rule = item.get_rrule_object(tz) if rule: event.add('rrule').value = reqstr(rule) response = HttpResponse(cal.serialize()) response['Content-Type'] = 'text/calendar' return response
def total_cal(request, account): parameters = ['-E', '-p', 'daily', '-J', '-d', 'd>=[today]-14 & d < [tomorrow]', '--sort', 'd', 'reg', '^' + account] output = runledger(parameters) cal = vobject.iCalendar() cal.add('method').value = 'PUBLISH' # IE/Outlook needs this cal.add('x-wr-calname').value = prettyname(account) + " Balance" cal.add('X-WR-TIMEZONE').value = "US/Pacific" cal.add('X-WR-CALDESC').value = "Daily Balance of account " + account for line in output.split('\n'): if(line == ""): continue date, total = line.split(' ') vevent = cal.add('vevent') vevent.add('summary').value = moneyFmt(float(total), 0) vevent.add('dtstart').value = datetime.datetime.strptime(date, "%Y-%m-%d").date() icalstream = cal.serialize() response = HttpResponse(icalstream, mimetype='text/calendar') response['Filename'] = 'ledger-balance.ics' # IE needs this response['Content-Disposition'] = 'attachment; filename=ledger-balance.ics' return response
def send(dbcal, calendar, client): n = 0 for obj in dbcal.event_set.filter(user_modified=True): dblogger.info("Gonna send %s", obj) n += 1 mycal = vobject.iCalendar() #~ mycal.add('vevent') #~ mycal = vobject.iCalendar() #~ vevent = vobject.newFromBehavior('vevent', '2.0') vevent = mycal.add('vevent') vevent.add('uid').value = obj.uid vevent.add('dtstamp').value = obj.modified if obj.start_time: vevent.add('dtstart').value = datetime.datetime.combine( obj.start_date, obj.start_time) else: vevent.add('dtstart').value = obj.start_date if obj.end_time: vevent.add('dtend').value = datetime.datetime.combine( obj.end_date, obj.end_time) else: vevent.add('dtend').value = obj.end_date vevent.add('transp').value = iif( obj.transparent, 'TRANSPARENT', 'OPAQUE') vevent.add('summary').value = obj.summary if obj.place: vevent.add('location').value = obj.place.name vevent.add('description').value = obj.description event = caldav.Event( client, data=mycal.serialize(), parent=calendar).save() dblogger.info("--> Sent %d events to calendar server.", n)
def xacts_cal(request, account): parameters = ['-F', '%(date)\t%(payee)\t%(amount)\n', '-d', 'd>=[today]-14 & d < [tomorrow]', '--sort', 'd', 'reg', '^' + account] output = runledger(parameters) cal = vobject.iCalendar() cal.add('method').value = 'PUBLISH' # IE/Outlook needs this cal.add('x-wr-calname').value = prettyname(account) + " Events" cal.add('X-WR-TIMEZONE').value = "US/Pacific" cal.add('X-WR-CALDESC').value = "Daily events of account " + account for line in output.split('\n'): if(line == ""): continue date, payee, amount = line.split('\t') vevent = cal.add('vevent') vevent.add('summary').value = payee + "\t" + amount vevent.add('dtstart').value = datetime.datetime.strptime(date, "%Y/%m/%d").date() icalstream = cal.serialize() response = HttpResponse(icalstream, mimetype='text/calendar') response['Filename'] = 'ledger-events.ics' # IE needs this response['Content-Disposition'] = 'attachment; filename=ledger-events.ics' return response
def schedule(): form = FORM( INPUT(_name='user_email', requires=IS_NOT_EMPTY()), TEXTAREA(_name="schedule_text", requires=IS_NOT_EMPTY()), INPUT(_type='submit')) if form.process().accepted: session.user_email = form.vars.user_email session.raw_schedule = form.vars.schedule_text #redirect(URL('second')) #Here is where we process the raw form data and create a vobject ical object m = re.search("(\w{1,})\s(\d{1,}),\s(\d{4})", session.raw_schedule) dt = m.group(0) start_date = datetime.strptime(dt, "%b %d, %Y") return dict(result="success", email=session.user_email, raw_schedule=session.raw_schedule) cal = vobject.iCalendar() cal.add('vevent') cal.vevent.add('summary').value = "This is a note" # return dict(email=session.user_email, text=session.schedule_text) return dict(form=form)
def process_read(self, cr, uid, node, context=None): """ @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param node: pass the node @param context: A standard dictionary for contextual values """ def ics_datetime(idate, short=False): if short: return datetime.date.fromtimestamp( time.mktime(time.strptime(idate, '%Y-%m-%d'))) else: return datetime.datetime.strptime(idate, '%Y-%m-%d %H:%M:%S') if node.extension != '.ics': return super(document_directory_content, self).process_read(cr, uid, node, context) import vobject ctx = (context or {}) ctx.update(node.context.context.copy()) ctx.update(node.dctx) content = self.browse(cr, uid, node.cnt_id, ctx) if not content.object_id: return super(document_directory_content, self).process_read(cr, uid, node, context) obj_class = self.pool.get(content.object_id.model) if content.ics_domain: domain = safe_eval(content.ics_domain, ctx) else: domain = [] if node.act_id: domain.append(('id', '=', node.act_id)) # print "process read clause:",domain ids = obj_class.search(cr, uid, domain, context=ctx) cal = vobject.iCalendar() for obj in obj_class.browse(cr, uid, ids): event = cal.add('vevent') # Fix dtstamp et last-modified with create and write date on the object line perm = obj_class.perm_read(cr, uid, [obj.id], context) event.add('created').value = ics_datetime( time.strftime('%Y-%m-%d %H:%M:%S')) event.add('dtstamp').value = ics_datetime( perm[0]['create_date'][:19]) if perm[0]['write_date']: event.add('last-modified').value = ics_datetime( perm[0]['write_date'][:19]) for field in content.ics_field_ids: if field.field_id.name: value = getattr(obj, field.field_id.name) else: value = None if (not value) and field.name == 'uid': value = 'OpenERP-%s_%s@%s' % ( content.object_id.model, str(obj.id), cr.dbname, ) # Why? obj_class.write(cr, uid, [obj.id], {field.field_id.name: value}) if ICS_TAGS[field.name] == 'normal': if type(value) == type(obj): value = value.name event.add(field.name).value = tools.ustr(value) or '' elif ICS_TAGS[field.name] == 'date' and value: if field.name == 'dtstart': date_start = start_date = datetime.datetime.fromtimestamp( time.mktime( time.strptime(value, "%Y-%m-%d %H:%M:%S"))) if field.name == 'dtend' and (isinstance(value, float) or field.fn == 'hours'): value = (start_date + datetime.timedelta(hours=value) ).strftime('%Y-%m-%d %H:%M:%S') if len(value) == 10: value = ics_datetime(value, True) else: value = ics_datetime(value) event.add(field.name).value = value s = cal.serialize() return s
parser.add_argument("input", help="Flat JSON input file") parser.add_argument("output", help="Output folder for calendar files") args = parser.parse_args() print("Using flat data file: " + args.input) # Read flat data with open(args.input) as input_file: data = load(input_file) # Generate calendar file for each tour for tour in data: tour_id = tour["tour_id"] cal = vobject.iCalendar() for event in tour["events"]: event_id = str(uuid.uuid4()) event_start = datetime.strptime(event, "%Y-%m-%d") event_start = date(event_start.year, event_start.month, event_start.day) event_end = event_start + timedelta(days=1) e = cal.add('vevent') e.add('uid').value = event_id e.add('summary').value = 'Blaue Tonne' e.add('dtstart').value = event_start e.add('dtend').value = event_end
def ical(self, id): c.schedule_collection = Location.find_by_id(id).schedule ical = vobject.iCalendar() for schedule in c.schedule_collection: if not schedule.time_slot.heading: event = ical.add('vevent') event.add('uid').value = str( schedule.id) + '@' + Config.get('event_host') # Created tz = timezone(Config.get('time_zone')) event.add( 'created').value = schedule.creation_timestamp.replace( tzinfo=tz) # Last Modified event.add( 'dtstamp' ).value = schedule.last_modification_timestamp.replace( tzinfo=tz) event.add( 'last-modified' ).value = schedule.last_modification_timestamp.replace( tzinfo=tz) # Start and End Time event.add( 'dtstart').value = schedule.time_slot.start_time.replace( tzinfo=tz) event.add('dtend').value = schedule.time_slot.end_time.replace( tzinfo=tz) # Title and Author (need to add Author here) event.add('summary').value = schedule.event.computed_title( ) + '. ' + h.list_to_string(schedule.event.computed_speakers()) # Abstract, if we have one event.add( 'description').value = schedule.event.computed_abstract() # Add a URL if schedule.event.proposal: event.add('url').value = h.url_for( qualified=True, controller='schedule', action='view_talk', id=schedule.event.proposal.id) elif not (schedule.event.url is None or schedule.event.url == ''): if schedule.event.url.startswith( 'https://') or schedule.event.url.startswith( 'http://'): event.add('url').value = h.url_for( str(schedule.event.url)) else: event.add('url').value = h.url_for(str( schedule.event.url), qualified=True) concurrent_schedules = schedule.event.schedule_by_time_slot( schedule.time_slot) for concurrent_schedule in concurrent_schedules: if concurrent_schedule != schedule: if concurrent_schedule in c.schedule_collection: c.schedule_collection.remove(concurrent_schedule) locations = [ concurrent_schedule.location.display_name for concurrent_schedule in concurrent_schedules ] event.add('location').value = h.list_to_string(locations) response.charset = 'utf8' response.headers['content-type'] = 'text/calendar; charset=utf8' response.headers.add('content-transfer-encoding', 'binary') response.headers.add('Pragma', 'cache') response.headers.add('Cache-Control', 'max-age=3600,public') return ical.serialize()
def events_calendar_ical(request, privacy=None): cache_key = 'calendar' if privacy: cache_key += '_%s' % privacy if request.GET.get('location'): if request.GET.get('location').isdigit(): location = get_object_or_404(Location, pk=request.GET.get('location')) else: location = get_object_or_404(Location, name=request.GET.get('location')) cache_key += str(location.pk) cached = None else: location = None cached = cache.get(cache_key) if cached: # additional response headers aren't remembered so add them again cached['Access-Control-Allow-Origin'] = '*' return cached cal = vobject.iCalendar() now = timezone.now() base_qs = Event.objects.approved() if privacy == 'public': base_qs = base_qs.filter(privacy=Event.PRIVACY_PUBLIC) title = 'Air Mozilla Public Events' elif privacy == 'private': base_qs = base_qs.exclude(privacy=Event.PRIVACY_PUBLIC) title = 'Air Mozilla Private Events' else: title = 'Air Mozilla Events' if location: base_qs = base_qs.filter(location=location) cal.add('X-WR-CALNAME').value = title events = list( base_qs.filter(start_time__lt=now).order_by('-start_time') [:settings.CALENDAR_SIZE]) events += list(base_qs.filter(start_time__gte=now).order_by('start_time')) base_url = get_base_url(request) for event in events: vevent = cal.add('vevent') vevent.add('summary').value = event.title vevent.add('dtstart').value = event.start_time vevent.add('dtend').value = ( event.start_time + datetime.timedelta(seconds=(event.duration or 3600))) vevent.add('description').value = short_desc(event, strip_html=True) if event.location: vevent.add('location').value = event.location.name vevent.add('url').value = base_url + event.slug + '/' icalstream = cal.serialize() # response = http.HttpResponse( # icalstream, # content_type='text/plain; charset=utf-8' # ) response = http.HttpResponse(icalstream, content_type='text/calendar; charset=utf-8') filename = 'AirMozillaEvents%s' % (privacy and privacy or '') if location: filename += '_%s' % slugify(location.name) filename += '.ics' response['Content-Disposition'] = ('inline; filename=%s' % filename) if not location: cache.set(cache_key, response, 60 * 10) # 10 minutes # https://bugzilla.mozilla.org/show_bug.cgi?id=909516 response['Access-Control-Allow-Origin'] = '*' return response
action="store", type="string", dest="icalfile", help="iCalendar file output") (options, args) = parser.parse_args() if options.icalfile == "": options.icalfile = None if options.icalfile != None: options.icalfile = os.path.realpath(options.icalfile) files = glob.glob(options.files) combinedCalendar = vobject.iCalendar() for i in files: if (DEBUG): print("Opening '%s'.." % i) f = open(i, 'rb') if (DEBUG): print("Reading '%s'.." % i) contents = f.read() out = open('%s_modded.ics' % i, 'w') s = [] for sentence in contents: s.append(sentence) if (sentence == "\n"): check_property = (''.join([str(x) for x in s])) check_property = check_property.replace(
def create_collection(cls, href, collection=None, props=None): folder = cls._get_collection_root_folder() # Path should already be sanitized sane_path = sanitize_path(href).strip("/") attributes = sane_path.split("/") if not attributes[0]: attributes.pop() principal = len(attributes) == 1 filesystem_path = path_to_filesystem(folder, sane_path) if not props: props = {} if not props.get("tag") and collection: props["tag"] = collection[0].name if not props: cls._makedirs_synced(filesystem_path) return cls(sane_path, principal=principal) parent_dir = os.path.dirname(filesystem_path) cls._makedirs_synced(parent_dir) # Create a temporary directory with an unsafe name with TemporaryDirectory(prefix=".Radicale.tmp-", dir=parent_dir) as tmp_dir: # The temporary directory itself can't be renamed tmp_filesystem_path = os.path.join(tmp_dir, "collection") os.makedirs(tmp_filesystem_path) self = cls("/", principal=principal, folder=tmp_filesystem_path) self.set_meta(props) if collection: if props.get("tag") == "VCALENDAR": collection, = collection items = [] for content in ("vevent", "vtodo", "vjournal"): items.extend( getattr(collection, "%s_list" % content, [])) items_by_uid = groupby(sorted(items, key=get_uid), get_uid) vobject_items = {} for uid, items in items_by_uid: new_collection = vobject.iCalendar() for item in items: new_collection.add(item) href = self._find_available_file_name( vobject_items.get) vobject_items[href] = new_collection self.upload_all_nonatomic(vobject_items) elif props.get("tag") == "VCARD": vobject_items = {} for card in collection: href = self._find_available_file_name( vobject_items.get) vobject_items[href] = card self.upload_all_nonatomic(vobject_items) # This operation is not atomic on the filesystem level but it's # very unlikely that one rename operations succeeds while the # other fails or that only one gets written to disk. if os.path.exists(filesystem_path): os.rename(filesystem_path, os.path.join(tmp_dir, "delete")) os.rename(tmp_filesystem_path, filesystem_path) cls._sync_directory(parent_dir) return cls(sane_path, principal=principal)
def event_assignments_ical(request): cache_key = 'event_assignements_ical' assignee = request.GET.get('assignee') if assignee: assignee = get_object_or_404(User, email=assignee) cache_key += str(assignee.pk) cached = cache.get(cache_key) if cached: # additional response headers aren't remembered so add them again cached['Access-Control-Allow-Origin'] = '*' return cached cal = vobject.iCalendar() now = timezone.now() base_qs = EventAssignment.objects.all().order_by('-event__start_time') if assignee: base_qs = base_qs.filter(users=assignee) title = 'Airmo' if assignee: title += ' for %s' % assignee.email else: title += ' crew assignments' cal.add('X-WR-CALNAME').value = title assignments = list( base_qs .filter(event__start_time__lt=now) [:settings.CALENDAR_SIZE] ) assignments += list( base_qs .filter(event__start_time__gte=now) ) base_url = get_base_url(request) for assignment in assignments: event = assignment.event vevent = cal.add('vevent') vevent.add('summary').value = "[AirMo crew] %s" % event.title # Adjusted start times for Event Assignment iCal feeds # to allow staff sufficient time for event set-up. vevent.add('dtstart').value = ( event.start_time - datetime.timedelta(minutes=30) ) vevent.add('dtend').value = ( event.start_time + datetime.timedelta(hours=1) ) emails = [u.email for u in assignment.users.all().order_by('email')] vevent.add('description').value = 'Assigned to: ' + ', '.join(emails) locations = [] if event.location: locations.append(event.location.name) locations.extend([ x.name for x in assignment.locations.all().order_by('name') ]) locations.sort() vevent.add('location').value = ', '.join(locations) vevent.add('url').value = ( base_url + reverse('main:event', args=(event.slug,)) ) icalstream = cal.serialize() # return http.HttpResponse(icalstream, # content_type='text/plain; charset=utf-8') response = http.HttpResponse(icalstream, content_type='text/calendar; charset=utf-8') filename = 'AirMozillaEventAssignments' filename += '.ics' response['Content-Disposition'] = ( 'inline; filename=%s' % filename) cache.set(cache_key, response, 60 * 10) # 10 minutes # https://bugzilla.mozilla.org/show_bug.cgi?id=909516 response['Access-Control-Allow-Origin'] = '*' return response
weeksToSearch = 6 resultCal = None if (os.path.isfile(os.getcwd() + '/calendar.cal')): print('Found cached calendar response') with open('calendar.cal', 'r') as cal: resultCal = vobject.readOne(cal) stale=time() - os.path.getmtime(os.getcwd() + '/calendar.cal') > (1*60*60) if stale: print('Calender data is stale, calling', calDavServer) try: client = caldav.DAVClient(calDavServer, username=user, password=passwd) principal = client.principal() start = datetime.utcnow() resultCal = vobject.iCalendar() for calendar in principal.calendars(): events = calendar.date_search( start=start, end=(start + timedelta(weeks=weeksToSearch)), expand=True) # expands recurring events to their _next_ occurrence for comp in events: vevent = comp.vobject_instance.vevent # Expanded recurring events contain 2 DTSTAMP lines, which is illegal, # leading to errors on .serialize(). Fix is to remove one such line (or all?) vevent.remove(vevent.dtstamp) resultCal.add(vevent) with open('calendar.cal', 'w') as cal: cal.write(resultCal.serialize()) except: print('Failed to get new API response, will use older response')
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') parser.add_argument('-i', '--insecure', action='store_true', help='Ignore SSL certificate') 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()) 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 = {} if args.davuser and args.davpass: user = args.davuser passwd = args.davpass else: try: (user, _, passwd) = netrc().authenticators(urlparse(args.davurl).netloc) except (IOError, TypeError): if not args.davuser: print( 'rem2dav: Error, argument -u/--davuser or netrc is required' ) return 1 user = args.davuser try: from keyring import get_password passwd = get_password(urlparse(args.davurl).netloc, user) except ImportError: passwd = None if not passwd: passwd = getpass() client = DAVClient(args.davurl, username=user, password=passwd, ssl_verify_cert=not args.insecure) calendar = Calendar(client, args.davurl) 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.keys() & odict.keys() rdict = {key: rdict[key] for key in intersect} else: rdict = {} local = ldict.keys() - rdict.keys() for uid in local: ncal = iCalendar() ncal.add(ldict[uid]) calendar.add_event(ncal.serialize()) if args.delete or args.old: remote = rdict.keys() - ldict.keys() for uid in remote: rdict[uid].delete()
def get_private_icals(event, positions): """ Return a list of ical objects based on a sequence of positions. Unlike get_public_ical, this will - Generate multiple ical files instead of one (but with deduplication applied) - Respect the mail_attach_ical_description setting It is private in the sense that mail_attach_ical_description may contain content not suited for public display. We however intentionally do not allow using placeholders based on the order and position specifically. This is for two reasons: - In reality, many people will add their invite to their calendar which is shared with a larger team. People are probably not aware that they're sharing sensitive information such as their secret ticket link with everyone they share their calendar with. - It would be pretty hard to implement it in a way that doesn't require us to use distinct settings fields for emails to customers and to attendees, which feels like an overcomplication. """ from pretix.base.services.mail import TolerantDict tz = pytz.timezone(event.settings.timezone) creation_time = datetime.datetime.now(pytz.utc) calobjects = [] evs = set(p.subevent or event for p in positions) for ev in evs: if isinstance(ev, Event): url = build_absolute_uri(event, 'presale:event.index') else: url = build_absolute_uri(event, 'presale:event.index', { 'subevent': ev.pk }) if event.settings.mail_attach_ical_description: ctx = get_email_context(event=event, event_or_subevent=ev) description = str(event.settings.mail_attach_ical_description).format_map(TolerantDict(ctx)) else: # Default description descr = [] descr.append(_('Tickets: {url}').format(url=url)) if ev.date_admission: descr.append(str(_('Admission: {datetime}')).format( datetime=date_format(ev.date_admission.astimezone(tz), 'SHORT_DATETIME_FORMAT') )) descr.append(_('Organizer: {organizer}').format(organizer=event.organizer.name)) description = '\n'.join(descr) cal = vobject.iCalendar() cal.add('prodid').value = '-//pretix//{}//'.format(settings.PRETIX_INSTANCE_NAME.replace(" ", "_")) vevent = cal.add('vevent') vevent.add('summary').value = str(ev.name) vevent.add('description').value = description vevent.add('dtstamp').value = creation_time if ev.location: vevent.add('location').value = str(ev.location) vevent.add('uid').value = 'pretix-{}-{}-{}@{}'.format( event.organizer.slug, event.organizer.slug, event.slug, ev.pk if not isinstance(ev, Event) else '0', urlparse(url).netloc ) if event.settings.show_times: vevent.add('dtstart').value = ev.date_from.astimezone(tz) else: vevent.add('dtstart').value = ev.date_from.astimezone(tz).date() if event.settings.show_date_to and ev.date_to: if event.settings.show_times: vevent.add('dtend').value = ev.date_to.astimezone(tz) else: # with full-day events date_to in pretix is included (e.g. last day) # whereas dtend in vcalendar is non-inclusive => add one day for export vevent.add('dtend').value = ev.date_to.astimezone(tz).date() + datetime.timedelta(days=1) calobjects.append(cal) return calobjects
def create_collection(cls, href, collection=None, props=None): """Create a collection. If the collection already exists and neither ``collection`` nor ``props`` are set, this method shouldn't do anything. Otherwise the existing collection must be replaced. ``collection`` is a list of vobject components. ``props`` are metadata values for the collection. ``props["tag"]`` is the type of collection (VCALENDAR or VADDRESSBOOK). If the key ``tag`` is missing, it is guessed from the collection. """ # Path should already be sanitized attributes = _get_attributes_from_path(href) if len(attributes) <= 1: raise PrincipalNotAllowedError # Try to infer tag if not props: props = {} if not props.get("tag") and collection: props["tag"] = collection[0].name # Try first getting the collection if exists, or create a new one otherwise. try: self = cls(href, principal=False, tag=props.get("tag")) except api.exceptions.DoesNotExist: user_path = posixpath.join('/', cls.user) collection_name = hashlib.sha256(str(time.time()).encode()).hexdigest() sane_path = posixpath.join(user_path, collection_name) if props.get("tag") == "VCALENDAR": inst = api.Calendar.create(cls.etesync, collection_name, None) elif props.get("tag") == "VADDRESSBOOK": inst = api.AddressBook.create(cls.etesync, collection_name, None) else: raise RuntimeError("Bad tag.") inst.save() self = cls(sane_path, principal=False) self.set_meta(props) if collection: if props.get("tag") == "VCALENDAR": collection, = collection items = [] for content in ("vevent", "vtodo", "vjournal"): items.extend( getattr(collection, "%s_list" % content, [])) items_by_uid = groupby(sorted(items, key=get_uid), get_uid) vobject_items = {} for uid, items in items_by_uid: new_collection = vobject.iCalendar() for item in items: new_collection.add(item) href = self._find_available_file_name( vobject_items.get) vobject_items[href] = new_collection self.upload_all_nonatomic(vobject_items) elif props.get("tag") == "VADDRESSBOOK": vobject_items = {} for card in collection: href = self._find_available_file_name( vobject_items.get) vobject_items[href] = card self.upload_all_nonatomic(vobject_items) return self
def ical(request, event_pk=None): """Return an ICS file for the given event, or for all events if no event primary key is provided. If a "term" URL parameter is given and no event pk is given, the view returns all events for that term. The view only shows events that the user is allowed to see, based on the "user" and "key" URL parameters (which correspond to the user's PK and API key, respectively). If the "user" and "key" parameters are not valid or are not provided, only publicly visible events are included. """ cal = vobject.iCalendar() cal.add('calscale').value = 'Gregorian' cal.add('X-WR-TIMEZONE').value = 'America/Los_Angeles' calendar_name = '{} Events'.format(settings.SITE_TAG) cal.add('X-WR-CALNAME').value = calendar_name cal.add('X-WR-CALDESC').value = calendar_name vtimezone = cal.add('vtimezone') vtimezone.add('tzid').value = 'America/Los_Angeles' vtimezone.add('X-LIC-LOCATION').value = 'America/Los_Angeles' dst = vtimezone.add('daylight') dst.add('tzoffsetfrom').value = '-0800' dst.add('tzoffsetto').value = '-0700' dst.add('tzname').value = 'PDT' dst.add('dtstart').value = datetime( 1970, 3, 8, 2, 0, 0, 0, tz('US/Pacific')) # '19700308T020000' dst.add('rrule').value = 'FREQ=YEARLY;BYMONTH=3;BYDAY=2SU' std = vtimezone.add('standard') std.add('tzoffsetfrom').value = '-0700' std.add('tzoffsetto').value = '-0800' std.add('tzname').value = 'PST' std.add('dtstart').value = datetime( 1970, 11, 1, 2, 0, 0, 0, tz('US/Pacific')) # '19701101T020000' std.add('rrule').value = 'FREQ=YEARLY;BYMONTH=11;BYDAY=1SU' user = None user_pk = request.GET.get('user', None) key = request.GET.get('key', None) if user_pk and key: try: api_key = APIKey.objects.get(user__pk=user_pk, key=key) user = api_key.user except APIKey.DoesNotExist: pass if event_pk is None: # We want multiple events filename = 'events.ics' term = request.GET.get('term', '') events = Event.objects.get_user_viewable(user).filter(cancelled=False) if term: # Filter by the given term term_obj = Term.objects.get_by_url_name(term) events = events.filter(term=term_obj) for event in events: add_event_to_ical(event, cal) else: # We want a specific event event = get_object_or_404(Event, pk=event_pk) if not event.can_user_view(user): raise PermissionDenied filename = 'event.ics' add_event_to_ical(event, cal) response = HttpResponse(cal.serialize(), content_type='text/calendar') response['Filename'] = filename # IE needs this response['Content-Disposition'] = 'attachment; filename={}'.format(filename) return response
def _get_ics_file(self): """ Returns iCalendar file for the event invitation. :returns a dict of .ics file content for each meeting """ result = {} def ics_datetime(idate, allday=False): if idate: if allday: return idate else: return idate.replace(tzinfo=pytz.timezone('UTC')) return False try: # FIXME: why isn't this in CalDAV? import vobject except ImportError: _logger.warning( "The `vobject` Python module is not installed, so iCal file generation is unavailable. Please install the `vobject` Python module" ) return result for meeting in self: cal = vobject.iCalendar() cal.add('method').value = 'REQUEST' cal.add('calscale').value = 'GREGORIAN' event = cal.add('vevent') if not meeting.start or not meeting.stop: raise UserError( _("First you have to specify the date of the invitation.")) event.add('created').value = ics_datetime(fields.Datetime.now()) event.add('dtstart').value = ics_datetime(meeting.start, meeting.allday) event.add('dtend').value = ics_datetime(meeting.stop, meeting.allday) event.add('summary').value = meeting.name event.add('class').value = 'PUBLIC' event.add('trans').value = 'OPAQUE' event.add('location').value = 'COLOMBIA' #event already cancel state if self.state == 'cancel': event.add('status').value = 'CANCELLED' else: event.add('status').value = 'CONFIRMED' #event.add('uid').value = self.appointment_id.process_number + datetime.datetime.now().strftime("%Y%m%Y/%H:%M:%S") event.add( 'uid' ).value = self.appointment_id.process_number + self.appointment_id.calendar_datetime.strftime( "%Y%m%d %H%M%S") event.add('sequence').value = str( self.appointment_id.sequence_icsfile_ctl) if meeting.description: event.add('description').value = meeting.description if meeting.location: event.add('location').value = meeting.location if meeting.rrule: event.add('rrule').value = meeting.rrule organizer_add = event.add('organizer') cn_value = "*****@*****.**" for server_id in self.env['ir.mail_server'].search([]): for user_id in server_id.user_ids: if self.env.uid == user_id.id: cn_value = server_id.smtp_user organizer_add.params['CN'] = [ "sistemaaudiencias.ramajudicial.gov.co" ] organizer_add.params['ROLE'] = ["CHAIR"] organizer_add.params['RSVP'] = ["TRUE"] organizer_add.value = cn_value if meeting.alarm_ids: for alarm in meeting.alarm_ids: valarm = event.add('valarm') interval = alarm.interval duration = alarm.duration trigger = valarm.add('TRIGGER') trigger.params['related'] = ["START"] if interval == 'days': delta = timedelta(days=duration) elif interval == 'hours': delta = timedelta(hours=duration) elif interval == 'minutes': delta = timedelta(minutes=duration) trigger.value = delta valarm.add('DESCRIPTION').value = alarm.name or u'Odoo' for attendee in meeting.attendee_ids: attendee_add = event.add('attendee') attendee_add.params['CUTYPE'] = ["INDIVIDUAL"] attendee_add.params['ROLE'] = ["REQ-PARTICIPANT"] attendee_add.params['PARTSTAT'] = ["ACCEPTED"] #attendee_add.params['PARTSTAT'] = ["NEEDS-ACTION"] attendee_add.params['RSVP'] = ["TRUE"] attendee_add.params['CN'] = [attendee.email] attendee_add.params['X-NUM-GUESTS'] = ["0"] attendee_add.value = (attendee.email or u'') result[meeting.id] = cal.serialize().encode('utf-8') return result
def events_calendar(request, privacy=None): cache_key = 'calendar' if privacy: cache_key += '_%s' % privacy if request.GET.get('location'): location = get_object_or_404(Location, name=request.GET.get('location')) cache_key += str(location.pk) cached = None else: location = None cached = cache.get(cache_key) if cached: return cached cal = vobject.iCalendar() now = datetime.datetime.utcnow().replace(tzinfo=utc) base_qs = Event.objects.approved() if privacy == 'public': base_qs = base_qs.filter(privacy=Event.PRIVACY_PUBLIC) title = 'Air Mozilla Public Events' elif privacy == 'private': base_qs = base_qs.exclude(privacy=Event.PRIVACY_PUBLIC) title = 'Air Mozilla Private Events' else: title = 'Air Mozilla Events' if location: base_qs = base_qs.filter(location=location) cal.add('X-WR-CALNAME').value = title events = list( base_qs.filter(start_time__lt=now).order_by('-start_time') [:settings.CALENDAR_SIZE]) events += list( base_qs.filter(start_time__gte=now).order_by('start_time') [:settings.CALENDAR_SIZE]) base_url = '%s://%s/' % (request.is_secure() and 'https' or 'http', RequestSite(request).domain) for event in events: vevent = cal.add('vevent') vevent.add('summary').value = event.title vevent.add('dtstart').value = event.start_time vevent.add('dtend').value = (event.start_time + datetime.timedelta(hours=1)) vevent.add('description').value = unhtml(short_desc(event)) if event.location: vevent.add('location').value = event.location.name vevent.add('url').value = base_url + event.slug + '/' icalstream = cal.serialize() #response = http.HttpResponse(icalstream, # mimetype='text/plain; charset=utf-8') response = http.HttpResponse(icalstream, mimetype='text/calendar; charset=utf-8') filename = 'AirMozillaEvents%s' % (privacy and privacy or '') if location: filename += '_%s' % slugify(location.name) filename += '.ics' response['Content-Disposition'] = ('inline; filename=%s' % filename) if not location: cache.set(cache_key, response) return response
def __init__(self): self._cal = vobject.iCalendar() self._cal.add(vobject.readOne(TIMEZONE, validate=True))
def icalendar(request, queryset, date_field, ical_filename, title_field='title', description_field='description', last_modified_field=None, location_field=None, start_time_field=None, end_time_field=None, num_objects=15, extra_context=None, mimetype=None, context_processors=None): now = datetime.now() queryset = queryset.filter(begin_date__gte=now - timedelta(days=1)) cal = vobject.iCalendar() utc = vobject.icalendar.utc cal.add('method').value = 'PUBLISH' # IE/Outlook needs this # Timezone code borrowed from now = datetime.now() utcnow = datetime.utcnow() # Must always subtract smaller time from larger time here. if utcnow > now: sign = -1 tzDifference = (utcnow - now) else: sign = 1 tzDifference = (now - utcnow) # Round the timezone offset to the nearest half hour. tzOffsetMinutes = sign * ((tzDifference.seconds / 60 + 15) / 30) * 30 tzOffset = timedelta(minutes=tzOffsetMinutes) #cal.add('vtimezone').value = FixedOffset(tzOffset) mytz = FixedOffset(tzOffset) for event in queryset: vevent = cal.add('vevent') vevent.add('summary').value = strip_tags(getattr(event, title_field)) vevent.add('description').value = strip_tags(getattr(event, description_field)) start_time = getattr(event, start_time_field, None) if start_time: start_date = datetime.combine(getattr(event, date_field), event.start_time) end_time = getattr(event, end_time_field, None) if end_time: end_date = datetime.combine(getattr(event, date_field), event.end_time) vevent.add('dtend').value = end_date.replace(tzinfo = mytz) else: start_date = getattr(event, date_field) vevent.add('dtstart').value = start_date.replace(tzinfo = mytz) last_modified = getattr(event, last_modified_field, None) if last_modified: vevent.add('last-modified').value = last_modified.replace(tzinfo = mytz) location = getattr(event, location_field, None) if location: vevent.add('location').value = strip_tags(location) icalstream = cal.serialize() response = HttpResponse(icalstream, mimetype='text/calendar') response['Filename'] = ical_filename # IE needs this response['Content-Disposition'] = 'attachment; filename=%s' % ical_filename return response
def _get_ics_file(self): """ Returns iCalendar file for the event invitation. :returns a dict of .ics file content for each meeting """ result = {} def ics_datetime(idate, allday=False): if idate: if allday: return idate return idate.replace(tzinfo=pytz.timezone('UTC')) return False try: # FIXME: why isn't this in CalDAV? import vobject except ImportError: _logger.warning( "The `vobject` Python module is not installed, so iCal file generation is unavailable. Please install the `vobject` Python module" ) return result for meeting in self: cal = vobject.iCalendar() event = cal.add('vevent') if not meeting.start or not meeting.stop: raise UserError( _("First you have to specify the date of the invitation.")) event.add('created').value = ics_datetime(fields.Datetime.now()) event.add('dtstart').value = ics_datetime(meeting.start, meeting.allday) event.add('dtend').value = ics_datetime(meeting.stop, meeting.allday) event.add('summary').value = meeting.name if meeting.description: event.add('description').value = meeting.description if meeting.location: event.add('location').value = meeting.location if meeting.rrule: event.add('rrule').value = meeting.rrule if meeting.alarm_ids: for alarm in meeting.alarm_ids: valarm = event.add('valarm') interval = alarm.interval duration = alarm.duration trigger = valarm.add('TRIGGER') trigger.params['related'] = ["START"] if interval == 'days': delta = timedelta(days=duration) elif interval == 'hours': delta = timedelta(hours=duration) elif interval == 'minutes': delta = timedelta(minutes=duration) trigger.value = delta valarm.add('DESCRIPTION').value = alarm.name or u'Odoo' for attendee in meeting.attendee_ids: attendee_add = event.add('attendee') attendee_add.value = u'MAILTO:' + (attendee.email or u'') result[meeting.id] = cal.serialize().encode('utf-8') return result
def _get_ics_file(self): """ Returns iCalendar file for the event invitation. :returns a dict of .ics file content for each meeting """ result = {} def ics_datetime(idate, allday=False): if idate: if allday: return idate return idate.replace(tzinfo=pytz.timezone('UTC')) return False if not vobject: return result for meeting in self: cal = vobject.iCalendar() event = cal.add('vevent') if not meeting.start or not meeting.stop: raise UserError( _("First you have to specify the date of the invitation.")) event.add('created').value = ics_datetime(fields.Datetime.now()) event.add('dtstart').value = ics_datetime(meeting.start, meeting.allday) event.add('dtend').value = ics_datetime(meeting.stop, meeting.allday) event.add('summary').value = meeting.name if not is_html_empty(meeting.description): if 'appointment_type_id' in meeting._fields and self.appointment_type_id: # convert_online_event_desc_to_text method for correct data formatting in external calendars event.add('description' ).value = self.convert_online_event_desc_to_text( meeting.description) else: event.add('description').value = html2plaintext( meeting.description) if meeting.location: event.add('location').value = meeting.location if meeting.rrule: event.add('rrule').value = meeting.rrule if meeting.alarm_ids: for alarm in meeting.alarm_ids: valarm = event.add('valarm') interval = alarm.interval duration = alarm.duration trigger = valarm.add('TRIGGER') trigger.params['related'] = ["START"] if interval == 'days': delta = timedelta(days=duration) elif interval == 'hours': delta = timedelta(hours=duration) elif interval == 'minutes': delta = timedelta(minutes=duration) trigger.value = delta valarm.add('DESCRIPTION').value = alarm.name or u'Odoo' for attendee in meeting.attendee_ids: attendee_add = event.add('attendee') attendee_add.value = u'MAILTO:' + (attendee.email or u'') result[meeting.id] = cal.serialize().encode('utf-8') return result
def handle_add_appoint(self, message): event = message.data.get("event") while not event: #We need to get the event event = self.get_response("new.event.name") utterance = message.data['utterance'] date, rest = extract_datetime(utterance, datetime.now(), self.lang) while rest == normalize(utterance): utterance = self.get_response("new.event.date") date, rest = extract_datetime(utterance) self.log.info(" Calendar skill new event: date: " + str(date) + " event: " + event) #ADD EVENT server_type = self.check_server() if server_type == "Handled": return elif server_type == True: #Home server_address = self.settings.get("server_address") port = self.settings.get("port") username = self.settings.get("username") password = self.settings.get("password") #start creating a vevent: cal = vobject.iCalendar() cal.add("vevent") #add name cal.vevent.add("summary").value = str(event) #add date cal.vevent.add('dtstart').value = date # add it to the calendar url = "http://{}:{}@{}:{}/".format(username, password, server_address, port) try: client = caldav.DAVClient(url) principal = client.principal() #Select calendar events = [] for calendar in principal.calendars(): calendar.add_event(str(cal.serialize())) self.speak_dialog("new.event.summary", data={"event": str(event)}) except: self.speak_dialog("error.logging.in") return None elif server_type is None: # Password local server_address = self.settings.get("server_address") calendar = self.settings.get("calendar") port = self.settings.get("port") account_config = self.config_core.get("calendar", {}) username = account_config.get("username") password = account_config.get("password") # start creating a vevent: cal = vobject.iCalendar() cal.add("vevent") # add name cal.vevent.add("summary").value = str(event) # add date cal.vevent.add('dtstart').value = date # add it to the calendar url = "http://{}:{}@{}:{}/".format(username, password, server_address, port) try: client = caldav.DAVClient(url) principal = client.principal() # Select calendar events = [] for calendar in principal.calendars(): calendar.add_event(str(cal.serialize())) self.speak_dialog("new.event.summary", data={"event": str(event)}) except: self.speak_dialog("error.logging.in") return None elif server_type == False: #Local #The calendar is on the device #Check if it needs to be made... fs = FileSystemAccess(str(self.skill_id)) if fs.exists("calendar.ics"): #YAY! exists calendar = self.read_file("calendar.ics") c = ics.Calendar(calendar) e = ics.Event() #add event e.name = str(event) e.begin = str(arrow.get(date)) c.events.apaddpend(e) self.write_file("calendar.ics", str(c)) self.speak_dialog("new.event.summary", data={"event": str(event)}) else: #create calendar c = ics.Calendar() e = ics.Event() #add event e.name = str(event) e.begin = str(arrow.get(date)) c.events.add(e) self.write_file("calendar.ics", str(c)) self.speak_dialog("new.event.summary", data={"event": str(event)})
def calendar_vcal(request, key): base_url = '%s://%s' % (request.is_secure() and 'https' or 'http', RequestSite(request).domain) home_url = base_url + '/' cal = vobject.iCalendar() cal.add('x-wr-calname').value = 'Mozilla PTO' try: user = UserKey.objects.get(key=key).user except UserKey.DoesNotExist: # instead of raising a HTTP error, respond a calendar # that urges the user to update the stale URL event = cal.add('vevent') event.add('summary').value = ( "Calendar expired. Visit %s#calendarurl to get the " "new calendar URL" % home_url) today = datetime.date.today() event.add('dtstart').value = today event.add('dtend').value = today event.add('url').value = '%s#calendarurl' % (home_url, ) event.add('description').value = ( "The calendar you used has expired " "and is no longer associated with any user") return _render_vcalendar(cal, key) # always start on the first of this month today = datetime.date.today() #first = datetime.date(today.year, today.month, 1) user_ids = [user.pk] for user_ in get_observed_users(user, max_depth=2): user_ids.append(user_.pk) entries = (Entry.objects.filter(user__in=user_ids, total_hours__gte=0, total_hours__isnull=False, end__gte=today).select_related('user')) _list_base_url = base_url + reverse('dates.list') def make_list_url(entry): name = entry.user.get_full_name() if not name: name = entry.user.username data = { 'date_from': entry.start.strftime('%d %B %Y'), 'date_to': entry.end.strftime('%d %B %Y'), 'name': name } return _list_base_url + '?' + urlencode(data, True) for entry in entries: event = cal.add('vevent') event.add('summary').value = '%s PTO' % make_entry_title( entry, user, include_details=False) event.add('dtstart').value = entry.start event.add('dtend').value = entry.end #url = (home_url + '?cal_y=%d&cal_m=%d' % # (slot.date.year, slot.date.month)) event.add('url').value = make_list_url(entry) #event.add('description').value = entry.details event.add('description').value = "Log in to see the details" return _render_vcalendar(cal, key)
def _get_new_vcal(cls) -> iCalendar: vcal = iCalendar() vcal.add('PRODID').value = cls.GTG_PRODID vcal.add('vtodo') return vcal
def createExamEvent(self, exam: Exam, lesson: Lesson, title: str = "", body: str = ""): calendar = vobject.iCalendar() _title = exam.subject.name + ": " + exam.description if title != "": _title += " " + title calendar.add('vevent').add('summary').value = _title calendar.vevent.add( 'description' ).value = "Teacher: " + exam.teacher.name + "(" + exam.teacher.short + ")\n" + "Description: " + exam.description calendar.vevent.add("dtstart").value = timezone( "Europe/Warsaw").localize( datetime.datetime(lesson.from_.year, lesson.from_.month, lesson.from_.day, lesson.from_.hour, lesson.from_.minute, lesson.from_.second)).astimezone( timezone("UTC")) calendar.vevent.add("dtend").value = timezone( "Europe/Warsaw").localize( datetime.datetime( lesson.to.year, lesson.to.month, lesson.to.day, lesson.to.hour, lesson.to.minute, lesson.to.second)).astimezone(timezone("UTC")) if exam.type == ExamType.EXAM: valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=14) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=7) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=3) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=2) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=1) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=0) elif exam.type == ExamType.SHORT_TEST: valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=3) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=2) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=1) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=0) else: valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=1) valarm = calendar.vevent.add('valarm') valarm.add('action').value = "AUDIO" valarm.add("trigger").value = lesson.from_ - datetime.timedelta( days=0) return calendar
def prepare(vobject_items, tag=None, write_whole_collection=None): if (write_whole_collection or permissions and not parent_permissions): write_whole_collection = True content_type = environ.get("CONTENT_TYPE", "").split(";")[0] tags = { value: key for key, value in xmlutils.MIMETYPES.items() } tag = radicale_item.predict_tag_of_whole_collection( vobject_items, tags.get(content_type)) if not tag: raise ValueError("Can't determine collection tag") collection_path = pathutils.strip_path(path) elif (write_whole_collection is not None and not write_whole_collection or not permissions and parent_permissions): write_whole_collection = False if tag is None: tag = radicale_item.predict_tag_of_parent_collection( vobject_items) collection_path = posixpath.dirname(pathutils.strip_path(path)) props = None stored_exc_info = None items = [] try: if tag: radicale_item.check_and_sanitize_items( vobject_items, is_collection=write_whole_collection, tag=tag) if write_whole_collection and tag == "VCALENDAR": vobject_components = [] vobject_item, = vobject_items for content in ("vevent", "vtodo", "vjournal"): vobject_components.extend( getattr(vobject_item, "%s_list" % content, [])) vobject_components_by_uid = itertools.groupby( sorted(vobject_components, key=radicale_item.get_uid), radicale_item.get_uid) for uid, components in vobject_components_by_uid: vobject_collection = vobject.iCalendar() for component in components: vobject_collection.add(component) item = radicale_item.Item( collection_path=collection_path, vobject_item=vobject_collection) item.prepare() items.append(item) elif write_whole_collection and tag == "VADDRESSBOOK": for vobject_item in vobject_items: item = radicale_item.Item( collection_path=collection_path, vobject_item=vobject_item) item.prepare() items.append(item) elif not write_whole_collection: vobject_item, = vobject_items item = radicale_item.Item( collection_path=collection_path, vobject_item=vobject_item) item.prepare() items.append(item) if write_whole_collection: props = {} if tag: props["tag"] = tag if tag == "VCALENDAR" and vobject_items: if hasattr(vobject_items[0], "x_wr_calname"): calname = vobject_items[0].x_wr_calname.value if calname: props["D:displayname"] = calname if hasattr(vobject_items[0], "x_wr_caldesc"): caldesc = vobject_items[0].x_wr_caldesc.value if caldesc: props["C:calendar-description"] = caldesc radicale_item.check_and_sanitize_props(props) except Exception: stored_exc_info = sys.exc_info() # Use generator for items and delete references to free memory # early def items_generator(): while items: yield items.pop(0) return (items_generator(), tag, write_whole_collection, props, stored_exc_info)
def testInvitationDelegation(self): """ invitation delegation """ ics_name = "test-delegation.ics" self.ics_list += [ics_name] # the invitation must not exist self._deleteEvent(self.client, "%s%s" % (self.user_calendar, ics_name), None) self._deleteEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), None) self._deleteEvent( self.attendee1_delegate_client, "%s%s" % (self.attendee1_delegate_calendar, ics_name), None) # 1. org -> attendee => org: 1, attendee: 1 (pst=N-A), delegate: 0 invitation = self._newEvent() invitation.add("method").value = "REQUEST" organizer = invitation.vevent.add('organizer') organizer.cn_param = self.user_name organizer.value = self.user_email attendee = invitation.vevent.add('attendee') attendee.cn_param = self.attendee1_name attendee.rsvp_param = "TRUE" attendee.partstat_param = "NEEDS-ACTION" attendee.value = self.attendee1_email self._postEvent(self.client, self.user_calendar, invitation, self.user_email, [self.attendee1_email]) del invitation.method self._putEvent(self.client, "%stest-delegation.ics" % self.user_calendar, invitation) att_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar) self._compareAttendees(att_inv, invitation) # 2. attendee delegates to delegate # => org: 1 (updated), attendee: 1 (updated,pst=D), # delegate: 1 (new,pst=N-A) invitation.add("method").value = "REQUEST" attendee1 = invitation.vevent.attendee attendee1.partstat_param = "DELEGATED" attendee1.delegated_to_param = self.attendee1_delegate_email delegate = invitation.vevent.add('attendee') delegate.delegated_from_param = self.attendee1_email delegate.cn_param = self.attendee1_delegate_name delegate.rsvp_param = "TRUE" delegate.partstat_param = "NEEDS-ACTION" delegate.value = self.attendee1_delegate_email self._postEvent(self.attendee1_client, self.attendee1_calendar, invitation, self.attendee1_email, [self.attendee1_delegate_email]) invitation.method.value = "REPLY" self._postEvent(self.attendee1_client, self.attendee1_calendar, invitation, self.attendee1_email, [self.user_email]) del invitation.method self._putEvent(self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar, invitation, 204) del_inv = self._getEvent( self.attendee1_delegate_client, "%stest-delegation.ics" % self.attendee1_delegate_calendar) self._compareAttendees(del_inv, invitation) org_inv = self._getEvent(self.client, "%stest-delegation.ics" % self.user_calendar) self._compareAttendees(org_inv, invitation) # 3. delegate accepts # => org: 1 (updated), attendee: 1 (updated,pst=D), # delegate: 1 (accepted,pst=A) invitation.add("method").value = "REPLY" delegate.partstat_param = "ACCEPTED" self._postEvent(self.attendee1_delegate_client, self.attendee1_delegate_calendar, invitation, self.attendee1_delegate_email, [self.user_email, self.attendee1_email]) del invitation.method self._putEvent( self.attendee1_delegate_client, "%stest-delegation.ics" % self.attendee1_delegate_calendar, invitation, 204) org_inv = self._getEvent(self.client, "%stest-delegation.ics" % self.user_calendar) self._compareAttendees(org_inv, invitation) att_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar) self._compareAttendees(att_inv, invitation) # 4. attendee accepts # => org: 1 (updated), attendee: 1 (updated,pst=A), # delegate: 0 (cancelled, deleted) cancellation = vobject.iCalendar() cancellation.copy(invitation) cancellation.add("method").value = "CANCEL" cancellation.vevent.sequence.value = "1" self._postEvent(self.attendee1_client, self.attendee1_calendar, cancellation, self.attendee1_email, [self.attendee1_delegate_email]) attendee1 = invitation.vevent.attendee attendee1.partstat_param = "ACCEPTED" del attendee1.delegated_to_param invitation.add("method").value = "REPLY" invitation.vevent.remove(delegate) self._postEvent(self.attendee1_client, self.attendee1_calendar, invitation, self.attendee1_email, [self.user_email]) del invitation.method self._putEvent(self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar, invitation, 204) org_inv = self._getEvent(self.client, "%stest-delegation.ics" % self.user_calendar) self._compareAttendees(org_inv, invitation) del_inv = self._getEvent( self.attendee1_delegate_client, "%stest-delegation.ics" % self.attendee1_delegate_calendar, 404) # 5. org updates inv. # => org: 1 (updated), attendee: 1 (updated), delegate: 0 invitation.add("method").value = "REQUEST" invitation.vevent.summary.value = "Updated invitation" invitation.vevent.sequence.value = "1" attendee.partstat_param = "NEEDS-ACTION" now = datetime.datetime.now() invitation.vevent.last_modified.value = now invitation.vevent.dtstamp.value = now self._postEvent(self.client, self.user_calendar, invitation, self.user_email, [self.attendee1_email]) del invitation.method self._putEvent(self.client, "%stest-delegation.ics" % self.user_calendar, invitation, 204) att_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar) self._compareAttendees(att_inv, invitation) # 6. attendee delegates to delegate # => org: 1 (updated), attendee: 1 (updated), delegate: 1 (new) invitation.add("method").value = "REQUEST" attendee1.partstat_param = "DELEGATED" attendee1.delegated_to_param = self.attendee1_delegate_email delegate = invitation.vevent.add('attendee') delegate.delegated_from_param = self.attendee1_email delegate.cn_param = self.attendee1_delegate_name delegate.rsvp_param = "TRUE" delegate.partstat_param = "NEEDS-ACTION" delegate.value = self.attendee1_delegate_email self._postEvent(self.attendee1_client, self.attendee1_calendar, invitation, self.attendee1_email, [self.attendee1_delegate_email]) invitation.method.value = "REPLY" self._postEvent(self.attendee1_client, self.attendee1_calendar, invitation, self.attendee1_email, [self.user_email]) del invitation.method self._putEvent(self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar, invitation, 204) org_inv = self._getEvent(self.client, "%stest-delegation.ics" % self.user_calendar) self._compareAttendees(org_inv, invitation) del_inv = self._getEvent( self.attendee1_delegate_client, "%stest-delegation.ics" % self.attendee1_delegate_calendar) self._compareAttendees(del_inv, invitation) # 7. delegate accepts # => org: 1 (updated), attendee: 1 (updated), delegate: 1 (accepted) invitation.add("method").value = "REPLY" delegate.partstat_param = "ACCEPTED" self._postEvent(self.attendee1_delegate_client, self.attendee1_delegate_calendar, invitation, self.attendee1_delegate_email, [self.user_email, self.attendee1_email]) del invitation.method self._putEvent( self.attendee1_delegate_client, "%stest-delegation.ics" % self.attendee1_delegate_calendar, invitation, 204) org_inv = self._getEvent(self.client, "%stest-delegation.ics" % self.user_calendar) self._compareAttendees(org_inv, invitation) att_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar) self._compareAttendees(att_inv, invitation) # 8. org updates inv. # => org: 1 (updated), attendee: 1 (updated,partstat unchanged), # delegate: 1 (updated,partstat reset) invitation.add("method").value = "REQUEST" now = datetime.datetime.now() invitation.vevent.last_modified.value = now invitation.vevent.dtstamp.value = now invitation.vevent.summary.value = "Updated invitation (again)" invitation.vevent.sequence.value = "2" delegate.partstat_param = "NEEDS-ACTION" self._postEvent(self.client, self.user_calendar, invitation, self.user_email, [self.attendee1_email, self.attendee1_delegate_email]) del invitation.method self._putEvent(self.client, "%stest-delegation.ics" % self.user_calendar, invitation, 204) att_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar) self._compareAttendees(att_inv, invitation) del_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar) self._compareAttendees(del_inv, invitation) # 9. org cancels invitation # => org: 1 (updated), attendee: 0 (cancelled, deleted), # delegate: 0 (cancelled, deleted) invitation.add("method").value = "CANCEL" now = datetime.datetime.now() invitation.vevent.last_modified.value = now invitation.vevent.dtstamp.value = now invitation.vevent.summary.value = "Cancelled invitation (again)" invitation.vevent.sequence.value = "3" self._postEvent(self.client, self.user_calendar, invitation, self.user_email, [self.attendee1_email, self.attendee1_delegate_email]) del invitation.method invitation.vevent.remove(attendee) invitation.vevent.remove(delegate) self._putEvent(self.client, "%stest-delegation.ics" % self.user_calendar, invitation, 204) att_inv = self._getEvent( self.attendee1_client, "%stest-delegation.ics" % self.attendee1_calendar, 404) del_inv = self._getEvent( self.attendee1_delegate_client, "%stest-delegation.ics" % self.attendee1_delegate_calendar, 404)
def itemsToVObject(view, items, cal=None, filters=None): """Iterate through items, add to cal, create a new vcalendar if needed. Chandler doesn't do recurrence yet, so for now we don't worry about timezones. """ taskKind = view.findPath(ICalendarFormat._taskPath) eventKind = view.findPath(ICalendarFormat._calendarEventPath) if cal is None: cal = vobject.iCalendar() for item in items: if item.isItemOf(taskKind): taskorevent = 'TASK' comp = cal.add('vtodo') elif item.isItemOf(eventKind): taskorevent = 'EVENT' comp = cal.add('vevent') else: continue if item.getAttributeValue('icalUID', default=None) is None: item.icalUID = unicode(item.itsUUID) comp.add('uid').value = item.icalUID try: comp.add('summary').value = item.displayName except AttributeError: pass try: comp.add('dtstart').value = dateForVObject(item.startTime, item.allDay) except AttributeError: pass try: if taskorevent == 'TASK': comp.add('due').value = dateForVObject(item.dueDate, item.allDay) else: comp.add('dtend').value = dateForVObject( item.endTime, item.allDay) except AttributeError: pass if not filters or "transparency" not in filters: try: if taskorevent == 'EVENT': status = item.transparency.upper() if status == 'FYI': status = 'CANCELLED' comp.add('status').value = status except AttributeError: pass try: comp.add('description').value = item.body.getReader().read() except AttributeError: pass try: comp.add('location').value = item.location.displayName except AttributeError: pass if not filters or "reminderTime" not in filters: try: comp.add('valarm').add('trigger').value = \ dateForVObject(item.reminderTime) - \ dateForVObject(item.startTime) except AttributeError: pass return cal
def itemsToVObject(view, items, cal=None, filters=None): """ Iterate through items, add to cal, create a new vcalendar if needed. Consider only master events (then serialize all modifications). For now, set all timezones to Pacific. """ if filters is None: filters = () # we want filters to be iterable def makeDateTimeValue(dt, asDate=False): if asDate: return dt.date() elif dt.tzinfo == view.tzinfo.floating: return dt.replace(tzinfo=None) else: return dt def populateCommon(comp, item): """ Populate the given vevent or vtodo vobject with values for attributes common to Events or Tasks). """ if getattr(item, Note.icalUID.name, None) is None: item.icalUID = unicode(item.itsUUID) comp.add('uid').value = item.icalUID # displayName --> SUMMARY try: summary = item.displayName except AttributeError: pass else: comp.add('summary').value = summary # body --> DESCRIPTION try: description = item.body except AttributeError: pass else: if description: comp.add('description').value = description # userReminder --> VALARM if Remindable.reminders.name not in filters: firstReminder = item.getUserReminder() if firstReminder is not None: if firstReminder.absoluteTime is not None: value = firstReminder.absoluteTime else: # @@@ For now, all relative reminders are relative to starttime assert firstReminder.relativeTo == EventStamp.effectiveStartTime.name value = firstReminder.delta comp.add('valarm').add('trigger').value = value def populateCustom(comp, item): # custom properties for name, value in item.icalendarProperties.iteritems(): prop = comp.add(name) # for unrecognized properties, import stores strings, not # native types like datetimes. So value should just be a # string, not a more complicated python data structure. Don't # try to transform the value when serializing prop.isNative = False # encoding escapes characters like backslash and comma and # combines list values into a single string. This was already # done when the icalendar was imported, so don't escape again prop.encoded = True prop.value = value for name, paramstring in item.icalendarParameters.iteritems(): paramdict = comp.contents[name][0].params for paramlist in vobject.base.parseParams(paramstring): # parseParams gives a list of lists of parameters, with the # first element of each list being the name of the # parameter, followed by the parameter values, if any paramname = paramlist[0].upper() if paramname.lower() in parametersUnderstood: # parameters understood by Chandler shouldn't be stored # in icalendarParameters, but changes to which # parameters Chandler understands can lead to spurious # parameters, ignore them continue paramvalues = paramdict.setdefault(paramname, []) paramvalues.extend(paramlist[1:]) def populateEvent(comp, event): """Populate the given vobject vevent with data from event.""" populateCommon(comp, event.itsItem) try: dtstartLine = comp.add('dtstart') # allDay-ness overrides anyTime-ness if event.anyTime and not event.allDay: dtstartLine.x_osaf_anytime_param = 'TRUE' dtstartLine.value = makeDateTimeValue(event.startTime, event.anyTime or event.allDay) except AttributeError: comp.dtstart = [] # delete the dtstart that was added try: if not (event.duration == datetime.timedelta(0) or ( (event.anyTime or event.allDay) and event.duration <= oneDay)): dtendLine = comp.add('dtend') #convert Chandler's notion of allDay duration to iCalendar's if event.allDay: dtendLine.value = event.endTime.date() + oneDay else: if event.anyTime: dtendLine.x_osaf_anytime_param = 'TRUE' # anyTime should be exported as allDay for non-Chandler apps dtendLine.value = makeDateTimeValue(event.endTime, event.anyTime) except AttributeError: comp.dtend = [] # delete the dtend that was added if EventStamp.transparency.name not in filters: try: status = event.transparency.upper() # anytime events should be interpreted as not taking up time, # but all-day shouldn't if status == 'FYI' or (not event.allDay and event.anyTime): status = 'CANCELLED' comp.add('status').value = status except AttributeError: pass try: comp.add('location').value = event.location.displayName except AttributeError: pass view = event.itsItem.itsView timestamp = datetime.datetime.utcnow() comp.add('dtstamp').value = timestamp.replace(tzinfo=view.tzinfo.UTC) if event.modificationFor is not None: recurrenceid = comp.add('recurrence-id') masterEvent = event.getMaster() allDay = masterEvent.allDay or masterEvent.anyTime recurrenceid.value = makeDateTimeValue(event.recurrenceID, allDay) # logic for serializing rrules needs to move to vobject try: # hack, create RRULE line last, because it means running transformFromNative if event.getMaster() == event and event.rruleset is not None: # False because we don't want to ignore isCount for export # True because we don't want to use view.tzinfo.floating cal.vevent_list[-1].rruleset = event.createDateUtilFromRule(False, True, False) except AttributeError: logger.error('Failed to export RRULE for %s' % event.itsItem.itsUUID) # end of populateEvent function populateCustom(comp, event.itsItem) def populateModifications(event, cal): for modification in itertools.imap(EventStamp, event.modifications): for attr, val in modification.itsItem.iterModifiedAttributes(): if attr in attributesUsedWhenExporting and attr not in filters: populateEvent(cal.add('vevent'), modification) break #end helper functions def populateTask(comp, task): """Populate the given vobject vtodo with data from task.""" populateCommon(comp, task.itsItem) # @@@ [grant] Once we start writing out Event+Tasks as # VTODO, write out DUE (or maybe DTSTART) here. if Note._triageStatus.name not in filters: triageStatus = task.itsItem._triageStatus # VTODO STATUS mapping: # --------------------- # # [ICalendar] [Triage Enum] # <no value>/IN-PROCESS now (needsReply=False) # NEEDS-ACTION now (needsReply=True) # COMPLETED done # CANCELLED later if triageStatus == TriageEnum.now: if task.itsItem.needsReply: comp.add('status').value = 'needs-action' else: comp.add('status').value = 'in-process' elif triageStatus == TriageEnum.later: comp.add('status').value = 'cancelled' else: comp.add('status').value = 'completed' populateCustom(comp, task.itsItem) if cal is None: cal = vobject.iCalendar() for item in items: # main loop try: # ignore any events that aren't masters # # Note: [grant] # At the moment, we allow Event-ness to take precedence over # Task-ness. So, we serialize Event+Task objects as VEVENTs. # Part of the reason for this is that recurring VTODOs aren't # so well-supported by other iCalendar clients. For RFC 2445 # issues with VTODO+RRULE, see e.g. # <http://lists.osafoundation.org/pipermail/ietf-calsify/2006-August/001134.html> if has_stamp(item, EventStamp): event = EventStamp(item) if event.getMaster() == event: populateEvent(cal.add('vevent'), event) populateModifications(event, cal) elif has_stamp(item, TaskStamp): populateTask(cal.add('vtodo'), TaskStamp(item)) except: logger.exception("Exception while exporting %s" % item) continue return cal
def testResourceBookingOverlapDetection(self): """ Resource booking overlap detection - bug #1837""" # There used to be some problems with recurring events and resources booking # This test implements these edge cases # 1. Create recurring event (with resource) # 2. Create single event overlaping one instance for the previous event # (should fail) # 3. Create recurring event which _doesn't_ overlap the first event # (should be OK, used to fail pre1.3.17) # 4. Create recurring event overlapping the previous recurring event # (should fail) # make sure there are no events in the resource calendar self._deleteAllEvents(self.superuser_client, self.res_calendar) # make sure the event doesn't exist ics_name = "test-res-overlap-detection.ics" self.ics_list += [ics_name] self._deleteEvent(self.client, "%s%s" % (self.user_calendar, ics_name), None) overlap_ics_name = "test-res-overlap-detection-overlap.ics" self.ics_list += [overlap_ics_name] self._deleteEvent(self.client, "%s%s" % (self.attendee1_calendar, overlap_ics_name), None) nooverlap_recurring_ics_name = "test-res-overlap-detection-nooverlap.ics" self.ics_list += [nooverlap_recurring_ics_name] self._deleteEvent( self.client, "%s%s" % (self.user_calendar, nooverlap_recurring_ics_name), None) overlap_recurring_ics_name = "test-res-overlap-detection-overlap-recurring.ics" self.ics_list += [overlap_recurring_ics_name] self._deleteEvent( self.client, "%s%s" % (self.user_calendar, overlap_recurring_ics_name), None) # 1. create recurring event with resource event = self._newEvent(summary="recurring event with resource", uid="recurring event w resource") event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5" organizer = event.vevent.add('organizer') organizer.cn_param = self.user_name organizer.value = self.user_email attendee = event.vevent.add('attendee') attendee.cn_param = self.res_no_ob_name attendee.rsvp_param = "TRUE" attendee.partstat_param = "NEEDS-ACTION" attendee.value = self.res_no_ob_email # keep a copy around for #3 nooverlap_event = vobject.iCalendar() nooverlap_event.copy(event) self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) # 2. Create single event overlaping one instance for the previous event event = self._newEvent(summary="recurring event with resource", uid="recurring event w resource - overlap") organizer = event.vevent.add('organizer') organizer.cn_param = self.attendee1_name organizer.value = self.attendee1_email attendee = event.vevent.add('attendee') attendee.cn_param = self.res_no_ob_name attendee.rsvp_param = "TRUE" attendee.partstat_param = "NEEDS-ACTION" attendee.value = self.res_no_ob_email # should fail self._putEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, overlap_ics_name), event, exp_status=409) # 3. Create recurring event which _doesn't_ overlap the first event # (should be OK, used to fail pre1.3.17) # shift the start date to one hour after the original event end time nstartdate = nooverlap_event.vevent.dtend.value + datetime.timedelta( 0, 3600) nooverlap_event.vevent.dtstart.value = nstartdate nooverlap_event.vevent.dtend.value = nstartdate + datetime.timedelta( 0, 3600) nooverlap_event.vevent.uid.value = "recurring - nooverlap" self._putEvent( self.client, "%s%s" % (self.user_calendar, nooverlap_recurring_ics_name), nooverlap_event) # 4. Create recurring event overlapping the previous recurring event # should fail with a 409 nstartdate = nooverlap_event.vevent.dtstart.value + datetime.timedelta( 0, 300) nooverlap_event.vevent.dtstart.value = nstartdate nooverlap_event.vevent.dtend.value = nstartdate + datetime.timedelta( 0, 3600) nooverlap_event.vevent.uid.value = "recurring - overlap" self._putEvent(self.client, "%s%s" % (self.user_calendar, overlap_recurring_ics_name), nooverlap_event, exp_status=409)
def vevent_list(self, for_print=False, include_section_events=True): """ Return the vevent object list for all classes in this section. This specifically DOES NOT take into account cancelled classes, midterm, final exam, due dates, or office hours UNLESS ``include_section_events`` is ``True`` and there is something else that the calendar generator can use. """ cal = vobject.iCalendar() if not self.active: return [] if not self.section.active: return [] if not self.section.course.active: return [] if not self.section.course.department.active: return [] def _get_class_schedule(): if self.timeslot.name.lower() == "none": return [] if not self.date_range: return [] term_dtstart = utils.blend_date_and_time(self.date_range.start, self.timeslot.start_time) term_dtfinish = utils.blend_date_and_time(self.date_range.finish, self.timeslot.start_time) rrs = dateutil.rrule.rruleset() for impdate in ImportantDate.objects.filter( active=True, date__gte=self.date_range.start, date__lte=self.date_range.finish, no_class=True, ): dt = utils.blend_date_and_time(impdate.date, self.timeslot.start_time) rrs.exdate(dt) if impdate.end_date is not None: enddt = utils.blend_date_and_time( impdate.end_date, datetime.time(hour=23, minute=59, second=59)) while dt <= enddt: dt += datetime.timedelta(days=1) rrs.exdate(dt) term = dateutil.rrule.rrule( dateutil.rrule.WEEKLY, # count=26, wkst=dateutil.rrule.SU, byweekday=self.timeslot.get_rrule_days(), dtstart=term_dtstart, ) rrs.rrule(term) for dtstart in rrs.between(term_dtstart, term_dtfinish, inc=True): ev = cal.add("vevent") ev.add("dtstamp").value = dtstart ev.add("dtstart").value = dtstart ev.add("dtend").value = utils.blend_date_and_time( datetime.date(*(dtstart.timetuple()[:3])), self.timeslot.stop_time) if not for_print: ev.add("location").value = "{}".format(self.room) if for_print: summary = (self.section.course.label + " " + self.section.section_name + " " + self.lab_section_name) if self.instructor: summary += " " + self.section.instructor.cn ev.add("summary").value = summary else: ev.add("summary").value = "{}".format(self.section) return cal.vevent_list if cal.vevent_list is not None else [] def _get_related_events(): # include other section events if not include_section_events: return [] # add events of objects that relate to this object related_sets = [ attr for attr in dir(self.section) if attr.endswith("_set") and attr != "sectionschedule_set" ] for attr in related_sets: related = getattr(self.section, attr) if hasattr(related.model, "vevent"): for subobj in related.all(): ev = subobj.vevent() if ev is not None: cal.add(ev) if hasattr(related.model, "vevent_list"): for subobj in related.all(): for ev in subobj.vevent_list(): cal.add(ev) _get_class_schedule() _get_related_events() return getattr(cal, "vevent_list", [])
def serializeTimeZone(tzinfo): """Given a tzinfo class, return a VTIMEZONE in an iCalendar object.""" cal = vobject.iCalendar() cal.add(vobject.icalendar.TimezoneComponent(tzinfo=tzinfo)) return cal.serialize()
def setup(): global calendar c = vobject.iCalendar() calendar = c e = c.add('vevent') e.add('summary').value = "weekly expired" start = e.add('dtstart') start.value = datetime.datetime(2008, 11, 26, 13, 00, tzinfo=local) end = e.add('dtend') end.value = datetime.datetime(2008, 11, 26, 14, 00, tzinfo=local) rrule = e.add('rrule') rrule.value = 'FREQ=WEEKLY;INTERVAL=1;UNTIL=20090101T045959Z' e = c.add('vevent') e.add('summary').value = "daily expired" start = e.add('dtstart') start.value = datetime.datetime(2008, 11, 26, 13, 00, tzinfo=local) end = e.add('dtend') end.value = datetime.datetime(2008, 11, 26, 14, 00, tzinfo=local) rrule = e.add('rrule') rrule.value = 'FREQ=DAILY;INTERVAL=1;UNTIL=20090101T045959Z' e = c.add('vevent') e.add('summary').value = "weekly" start = e.add('dtstart') start.value = datetime.datetime(2009, 11, 26, 13, 00, tzinfo=local) end = e.add('dtend') end.value = datetime.datetime(2009, 11, 26, 14, 00, tzinfo=local) rrule = e.add('rrule') rrule.value = 'FREQ=WEEKLY;INTERVAL=1' e = c.add('vevent') e.add('summary').value = "daily" start = e.add('dtstart') start.value = datetime.datetime(2009, 11, 26, 13, 00, tzinfo=local) end = e.add('dtend') end.value = datetime.datetime(2009, 11, 26, 14, 00, tzinfo=local) rrule = e.add('rrule') rrule.value = 'FREQ=DAILY;INTERVAL=1' e = c.add('vevent') e.add('summary').value = "weekly future" start = e.add('dtstart') start.value = datetime.datetime(2010, 11, 26, 13, 00, tzinfo=local) end = e.add('dtend') end.value = datetime.datetime(2010, 11, 26, 14, 00, tzinfo=local) rrule = e.add('rrule') rrule.value = 'FREQ=WEEKLY;INTERVAL=1' e = c.add('vevent') e.add('summary').value = "daily future" start = e.add('dtstart') start.value = datetime.datetime(2010, 11, 26, 13, 00, tzinfo=local) end = e.add('dtend') end.value = datetime.datetime(2010, 11, 26, 14, 00, tzinfo=local) rrule = e.add('rrule') rrule.value = 'FREQ=DAILY;INTERVAL=1' return