def on_step(self):
        # check reminders no more than once every 10 seconds
        current_time = time.time()
        if current_time - self.last_reminder_check_time < 10: return False
        self.last_reminder_check_time = current_time

        now = datetime.now()
        new_reminders = []
        reminders_updated = False
        for occurrence, recurrence, channel, description in self.reminders:
            if occurrence <= now: # reminder triggered, send notification
                reminders_updated = True
                if recurrence is not None: # recurring reminder, set up the next one
                    rrule = dateutil.rrule.rrulestr(recurrence)
                    next_occurrence = rrule.after(now) # get the next occurrence after the current one
                    if next_occurrence is not None:
                        new_reminders.append([next_occurrence, recurrence, channel, description])
                else:
                    next_occurrence = None
                self.remind(occurrence, next_occurrence, channel, description)
            else: # reminder has not triggered yet
                new_reminders.append([occurrence, recurrence, channel, description])
        if reminders_updated: self.save_reminders(new_reminders)
        self.reminders = new_reminders
        return True
    def handle(self, *args, **options):
        from django.conf import settings

        occurances = {}
        for event in Event.objects.all():
            for occurance in event.occurances.all():
                try:
                    occurances[occurance.of.name].append(occurance)
                except KeyError:
                    occurances[occurance.of.name] = [occurance]
        for occurances_of_event in occurances.values():
            occurances_of_event.sort(key=occurances.date)
            for occurance in occurances_of_event:
                if datetime.combine(occurance.date, occurance.of.start) < datetime.now(settings.TIME_ZONE):
                    occurance.delete()
                    print u'Removed Occurance of event "%s" at %s' % (
                        occurance.of.name,
                        datetime.combine(occurance.date, occurance.of.start),
                    )
            if len(occurances_of_event) < getattr(settings, "MIN_EVENT_OCCURANCES", 3):
                latest_occurance = occurances_of_event[-1]
                if latest_occurance.of.freq == "DAILY":
                    rrule = dateutil.rrule.rrule(dateutil.rrule.DAILY, count=1, interval=latest_occurance.of.interval)
                elif latest_occurance.of.freq == "WEEKLY":
                    rrule = dateutil.rrule.rrule(
                        dateutil.rrule.WEEKLY,
                        count=1,
                        byweekday=getattr(dateutil.rrule, latest_occurance.of.rule),
                        interval=latest_occurance.of.interval,
                    )
                elif latest_occurance.of.freq == "MONTHLY":
                    rrule = dateutil.rrule.rrule(
                        dateutil.rrule.MONTHLY,
                        count=1,
                        byweekday=getattr(dateutil.rrule, latest_occurance.of.rule[1:]),
                        bysetpos=int(latest_occurance.of.rule[:1]),
                        interval=latest_occurance.of.interval,
                    )
                elif latest_occurance.of.freq == "YEARLY":
                    match = re.match(r"(\d\d?)([MO|TU|WE|TH|FR|SA|SU])", latest_occurance.of.interval)
                    rrule = dateutil.rrule.rrule(
                        dateutil.rrule.YEARLY,
                        count=1,
                        byweekday=getattr(dateutil.rrule, match.group(2)),
                        byweekno=int(match.group(1)),
                        interval=latest_occurance.of.interval,
                    )
                occurance = Occurance(
                    of=latest_occurance.of,
                    date=rrule.after(datetime.combine(latest_occurance.of.date, latest_occurance.of.start)).date(),
                )
                occurance.save()
                print 'Added Occurance of Event "%s" on %s' % (latest_occurance.of.name, occurance.date)
Exemple #3
0
def get_next_event(after=None, all=False):
	cal_data = get_calendar_data()
	events = []
	if after is None:
		after = datetime.datetime.now(datetime.timezone.utc)
	for ev in cal_data.subcomponents:
		if isinstance(ev, icalendar.Event):
			event_name = str(ev['summary'])
			event_time = ev['dtstart'].dt
			exception_times = ev.get('exdate')
			if not exception_times:
				exception_times = []
			elif not isinstance(exception_times, (tuple, list)):
				exception_times = [exception_times]
			exception_times = set(j.dt for i in exception_times for j in i.dts)
			if not isinstance(event_time, datetime.datetime):
				# ignore full-day events
				continue
			# Report episodes that are either in the first half, or started less than an hour ago
			# Whichever is shorter
			cutoff_delay = (ev['dtend'].dt - ev['dtstart'].dt) / 2
			if cutoff_delay > datetime.timedelta(hours=1):
				cutoff_delay = datetime.timedelta(hours=1)
			event_time_cutoff = event_time + cutoff_delay
			if 'rrule' in ev:
				rrule = dateutil.rrule.rrulestr(ev['rrule'].to_ical().decode('utf-8'), dtstart=event_time_cutoff)
				### MASSIVE HACK ALERT
				_apply_monkey_patch(rrule)
				### END MASSIVE HACK ALERT
				# Find the next event in the recurrence that isn't an exception
				search_date = after
				while True:
					search_date = rrule.after(search_date)
					if search_date is None or search_date - cutoff_delay not in exception_times:
						break
				event_time_cutoff = search_date
				if event_time_cutoff is None:
					continue
				event_time = event_time_cutoff - cutoff_delay
			if event_time_cutoff > after:
				events.append((event_name, event_time))
	if all:
		events.sort(key=operator.itemgetter(1))
		return events
	if events:
		event_name, event_time = min(events, key=operator.itemgetter(1))
		event_wait = (event_time - after).total_seconds()
		return event_name, event_time, event_wait
	else:
		return None, None, None
Exemple #4
0
def process_alarm_object(alarm_object, stamp_object, start_object, duration_object, end_object, rrule_object, email_addresses, email_content, email_start_time, minutes_ahead, smtp_object, from_address, regex_list, report):
  # print(alarm_object.to_ical().decode("utf-8"))
  start_time = None
  all_day = False
  if(isinstance(start_object, prop.vDDDTypes)):
    if(isinstance(start_object.dt, datetime.datetime)):
      start_time = start_object.dt
    else:
      start_time = datetime.datetime(year=start_object.dt.year, month=start_object.dt.month, day=start_object.dt.day, tzinfo=stamp_object.dt.tzinfo)
      all_day = True
  else:
    start_time = stamp_object.dt
  if(start_time.tzinfo is None):
    start_time = start_time.replace(tzinfo=stamp_object.dt.tzinfo)
  duration = None
  if(isinstance(duration_object, prop.vDDDTypes)):
    if(isinstance(duration_object.dt, datetime.timedelta)):
      duration = duration_object.dt
  elif(isinstance(end_object, prop.vDDDTypes)):
    if(isinstance(end_object.dt, datetime.datetime)):
      duration = end_object.dt - start_time
    else:
      duration = datetime.datetime(year=end_object.dt.year, month=end_object.dt.month, day=end_object.dt.day, tzinfo=start_time.tzinfo) - start_time
  if(isinstance(rrule_object, prop.vRecur)):
    start_time_tz = start_time.tzinfo
    start_time_dst = start_time.dst()
    start_time = start_time.astimezone(email_start_time.tzinfo)
    rrule = dateutil.rrule.rrulestr(rrule_object.to_ical().decode('utf-8'), dtstart=start_time)
    start_time = rrule.after(email_start_time)
    if(isinstance(start_time, datetime.datetime)):
      start_time = start_time.astimezone(start_time_tz)
      start_time += start_time_dst - start_time.dst()
    else:
      report["alarms_expired"] += 1
      return
  end_time = None
  if(isinstance(duration, datetime.timedelta)):
    end_time = start_time + duration
  email_content["end_time"] = ""
  if(all_day):
    email_content["start_time"] = start_time.strftime("%a %b %d, %Y")
    if(isinstance(end_time, datetime.datetime)):
      email_content["end_time"] = (end_time.date() - datetime.timedelta(days=1)).strftime("%a %b %d, %Y")
      if(email_content["start_time"] == email_content["end_time"]):
        email_content["end_time"] = ""
  else:
    email_content["start_time"] = start_time.strftime("%a %b %d, %Y %I:%M%p %Z").replace(" 0", " ")
    if(isinstance(end_time, datetime.datetime)):
      if(end_time.year == start_time.year and end_time.month == start_time.month and end_time.day == start_time.day):
        email_content["start_time"] = regex_list["end_time_injector"].sub(end_time.strftime("-%I:%M%p %Z").replace("-0", "-"), email_content["start_time"])
      else:
        email_content["end_time"] = end_time.strftime("%a %b %d, %Y %I:%M%p %Z").replace(" 0", " ")
  alarm_time = alarm_object["TRIGGER"].dt
  if(isinstance(alarm_time, datetime.datetime)):
    if(alarm_time.tzinfo is None):
      alarm_time = alarm_time.replace(tzinfo=start_time.tzinfo)
  else:
    alarm_time = start_time + alarm_time;
  email_end_time = email_start_time + minutes_ahead
  # print([alarm_time.isoformat(), email_start_time.isoformat(), email_end_time.isoformat()])
  if(alarm_time >= email_start_time):
    if(alarm_time < email_end_time):
      send_email(email_addresses, email_content, smtp_object, from_address, report)
      report["alarms_triggered"] += 1
    else:
      report["alarms_pending"] += 1
  else:
    report["alarms_expired"] += 1
  return
    def on_message(self, message):
        text, channel, user = self.get_message_text(message), self.get_message_channel(message), self.get_message_sender(message)
        if text is None or channel is None or user is None: return False

        # reminder setting command
        match = re.search(r"^\s*\bbotty[\s,\.]+remind\s+(\S+)\s+(.*?):\s+(.*)", text, re.IGNORECASE)
        if match:
            target_name = match.group(1).strip()
            occurrences = match.group(2).strip()
            description = match.group(3).strip()

            # validate channel ID
            if target_name == "me": target_name = self.get_user_name_by_id(user)
            target = self.get_channel_id_by_name(target_name)
            if target is None: # not a channel/private group/direct message
                target_user = self.get_user_id_by_name(target_name)
                if target_user is None: # not a user
                    self.respond("what kind of channel or user is \"{}\" anyway".format(target_name))
                    return True
                direct_message_channel = self.get_direct_message_channel_id_by_user_id(target_user)
                if direct_message_channel is None:
                    self.respond("there's no direct messaging with \"{}\"".format(target_name))
                    return True
                target = direct_message_channel

            # parse event occurrences
            try:
                r = recurrent.RecurringEvent()
                rrule_or_datetime = r.parse(occurrences)
                if rrule_or_datetime is None: raise ValueError
            except: # unknown or invalid event occurrences format
                self.respond("what's \"{}\" supposed to mean".format(occurrences))
                return True

            if isinstance(rrule_or_datetime, datetime): # single occurrence reminder
                self.reminders.append([rrule_or_datetime, None, target, description])
                self.respond("{}'s reminder for \"{}\" set at {}".format(target_name, description, self.text_to_sendable_text(str(rrule_or_datetime))))
            else: # repeating reminder
                rrule = dateutil.rrule.rrulestr(rrule_or_datetime)
                next_occurrence = rrule.after(datetime.now())
                if next_occurrence is None:
                    self.respond("\"{}\" will never trigger, rrule is {}".format(occurrences, self.text_to_sendable_text(rrule_or_datetime)))
                    return True
                self.reminders.append([next_occurrence, rrule_or_datetime, target, description])
                self.respond("{}'s recurring reminder for \"{}\" set, next reminder is at {}".format(target_name, description, self.text_to_sendable_text(str(next_occurrence))))
            self.save_reminders(self.reminders)
            return True

        # reminder unsetting command
        match = re.search(r"^\s*\bbotty[\s,\.]+(?:unremind|stop\s+reminding\s+(?:(?:us|me|them)\s+)?about|stop\s+reminders?\s+for)\s+(.*)", text, re.IGNORECASE)
        if match:
            description = match.group(1).strip()
            new_reminders = [r for r in self.reminders if r[3] != description]
            if len(new_reminders) < len(self.reminders):
                self.respond("removed reminder for \"{}\"".format(description))
                self.save_reminders(new_reminders)
            else:
                self.respond("there were already no reminders for \"{}\"".format(description))
            self.reminders = new_reminders
            return True

        return False
Exemple #6
0
def get_next_date(dtstart, rrule):
    return rrule.after(dtstart, inc=True)
Exemple #7
0
 def compute_next_due_time(self, curr_time, recurrence_pattern, 
                           add_random=False):
     """
     Compute next due time for recurrent tasks.
     
     Parameters
     ----------
     
     curr_time : datetime
         Start time of the recurrence. Current time should be used.
         
     recurrence_pattern: str 
         How often should the task be executed? One of:
             * "m", "month", "monthly"
             * "w", "week", "weekly"
             * "d", "day", "daily"
             * "h", "hour", "hourly"
     
     add_random: bool
         If ``True``, add a random amount of time to the computed due time,
         to avoid load spikes. 
         If ``False``, the computed times are at the start of the interval,
         for example at 00:00 o'clock for "daily" recurrence.
         
     Returns
     -------
     datetime
         The new due time
     """
     bymonth = None; bymonthday = None; byweekday = None; byhour = None
     byminute = 0; bysecond = 0
     
     recurrence_pattern = recurrence_pattern.lower()
     if recurrence_pattern in ["m", "month", "monthly"]:
         freq = dateutil.rrule.MONTHLY
         byhour = 0
         bymonthday = 1
         rand_max = 15 * 24 * 60 * 60 #sec - 15 days
     elif recurrence_pattern in ["w", "week", "weekly"]:
         freq = dateutil.rrule.WEEKLY
         byhour = 0
         byweekday = 0
         rand_max = 3.5 * 24 * 60 * 60 #sec - 3.5 days
     elif recurrence_pattern in ["d", "day", "daily"]:
         freq = dateutil.rrule.DAILY
         byhour = 0
         rand_max = 12 * 60 * 60 #sec - 12 hours
     elif recurrence_pattern in ["h", "hour", "hourly"]:
         freq = dateutil.rrule.HOURLY
         rand_max = 30 * 60 #sec - 30 minutes
     else:
         raise ValueError("Unkown recurrence_pattern: " + 
                          str(recurrence_pattern))
     
     rrule = dateutil.rrule.rrule(freq=freq, dtstart=curr_time, count=2, 
                                  bymonth=bymonth, bymonthday=bymonthday, 
                                  byweekday=byweekday, byhour=byhour, 
                                  byminute=byminute, bysecond=bysecond,
                                  cache=True)
     new_time = rrule.after(curr_time)
     
     #Add add_random component.
     if add_random:
         rand_secs = randint(0, rand_max)
         new_time += timedelta(seconds=rand_secs)
     
     return new_time
Exemple #8
0
def get_next_date(dtstart, rrule):
    return rrule.after(dtstart, inc=True)