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)
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
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
def get_next_date(dtstart, rrule): return rrule.after(dtstart, inc=True)
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