Пример #1
0
 def setUp(self):
     Site.objects.update(hostname="joy.test")
     self.home = Page.objects.get(slug='home')
     self.user = User.objects.create_user('i', '*****@*****.**', 's3cr3t')
     self.requestFactory = RequestFactory()
     self.calendar = CalendarPage(owner=self.user,
                                  slug="events",
                                  title="Events")
     self.home.add_child(instance=self.calendar)
     self.calendar.save_revision().publish()
     self.handler = ICalHandler()
Пример #2
0
 def setUp(self):
     Site.objects.update(hostname="joy.test")
     self.home = Page.objects.get(slug='home')
     self.user = User.objects.create_user('i', '*****@*****.**', 's3(R3t')
     self.requestFactory = RequestFactory()
     self.calendar = CalendarPage(owner=self.user,
                                  slug="events",
                                  title="Events")
     self.home.add_child(instance=self.calendar)
     self.calendar.save_revision().publish()
     self.dicerun = SimpleEventPage(owner=self.user,
                                    slug="mercy-dice-run",
                                    title="Mercy Dice Run",
                                    date=dt.date(2020, 3, 16),
                                    location="Newtown")
     self.calendar.add_child(instance=self.dicerun)
     self.dicerun.save_revision().publish()
     event = SimpleEventPage(owner=self.user,
                             slug="workshop",
                             title="Workshop",
                             date=dt.date(2020, 3, 22))
     self.calendar.add_child(instance=event)
     event.save_revision().publish()
     self.handler = ICalHandler()
Пример #3
0
class TestExport(TestCase):
    def setUp(self):
        Site.objects.update(hostname="joy.test")
        self.home = Page.objects.get(slug='home')
        self.user = User.objects.create_user('i', '*****@*****.**', 's3(R3t')
        self.requestFactory = RequestFactory()
        self.calendar = CalendarPage(owner=self.user,
                                     slug="events",
                                     title="Events")
        self.home.add_child(instance=self.calendar)
        self.calendar.save_revision().publish()
        self.dicerun = SimpleEventPage(owner=self.user,
                                       slug="mercy-dice-run",
                                       title="Mercy Dice Run",
                                       date=dt.date(2020, 3, 16),
                                       location="Newtown")
        self.calendar.add_child(instance=self.dicerun)
        self.dicerun.save_revision().publish()
        event = SimpleEventPage(owner=self.user,
                                slug="workshop",
                                title="Workshop",
                                date=dt.date(2020, 3, 22))
        self.calendar.add_child(instance=event)
        event.save_revision().publish()
        self.handler = ICalHandler()

    def _getRequest(self, path="/"):
        request = self.requestFactory.get(path)
        request.user = self.user
        request.site = self.home.get_site()
        request.session = {}
        request._messages = FallbackStorage(request)
        request.POST = request.POST.copy()
        request.POST['action-publish'] = "action-publish"
        return request

    def testServeCalendar(self):
        response = self.handler.serve(self.calendar,
                                      self._getRequest("/events/"))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.get('Content-Type'), "text/calendar")
        self.assertEqual(response.get('Content-Disposition'),
                         "attachment; filename=events.ics")
        self.assertEqual(response.content.count(b"BEGIN:VEVENT"), 2)

    def testServeEvent(self):
        response = self.handler.serve(
            self.dicerun, self._getRequest("/events/mercy-dice-run/"))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.get('Content-Type'), "text/calendar")
        self.assertEqual(response.get('Content-Disposition'),
                         "attachment; filename=mercy-dice-run.ics")
        self.assertEqual(response.content.count(b"BEGIN:VEVENT"), 1)
        self.assertIn(b"SUMMARY:Mercy Dice Run", response.content)
        self.assertIn(b"DTSTART;TZID=Asia/Tokyo:20200316T000000",
                      response.content)
        self.assertIn(b"DTEND;TZID=Asia/Tokyo:20200316T235959",
                      response.content)
        self.assertIn(b"LOCATION:Newtown", response.content)
        self.assertIn(b"URL:http://joy.test/events/mercy-dice-run",
                      response.content)

    def testServePage(self):
        response = self.handler.serve(self.home, self._getRequest("/"))
        self.assertIsNone(response)
Пример #4
0
class TestImport(TestCase):
    def setUp(self):
        Site.objects.update(hostname="joy.test")
        self.home = Page.objects.get(slug='home')
        self.user = User.objects.create_user('i', '*****@*****.**', 's3cr3t')
        self.requestFactory = RequestFactory()
        self.calendar = CalendarPage(owner=self.user,
                                     slug="events",
                                     title="Events")
        self.home.add_child(instance=self.calendar)
        self.calendar.save_revision().publish()
        self.handler = ICalHandler()

    def _getRequest(self, path="/"):
        request = self.requestFactory.get(path)
        request.user = self.user
        request.site = self.home.get_site()
        request.session = {}
        request._messages = FallbackStorage(request)
        request.POST = request.POST.copy()
        request.POST['action-publish'] = "action-publish"
        return request

    @freeze_time("2018-07-24 19:00:00")
    def testMeetup(self):
        stream = BytesIO(b"""\
BEGIN:VCALENDAR\r
VERSION:2.0\r
PRODID:-//Meetup//RemoteApi//EN\r
CALSCALE:GREGORIAN\r
METHOD:PUBLISH\r
X-ORIGINAL-URL:https://www.meetup.com/Code-for-Boston/events/249894034/ic\r
 al/Weekly+Hack+Night.ics\r
X-WR-CALNAME:Events - Weekly Hack Night.ics\r
X-MS-OLK-FORCEINSPECTOROPEN:TRUE\r
BEGIN:VTIMEZONE\r
TZID:America/New_York\r
X-LIC-LOCATION:America/New_York\r
BEGIN:DAYLIGHT\r
TZOFFSETFROM:-0500\r
TZOFFSETTO:-0400\r
TZNAME:EDT\r
DTSTART:19700308T020000\r
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r
END:DAYLIGHT\r
BEGIN:STANDARD\r
TZOFFSETFROM:-0400\r
TZOFFSETTO:-0500\r
TZNAME:EST\r
DTSTART:19701101T020000\r
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r
END:STANDARD\r
END:VTIMEZONE\r
BEGIN:VEVENT\r
DTSTAMP:20180721T015100Z\r
DTSTART;TZID=America/New_York:20180724T190000\r
DTEND;TZID=America/New_York:20180724T213000\r
STATUS:CONFIRMED\r
SUMMARY:Weekly Hack Night\r
DESCRIPTION:Code for Boston\\nTuesday\\, July 24 at 7:00 PM\\n\\nOur weekly w\r
 ork session will be at the Cambridge Innovation Center in Kendall Square\r
 \\, on the FOURTH FLOOR\\, in the CAFE. These Hack Nights are our time...\\\r
 n\\nhttps://www.meetup.com/Code-for-Boston/events/249894034/\r
CLASS:PUBLIC\r
CREATED:20180404T010420Z\r
GEO:42.36;-71.09\r
LOCATION:Cambridge Innovation Center\\, 4th Floor Cafe (1 Broadway\\, Cambr\r
 idge\\, MA)\r
URL:https://www.meetup.com/Code-for-Boston/events/249894034/\r
LAST-MODIFIED:20180404T010420Z\r
UID:[email protected]\r
END:VEVENT\r
END:VCALENDAR""")
        self.handler.load(self.calendar, self._getRequest(), stream)
        events = SimpleEventPage.events.child_of(self.calendar).all()
        self.assertEqual(len(events), 1)
        event = events[0]
        self.assertEqual(event.owner, self.user)
        self.assertEqual(event.slug, "weekly-hack-night")
        self.assertEqual(event.title, "Weekly Hack Night")
        self.assertEqual(
            event.details, "\n".join([
                "Code for Boston", "Tuesday, July 24 at 7:00 PM", "",
                "Our weekly work session will be at the Cambridge Innovation Center in Kendall Square"
                ", on the FOURTH FLOOR, in the CAFE. These Hack Nights are our time...",
                "", "https://www.meetup.com/Code-for-Boston/events/249894034/"
            ]))
        self.assertEqual(event.date, dt.date(2018, 7, 24))
        self.assertEqual(event.time_from, dt.time(19))
        self.assertEqual(event.time_to, dt.time(21, 30))
        self.assertEqual(event.tz.zone, "America/New_York")

    @freeze_time("2018-02-01")
    @timezone.override("Pacific/Auckland")
    def testGoogleCalendar(self):
        stream = BytesIO(rb"""
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Test Data
X-WR-TIMEZONE:Pacific/Auckland
X-WR-CALDESC:Sample data for Joyous test_ical unittest
BEGIN:VTIMEZONE
TZID:Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19700927T020000
RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700405T030000
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART:20180725T210000Z
DTEND:20180726T083000Z
DTSTAMP:20180722T060025Z
UID:[email protected]
CREATED:20180722T035919Z
DESCRIPTION:Hounit <b>catlike</b> at ethatial to thin a usistiques onshiend
  alits mily tente duse prommuniss ind sedships itommunte of perpollood.
LAST-MODIFIED:20180722T035919Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Big Thursday
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Pacific/Auckland:20180703T093000
DTEND;TZID=Pacific/Auckland:20180703T113000
RRULE:FREQ=WEEKLY;UNTIL=20180828T115959Z;BYDAY=TU
EXDATE;TZID=Pacific/Auckland:20180814T093000
DTSTAMP:20180722T060025Z
UID:[email protected]
CREATED:20180722T035429Z
DESCRIPTION:\nFammulturacha matent theaminerviencess atinjuse it shin sue o
 f Aothips to ming an sed prage thnisithass invernships oftegruct and encome
 . Taimen in grose to to ner grough ingin orgagences' of Fries seed\n\nFrith
 erovere Houps of custims analienessuppol. Tiriendindnew\, vality a gruccous
 er to be the juse Truch ince lity Te therneramparcialues the the neshipland
 s tortandamength\,  Comene ups a mitioney dend peachassfy de are to entices
  meand evelas of Friscerple th iseek arces a wind.
LAST-MODIFIED:20180722T035937Z
LOCATION:Coast Rd\, Barrytown\, New Zealand
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Tuesday Mornings
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20180713
DTEND;VALUE=DATE:20180716
DTSTAMP:20180722T060025Z
UID:[email protected]
CREATED:20180722T040054Z
DESCRIPTION:
LAST-MODIFIED:20180722T040054Z
LOCATION:Home
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Three days off
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Pacific/Auckland:20180725T093000
DTEND;TZID=Pacific/Auckland:20180725T113000
DTSTAMP:20180722T060025Z
UID:[email protected]
RECURRENCE-ID;TZID=Pacific/Auckland:20180724T093000
CREATED:20180722T035429Z
DESCRIPTION:\nFammulturacha matent theaminerviencess atinjuse it shin sue o
 f Aothips to ming an sed prage thnisithass invernships oftegruct and encome
 . Taimen in grose to to ner grough ingin orgagences' of Fries seed\n\nFrith
 erovere Houps of custims analienessuppol. Tiriendindnew\, vality a gruccous
 er to be the juse Truch ince lity Te therneramparcialues the the neshipland
 s tortandamength\,  Comene ups a mitioney dend peachassfy de are to entices
  meand evelas of Friscerple th iseek arces a wind.
LAST-MODIFIED:20180722T051000Z
LOCATION:Coast Rd\, Barrytown\, New Zealand
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:Tuesday Mornings Postponed
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Pacific/Auckland:20180731T093000
DTEND;TZID=Pacific/Auckland:20180731T113000
DTSTAMP:20180722T060025Z
UID:[email protected]
RECURRENCE-ID;TZID=Pacific/Auckland:20180731T093000
CREATED:20180722T035429Z
DESCRIPTION:\nExtra Famin fork\, andivery\,  Hough in the re of re whels ot
 edshiplue porturat inve in nurectic.
LAST-MODIFIED:20180722T051201Z
LOCATION:Coast Rd\, Barrytown\, New Zealand
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Tuesday Morning Extra Info
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART:20180717T220000Z
DTEND:20180717T223000Z
DTSTAMP:20180722T060025Z
UID:[email protected]
CREATED:20180722T050847Z
DESCRIPTION:
LAST-MODIFIED:20180722T055756Z
LOCATION:Pariroa Beach
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Little Wednesday
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART:20180723T190000Z
DTEND:20180723T200000Z
DTSTAMP:20180722T060025Z
UID:[email protected]
CREATED:20180722T055954Z
DESCRIPTION:
LAST-MODIFIED:20180722T055954Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Conference Call
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
""")
        request = self._getRequest()
        self.handler.load(self.calendar, request, stream)
        events = getAllEvents(request, home=self.calendar)
        self.assertEqual(len(events), 5)
        tueMorn, daysOff, lilWeds, cnfCall, bigThur = events

        self.assertEqual(tueMorn.owner, self.user)
        self.assertEqual(tueMorn.slug, "tuesday-mornings")
        self.assertEqual(tueMorn.title, "Tuesday Mornings")
        self.assertEqual(
            tueMorn.details, "\n".join([
                "",
                "Fammulturacha matent theaminerviencess atinjuse it shin sue of "
                "Aothips to ming an sed prage thnisithass invernships oftegruct "
                "and encome. Taimen in grose to to ner grough ingin orgagences' "
                "of Fries seed", "",
                "Fritherovere Houps of custims analienessuppol. Tiriendindnew, "
                "vality a gruccouser to be the juse Truch ince lity Te "
                "therneramparcialues the the neshiplands tortandamength,  "
                "Comene ups a mitioney dend peachassfy de are to entices meand "
                "evelas of Friscerple th iseek arces a wind."
            ]))
        self.assertEqual(tueMorn.tz.zone, "Pacific/Auckland")
        self.assertEqual(tueMorn.time_from, dt.time(9, 30))
        self.assertEqual(tueMorn.time_to, dt.time(11, 30))
        self.assertEqual(tueMorn.location, "Coast Rd, Barrytown, New Zealand")
        self.assertEqual(
            tueMorn.when,
            "Tuesdays (until 28 August 2018) at 9:30am to 11:30am")

        tueExceptions = tueMorn.get_children()
        self.assertEqual(len(tueExceptions), 3)
        tue24th, tue31st, tue14th = [page.specific for page in tueExceptions]

        self.assertEqual(tue24th.owner, self.user)
        self.assertEqual(tue24th.overrides, tueMorn)
        self.assertEqual(tue24th.slug, "2018-07-24-postponement")
        self.assertEqual(tue24th.title,
                         "Postponement for Tuesday 24th of July")
        self.assertEqual(tue24th.details, tueMorn.details)
        self.assertEqual(tue24th.tz.zone, "Pacific/Auckland")
        self.assertEqual(tue24th.except_date, dt.date(2018, 7, 24))
        self.assertEqual(tue24th.date, dt.date(2018, 7, 25))
        self.assertEqual(tue24th.time_from, dt.time(9, 30))
        self.assertEqual(tue24th.time_to, dt.time(11, 30))
        self.assertEqual(tue24th.location, "Coast Rd, Barrytown, New Zealand")

        self.assertEqual(tue31st.owner, self.user)
        self.assertEqual(tue31st.overrides, tueMorn)
        self.assertEqual(tue31st.slug, "2018-07-31-extra-info")
        self.assertEqual(tue31st.title, "Extra-Info for Tuesday 31st of July")
        self.assertEqual(tue31st.extra_title, "Tuesday Morning Extra Info")
        self.assertEqual(
            tue31st.extra_information, "\n".join([
                "", "Extra Famin fork, andivery,  Hough in the re of re whels "
                "otedshiplue porturat inve in nurectic."
            ]))
        self.assertEqual(tue31st.tz.zone, "Pacific/Auckland")
        self.assertEqual(tue31st.except_date, dt.date(2018, 7, 31))

        self.assertEqual(tue14th.owner, self.user)
        self.assertEqual(tue14th.overrides, tueMorn)
        self.assertEqual(tue14th.slug, "2018-08-14-cancellation")
        self.assertEqual(tue14th.title,
                         "Cancellation for Tuesday 14th of August")
        self.assertEqual(tue14th.cancellation_title, "")
        self.assertEqual(tue14th.cancellation_details, "")
        self.assertEqual(tue14th.tz.zone, "Pacific/Auckland")
        self.assertEqual(tue14th.except_date, dt.date(2018, 8, 14))

        self.assertEqual(daysOff.owner, self.user)
        self.assertEqual(daysOff.slug, "three-days-off")
        self.assertEqual(daysOff.title, "Three days off")
        self.assertEqual(daysOff.details, "")
        self.assertEqual(daysOff.tz.zone, "Pacific/Auckland")
        self.assertEqual(daysOff.date_from, dt.date(2018, 7, 13))
        self.assertEqual(daysOff.time_from, None)
        self.assertEqual(daysOff.date_to, dt.date(2018, 7, 15))
        self.assertEqual(daysOff.time_to, None)
        self.assertEqual(daysOff.location, "Home")

        self.assertEqual(lilWeds.owner, self.user)
        self.assertEqual(lilWeds.slug, "little-wednesday")
        self.assertEqual(lilWeds.title, "Little Wednesday")
        self.assertEqual(lilWeds.details, "")
        self.assertEqual(lilWeds.tz, pytz.utc)
        self.assertEqual(lilWeds.date, dt.date(2018, 7, 17))
        self.assertEqual(lilWeds.time_from, dt.time(22))
        self.assertEqual(lilWeds.time_to, dt.time(22, 30))
        self.assertEqual(lilWeds.location, "Pariroa Beach")
        self.assertEqual(lilWeds.when,
                         "Wednesday 18th of July at 10am to 10:30am")

        self.assertEqual(cnfCall.owner, self.user)
        self.assertEqual(cnfCall.slug, "conference-call")
        self.assertEqual(cnfCall.title, "Conference Call")
        self.assertEqual(cnfCall.details, "")
        self.assertEqual(cnfCall.tz, pytz.utc)
        self.assertEqual(cnfCall.date, dt.date(2018, 7, 23))
        self.assertEqual(cnfCall.time_from, dt.time(19))
        self.assertEqual(cnfCall.time_to, dt.time(20))

        self.assertEqual(bigThur.owner, self.user)
        self.assertEqual(bigThur.slug, "big-thursday")
        self.assertEqual(bigThur.title, "Big Thursday")
        self.assertEqual(
            bigThur.details,
            "Hounit <b>catlike</b> at ethatial to thin a usistiques onshiend "
            "alits mily tente duse prommuniss ind sedships itommunte of perpollood."
        )
        self.assertEqual(bigThur.tz, pytz.utc)
        self.assertEqual(bigThur.date_from, dt.date(2018, 7, 25))
        self.assertEqual(bigThur.time_from, dt.time(21))
        self.assertEqual(bigThur.date_to, dt.date(2018, 7, 26))
        self.assertEqual(bigThur.time_to, dt.time(8, 30))
        self.assertEqual(bigThur.when,
                         "Thursday 26th of July at 9am to 8:30pm")

    @freeze_time("2018-02-01")
    @timezone.override("Pacific/Auckland")
    def testUtc2Local(self):
        stream = BytesIO(rb"""
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Test Data
X-WR-TIMEZONE:Australia/Sydney
X-WR-CALDESC:Sample data for Joyous test_ical unittest
BEGIN:VEVENT
DTSTART:20180725T210000Z
DTEND:20180726T083000Z
DTSTAMP:20180722T060025Z
UID:[email protected]
CREATED:20180722T035919Z
DESCRIPTION:Hounit <b>catlike</b> at ethatial to thin a usistiques onshiend
  alits mily tente duse prommuniss ind sedships itommunte of perpollood.
LAST-MODIFIED:20180722T035919Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Big Thursday
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
""")
        request = self._getRequest()
        self.handler.load(self.calendar, request, stream, utc2local=True)
        events = getAllEvents(request, home=self.calendar)
        self.assertEqual(len(events), 1)
        bigThur = events[0]
        self.assertEqual(bigThur.owner, self.user)
        self.assertEqual(bigThur.slug, "big-thursday")
        self.assertEqual(bigThur.title, "Big Thursday")
        self.assertEqual(
            bigThur.details,
            "Hounit <b>catlike</b> at ethatial to thin a usistiques onshiend "
            "alits mily tente duse prommuniss ind sedships itommunte of perpollood."
        )
        self.assertEqual(bigThur.tz.zone, "Australia/Sydney")
        self.assertEqual(bigThur.date_from, dt.date(2018, 7, 26))
        self.assertEqual(bigThur.time_from, dt.time(7))
        self.assertEqual(bigThur.date_to, dt.date(2018, 7, 26))
        self.assertEqual(bigThur.time_to, dt.time(18, 30))
        self.assertEqual(bigThur.when,
                         "Thursday 26th of July at 9am to 8:30pm")

    def testOutlook(self):
        stream = BytesIO(rb"""
BEGIN:VCALENDAR
PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN
VERSION:2.0
METHOD:PUBLISH
BEGIN:VEVENT
DTSTART:20180730T092500
DTEND:20180730T101500
UID:7N7Y7V6J4N2U4I3U7H0N7W5O4V2U0K3H2E4Q4O7A2H0W1A5M6N
DTSTAMP:20180728T035656
DESCRIPTION;ENCODING=QUOTED-PRINTABLE:Booking number 9876543=0D=0A=0D=0AYour outgoing route is Westport > Wellington.=0D=0AThis route departs Westport on 30/Jul/2018 09:25 and arrives at Wellington at 10:15. The check-in time is 08:55.=0A
SUMMARY;ENCODING=QUOTED-PRINTABLE:Sounds Air - Flight Reminder
PRIORITY:3
BEGIN:VALARM
TRIGGER:-PT24H
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
BEGIN:VCALENDAR
PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN
VERSION:2.0
METHOD:PUBLISH
BEGIN:VEVENT
DTSTART:20180731T081500
DTEND:20180731T090000
UID:1G0K0V7K4L0H4Q4T5F4R8U2E0D0S4H2M6O1J6M5C5S2R4D0S2Q
DTSTAMP:20180728T035656
DESCRIPTION;ENCODING=QUOTED-PRINTABLE:Booking number 9876543=0D=0A=0D=0A=0D=0AYour return route is Wellington > Westport.=0D=0AThis route departs Wellington on 31/Jul/2018 08:15 and arrives at Westport at 09:00. The check-in time is 07:45.=0A
SUMMARY;ENCODING=QUOTED-PRINTABLE:Sounds Air - Flight Reminder
PRIORITY:3
BEGIN:VALARM
TRIGGER:-PT24H
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
""")
        request = self._getRequest()
        self.handler.load(self.calendar, request, stream)
        events = [page.specific for page in self.calendar.get_children()]
        self.assertEqual(len(events), 2)
        flight1, flight2 = events

        self.assertEqual(flight1.slug, "sounds-air-flight-reminder")
        self.assertEqual(flight1.title, "Sounds Air - Flight Reminder")
        self.assertEqual(
            flight1.details, "\r\n".join([
                "Booking number 9876543", "",
                "Your outgoing route is Westport > Wellington.",
                "This route departs Westport on 30/Jul/2018 09:25 and arrives at "
                "Wellington at 10:15. The check-in time is 08:55.\n"
            ]))
        self.assertEqual(flight1.tz.zone, "Asia/Tokyo")
        self.assertEqual(flight1.date, dt.date(2018, 7, 30))
        self.assertEqual(flight1.time_from, dt.time(9, 25))
        self.assertEqual(flight1.time_to, dt.time(10, 15))

        self.assertEqual(flight2.slug, "sounds-air-flight-reminder-2")
        self.assertEqual(flight2.title, "Sounds Air - Flight Reminder")
        self.assertEqual(
            flight2.details, "\r\n".join([
                "Booking number 9876543", "", "",
                "Your return route is Wellington > Westport.",
                "This route departs Wellington on 31/Jul/2018 08:15 and arrives at "
                "Westport at 09:00. The check-in time is 07:45.\n"
            ]))
        self.assertEqual(flight2.tz.zone, "Asia/Tokyo")
        self.assertEqual(flight2.date, dt.date(2018, 7, 31))
        self.assertEqual(flight2.time_from, dt.time(8, 15))
        self.assertEqual(flight2.time_to, dt.time(9))

    def testFacebook(self):
        stream = BytesIO(rb"""
BEGIN:VCALENDAR
PRODID:-//Facebook//NONSGML Facebook Events V1.0//EN
X-PUBLISHED-TTL:PT12H
X-ORIGINAL-URL:https://www.facebook.com/events/501511573641525/
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
DTSTAMP:20180729T102010Z
LAST-MODIFIED:20180729T102010Z
CREATED:20180729T102010Z
SEQUENCE:0
ORGANIZER;CN=Jjjj Bbbbb:MAILTO:[email protected]
ATTENDEE;CN=Bbbbb Wwwwww;PARTSTAT=ACCEPTED:https://www.facebook.com/bbwwwwww
ATTENDEE;CN=Jjjj Bbbbb;PARTSTAT=ACCEPTED:https://www.facebook.com/jjjj.bbbbb
ATTENDEE;CN=Pppp Tttttt;PARTSTAT=TENTATIVE:https://www.facebook.com/pppp.tttttt.123
DTSTART:20180831T070000Z
DTEND:20180831T100000Z
UID:[email protected]
SUMMARY:Photo Comp - Prize Giving
LOCATION:TBC
URL:https://www.facebook.com/events/501511573641525/
DESCRIPTION:The much anticipated 2018 West Coa
 st Alpine Club is open!\nEntries cl
 ose midnight Friday 24th August. F
 ull details and entry form in the 
 linked PDF: https://www.dropbox.co
 m/s/5vxnep33ccxok9z/PhotoCompDetai
 ls.pdf?dl=0\nDetails of the prize g
 iving will be added here in due co
 urse\, but save the date in the mea
 n time.\n\nhttps://www.facebook.com/
 events/501511573641525/
CLASS:PUBLIC
STATUS:CONFIRMED
PARTSTAT:NEEDS-ACTION
END:VEVENT
END:VCALENDAR
""")
        request = self._getRequest()
        self.handler.load(self.calendar, request, stream)
        events = self.calendar.get_children()
        self.assertEqual(len(events), 1)
        event = events[0].specific

        self.assertEqual(event.slug, "photo-comp-prize-giving")
        self.assertEqual(event.title, "Photo Comp - Prize Giving")
        self.assertEqual(
            event.details, "\n".join([
                "The much anticipated 2018 West Coast Alpine Club is open!",
                "Entries close midnight Friday 24th August. Full details and "
                "entry form in the linked PDF: https://www.dropbox.com/s/"
                "5vxnep33ccxok9z/PhotoCompDetails.pdf?dl=0",
                "Details of the prize giving will be added here in due course, "
                "but save the date in the mean time.", "",
                "https://www.facebook.com/events/501511573641525/"
            ]))
        self.assertEqual(event.tz.zone, "UTC")
        self.assertEqual(event.date, dt.date(2018, 8, 31))
        self.assertEqual(event.time_from, dt.time(7))
        self.assertEqual(event.time_to, dt.time(10))

    def testUntilTZ(self):
        stream = BytesIO(rb"""
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:[email protected]
X-WR-TIMEZONE:Pacific/Auckland
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=America/New_York:20310101T050000
DTEND;TZID=America/New_York:20310101T070000
RRULE:FREQ=DAILY;UNTIL=20310108T045959Z
DTSTAMP:20190331T203301Z
UID:[email protected]
CREATED:20190331T200304Z
DESCRIPTION:New Year resolution
LAST-MODIFIED:20190331T203219Z
LOCATION:New York\, NY\, USA
SEQUENCE:5
STATUS:CONFIRMED
SUMMARY:Exercise
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR""")
        request = self._getRequest()
        self.handler.load(self.calendar, request, stream)
        events = self.calendar.get_children()
        self.assertEqual(len(events), 1)
        event = events[0].specific

        self.assertIs(type(event), RecurringEventPage)
        self.assertEqual(event.slug, "exercise")
        self.assertEqual(event.tz.zone, "America/New_York")
        self.assertEqual(event.time_from, dt.time(5))
        self.assertEqual(event.time_to, dt.time(7))
        self.assertEqual(event.repeat.getCount(), 7)
        self.assertTrue(event._occursOn(dt.date(2031, 1, 1)))
        self.assertFalse(event._occursOn(dt.date(2031, 1, 8)))

    def testMultidayRecurringEvent(self):
        stream = BytesIO(rb"""
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//linuxsoftware.nz//NONSGML Joyous v0.8//EN
BEGIN:VEVENT
SUMMARY:Bought from a Rubber Man
DTSTART;TZID=Pacific/Auckland:20190402T160000
DTEND;TZID=Pacific/Auckland:20190404T180000
DTSTAMP:20190405T054311Z
UID:e6936872-f15c-4c47-92f2-3559a6610c78
SEQUENCE:1
RRULE:FREQ=WEEKLY;BYDAY=TU;WKST=SU
CREATED:20190405T054255Z
DESCRIPTION:<p></p>
LAST-MODIFIED:20190405T054255Z
LOCATION:
URL:http://localhost/calendar/bought-rubber-man/
END:VEVENT
BEGIN:VTIMEZONE
TZID:Pacific/Auckland
BEGIN:DAYLIGHT
DTSTART;VALUE=DATE-TIME:20180930T030000
RDATE:20190929T030000,20200927T030000,20210926T030000,20220925T030000,2023
 0924T030000,20240929T030000,20250928T030000,20260927T030000,20270926T03000
 0,20280924T030000,20290930T030000,20300929T030000,20310928T030000,20320926
 T030000,20330925T030000,20340924T030000,20350930T030000,20360928T030000,20
 370927T030000
TZNAME:NZDT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
END:DAYLIGHT
BEGIN:STANDARD
DTSTART;VALUE=DATE-TIME:20190407T020000
RDATE:20200405T020000,20210404T020000,20220403T020000,20230402T020000,2024
 0407T020000,20250406T020000,20260405T020000,20270404T020000,20280402T02000
 0,20290401T020000,20300407T020000,20310406T020000,20320404T020000,20330403
 T020000,20340402T020000,20350401T020000,20360406T020000,20370405T020000
TZNAME:NZST
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
END:STANDARD
END:VTIMEZONE
END:VCALENDAR""")
        request = self._getRequest()
        self.handler.load(self.calendar, request, stream)
        events = self.calendar.get_children()
        self.assertEqual(len(events), 1)
        event = events[0].specific

        self.assertIs(type(event), MultidayRecurringEventPage)
        self.assertEqual(event.title, "Bought from a Rubber Man")
        self.assertEqual(event.tz.zone, "Pacific/Auckland")
        self.assertEqual(event.num_days, 3)
        self.assertEqual(event.time_from, dt.time(16))
        self.assertEqual(event.time_to, dt.time(18))