Exemple #1
0
def _parse_time_range(el):
    start = el.get('start')
    end = el.get('end')
    # Either start OR end OR both need to be specified.
    # https://tools.ietf.org/html/rfc4791, section 9.9
    assert start is not None or end is not None
    if start is None:
        start = "00010101T000000Z"
    if end is None:
        end = "99991231T235959Z"
    start = vDDDTypes.from_ical(start)
    end = vDDDTypes.from_ical(end)
    assert end > start
    assert end.tzinfo
    assert start.tzinfo
    return (start, end)
Exemple #2
0
def AddOrUpdateWebcal(webcal):
	calString = urllib.urlopen(webcal.webcal_url).read()
	cal = VCALENDAR.from_string(calString)
	eventCluster = EventCluster(
			cluster_title = webcal.webcal_title,
			cluster_description = webcal.webcal_description,
			cluster_user_created = webcal.webcal_user_added,
			cluster_category = webcal.webcal_default_category,
			cluster_rsvp_enabled = False,
			cluster_board_enabled = True,
			cluster_notify_boardpost = False,
		)
	eventCluster.save()
	
	for component in cal.walk():
		if(component.name == 'VEVENT'):
			valid = True
			proplist = {}
			REQ_PROPS = ('UID','SUMMARY','DTSTART','DTEND')
			for prop in component.property_items():
				proplist[prop[0]] = prop[1]

			for rprop in REQ_PROPS:
				if rprop not in proplist:
					print 'MISSING %s' % rprop
					valid = False

			if valid:
				try:
					updateEvent = Event.objects.get(event_webcal_uid = proplist['UID'])
					print 'I found my old friend, %s' % proplist['UID']
				except:
					dtstart = vDDDTypes.from_ical(proplist['DTSTART'].ical())
					dtend = vDDDTypes.from_ical(proplist['DTEND'].ical())
					add_event = Event(
						event_webcal_uid = proplist['UID'],
						event_user_last_modified = CalUser.objects.get(user_netid='yaro'),
						event_subtitle = proplist['SUMMARY'],
						event_subdescription = proplist.get('DESCRIPTION','No description provided.'),
						event_date_time_start = dtstart,
						event_date_time_end = dtend,
						event_location_details = proplist.get('LOCATION',''),
						event_cluster = eventCluster,
						event_cancelled = False,
						event_attendee_count = 0,)
					add_event.save()
Exemple #3
0
def output_dates(bot, target, now, then, filter_location, announce=0):
    """
    Output dates between now and then and filter default location.
    Set announce greater 0 to add announce message and
    suppresses the No dates found message.
    """

    config = dates_configuration(bot)

    try:
        file = open(config['cache'])
        r = file.read()
    except OSError as e:
        raise Exception(e)

    try:
        cal = Calendar.from_ical(r)
        found = 0

        data = []
        timezoneEF = timezone('Europe/Berlin')
        fmt = "%d.%m.%Y %H:%M"

        # iterate all VEVENTS
        for ev in cal.walk('VEVENT'):
            start = vDDDTypes.from_ical(ev["DTSTART"])

            """
            check if DTSTART could be casted into some instance of
            dateime. If so, this indicates an event with a given start
            and stop time. There are other events too, e.g. Events
            lasting a whole day. Reading DTSTART of such whole day
            events will result in some instance of date. We will
            handle this case later.
            """

            if isinstance(start, datetime):
                rset = rruleset()  # create a set of recurrence rules
                info = ""
                loc = ""

                """
                Everyone interested in calendar events wants to get
                some summary about the event. So each event
                handled here has to have a SUMMARY. If not, we will
                discard handling the VEVENT here
                """
                if "SUMMARY" in ev:
                    info = ev["SUMMARY"]
                else:
                    continue  # events ohne summary zeigen wir nicht an!

                """
                Printing the location of an event is important too.
                However,
                the string containing location info may be too long
                to be viewed nicely in IRC.
                We filter our default location and strip every other
                location to the location name without address.
                """

                if "LOCATION" in ev:
                    if not ev["LOCATION"].startswith(filter_location):
                        loc = ev["LOCATION"].split(', ')[0]

                """
                Recurrence handling starts here.
                First, we check if there is a recurrence rule (RRULE)
                inside the VEVENT, If so, we use the ical like
                expression of DTSTART and RRULE to feed
                our ruleset with.
                """
                if "RRULE" in ev:  # recurrence
                    ical_dtstart = (ev.get("DTSTART")).to_ical().decode()
                    ical_rrule = (ev.get('RRULE')).to_ical().decode()
                    rset.rrule(rrulestr(ical_rrule,
                                        dtstart=parse(ical_dtstart),
                                        ignoretz=1))

                    """
                    Recurrence handling includes exceptions in EXDATE.
                    First we check if there are EXDATE values. If there
                    is only one we will convert this also to a list to
                    simplify handling. We use list entries to feed our
                    ruleset with.
                    """
                    if "EXDATE" in ev:
                        ical_exdate = ev.get('EXDATE')
                        if isinstance(ical_exdate, vDDDLists):
                            ical_exdate = [ical_exdate]
                        for exdate in ical_exdate:
                            rset.exdate(parse(exdate.to_ical()))

                    """
                    the ruleset now may be used to calculate any datetime
                    the event happened and will happen.
                    Since we are only interested
                    in upcoming events between now and then, we just use
                    the between() method of the ruleset which will return an
                    array of datetimes. Since timeutils cares about tumezones,
                    no convertion between utc and ME(S)Z needs to be done.
                    We just iterate the array of datetimes and put starting
                    time (DTSTART) info (SUMMARY) and location (LOCATION)
                    into our "database" of events
                    """
                    for e in rset.between(now, then):
                        found += 1
                        data.append({
                            'datetime': e.strftime(fmt),
                            'datetime_sort': e.strftime(fmt),
                            'info': info,
                            'loc': loc,
                        })

                    """
                    Recurrence rules do also know about EXDATEs, handling this
                    should be easy through rset (ruleset)...
                    TODO handling of EXDATE
                    """

                else:  # no recurrence
                    """
                    there was no recurrence rule (RRULE), so we do not need
                    to handle recurrece for this VEVENT. We do, however, need
                    to handle conversion between UTC and ME(S)Z, because now
                    timeutils, which would do this for us automatically, is
                    not involved

                    first we check if the DTSTART is between now and then.
                    If so, we put the VEVENTs starttime (DTSTART), info
                    (SUMMARY) and location (LOCATION) into our database.
                    """

                    if start < utc.localize(now) or start > utc.localize(then):
                        continue

                    found += 1
                    data.append({
                        'datetime': start.astimezone(timezoneEF).strftime(fmt),
                        'datetime_sort':
                            start.astimezone(timezoneEF).strftime(fmt),
                        'info': info,
                        'loc': loc,
                    })

            """
            So far we only have handled short time events, but there are
            whole day events too. So lets handle them here...

            TODO handling of whole day events

            if isinstance(start, date):
            """

        # lets sort our database, nearest events coming first...
        data = sorted(data,
                      key=lambda k: time.mktime(datetime.strptime(
                          k['datetime_sort'], "%d.%m.%Y %H:%M").timetuple()))

        """
        Spit out all events in database into IRC. Suppress duplicate lines
        from nonconforming ics files. Add message on announcing events. If
        there were no events, print some message about this...
        """

        if found > 0 and announce > 0:
            bot.privmsg(target, "Please notice the next following event(s):")

        last_output = None
        for ev in data:
            output = "  %s - %s" % (ev['datetime'], ev['info'])
            if ev['loc']:
                output = "%s (%s)" % (output, ev['loc'])

            if last_output != output:
                last_output = output
                bot.privmsg(target, output)

        if found == 0 and announce == 0:
            bot.privmsg(
                target,
                "No dates during the next %d days" % config['list_days']
            )

    except KeyError:
        bot.privmsg(target, "Error while retrieving dates data")
        raise Exception()
Exemple #4
0
			proplist = {}
			REQ_PROPS = ('UID','SUMMARY','DTSTART','DTEND')
			for prop in component.property_items():
				proplist[prop[0]] = prop[1]

			for rprop in REQ_PROPS:
				if rprop not in proplist:
					print 'MISSING %s' % rprop
					valid = False

			if valid:
				try:
					updateEvent = Event.objects.get(event_webcal_uid = proplist['UID'])
					print 'I found my old friend, %s' % proplist['UID']
				except:
					dtstart = vDDDTypes.from_ical(proplist['DTSTART'].ical())
					dtend = vDDDTypes.from_ical(proplist['DTEND'].ical())
					add_event = Event(
						event_webcal_uid = proplist['UID'],
						event_user_last_modified = CalUser.objects.get(user_netid='yaro'),
						event_subtitle = proplist['SUMMARY'],
						event_subdescription = proplist.get('DESCRIPTION','No description provided.'),
						event_date_time_start = dtstart,
						event_date_time_end = dtend,
						event_location_details = proplist.get('LOCATION',''),
						event_cluster = eventCluster,
						event_cancelled = False,
						event_attendee_count = 0,)
					add_event.save()

AddOrUpdateWebcal(webcal);
from pytz import utc, timezone
import smtplib

f = urlopen(cal_file)
cal = Calendar.from_ical(f.read())
mail = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n"
        % (mail_from, mail_to, mail_subject))
now = datetime.now(utc).replace(hour=0, minute=0, second=0, microsecond=0)
nweek = now + timedelta(weeks=1)
timezoneEF = timezone('Europe/Berlin')
fmt = "%d.%m.%Y, %H:%M"

for ev in cal.walk():
    if ev.name == 'VEVENT':
        # Check the start and end dates of the event
        if len(str(vDDDTypes.from_ical(ev.get('dtstart'))).split(' ')) > 1:
            start = vDDDTypes.from_ical(ev.get('dtstart')).replace(tzinfo=utc)
            end = vDDDTypes.from_ical(ev.get('dtend')).replace(tzinfo=utc)
            if start < now or start > nweek:
                continue
        else:
            start = vDDDTypes.from_ical(ev.get('dtstart'))
            end = vDDDTypes.from_ical(ev.get('dtend'))
            if start < now.date() or start > nweek.date():
                continue

        mail += 'Betreff:      ' + ev.get('summary').encode('utf-8') + "\n"
        mail += 'Start:        ' + str(ev.get('dtstart').dt.
                                       astimezone(timezoneEF).
                                       strftime(fmt)) + "\n"
        mail += 'Ende:         ' + str(ev.get('dtend').dt.
Exemple #6
0
    def onPrivmsg(self, irc, msg, channel, user):
        """Looks for a '!dates' command in messages posted to the channel and
        returns a list of dates within the next week.

        irc: An instance of the bytebot. Will be passed by the plugin loader
        msg: The msg sent to the channel
        channel: The channels name
        user: The user who sent the message
        """

        if msg.find('!dates') == -1:
            return

        f = urlopen(BYTEBOT_PLUGIN_CONFIG['dates']['url'])
        """
        icalender does not like to see any X-APPLE-MAPKIT-HANDLEs,
        so lets replace all X-APPLE-MAPKIT-HANDLEs with nothing
        """
        applefilter = re.compile(r"X-APPLE-MAPKIT-HANDLE.*?;",
                                 re.MULTILINE | re.DOTALL)
        ical_orig = f.read()
        ical_filtered = applefilter.sub("", ical_orig)

        cal = Calendar.from_ical(ical_filtered)

        now = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
        then = now + timedelta(
            days=BYTEBOT_PLUGIN_CONFIG['dates']['timedelta'])
        found = 0

        data = []
        timezoneEF = timezone('Europe/Berlin')
        fmt = "%d.%m.%Y %H:%M"

        # iterate all VEVENTS
        for ev in cal.walk('VEVENT'):
            start = vDDDTypes.from_ical(ev["DTSTART"])
            """
            check if DTSTART could be casted into some instance of
            dateime. If so, this indicates an event with a given start
            and stop time. There are other events too, e.g. Events
            lasting a whole day. Reading DTSTART of such whole day
            events will result in some instance of date. We will
            handle this case later.
            """

            if isinstance(start, datetime):
                rset = rruleset()  # create a set of recurrence rules
                info = ""
                loc = ""
                """
                Everyone interested in calendar events wants to get
                some summary about the event. So each event
                handled here has to have a SUMMARY. If not, we will
                discard handling the VEVENT here
                """
                if "SUMMARY" in ev:
                    found += 1
                    info = ev["SUMMARY"].encode("utf-8")
                else:
                    continue  # events ohne summary zeigen wir nicht an!
                """
                Printing the location of an event is important too.
                However,
                the string containing location info may be too long
                to be viewed nicely in IRC. Since there exits no
                good solution for this, this feature is coming soon.

                if "LOCATION" in ev:
                    loc = ev["LOCATION"]
                else:
                    loc = "Liebknechtstrasse 8"

                Recurrence handling starts here.
                First, we check if there is a recurrence rule (RRULE)
                inside the VEVENT, If so, we use the ical like
                expression of DTSTART and RRULE to feed
                our ruleset with.
                """
                if "RRULE" in ev:  # recurrence
                    ical_dtstart = (ev.get("DTSTART")).to_ical()
                    ical_rrule = (ev.get('RRULE')).to_ical()
                    rset.rrule(
                        rrulestr(ical_rrule,
                                 dtstart=parse(ical_dtstart),
                                 ignoretz=1))
                    """
                    the ruleset now may be used to calculate any datetime
                    the event happened and will happen.
                    Since we are only interested
                    in upcoming events between now and then, we just use
                    the between() method of the ruleset which will return an
                    array of datetimes. Since timeutils cares about tumezones,
                    no convertion between utc and ME(S)Z needs to be done.
                    We just iterate the array of datetimes and put starting
                    time (DTSTART) info (SUMMARY) and location (LOCATION)
                    into our "database" of events
                    """
                    for e in rset.between(now, then):
                        data.append({
                            'datetime': e.strftime(fmt),
                            'datetime_sort': e.strftime(fmt),
                            'info': info,
                            'loc': loc,
                        })
                    """
                    Recurrence rules do also know about EXDATEs, handling this
                    should be easy through rset (ruleset)...
                    TODO handling of EXDATE
                    """

                else:  # no recurrence
                    """
                    there was no recurrence rule (RRULE), so we do not need
                    to handle recurrece for this VEVENT. We do, however, need
                    to handle conversion between UTC and ME(S)Z, because now
                    timeutils, which would do this for us automatically, is
                    not involved

                    first we check if the DTSTART is between now and then.
                    If so, we put the VEVENTs starttime (DTSTART), info
                    (SUMMARY) and location (LOCATION) into our database.
                    """

                    if start < utc.localize(now) or start > utc.localize(then):
                        continue

                    data.append({
                        'datetime':
                        start.astimezone(timezoneEF).strftime(fmt),
                        'datetime_sort':
                        start.astimezone(timezoneEF).strftime(fmt),
                        'info':
                        info,
                        'loc':
                        loc,
                    })
            """
            So far we only have handled short time events, but there are
            whole day events too. So lets handle them here...

            TODO handling of whole day events

            if isinstance(start, date):
            """

        # lets sort our database, nearest events coming first...
        data = sorted(data,
                      key=lambda k: time.mktime(
                          datetime.strptime(k['datetime_sort'],
                                            "%d.%m.%Y %H:%M").timetuple()))
        """
        spit out all events in database into IRC. If there were no
        events, print some message about this...
        """
        for ev in data:
            irc.msg(channel, "  %s - %s" % (ev['datetime'], ev['info']))

        if found == 0:
            irc.msg(channel, "No dates during the next week")

        f.close()
Exemple #7
0
def dates(bot, mask, target, args):
    """Show the planned dates within the next days

        %%dates
    """

    """Load configuration"""
    config = {'url': '', 'timedelta': 21}
    config.update(bot.config.get(__name__, {}))

    """Request the ical file."""
    with aiohttp.Timeout(10):
        with aiohttp.ClientSession(loop=bot.loop) as session:
            resp = yield from session.get(config['url'])
            if resp.status == 200:
                """Get text content from http request."""
                r = yield from resp.text()
            else:
                bot.privmsg(target, "Error while retrieving calendar data")
                raise Exception()

    try:
        cal = Calendar.from_ical(r)
        now = datetime.now().replace(
            hour=0, minute=0, second=0, microsecond=0)
        then = now + timedelta(
            days=config['timedelta'])
        found = 0

        data = []
        timezoneEF = timezone('Europe/Berlin')
        fmt = "%d.%m.%Y %H:%M"

        # iterate all VEVENTS
        for ev in cal.walk('VEVENT'):
            start = vDDDTypes.from_ical(ev["DTSTART"])

            """
            check if DTSTART could be casted into some instance of
            dateime. If so, this indicates an event with a given start
            and stop time. There are other events too, e.g. Events
            lasting a whole day. Reading DTSTART of such whole day
            events will result in some instance of date. We will
            handle this case later.
            """

            if isinstance(start, datetime):
                rset = rruleset()  # create a set of recurrence rules
                info = ""
                loc = ""

                """
                Everyone interested in calendar events wants to get
                some summary about the event. So each event
                handled here has to have a SUMMARY. If not, we will
                discard handling the VEVENT here
                """
                if "SUMMARY" in ev:
                    found += 1
                    info = ev["SUMMARY"]
                else:
                    continue  # events ohne summary zeigen wir nicht an!

                """
                Printing the location of an event is important too.
                However,
                the string containing location info may be too long
                to be viewed nicely in IRC. Since there exits no
                good solution for this, this feature is coming soon.

                if "LOCATION" in ev:
                    loc = ev["LOCATION"]
                else:
                    loc = "Liebknechtstrasse 8"

                Recurrence handling starts here.
                First, we check if there is a recurrence rule (RRULE)
                inside the VEVENT, If so, we use the ical like
                expression of DTSTART and RRULE to feed
                our ruleset with.
                """
                if "RRULE" in ev:  # recurrence
                    ical_dtstart = (ev.get("DTSTART")).to_ical().decode()
                    ical_rrule = (ev.get('RRULE')).to_ical().decode()
                    rset.rrule(rrulestr(ical_rrule,
                                        dtstart=parse(ical_dtstart),
                                        ignoretz=1))

                    """
                    Recurrence handling includes exceptions in EXDATE.
                    First we check if there are EXDATE values. If there
                    is only one we will convert this also to a list to
                    simplify handling. We use list entries to feed our
                    ruleset with.
                    """
                    if "EXDATE" in ev:
                        ical_exdate = ev.get('EXDATE')
                        if isinstance(ical_exdate, vDDDLists):
                            ical_exdate = [ical_exdate]
                        for exdate in ical_exdate:
                            rset.exdate(parse(exdate.to_ical()))

                    """
                    the ruleset now may be used to calculate any datetime
                    the event happened and will happen.
                    Since we are only interested
                    in upcoming events between now and then, we just use
                    the between() method of the ruleset which will return an
                    array of datetimes. Since timeutils cares about tumezones,
                    no convertion between utc and ME(S)Z needs to be done.
                    We just iterate the array of datetimes and put starting
                    time (DTSTART) info (SUMMARY) and location (LOCATION)
                    into our "database" of events
                    """
                    for e in rset.between(now, then):
                        data.append({
                            'datetime': e.strftime(fmt),
                            'datetime_sort': e.strftime(fmt),
                            'info': info,
                            'loc': loc,
                        })

                    """
                    Recurrence rules do also know about EXDATEs, handling this
                    should be easy through rset (ruleset)...
                    TODO handling of EXDATE
                    """

                else:  # no recurrence
                    """
                    there was no recurrence rule (RRULE), so we do not need
                    to handle recurrece for this VEVENT. We do, however, need
                    to handle conversion between UTC and ME(S)Z, because now
                    timeutils, which would do this for us automatically, is
                    not involved

                    first we check if the DTSTART is between now and then.
                    If so, we put the VEVENTs starttime (DTSTART), info
                    (SUMMARY) and location (LOCATION) into our database.
                    """

                    if start < utc.localize(now) or start > utc.localize(then):
                        continue

                    data.append({
                        'datetime': start.astimezone(timezoneEF).strftime(fmt),
                        'datetime_sort':
                            start.astimezone(timezoneEF).strftime(fmt),
                        'info': info,
                        'loc': loc,
                    })

            """
            So far we only have handled short time events, but there are
            whole day events too. So lets handle them here...

            TODO handling of whole day events

            if isinstance(start, date):
            """

        # lets sort our database, nearest events coming first...
        data = sorted(data,
                      key=lambda k: time.mktime(datetime.strptime(
                          k['datetime_sort'], "%d.%m.%Y %H:%M").timetuple()))

        """
        Spit out all events in database into IRC. Suppress duplicate lines
        from nonconforming ics files. If there were no events, print some
        message about this...
        """
        last_output = None
        for ev in data:
            output = "  %s - %s" % (ev['datetime'], ev['info'])
            if last_output != output:
                last_output = output
                bot.privmsg(target, output)

        if found == 0:
            bot.privmsg(target, "No dates during the next week")

    except KeyError:
        bot.privmsg(target, "Error while retrieving rss data")
        raise Exception()
    def output_dates(self, now, then, filter_location, room, announce=0):
        """
        Output dates between now and then and filter default location.
        Set announce greater 0 to add announce message and
        suppresses the No dates found message.
        """

        try:
            tmp_dates_cache = Path(self.config['cache'])
            if tmp_dates_cache.exists():
                file = open(str(tmp_dates_cache), encoding='UTF-8')
                raw_text = file.read()
                text = raw_text
                if not text:
                    return
            else:
                DATES_LOG.error("%s file not found from %s", tmp_dates_cache, os.getcwd())
                return
        except OSError as error:
            raise Exception(error)

        try:
            cal = Calendar.from_ical(text)
            found = 0

            data = []
            timezone_ef = timezone('Europe/Berlin')
            fmt = "%d.%m.%Y %H:%M"

            # iterate all VEVENTS
            for event in cal.walk('VEVENT'):
                start = vDDDTypes.from_ical(event["DTSTART"])
                info = ""
                loc = ""

                # Everyone interested in calendar events wants to get
                # some summary about the event. So each event
                # handled here has to have a SUMMARY. If not, we will
                # discard handling the VEVENT here
                if "SUMMARY" in event:
                    info = event["SUMMARY"]
                else:
                    continue  # events ohne summary zeigen wir nicht an!

                # Printing the location of an event is important too.
                # However,
                # the string containing location info may be too long
                # to be viewed nicely in IRC.
                # We filter our default location and strip every other
                # location to the location name without address.

                if "LOCATION" in event:
                    if filter_location and not event["LOCATION"].startswith(filter_location):
                        loc = event["LOCATION"].split(', ')[0]

                # check if DTSTART could be casted into some instance of
                # dateime. If so, this indicates an event with a given start
                # and stop time. There are other events too, e.g. Events
                # lasting a whole day. Reading DTSTART of such whole day
                # events will result in some instance of date. We will
                # handle this case later.
                if isinstance(start, datetime):
                    rset = rruleset()  # create a set of recurrence rules
  
                    # Recurrence handling starts here.
                    # First, we check if there is a recurrence rule (RRULE)
                    # inside the VEVENT, If so, we use the ical like
                    # expression of DTSTART and RRULE to feed
                    # our ruleset with.
                    if "RRULE" in event:  # recurrence
                        ical_dtstart = (event.get("DTSTART")).to_ical().decode()
                        ical_rrule = (event.get('RRULE')).to_ical().decode()
                        rset.rrule(rrulestr(ical_rrule,
                                            dtstart=parse(ical_dtstart),
                                            ignoretz=1))

                        # Recurrence handling includes exceptions in EXDATE.
                        # First we check if there are EXDATE values. If there
                        # is only one we will convert this also to a list to
                        # simplify handling. We use list entries to feed our
                        # ruleset with.
                        if "EXDATE" in event:
                            ical_exdate = event.get('EXDATE')
                            if isinstance(ical_exdate, vDDDLists):
                                ical_exdate = [ical_exdate]
                            for exdate in ical_exdate:
                                rset.exdate(parse(exdate.to_ical()))

                        # the ruleset now may be used to calculate any datetime
                        # the event happened and will happen.
                        # Since we are only interested
                        # in upcoming events between now and then, we just use
                        # the between() method of the ruleset which will return an
                        # array of datetimes. Since timeutils cares about tumezones,
                        # no convertion between utc and ME(S)Z needs to be done.
                        # We just iterate the array of datetimes and put starting
                        # time (DTSTART) info (SUMMARY) and location (LOCATION)
                        # into our "database" of events
                        for upcoming_event in rset.between(now, then):
                            found += 1
                            data.append({
                                'datetime': upcoming_event.strftime(fmt),
                                'datetime_sort': upcoming_event.strftime(fmt),
                                'info': info,
                                'loc': loc,
                            })

                        # Recurrence rules do also know about EXDATEs, handling this
                        # should be easy through rset (ruleset)...
                        # TODO handling of EXDATE

                    else:  # no recurrence

                        # there was no recurrence rule (RRULE), so we do not need
                        # to handle recurrece for this VEVENT. We do, however, need
                        # to handle conversion between UTC and ME(S)Z, because now
                        # timeutils, which would do this for us automatically, is
                        # not involved
                        #
                        # first we check if the DTSTART is between now and then.
                        # If so, we put the VEVENTs starttime (DTSTART), info
                        # (SUMMARY) and location (LOCATION) into our database.

                        if start < utc.localize(now) or start > utc.localize(then):
                            continue

                        found += 1
                        data.append({
                            'datetime': start.astimezone(timezone_ef).strftime(fmt),
                            'datetime_sort':
                                start.astimezone(timezone_ef).strftime(fmt),
                            'info': info,
                            'loc': loc,
                        })

                # So far we only have handled short time events, but there are
                # whole day events too. So lets handle them here...
                #
                # TODO handling of whole day events
                #
                # if isinstance(start, date):
                else:
                    # set starttime at 8am on day one
                    starttime = utc.localize(datetime.combine(start,dtime(hour=8)));
                    end = vDDDTypes.from_ical(event["DTEND"])
                    endtime = utc.localize(datetime.combine(end,dtime(hour=8)));
                    duration = endtime - starttime
                    durdays = duration.days
                    if starttime < utc.localize(now) or starttime > utc.localize(then):
                        continue
                    for i in range(durdays):
                        found += 1
                        data.append({
                            'datetime': (starttime + timedelta(days=i)).astimezone(timezone_ef).strftime(fmt),
                            'datetime_sort': (starttime + timedelta(days=i)).astimezone(timezone_ef).strftime(fmt),
                            'info': "Ganztägig: " + info + " (Tag " + str(i+1) + ")",
                            'loc': loc,
                            })

            # lets sort our database, nearest events coming first...
            data = sorted(data,
                          key=lambda k: time.mktime(datetime.strptime(
                              k['datetime_sort'], "%d.%m.%Y %H:%M").timetuple()))

            # Spit out all events in database into IRC. Suppress duplicate lines
            # from nonconforming ics files. Add message on announcing events. If
            # there were no events, print some message about this...

            if found > 0 and announce > 0:
                room.send_text("Please notice the next following event(s):")

            last_output = None
            all_output = ''
            for event in data:
                output = "  %s - %s" % (event['datetime'], event['info'])
                if event['loc']:
                    output = "%s (%s)" % (output, event['loc'])

                output += "\n"

                if last_output != output:
                    last_output = output
                    all_output += output
                    #room.send_text(output)
            if all_output: # is > 0
                room.send_text(all_output)

            if found == 0 and announce == 0:
                look_back = self.config.getint('list_days')
                room.send_text(
                    "No dates during the next %d days" % look_back
                )

        except KeyError:
            room.send_text("Error while retrieving dates data")
            raise Exception()
Exemple #9
0
def output_dates(bot, target, now, then, filter_location, announce=0):
    """
    Output dates between now and then and filter default location.
    Set announce greater 0 to add announce message and
    suppresses the No dates found message.
    """

    config = dates_configuration(bot)

    try:
        file = open(config['cache'])
        r = file.read()
    except OSError as e:
        raise Exception(e)

    try:
        cal = Calendar.from_ical(r)
        found = 0

        data = []
        timezoneEF = timezone('Europe/Berlin')
        fmt = "%d.%m.%Y %H:%M"

        # iterate all VEVENTS
        for ev in cal.walk('VEVENT'):
            start = vDDDTypes.from_ical(ev["DTSTART"])
            """
            check if DTSTART could be casted into some instance of
            dateime. If so, this indicates an event with a given start
            and stop time. There are other events too, e.g. Events
            lasting a whole day. Reading DTSTART of such whole day
            events will result in some instance of date. We will
            handle this case later.
            """

            if isinstance(start, datetime):
                rset = rruleset()  # create a set of recurrence rules
                info = ""
                loc = ""
                """
                Everyone interested in calendar events wants to get
                some summary about the event. So each event
                handled here has to have a SUMMARY. If not, we will
                discard handling the VEVENT here
                """
                if "SUMMARY" in ev:
                    info = ev["SUMMARY"]
                else:
                    continue  # events ohne summary zeigen wir nicht an!
                """
                Printing the location of an event is important too.
                However,
                the string containing location info may be too long
                to be viewed nicely in IRC.
                We filter our default location and strip every other
                location to the location name without address.
                """

                if "LOCATION" in ev:
                    if not ev["LOCATION"].startswith(filter_location):
                        loc = ev["LOCATION"].split(', ')[0]
                """
                Recurrence handling starts here.
                First, we check if there is a recurrence rule (RRULE)
                inside the VEVENT, If so, we use the ical like
                expression of DTSTART and RRULE to feed
                our ruleset with.
                """
                if "RRULE" in ev:  # recurrence
                    ical_dtstart = (ev.get("DTSTART")).to_ical().decode()
                    ical_rrule = (ev.get('RRULE')).to_ical().decode()
                    rset.rrule(
                        rrulestr(ical_rrule,
                                 dtstart=parse(ical_dtstart),
                                 ignoretz=1))
                    """
                    Recurrence handling includes exceptions in EXDATE.
                    First we check if there are EXDATE values. If there
                    is only one we will convert this also to a list to
                    simplify handling. We use list entries to feed our
                    ruleset with.
                    """
                    if "EXDATE" in ev:
                        ical_exdate = ev.get('EXDATE')
                        if isinstance(ical_exdate, vDDDLists):
                            ical_exdate = [ical_exdate]
                        for exdate in ical_exdate:
                            rset.exdate(parse(exdate.to_ical()))
                    """
                    the ruleset now may be used to calculate any datetime
                    the event happened and will happen.
                    Since we are only interested
                    in upcoming events between now and then, we just use
                    the between() method of the ruleset which will return an
                    array of datetimes. Since timeutils cares about tumezones,
                    no convertion between utc and ME(S)Z needs to be done.
                    We just iterate the array of datetimes and put starting
                    time (DTSTART) info (SUMMARY) and location (LOCATION)
                    into our "database" of events
                    """
                    for e in rset.between(now, then):
                        found += 1
                        data.append({
                            'datetime': e.strftime(fmt),
                            'datetime_sort': e.strftime(fmt),
                            'info': info,
                            'loc': loc,
                        })
                    """
                    Recurrence rules do also know about EXDATEs, handling this
                    should be easy through rset (ruleset)...
                    TODO handling of EXDATE
                    """

                else:  # no recurrence
                    """
                    there was no recurrence rule (RRULE), so we do not need
                    to handle recurrece for this VEVENT. We do, however, need
                    to handle conversion between UTC and ME(S)Z, because now
                    timeutils, which would do this for us automatically, is
                    not involved

                    first we check if the DTSTART is between now and then.
                    If so, we put the VEVENTs starttime (DTSTART), info
                    (SUMMARY) and location (LOCATION) into our database.
                    """

                    if start < utc.localize(now) or start > utc.localize(then):
                        continue

                    found += 1
                    data.append({
                        'datetime':
                        start.astimezone(timezoneEF).strftime(fmt),
                        'datetime_sort':
                        start.astimezone(timezoneEF).strftime(fmt),
                        'info':
                        info,
                        'loc':
                        loc,
                    })
            """
            So far we only have handled short time events, but there are
            whole day events too. So lets handle them here...

            TODO handling of whole day events

            if isinstance(start, date):
            """

        # lets sort our database, nearest events coming first...
        data = sorted(data,
                      key=lambda k: time.mktime(
                          datetime.strptime(k['datetime_sort'],
                                            "%d.%m.%Y %H:%M").timetuple()))
        """
        Spit out all events in database into IRC. Suppress duplicate lines
        from nonconforming ics files. Add message on announcing events. If
        there were no events, print some message about this...
        """

        if found > 0 and announce > 0:
            bot.privmsg(target, "Please notice the next following event(s):")

        last_output = None
        for ev in data:
            output = "  %s - %s" % (ev['datetime'], ev['info'])
            if ev['loc']:
                output = "%s (%s)" % (output, ev['loc'])

            if last_output != output:
                last_output = output
                bot.privmsg(target, output)

        if found == 0 and announce == 0:
            bot.privmsg(
                target,
                "No dates during the next %d days" % config['list_days'])

    except KeyError:
        bot.privmsg(target, "Error while retrieving dates data")
        raise Exception()