def test_vtodo(self): vtodo = get_test_file("vtodo.ics") obj = base.readOne(vtodo) obj.vtodo.add('completed') obj.vtodo.completed.value = datetime.datetime(2015,5,5,13,30) self.assertEqual(obj.vtodo.completed.serialize()[0:23], 'COMPLETED:20150505T1330') obj = base.readOne(obj.serialize()) self.assertEqual(obj.vtodo.completed.value, datetime.datetime(2015,5,5,13,30))
def test_wrapping(self): """ Should support an input file with a long text field covering multiple lines """ test_journal = get_test_file("journal.ics") vobj = base.readOne(test_journal) vjournal = base.readOne(vobj.serialize()) self.assertTrue('Joe, Lisa, and Bob' in vjournal.description.value) self.assertTrue('Tuesday.\n2.' in vjournal.description.value)
def test_vtodo(self): vtodo = get_test_file("vtodo.ics") obj = base.readOne(vtodo) obj.vtodo.add('completed') obj.vtodo.completed.value = datetime.datetime(2015, 5, 5, 13, 30) self.assertEqual(obj.vtodo.completed.serialize()[0:23], 'COMPLETED:20150505T1330') obj = base.readOne(obj.serialize()) self.assertEqual(obj.vtodo.completed.value, datetime.datetime(2015, 5, 5, 13, 30))
def test_unicode(self): test_cal = get_test_file("utf8_test.ics") vevent = base.readOne(test_cal).vevent vevent2 = base.readOne(vevent.serialize()) self.assertEqual(str(vevent), str(vevent2)) self.assertEqual(vevent.summary.value, 'The title こんにちはキティ') if sys.version_info[0] < 3: test_cal = test_cal.decode('utf-8') vevent = base.readOne(test_cal).vevent vevent2 = base.readOne(vevent.serialize()) self.assertEqual(str(vevent), str(vevent2)) self.assertEqual(vevent.summary.value, 'The title こんにちはキティ')
def main(): options, args = get_options() if PyICU is None: print("Failure. change_tz requires PyICU, exiting") elif options.list: for tz_string in PyICU.TimeZone.createEnumeration(): print(tz_string) elif args: utc_only = options.utc if utc_only: which = "only UTC" else: which = "all" print("Converting {0!s} events".format(which)) ics_file = args[0] if len(args) > 1: timezone = PyICU.ICUtzinfo.getInstance(args[1]) else: timezone = PyICU.ICUtzinfo.default print("... Reading {0!s}".format(ics_file)) cal = base.readOne(open(ics_file)) change_tz(cal, timezone, PyICU.ICUtzinfo.default, utc_only) out_name = ics_file + '.converted' print("... Writing {0!s}".format(out_name)) with open(out_name, 'wb') as out: cal.serialize(out) print("Done")
def setUpClass(cls): """ Method for setting up class fixture before running tests in the class. Fetches test file. """ cls.test_file = get_test_file("vcard_with_groups.ics") cls.card = base.readOne(cls.test_file)
def ics2task() -> None: """Command line tool to convert from iCalendar to Taskwarrior.""" from argparse import ArgumentParser, FileType from sys import stdin parser = ArgumentParser( description="Converter from iCalendar to Taskwarrior syntax.") parser.add_argument( "infile", nargs="?", type=FileType("r"), default=stdin, help="Input iCalendar file (default: stdin)", ) parser.add_argument( "outdir", nargs="?", help="Output Taskwarrior directory (autodetect by default)", default="", ) args = parser.parse_args() vobject = readOne(args.infile.read()) task = IcsTask(args.outdir) for todo in vobject.vtodo_list: task.to_task(todo)
def test_importing(self): cal = get_test_file("standard_test.ics") c = base.readOne(cal, validate=True) self.assertEqual( str(c.vevent.valarm.trigger), "<TRIGGER{}-1 day, 0:00:00>" ) self.assertEqual( str(c.vevent.dtstart.value), "2002-10-28 14:00:00-08:00" ) self.assertTrue( isinstance(c.vevent.dtstart.value, datetime.datetime) ) self.assertEqual( str(c.vevent.dtend.value), "2002-10-28 15:00:00-08:00" ) self.assertTrue( isinstance(c.vevent.dtend.value, datetime.datetime) ) self.assertEqual( c.vevent.dtstamp.value, datetime.datetime(2002, 10, 28, 1, 17, 6, tzinfo=tzutc()) ) vevent = c.vevent.transformFromNative() self.assertEqual( str(vevent.rrule), "<RRULE{}FREQ=Weekly;COUNT=10>" )
def test_recurrence_mixed_with_and_without_tz(self): """ Ensure recurring vevent with DTSTART and EXDATE not both have or not have timezone information is parsing. """ test_file = get_test_file("recurrence-mixed-with-and-without-tz.ics") vcalendar = base.readOne(test_file, validate=True) expected = None for vobj in vcalendar.getChildren(): if vobj.name == 'VTIMEZONE': expected = iter([[ datetime.datetime(1991, 5, 20, 0, 0, 0), datetime.datetime(1993, 5, 20, 0, 0, 0) ], [ datetime.datetime( 2017, 10, 25, 14, 0, 0, tzinfo=vobj.gettzinfo()), datetime.datetime(2017, 11, 8, 14, 0, 0, tzinfo=vobj.gettzinfo()) ]]) elif vobj.name == 'VEVENT': dates = list(vobj.getrruleset(addRDate=True)) self.assertEqual(dates, next(expected))
def test_thunderbird_duration_pt0s_workaround(self): """ Test parsing thundebird generated ics streams having both a duration of 0 seconds and a dtend attribute. """ cal = get_test_file("thunderbird_duration_pt0s_workaround.ics") c = base.readOne(cal, validate=True)
def main(): options, args = get_options() if PyICU is None: print("Failure. change_tz requires PyICU, exiting") elif options.list: for tz_string in PyICU.TimeZone.createEnumeration(): print(tz_string) elif args: utc_only = options.utc if utc_only: which = "only UTC" else: which = "all" print("Converting {0!s} events".format(which)) ics_file = args[0] if len(args) > 1: timezone = PyICU.ICUtzinfo.getInstance(args[1]) else: timezone = PyICU.ICUtzinfo.default print("... Reading {0!s}".format(ics_file)) cal = base.readOne(open(ics_file)) change_tz(cal, timezone, PyICU.ICUtzinfo.default, utc_only) out_name = ics_file + '.converted' print("... Writing {0!s}".format(out_name)) out = file(out_name, 'wb') cal.serialize(out) print("Done")
def test_importing(self): """ Test importing ics """ cal = get_test_file("standard_test.ics") c = base.readOne(cal, validate=True) self.assertEqual( str(c.vevent.valarm.trigger), "<TRIGGER{}-1 day, 0:00:00>" ) self.assertEqual( str(c.vevent.dtstart.value), "2002-10-28 14:00:00-08:00" ) self.assertTrue( isinstance(c.vevent.dtstart.value, datetime.datetime) ) self.assertEqual( str(c.vevent.dtend.value), "2002-10-28 15:00:00-08:00" ) self.assertTrue( isinstance(c.vevent.dtend.value, datetime.datetime) ) self.assertEqual( c.vevent.dtstamp.value, datetime.datetime(2002, 10, 28, 1, 17, 6, tzinfo=tzutc()) ) vevent = c.vevent.transformFromNative() self.assertEqual( str(vevent.rrule), "<RRULE{}FREQ=Weekly;COUNT=10>" )
def main(): options, args = get_options() if PyICU is None: print "Failure. change_tz requires PyICU, exiting" elif options.list: for tz_string in PyICU.TimeZone.createEnumeration(): print tz_string elif args: utc_only = options.utc if utc_only: which = "only UTC" else: which = "all" print "Converting %s events" % which ics_file = args[0] if len(args) > 1: timezone = PyICU.ICUtzinfo.getInstance(args[1]) else: timezone = PyICU.ICUtzinfo.default print "... Reading %s" % ics_file cal = base.readOne(file(ics_file)) change_tz(cal, timezone, PyICU.ICUtzinfo.default, utc_only) out_name = ics_file + ".converted" print "... Writing %s" % out_name out = file(out_name, "wb") cal.serialize(out) print "Done"
def main(): options, args = get_options() if PyICU is None: print "Failure. change_tz requires PyICU, exiting" elif options.list: for tz_string in PyICU.TimeZone.createEnumeration(): print tz_string elif args: utc_only = options.utc if utc_only: which = "only UTC" else: which = "all" print "Converting %s events" % which ics_file = args[0] if len(args) > 1: timezone = PyICU.ICUtzinfo.getInstance(args[1]) else: timezone = PyICU.ICUtzinfo.default print "... Reading %s" % ics_file cal = base.readOne(file(ics_file)) change_tz(cal, timezone, PyICU.ICUtzinfo.default, utc_only) out_name = ics_file + '.converted' print "... Writing %s" % out_name out = file(out_name, 'wb') cal.serialize(out) print "Done"
def handle_admin_request(self, params, fields): if params["action"] == "add": self.db.table(self.table_prefix+"_items").insert( {'begin' : self.conc_date(fields,"begin"), 'end' : self.conc_date(fields,"end"), 'text' : fields['item_text'].value}) return "admin.py?id=Calendars" elif params["action"] == "del": self.db.table(self.table_prefix+"_items").delete([("id", "=", params["item_id"])]) return "admin.py?id=Calendars" elif params["action"] == "update": self.db.table(self.table_prefix+"_items").update({ 'id' : params["item_id"], 'begin' : self.conc_date(fields,"begin"), 'end' : self.conc_date(fields,"end"), 'text' : fields['item_text'].value}, [("id", "=", params["item_id"])]) elif params["action"] == "import": try: self.db.drop(self.table_prefix+"_temp") except: pass self.db.create(self.table_prefix+"_temp", {"text" :"varchar","begin" :"datetime", "end":"datetime"}); fcal = vobject.readOne(fields['cal'].file.read()).vevent_list for fev in fcal: self.db.table(self.table_prefix+"_temp").insert({'begin' : fev.dtstart.value , 'end' : fev.dtend.value , 'text' : fev.summary.value }) return "admin.py?id=Calendars&action=choose" elif params["action"] == "include": for i in range(int(fields["nb_it"].value)): if fields.has_key("check_"+str(i)): self.db.table(self.table_prefix+"_items").insert( {'begin' : fields["begin_"+str(i)].value, 'end' : fields["end_"+str(i)].value, 'text' : fields['text_'+str(i)].value}) return "admin.py?id=Calendars" else: return "admin.py?id=Calendars"
def test_bad_line(self): cal = get_test_file("badline.ics") self.assertRaises(ParseError, base.readOne, cal) newcal = base.readOne(cal, ignoreUnreadable=True) self.assertEqual(str(newcal.vevent.x_bad_underscore), '<X-BAD-UNDERSCORE{}TRUE>')
def test_readOne(self): cal = get_test_file("silly_test.ics") silly = base.readOne(cal, findBegin=False) self.assertEqual( str(silly), "<SILLYPROFILE| [<MORESTUFF{}this line is not folded, but in practice probably ought to be, as it is exceptionally long, and moreover demonstratively stupid>, <SILLYNAME{}name>, <STUFF{}foldedline>]>" ) self.assertEqual(str(silly.stuff), "<STUFF{}foldedline>")
def test_unicode(self): test_cal = get_test_file("utf8_test.ics") vevent = base.readOne(test_cal).vevent self.assertEqual( vevent.summary.value, 'The title こんにちはキティ' )
def test_bad_line(self): cal = get_test_file("badline.ics") self.assertRaises(ParseError, base.readOne, cal) newcal = base.readOne(cal, ignoreUnreadable=True) self.assertEqual( str(newcal.vevent.x_bad_underscore), '<X-BAD-UNDERSCORE{}TRUE>' )
def test_recurrence_without_tz(self): """ Test recurring vevent missing any time zone definitions. """ test_file = get_test_file("recurrence-without-tz.ics") cal = base.readOne(test_file) dates = list(cal.vevent.getrruleset()) self.assertEqual(dates[0], datetime.datetime(2013, 1, 17, 0, 0)) self.assertEqual(dates[1], datetime.datetime(2013, 1, 24, 0, 0)) self.assertEqual(dates[-1], datetime.datetime(2013, 3, 28, 0, 0))
def test_vcard_3_parsing(self): """ VCARD 3.0 parse test """ test_file = get_test_file("simple_3_0_test.ics") card = base.readOne(test_file) # value not rendering correctly? #self.assertEqual( # card.adr.value, # "<Address: Haight Street 512;\nEscape, Test\nNovosibirsk, 80214\nGnuland>" #) self.assertEqual( card.org.value, ["University of Novosibirsk", "Department of Octopus Parthenogenesis"] ) for _ in range(3): new_card = base.readOne(card.serialize()) self.assertEqual(new_card.org.value, card.org.value) card = new_card
def test_unicode(self): test_cal = get_test_file("utf8_test.ics") vevent = base.readOne(test_cal).vevent vevent2 = base.readOne(vevent.serialize()) self.assertEqual(str(vevent), str(vevent2)) self.assertEqual( vevent.summary.value, 'The title こんにちはキティ' ) if sys.version_info[0] < 3: test_cal = test_cal.decode('utf-8') vevent = base.readOne(test_cal).vevent vevent2 = base.readOne(vevent.serialize()) self.assertEqual(str(vevent), str(vevent2)) self.assertEqual( vevent.summary.value, 'The title こんにちはキティ' )
def test_recurrence_offset_naive(self): """ Ensure recurring vevent missing some time zone definitions is parsing. See isseu #75. """ test_file = get_test_file("recurrence-offset-naive.ics") cal = base.readOne(test_file) dates = list(cal.vevent.getrruleset()) self.assertEqual(dates[0], datetime.datetime(2013, 1, 17, 0, 0)) self.assertEqual(dates[1], datetime.datetime(2013, 1, 24, 0, 0)) self.assertEqual(dates[-1], datetime.datetime(2013, 3, 28, 0, 0))
def test_readOne(self): cal = get_test_file("silly_test.ics") silly = base.readOne(cal, findBegin=False) self.assertEqual( str(silly), "<SILLYPROFILE| [<MORESTUFF{}this line is not folded, but in practice probably ought to be, as it is exceptionally long, and moreover demonstratively stupid>, <SILLYNAME{}name>, <STUFF{}foldedline>]>" ) self.assertEqual( str(silly.stuff), "<STUFF{}foldedline>" )
def test_recurrence(self): # Ensure date valued UNTILs in rrules are in a reasonable timezone, # and include that day (12/28 in this test) test_file = get_test_file("recurrence.ics") cal = base.readOne(test_file, findBegin=False) dates = list(cal.vevent.getrruleset()) self.assertEqual(dates[0], datetime.datetime(2006, 1, 26, 23, 0, tzinfo=tzutc())) self.assertEqual(dates[1], datetime.datetime(2006, 2, 23, 23, 0, tzinfo=tzutc())) self.assertEqual( dates[-1], datetime.datetime(2006, 12, 28, 23, 0, tzinfo=tzutc()))
def test_vcard_3_parsing(self): """ VCARD 3.0 parse test """ test_file = get_test_file("simple_3_0_test.ics") card = base.readOne(test_file, findBegin=False) # value not rendering correctly? #self.assertEqual( # card.adr.value, # "<Address: Haight Street 512;\nEscape, Test\nNovosibirsk, 80214\nGnuland>" #) self.assertEqual( card.org.value, "University of Novosibirsk, Department of Octopus Parthenogenesis")
def test_vcard_3_parsing(self): """ VCARD 3.0 parse test """ test_file = get_test_file("simple_3_0_test.ics") card = base.readOne(test_file, findBegin=False) # value not rendering correctly? #self.assertEqual( # card.adr.value, # "<Address: Haight Street 512;\nEscape, Test\nNovosibirsk, 80214\nGnuland>" #) self.assertEqual( card.org.value, "University of Novosibirsk, Department of Octopus Parthenogenesis" )
def test_recurrence(self): # Ensure date valued UNTILs in rrules are in a reasonable timezone, # and include that day (12/28 in this test) test_file = get_test_file("recurrence.ics") cal = base.readOne(test_file, findBegin=False) dates = list(cal.vevent.getrruleset()) self.assertEqual( dates[0], datetime.datetime(2006, 1, 26, 23, 0, tzinfo=tzutc()) ) self.assertEqual( dates[1], datetime.datetime(2006, 2, 23, 23, 0, tzinfo=tzutc()) ) self.assertEqual( dates[-1], datetime.datetime(2006, 12, 28, 23, 0, tzinfo=tzutc()) )
def setUpClass(cls): cls.test_file = get_test_file("vcard_with_groups.ics") cls.card = base.readOne(cls.test_file)
def verify(self, manager, uri, response, respdata, args): #@UnusedVariable # Get arguments files = args.get("filepath", []) # status code must be 200, 207 if response.status not in (200,207): return False, " HTTP Status Code Wrong: %d" % (response.status,) # look for response data if not respdata: return False, " No response body" # look for one file if len(files) != 1: return False, " No file to compare response to" # read in all data from specified file fd = open( files[0], "r" ) try: try: data = fd.read() finally: fd.close() except: data = None if data is None: return False, " Could not read data file" data = manager.server_info.subs(data) def removePropertiesParameters(component): for item in tuple(component.getChildren()): if isinstance(item, Component): removePropertiesParameters(item) elif isinstance(item, ContentLine): # Always remove DTSTAMP if item.name == "DTSTAMP": component.remove(item) elif item.name == "X-CALENDARSERVER-ATTENDEE-COMMENT": if item.params.has_key("X-CALENDARSERVER-DTSTAMP"): item.params["X-CALENDARSERVER-DTSTAMP"] = ["20080101T000000Z"] s = StringIO.StringIO(respdata) try: resp_calendar = readOne(s) removePropertiesParameters(resp_calendar) respdata = resp_calendar.serialize() s = StringIO.StringIO(data) data_calendar = readOne(s) removePropertiesParameters(data_calendar) data = data_calendar.serialize() result = respdata == data if result: return True, "" else: error_diff = "\n".join([line for line in unified_diff(data.split("\n"), respdata.split("\n"))]) return False, " Response data does not exactly match file data%s" % (error_diff,) except: return False, " Response data is not calendar data data"
def verify(self, manager, uri, response, respdata, args): #@UnusedVariable # Must have status 200 if response.status != 200: return False, " HTTP Status Code Wrong: %d" % (response.status,) # Get expected FREEBUSY info busy = args.get("busy", []) tentative = args.get("tentative", []) unavailable = args.get("unavailable", []) # Parse data as calendar object try: s = StringIO.StringIO(respdata) calendar = readOne(s) # Check for calendar if calendar.name != "VCALENDAR": raise ValueError("Top-level component is not a calendar: %s" % (calendar.name, )) # Only one component comps = list(calendar.components()) if len(comps) != 1: raise ValueError("Wrong number of components in calendar") # Must be VFREEBUSY fb = comps[0] if fb.name != "VFREEBUSY": raise ValueError("Calendar contains unexpected component: %s" % (fb.name, )) # Extract periods busyp = [] tentativep = [] unavailablep = [] for fp in [x for x in fb.lines() if x.name == "FREEBUSY"]: periods = fp.value # Convert start/duration to start/end for i in range(len(periods)): if isinstance(periods[i][1], datetime.timedelta): periods[i] = (periods[i][0], periods[i][0] + periods[i][1]) # Check param fbtype = "BUSY" if "FBTYPE" in fp.params: fbtype = fp.params["FBTYPE"][0] if fbtype == "BUSY": busyp.extend(periods) elif fbtype == "BUSY-TENTATIVE": tentativep.extend(periods) elif fbtype == "BUSY-UNAVAILABLE": unavailablep.extend(periods) else: raise ValueError("Unknown FBTYPE: %s" % (fbtype,)) # Set sizes must match if ((len(busy) != len(busyp)) or (len(unavailable) != len(unavailablep)) or (len(tentative) != len(tentativep))): raise ValueError("Period list sizes do not match.") # Convert to string sets busy = set(busy) busyp[:] = [periodToString(x) for x in busyp] busyp = set(busyp) tentative = set(tentative) tentativep[:] = [periodToString(x) for x in tentativep] tentativep = set(tentativep) unavailable = set(unavailable) unavailablep[:] = [periodToString(x) for x in unavailablep] unavailablep = set(unavailablep) # Compare all periods if len(busyp.symmetric_difference(busy)): raise ValueError("Busy periods do not match") elif len(tentativep.symmetric_difference(tentative)): raise ValueError("Busy-tentative periods do not match") elif len(unavailablep.symmetric_difference(unavailable)): raise ValueError("Busy-unavailable periods do not match") except VObjectError: return False, " HTTP response data is not a calendar" except ValueError, txt: return False, " HTTP response data is invalid: %s" % (txt,)