def parse_frequency( frequency: Text) -> Optional[Union[datetime, RecurringEvent]]: """ Parse frequency or date using `recurrent` and `dateparser` module. """ if frequency.startswith("every"): rec = RecurringEvent() parsed_rrule = rec.parse(frequency) if rec.bymonthday or rec.byyearday: # not supported return None try: rrulestr(parsed_rrule) # this validates the parsing except (ValueError, TypeError): return None else: # ensure that hours are set, otherwise default to 9am if not rec.byhour and not rec.byminute: rec.byhour.append("9") rec.byminute.append("0") return rec else: value = dateparser.parse(frequency, settings={"PREFER_DATES_FROM": "future"}) if value and not value.hour and not value.minute: # default 9am if no times value = value.replace(hour=9) return value
def test_get_rrule_end(self): """ Test that get_rrule_end returns the rrule end correctly """ # when until is provided # pylint: disable=line-too-long rule1 = "DTSTART:20180501T210000Z RRULE:FREQ=YEARLY;BYDAY=SU;BYSETPOS=1;BYMONTH=1;UNTIL=20480521T210000Z" # noqa end1 = get_rrule_end(rrulestr(rule1)) # must be timezone aware self.assertTrue(timezone.is_aware(end1)) # must be 21 may 2018 self.assertEqual(2048, end1.year) self.assertEqual(5, end1.month) self.assertEqual(21, end1.day) # when count is provided instead rule2 = "RRULE:FREQ=DAILY;COUNT=5" end2 = get_rrule_end(rrulestr(rule2)) # must be timezone aware self.assertTrue(timezone.is_aware(end2)) # must be 4 days from now (5 occurrences with today as the first) now = timezone.now().astimezone(pytz.timezone("Africa/Nairobi")) then = now + timedelta(days=4) self.assertEqual(then.year, end2.year) self.assertEqual(then.month, end2.month) self.assertEqual(then.day, end2.day)
def test_get_occurrence_end_time(self): """ Test get_occurrence_end_time """ # pylint: disable=line-too-long rule = "DTSTART:20180501T070000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=500;UNTIL=20280521T210000Z" # noqa the_rrule = rrulestr(rule) task = mommy.make("tasking.Task", timing_rule=rule) rule2 = "RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5" the_rrule2 = rrulestr(rule2) # when end_time is not input then return start_time from timing_rule self.assertEqual( "21:00:00", get_occurrence_end_time(task, the_rrule, end_time_input=None).isoformat(), ) # when end_time is input then return start_time from timing_rule self.assertEqual( "19:15:00", get_occurrence_end_time(task, the_rrule, end_time_input=time(19, 15, 0, 0)).isoformat(), ) # return the end of the day when timing_rule has no end and end_ # time is not provided self.assertEqual( "23:59:59.999999", get_occurrence_end_time(task, the_rrule2, end_time_input=None).isoformat(), )
def load(cls, data, slug, *, cities, venues): recurrence = data['series'].get('recurrence') if recurrence: rrule_str = recurrence['rrule'] rrule.rrulestr(rrule_str) # check rrule syntax recurrence_attrs = { 'recurrence_rule': rrule_str, 'recurrence_scheme': recurrence['scheme'], 'recurrence_description_cs': recurrence['description']['cs'], 'recurrence_description_en': recurrence['description']['en'], } else: recurrence_attrs = { 'recurrence_rule': None, 'recurrence_scheme': None, 'recurrence_description_cs': None, 'recurrence_description_en': None, } self = cls( events=sorted((Event.load(e, slug, cities=cities, venues=venues) for slug, e in data.get('events', {}).items()), key=lambda e: e.start), slug=slug, name=data['series']['name'], home_city=cities[data['series']['city']], description_cs=data['series']['description']['cs'], description_en=data['series']['description']['en'], organizers=[dict(o) for o in data['series']['organizer-info']], source=data['series']['_source'], **recurrence_attrs, ) for event in self.events: event.series = self return self
def create_rule_with_start(self, rule_string): try: return rrulestr(rule_string, dtstart=self.start) except ValueError: # string: FREQ=WEEKLY;UNTIL=20191023;BYDAY=TH;WKST=SU # start: 2019-08-01 14:00:00+01:00 # ValueError: RRULE UNTIL values must be specified in UTC when DTSTART is timezone-aware rule_list = rule_string.split(";UNTIL=") assert len(rule_list) == 2 date_end_index = rule_list[1].find(";") if date_end_index == -1: date_end_index = len(rule_list[1]) until_string = rule_list[1][:date_end_index] if self.is_all_dates: until_string = until_string[:8] elif self.tzinfo is None: # remove the Z from the time zone until_string = until_string[:-1] else: # we assume the start is timezone aware but the until value is not, see the comment above if len(until_string) == 8: until_string += "T000000" assert len(until_string) == 15 until_string += "Z" # https://stackoverflow.com/a/49991809 new_rule_string = rule_list[0] + rule_list[1][ date_end_index:] + ";UNTIL=" + until_string return rrulestr(new_rule_string, dtstart=self.start)
def test_get_start_end_from_timing_rules(self): """ Test get_start_end_from_timing_rules """ zero = ['invalid'] one = [None] two = [] three = [None, 'RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5'] four = [ 'RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5', 'RRULE:FREQ=DAILY;INTERVAL=10;COUNT=17', 'DTSTART:20170521T210000Z RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5' ] self.assertEqual((None, None), get_start_end_from_timing_rules(zero)) self.assertEqual((None, None), get_start_end_from_timing_rules(one)) self.assertEqual((None, None), get_start_end_from_timing_rules(two)) self.assertEqual( (get_rrule_start(rrulestr('RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5')), get_rrule_end(rrulestr('RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5'))), get_start_end_from_timing_rules(three)) self.assertEqual( (get_rrule_start( rrulestr('DTSTART:20170521T210000Z RRULE:FREQ=DAILY;COUNT=5')), get_rrule_end(rrulestr('RRULE:FREQ=DAILY;INTERVAL=10;COUNT=17'))), get_start_end_from_timing_rules(four))
def test_serialize_rruleset_clocks(): s = ("DTSTART:20120201T023000Z\n" "RRULE:FREQ=MONTHLY;COUNT=5\n" "RDATE:20120701T023000Z,20120702T023000Z\n" "EXRULE:FREQ=MONTHLY;COUNT=2\n" "EXDATE:20120601T023000Z") rr = rrule.rrulestr(s) days = [ pendulum.datetime(2012, 4, 1, 2, 30), pendulum.datetime(2012, 5, 1, 2, 30) ] t = schedules.Schedule(clocks=[clocks.RRuleClock(rrule_obj=rr)]) assert t.next(2, after=pendulum.datetime(2012, 1, 1, 0, 0)) == days t2 = serialize_and_deserialize(t) assert t2.next(2, after=pendulum.datetime(2012, 1, 1, 0, 0)) == days s = ("DTSTART:20120201T023000Z\n" "RDATE:20120701T023000Z,20120702T023000Z\n" "EXDATE:20120601T023000Z") rr = rrule.rrulestr(s) days = [ pendulum.datetime(2012, 7, 1, 2, 30), pendulum.datetime(2012, 7, 2, 2, 30) ] t = schedules.Schedule(clocks=[clocks.RRuleClock(rrule_obj=rr)]) assert t.next(2, after=pendulum.datetime(2012, 1, 1, 0, 0)) == days t2 = serialize_and_deserialize(t) assert t2.next(2, after=pendulum.datetime(2012, 1, 1, 0, 0)) == days
def next_occurrence(self): # if not messages have been queued yet, then the next occurrence # is the start time next_occur = None if (not self.last_occurrence_in_utc): next_occur = self.start_datetime_in_utc.datetime return next_occur, arrow.utcnow().replace(minutes=+10) else: start = self.start_datetime_in_utc.datetime rule = rrulestr(self.ical, dtstart=start) next_after_now = rule.after(arrow.utcnow()) if not next_after_now: logging.info('No next_after_now, so next_occur=N/A') return 'N/A', arrow.utcnow() next_before_now = rule.before(next_after_now) if (self.last_occurrence_in_utc > next_before_now): next_occur = next_after_now else: next_occur = next_before_now rule = rrulestr(self.ical, dtstart=next_occur) expires = rule.after(next_occur) if not expires: return 'N/A', arrow.utcnow().replace(minutes=+10) if (expires > self.end_datetime_in_utc): expires = self.end_datetime_in_utc return arrow.get(next_occur), arrow.get(expires)
def validate(cls, value): # type: (unicode) -> List[str] """Returns a list of errors if value is not a valid serialized run window.""" data = json.loads(value) if not isinstance(data, list): return ["Serialized run window should be list."] errors = [] for w in data: try: timezone.datetime.strptime(w['start_time'], CheckRunWindow._TIME_FMT) timezone.datetime.strptime(w['end_time'], CheckRunWindow._TIME_FMT) except ValueError: errors.append("Invalid start/end time.") # ensure all windows have a valid recurrence rule try: rrule.rrulestr(w['rrule']) except ValueError as e: errors.append("You must select at least one day to run on. (" + str(e) + ").") return errors
def get_start_end_from_timing_rules(timing_rules: list): """ Get the start and end times from timing rules Will return the very very first start and the very very last end from the provided timing_rules """ start, end = None, None start_list = [] end_list = [] # remove obviously invalid timing_rules = [x for x in timing_rules if x] for timing_rule in timing_rules: try: start_list.append(get_rrule_start(rrulestr(timing_rule))) except ValueError: pass try: end_list.append(get_rrule_end(rrulestr(timing_rule))) except ValueError: pass if start_list: start = min(start_list) if end_list: end = max(end_list) return (start, end)
def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None): """ Get recurrent dates based on Rule string considering exdate and start date. @param rrulestring: rulestring @param exdate: list of exception dates for rrule @param startdate: startdate for computing recurrent dates @return: list of Recurrent dates """ def todate(date): val = parser.parse(''.join((re.compile('\d')).findall(date))) return val if not startdate: startdate = datetime.now() if not exdate: exdate = [] rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True) for date in exdate: datetime_obj = todate(date) rset1._exdate.append(datetime_obj) if exrule: rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate)) return list(rset1)
def test_get_occurrence_start_time(self): """ Test get_occurrence_start_time """ # pylint: disable=line-too-long rule = "DTSTART:20180501T070000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=500;UNTIL=20280521T210000Z" # noqa the_rrule = rrulestr(rule) rule2 = "RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5" the_rrule2 = rrulestr(rule2) # when start_time is not input then return start_time from timing_rule self.assertEqual( "07:00:00", get_occurrence_start_time(the_rrule, start_time_input=None).isoformat(), ) # if given an input, return that input self.assertEqual( "09:15:00", get_occurrence_start_time(the_rrule, start_time_input=time(9, 15, 0, 0)).isoformat(), ) # when timing_rule has no explicit start then we get back is right now now = timezone.now().astimezone(pytz.timezone("Africa/Nairobi")).time() result = get_occurrence_start_time(the_rrule2, start_time_input=None) diff = datetime.combine(timezone.now().date(), now) - datetime.combine( timezone.now().date(), result) # should be within one minutes of each other self.assertTrue(diff.seconds < 60)
def create_entries( settings: Settings, config: Config, spec_row: CollectionRowBlock, ) -> Generator[Dict[str, Any], None, None]: r = RecurringEvent(now_date=spec_row.start_date.start) times = r.parse(spec_row.recurrence) rr = rrule.rrulestr(r.get_RFC_rrule(), dtstart=spec_row.start_date.start) if get_row_prop(spec_row, 'not_on'): not_r = RecurringEvent(now_date=spec_row.start_date.start) not_times = not_r.parse(spec_row.not_on) not_dates = { d.date() for d in rrule.rrulestr( not_r.get_RFC_rrule(), dtstart=spec_row.start_date.start, ) } for dt in rr: if get_row_prop(spec_row, 'not_on') and dt.date() in not_dates: continue to_insert = { key: spec_row.get_property(key) for key in config.properties_to_sync } to_insert['title'] = spec_row.title if config.tags_property in to_insert: to_insert[config.tags_property].append(config.scheduled_tag) if config.status_property: to_insert[ config.status_property] = config.status_after_today if dt.date( ) >= datetime.date.today() else config.status_before_today reminder = None if get_row_prop(spec_row, 'reminder'): reminder = parse_reminder(spec_row.reminder) if get_row_prop(spec_row, 'include_time'): if get_row_prop(spec_row, 'duration'): duration = datetime.timedelta( minutes=Duration(spec_row.duration).to_minutes()) to_insert[spec_row.date_field] = NotionDate(dt, dt + duration, reminder=reminder) else: to_insert[spec_row.date_field] = NotionDate(dt, reminder=reminder) else: to_insert[spec_row.date_field] = NotionDate(dt.date(), reminder=reminder) if not settings.dry_run: yield to_insert logging.info( f"Added row '{to_insert.get('title', 'Untitled')}' for {dt:%Y-%m-%d}" )
def _next_time(self, entry): """ Here we examine an entry of the list of points in time and return the next execution time and the next value """ try: if not isinstance(entry, dict): return None, None if not 'value' in entry: return None, None if not 'active' in entry: return None, None if not 'time' in entry: return None, None now = datetime.now() value = entry['value'] active = entry['active'] today = datetime.today() yesterday = today - timedelta(days=1) time = entry['time'] if not active: return None, None if 'date' in entry: date = entry['date'] if 'rrule' in entry: if 'dtstart' in entry: rrule = rrulestr(entry['rrule'], dtstart=entry['dtstart']) else: try: rrule = rrulestr(entry['rrule'], dtstart=datetime.combine(yesterday, parser.parse(time.strip()).time())) except Exception as e: self.logger.debug("Tolerated Exception '{}' while examining '{}' with function rrulestr()".format(e,time)) if 'sun' in time: self.logger.debug("Looking for next sun-related time with rulestr()") rrule = rrulestr(entry['rrule'], dtstart=datetime.combine(yesterday, self._sun(datetime.combine(yesterday.date(), datetime.min.time()).replace(tzinfo=self._sh.tzinfo()), time).time())) else: self.logger.debug("Looking for next time with rulestr()") rrule = rrulestr(entry['rrule'], dtstart=datetime.combine(yesterday, datetime.min.time())) dt = now while self.alive: dt = rrule.after(dt) if dt is None: return None, None if 'sun' in time: next = self._sun(datetime.combine(dt.date(), datetime.min.time()).replace(tzinfo=self._sh.tzinfo()), time) self.logger.debug("Result parsing time (rrule){}: {}".format(time, next)) else: next = datetime.combine(dt.date(), parser.parse(time.strip()).time()).replace(tzinfo=self._sh.tzinfo()) if next and next.date() == dt.date() and next > datetime.now(self._sh.tzinfo()): return next, value if 'sun' in time: next = self.sun(datetime.combine(today, datetime.min.time()).replace(tzinfo=self._sh.tzinfo()), time) self.logger.debug("Result parsing time (sun) {}: {}".format(time, next)) else: next = datetime.combine(today, parser.parse(time.strip()).time()).replace(tzinfo=self._sh.tzinfo()) if next and next.date() == today and next > datetime.now(self._sh.tzinfo()): return next, value except Exception as e: self.logger.error("Error '{}' parsing time: {}".format(time, e)) return None, None
def _search_next_date(self, operator, value): if operator == '=': _after = parser.parse(value) _recs = self.search([]) _res = _recs.filtered( lambda r: rrule.rrulestr(r.rrule_str).after(_after, inc=True) and rrule.rrulestr(r.rrule_str).after(_after, inc=True). strftime('%Y-%m-%d') == _after.strftime('%Y-%m-%d')) return [('id', 'in', _res._ids)]
def __call__(self, value, *args, **kwargs): try: rrule.rrulestr(value) # TODO: rm dep. on rrule. check with regex assert('FREQ' in value) # TODO: check if freq before other # recurrence parms except (ValueError, TypeError, AssertionError): return "Validation failed: Please enter valid recurrence data." return True
def __call__(self, value, *args, **kwargs): try: rrule.rrulestr(value) # TODO: rm dep. on rrule. check with regex assert ('FREQ' in value) # TODO: check if freq before other # recurrence parms except (ValueError, TypeError, AssertionError): return "Validation failed: Please enter valid recurrence data." return True
def recurrence_rule(value): """ Validate that a ``rruleset`` object can be creted from ``value``. """ try: rrule.rrulestr(value) except ValueError: raise ValidationError( _('Enter a valid iCalendar (RFC2445) recurrence rule.'), code='invalid')
def test_(self): date = RecurringEvent(NOW) val = date.parse(string) back_again = date.format(val) expected_params = expected known_failure = False if isinstance(expected, ExpectedFailure): known_failure = True expected_params = expected.correct_value try: if expected_params is None: self.assertTrue( val is None or list(date.get_params().keys()) == ['interval'], "Non-date error: '%s' -> '%s', expected '%s'" % (string, val, expected_params)) elif isinstance(expected_params, datetime.datetime) or isinstance( expected_params, datetime.date): if isinstance(expected_params, datetime.datetime): self.assertEqual( val, expected_params, "Date parse error: '%s' -> '%s', expected '%s'" % (string, val, expected_params)) else: self.assertEqual( val.date(), expected_params, "Date parse error: '%s' -> '%s', expected '%s'" % (string, val, expected_params)) else: actual_params = date.get_params() for k, v in list(expected_params.items()): av = actual_params.pop(k, None) self.assertEqual( av, v, "Rule mismatch on rule '%s' for '%s'. Expected %s, got %s\nRules: %s" % (k, string, v, av, date.get_params())) # make sure any extra params are empty/false for k, v in list(actual_params.items()): self.assertFalse(v) # pragma nocover # ensure rrule string can be parsed by dateutil rrule.rrulestr(val) except AssertionError as e: if known_failure: print("Expected failure:", expected_params) return raise e # pragma nocover if known_failure: raise AssertionError("Known failure passed:", expected_params, string) # pragma nocover self.maxDiff = 1000 if de is not None: self.assertEqual(de, back_again) if back_again is not None and back_again != string: self.assertEqual(back_again, date.format( date.parse(back_again))) # Run it thru again!
def testEuropeanDaylightToStandardChange(self): dststart = datetime(1970, 03, 29, 2) stdstart = datetime(1970, 10, 25, 3) stdrrule = rrule.rrulestr("FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU", dtstart=stdstart) dstrrule = rrule.rrulestr("FREQ=YEARLY;BYMONTH=03;BYDAY=-1SU", dtstart=dststart) tzinfo = self.makeTimezone(stdstart, dststart, stdrrule, dstrrule) self.assertEquals(datetime(2011, 10, 29, 1, tzinfo=tzinfo).tzname(), "DAYLIGHT") self.assertEquals(datetime(2011, 10, 31, 1, tzinfo=tzinfo).tzname(), "STANDARD") self.assertEquals(datetime(2011, 10, 30, 1, tzinfo=tzinfo).tzname(), "DAYLIGHT") self.assertEquals(datetime(2011, 10, 30, 3, tzinfo=tzinfo).tzname(), "STANDARD") delta = datetime(2011, 10, 30, 4, tzinfo=tzinfo) - datetime(2011, 10, 30, 1, tzinfo=tzinfo.copy()) self.assertEquals(delta, timedelta(hours=4))
def _next_time(self, entry): try: if not isinstance(entry, dict): return None, None if not 'value' in entry: return None, None if not 'active' in entry: return None, None if not 'time' in entry: return None, None now = datetime.now() value = entry['value'] active = entry['active'] today = datetime.today() yesterday = today - timedelta(days=1) time = entry['time'] if not active: return None, None if 'date' in entry: date = entry['date'] if 'rrule' in entry: if 'dtstart' in entry: rrule = rrulestr(entry['rrule'], dtstart=entry['dtstart']) else: try: rrule = rrulestr(entry['rrule'], dtstart=datetime.combine(yesterday, parser.parse(time.strip()).time())) except: if 'sun' in time: rrule = rrulestr(entry['rrule'], dtstart=datetime.combine(yesterday, self._sun(datetime.combine(yesterday.date(), datetime.min.time()).replace(tzinfo=self._sh.tzinfo()), time).time())) else: rrule = rrulestr(entry['rrule'], dtstart=datetime.combine(yesterday, datetime.min.time())) dt = now while self.alive: dt = rrule.after(dt) if dt is None: return None, None if 'sun' in time: next = self._sun(datetime.combine(dt.date(), datetime.min.time()).replace(tzinfo=self._sh.tzinfo()), time) else: next = datetime.combine(dt.date(), parser.parse(time.strip()).time()).replace(tzinfo=self._sh.tzinfo()) if next and next.date() == dt.date() and next > datetime.now(self._sh.tzinfo()): return next, value if 'sun' in time: next = self.sun(datetime.combine(today, datetime.min.time()).replace(tzinfo=self._sh.tzinfo()), time) else: next = datetime.combine(today, parser.parse(time.strip()).time()).replace(tzinfo=self._sh.tzinfo()) if next and next.date() == today and next > datetime.now(self._sh.tzinfo()): return next, value except Exception as e: logger.error("Error parsing time {}: {}".format(time, e)) return None, None
def validate_rrule(rule_string): """ Validates an rrule string; returns True or False """ try: rrulestr(rule_string) except ValueError: # this string is not a valid rrule return False except TypeError: # this is not even a string return False else: return True
def test_rrule_to_json(): # Generate more test cases! # http://jakubroztocil.github.io/rrule/ r = "RRULE:FREQ=WEEKLY;UNTIL=20140918T203000Z;BYDAY=TH" r = rrulestr(r, dtstart=None) j = rrule_to_json(r) assert j.get("freq") == "WEEKLY" assert j.get("byweekday") == "TH" r = "FREQ=HOURLY;COUNT=30;WKST=MO;BYMONTH=1;BYMINUTE=42;BYSECOND=24" r = rrulestr(r, dtstart=None) j = rrule_to_json(r) assert j.get("until") is None assert j.get("byminute") == 42
def test_rrule_to_json(): # Generate more test cases! # http://jakubroztocil.github.io/rrule/ r = 'RRULE:FREQ=WEEKLY;UNTIL=20140918T203000Z;BYDAY=TH' r = rrulestr(r, dtstart=None) j = rrule_to_json(r) assert j.get('freq') == 'WEEKLY' assert j.get('byweekday') == 'TH' r = 'FREQ=HOURLY;COUNT=30;WKST=MO;BYMONTH=1;BYMINUTE=42;BYSECOND=24' r = rrulestr(r, dtstart=None) j = rrule_to_json(r) assert j.get('until') is None assert j.get('byminute') is 42
def _apply_recurrence_to_dt(self, dt): if not dt: return None recurrence = rrulestr(self.rrule, dtstart=dt) # Nasty hack around: https://github.com/dateutil/dateutil/issues/341 try: return recurrence.after(dt) except TypeError: tz = dt.tzinfo dt = dt.replace(tzinfo=LOCAL_TIMEZONE) recurrence = rrulestr(self.rrule, dtstart=dt) return recurrence.after(dt).replace(tzinfo=tz)
def get(self, request): passcode = self.request.query_params.get('passcode', None) queryset = Resident.objects.all().filter(passcode=passcode).first() print(queryset) rule_string = queryset.recurrance_str isValid = False print(rule_string) if rule_string: dtstart = '' str_without_dtstart = '' rule_string_perameters = rule_string.split(";") for pera in rule_string_perameters: if pera.split("=")[0] == 'DTSTART': dtstart = pera.split("=")[1] rule_string_perameters.remove(pera) if dtstart != '': final_rule_string = "DTSTART:" + dtstart + ";\n" + "RRULE:" + ";".join( rule_string_perameters) rule = rrulestr(final_rule_string) launchTime = datetime.now(tz=timezone.utc) next_occurance = rule.after(launchTime, inc=True) else: final_rule_string = "RRULE:" + ";".join(rule_string_perameters) rule = rrulestr(final_rule_string) launchTime = datetime.utcnow() next_occurance = rule.after(launchTime, inc=True) print(next_occurance.strftime("%d/%m/%Y")) print(datetime.now(tz=timezone.utc).strftime("%d/%m/%Y")) if next_occurance.strftime("%d/%m/%Y") == datetime.now( tz=timezone.utc).strftime("%d/%m/%Y"): if queryset.time_from and queryset.time_to: begin_time = datetime.time(queryset.time_from) end_time = datetime.time(queryset.time_to) check_time = datetime.utcnow().time() if begin_time < end_time: isValid = check_time >= begin_time and check_time <= end_time else: # crosses midnight isValid = check_time >= begin_time or check_time <= end_time else: isValid = True else: isValid = True return Response({'isValid': isValid})
def test_(self): date = RecurringEvent(NOW) val = date.parse(string) expected_params = expected known_failure = False if isinstance(expected, ExpectedFailure): known_failure = True expected_params = expected.correct_value try: if expected_params is None: self.assertTrue( val is None or date.get_params().keys() == ["interval"], "Non-date error: '%s' -> '%s', expected '%s'" % (string, val, expected_params), ) elif isinstance(expected_params, datetime.datetime) or isinstance(expected_params, datetime.date): if isinstance(expected_params, datetime.datetime): self.assertEqual( val, expected_params, "Date parse error: '%s' -> '%s', expected '%s'" % (string, val, expected_params), ) else: self.assertEqual( val.date(), expected_params, "Date parse error: '%s' -> '%s', expected '%s'" % (string, val, expected_params), ) else: actual_params = date.get_params() for k, v in expected_params.items(): av = actual_params.pop(k, None) self.assertEqual( av, v, "Rule mismatch on rule '%s' for '%s'. Expected %s, got %s\nRules: %s" % (k, string, v, av, date.get_params()), ) # make sure any extra params are empty/false for k, v in actual_params.items(): self.assertFalse(v) # ensure rrule string can be parsed by dateutil rrule.rrulestr(val) except AssertionError as e: if known_failure: print("Expected failure:", expected_params) return raise e if known_failure: raise AssertionError("Known failure passed:", expected_params, string)
def __init__(self, values, default=None): values = lowercase_keys_recursively(values) # anything not defined in the "values" dict will be defaulted init = self._defaults.copy() init.update(values) # convert the dict to attributes self.__dict__.update(init) try: self.first = parser.parse(self.first, default=default) if not self.first.tzinfo: self.first = self.first.replace(tzinfo=tz.tzutc()) except Exception as e: raise ValueError('Error parsing date from `first`.') try: if self.repeat: match = self.repeat_regex.match(self.repeat) interval = int(match.group(1)) if interval == 0: raise ValueError('Invalid repeat interval.') self.rrule = rrule.rrule(self.freq_dict[match.group(2)], interval=interval, dtstart=self.first) elif self.rrule: self.rrule = rrule.rrulestr(self.rrule, dtstart=self.first) except Exception as e: raise ValueError('Error parsing repeat interval.') self.title = self.replace_placeholders(self.title) self.text = self.replace_placeholders(self.text)
def schedule_station(station_id): station = Station.query.filter_by(id=station_id).first_or_404() # TODO: move this logic to an ajax call, like scheduled_block_json scheduled_blocks = ScheduledBlock.query.filter_by(station_id=station.id) block_list = [] for block in scheduled_blocks: r = rrule.rrulestr(block.recurrence) for instance in r[: 30]: # TODO: dynamically determine instance limit from calendar view d = { 'title': block.name, 'start': datetime.combine(instance, block.start_time), 'end': datetime.combine(instance, block.end_time) } block_list.append(d) form = ScheduleProgramForm() all_programs = Program.query.join(Program, Network.programs).join( User, Network.networkusers).filter(User.id == current_user.id).all() # TODO: filter by language? return render_template('radio/schedule.html', form=form, station=station, block_list=block_list, addable_programs=all_programs, active='schedule')
def target_dates(self): """ Returns the dates this reservation targets. Those should not be confused with the dates this reservation actually reserved. The reason for this difference is the fact that after the reservation is created, certain dates might be removed through removing reserved slots. This function only returns dates the reservation was originally targeted at. """ if self.target_type == u'allocation': return ((self.start, self.end),) if self.target_type == u'group': return self._target_allocations().with_entities( self.models.Allocation._start, self.models.Allocation._end ).all() if self.target_type == u'recurrence': time_start = self.start.time() time_end = self.end.time() date_start = self.start.date() from dateutil.rrule import rrulestr rule = rrulestr(self.rrule, dtstart=date_start) return [get_date_range(date, time_start, time_end) for date in rule] raise NotImplementedError
def testGetPoints(self): start = datetime(2007, 12, 22, 9, 0, 0, 0, LOCAL) end = datetime(2007, 12, 22, 11, 0, 0, 0, LOCAL) eventSet1 = eventcalendar.EventSet('uid1') eventSet2 = eventcalendar.EventSet('uid2') event1 = eventcalendar.Event('uid1', start, end, 'content') eventSet1.addEvent(event1) event2 = eventcalendar.Event('uid2', start, end, 'content') eventSet2.addEvent(event2) rid = self._events[0].get('RECURRENCE-ID') self.failUnless(rid) riddatetime = parser.parse(str(rid)) self.failUnless(str(rid).endswith('Z')) start = self._events[1].decoded('dtstart') self.failUnless(start) if start.tzinfo is None: tzinfo = tz.gettz( self._events[1]['dtstart'].params['TZID']) start = datetime(start.year, start.month, start.day, start.hour, start.minute, start.second, start.microsecond, tzinfo) rrulestr = str(self._events[1].get('RRULE')) self.failUnless(rrulestr) r = rrule.rrulestr(rrulestr, dtstart=start) self.failUnless(riddatetime in r)
def scheduled_block_json(station_id): scheduled_blocks = ScheduledBlock.query.filter_by(station_id=station_id) if not ('start' in request.args and 'end' in request.args): return { 'status': 'error', 'errors': 'scheduledblocks.json requires start and end', 'status_code': 400 } # TODO: fullcalendar updates based on these params start = dateutil.parser.parse(request.args.get('start')) end = dateutil.parser.parse(request.args.get('end')) resp = [] for block in scheduled_blocks: r = rrule.rrulestr(block.recurrence) for instance in r.between(start, end): d = { 'title': block.name, 'start': datetime.combine(instance, block.start_time), 'end': datetime.combine(instance, block.end_time), 'id': block.id, 'isBackground': True, # the magic flag that tells full calendar to render as block } resp.append(d) return resp
def __call__(self, *args, **kwargs): # Pop out custom keyword arguments added during scheduling and # previous run so as not to pollute the task function's # namespace. scheduled_task_id = kwargs.pop('scheduled_task_id') rrule_string = kwargs.pop('rrule_string') _first_eta = kwargs.pop('first_eta') _eta = kwargs.pop('eta') _until = kwargs.pop('until') first_eta = TaskScheduler.strptime(_first_eta) eta = TaskScheduler.strptime(_eta) until = TaskScheduler.strptime(_until) scheduled_task = ScheduledTask.objects.get(pk=scheduled_task_id) # If the task been manually set as ScheduledTask.STATUS_CANCELLED, # stop the execution. if scheduled_task.status == ScheduledTask.STATUS_CANCELLED: return scheduled_task.save_status(ScheduledTask.STATUS_RUNNING) ScheduledTaskRunLog.objects.create(task_id=self.request.id, scheduled_task=scheduled_task) # If a CancelSchedule exception is raied by the function, # cancel the schedule and exit. try: result = super(RepeatTask, self).__call__(*args, **kwargs) except CancelSchedule: TaskScheduler.cancel(scheduled_task_id=scheduled_task.id) return else: scheduled_task.save_status(ScheduledTask.STATUS_SUCCESS) # If rrule string is not specified, assume it to be a one time # task. if not rrule_string: return result # Preserve the start and end of rrule cycle. rrule_ = rrulestr(rrule_string).replace(dtstart=first_eta, until=until) next_eta = TaskScheduler.calculate_next_eta(rrule_=rrule_, current_eta=eta) # If rrule does not return an ETA, assume it to be the end of # schedule and exit. if not next_eta: return result # Add custom keyword arguments again for the next run. kwargs.update({ 'scheduled_task_id': scheduled_task.id, 'rrule_string': rrule_string, 'first_eta': _first_eta, 'eta': TaskScheduler.strftime(next_eta), 'until': TaskScheduler.strftime(until), }) self.apply_async(eta=next_eta, args=args, kwargs=kwargs) return result
def get_rrule(self, dtstart): if self.complex_rule: d = dtstart.date() weekday = weekdays[d.weekday()] n = 1 + (d.day - 1) / 7 start_day, days_in_month = calendar.monthrange(d.year, d.month) days_from_end = days_in_month - d.day minus_n = -1 - (days_from_end / 7) cr = ( self.complex_rule.replace("%date%", dtstart.strftime("%Y%m%d")) .replace("%day%", dtstart.strftime("%d")) .replace("%month%", dtstart.strftime("%m")) .replace("%year%", dtstart.strftime("%Y")) .replace("%time%", dtstart.strftime("%H%M%S")) .replace("%datetime%", dtstart.strftime("%Y%m%dT%H%M%S")) .replace("%nthday%", "%s%s" % (n, weekday)) .replace("%-nthday%", "%s%s" % (minus_n, weekday)) ) try: return rrule.rrulestr(str(cr), dtstart=dtstart) except ValueError: # eg. unsupported property pass params = self.get_params() frequency = "rrule.%s" % self.frequency simple_rule = rrule.rrule(eval(frequency), dtstart=dtstart, **params) rs = rrule.rruleset() rs.rrule(simple_rule) return rs
def run(self): self.logger.info('Running Scheduler') poll_interval = 5 try: while 1: next_event = now() + timedelta(1) for rule in self.rules: n = now() rrule = rrulestr(rule['rule'], dtstart=rule['created']) items = rrule.between(rule['checked'], n) if len(items) > 1: self.logger.warn("Schedular missed %i tasks! Enqueuing latest" % (len(items))) if items: if self.check_rule(rule, n): self.logger.info("Enqueueing task %r (%s)" % (rule['task'], items[0].ctime())) self.enqueue_from_rule(rule) else: self.logger.warn("Another schedular has already run this task. moving on") next_event = min(next_event, rrule.after(n)) self.logger.debug("Next event %s" % next_event.ctime()) next_event = (next_event - now()).total_seconds() self.logger.debug("Next event in %i seconds" % next_event) sleep = max(1, min(next_event, poll_interval)) self.logger.debug("Sleping for %i seconds" % sleep) time.sleep(sleep) except KeyboardInterrupt: self.logger.exception('Exiting main loop')
def _recurring_component_to_events(component): """ Given an icalendar component with an "RRULE" Return a list of events as dictionaries """ rrule_as_str = component.get('rrule').to_ical() recur_rule = rrule.rrulestr(rrule_as_str, dtstart=component.decoded('dtstart')) recur_set = rrule.rruleset() recur_set.rrule(recur_rule) if 'exdate' in component: for exdate_line in component.decoded('exdate'): for exdate in exdate_line.dts: recur_set.exdate(exdate.dt) # get list of events in MAX_FUTURE days utcnow = now() later = utcnow + datetime.timedelta(days=MAX_FUTURE) start_times = recur_set.between(utcnow, later) # build list of events event_length = component.decoded('dtend') - component.decoded('dtstart') events = [] for start in start_times: events.append({ 'start': start, 'end': start + event_length, 'summary': component.decoded('summary'), 'uid': component.decoded('uid'), 'last_modified': component.decoded('last-modified'), }) return events
def get_events_from_rrule(ical_event, event_template, start_date, end_date): events = [] ical_rrule = ical_event.get('rrule') ical_rrule_str = ical_rrule.to_ical().decode('utf-8') rrule = rrulestr(ical_rrule_str, ignoretz=True, dtstart=start_date.replace(tzinfo=None)) ruleset = rruleset() ruleset.rrule(rrule) exdates = get_exdates(ical_event) for exdate in exdates: for exdate_date in exdate.dts: ruleset.exdate(exdate_date.dt.replace(tzinfo=None)) after = datetime.utcnow() - timedelta(**INTO_PAST) before = datetime.utcnow() + timedelta(**INTO_FUTURE) rrule_instances = list(ruleset.between(after, before)) for rrule_instance in rrule_instances: event = copy(event_template) event['start'] = berlin.localize(rrule_instance).isoformat() if not event["allDay"]: instance_end_date = datetime(rrule_instance.year, rrule_instance.month, rrule_instance.day, end_date.hour, end_date.minute, end_date.second) event["end"] = berlin.localize(instance_end_date).isoformat() events.append(event) return events
def getRecurrence(observance, dtstart): if 'RRULE' in observance: return rrule.rrulestr(str(observance['RRULE']), dtstart=dtstart, cache=True) if 'RDATE' in observance: return rrule.rrule('YEARLY', str(observance['RDATE']), cache=True) return None
def _genere_occurrences(self): """Generate a list of occurrences.""" if self.start.is_datetime() and self.end.is_datetime(): delta = self.end.datetime - self.start.datetime elif self.start.is_date() and self.end.is_date(): delta = self.end.date - self.start.date else: raise ValueError('datetime or date must be set for both start and end') self.occurrences = [] if not self.recurrence: self.occurrences.append(self._create_occurrence(self.start.date_or_dt(), delta)) else: rrs = shrink_rruleset(rrulestr(self.recurrence, dtstart=self.start.date_or_dt(), forceset=True)) is_date = self.start.is_date() tz = self.get_start_timezone() for occurrence in rrs: # rruleset creates datetime objects if is_date: if occurrence.time() == time(): # If time is 0, consider that occurrence is a date occurrence = occurrence.date() elif is_naive(occurrence): # Else localize occurrence with event or calendar timezone occurrence = tz.localize(occurrence) self.occurrences.append(self._create_occurrence(occurrence, delta))
def get_events(ics, start, end): events = [] cal = Calendar.from_ical(res.text) start_no_tz = start.replace(tzinfo=None) end_no_tz = end.replace(tzinfo=None) for event in cal.walk('vevent'): this_event = { 'title': unicode(event.get('summary', '')), 'description': unicode(event.get('description', '')), 'location': unicode(event.get('location', '')), 'raw': event } if 'rrule' in event: duration = event['dtstart'].dt - event['dtend'].dt rrule = rrulestr(event['rrule'].to_ical(), dtstart=event['dtstart'].dt) if not isinstance(event['dtstart'].dt, datetime): occurances = rrule.between(start_no_tz, end_no_tz) else: occurances = rrule.between(start, end) for occ in occurances: print (dt_cmp(occ, start)) if dt_cmp(occ, start) >= 0: if dt_cmp(occ+duration, end) <= 0: new_event = this_event.copy() new_event['start'] = occ new_event['end'] = occ+duration events.append(new_event) elif dt_cmp(event['dtstart'].dt, start) >= 0: if dt_cmp(event['dtend'].dt, end) <= 0: this_event['start'] = event['dtstart'].dt this_event['end'] = event['dtend'].dt events.append(this_event) return events
def beat(self): self.logger.info('Reload jobs') now = int(time()) prev = now - self.beat_interval jobs = self.storage.find({'$and': [ {'crecord_type': 'job'}, {'$or': [ {'last_execution': {'$lte': prev}}, {'last_execution': None}, ]} ]}) for job in jobs: job = job.dump() self.logger.info('Job: {0}'.format(job)) if job['last_execution'] <= 0: self.do_job(job) else: jobStart = datetime.fromtimestamp(job['start']) dtstart = datetime.fromtimestamp(prev) dtend = datetime.fromtimestamp(now) occurences = list( rrulestr(job['rrule'], dtstart=jobStart).between( dtstart, dtend)) if len(occurences) > 0: self.do_job(job)
def Poll (self, within_seconds=None): # print "poll within", within_seconds results = [ ] candidates = self.master.openaps.things.get('schedules', [ ]) now = datetime.datetime.now( ) # print "polling schedules", len(candidates), now.isoformat( ), 'for', self.MaxTasksAhead, 'MaxTasksAhead' for configured in candidates: # print "SCHEDULE", configured.item.fields # spec = recurrent.parse(configured.item.fields['rrule'], now=self.since) spec = configured.item.fields['rrule'] rr = rrule.rrulestr(spec, dtstart=self.since) # print configured.item.fields['rrule'], spec upcoming = rr.after(now) # print "next", upcoming.isoformat( ) # XXX: bug in making: need to fill out all events before within_seconds as well. # if (upcoming - now).total_seconds( ) <= within_seconds: for upcoming in iter_triggers(upcoming, rr, within_seconds): # print "ARM THING", configured.path # print "ATTEMPT ARM", configured.item.name, configured.path, spec # self.enqueue(upcoming, configured) trigger = Armable(upcoming, configured) # exists = self.schedules[(upcoming, configured.item.name)] results.append(trigger) return results pass
def _set_recurring_reminder (r, orig_message): ''' Set Recurring Reminder Record a recurring reminder in the database to be sent. #TODO: Implement this -- @param r:dict The parsed message from the user @param orig_message:str The original message received @return str ''' logger.debug('Storing reminder for {id}'.format( id = orig_message['chat']['id'] )) RemindRecurring.create( orig_message = json.dumps(orig_message), rrules = r['parsed_time'], next_run = rrulestr(r['parsed_time'], dtstart = datetime.datetime.now()).after(datetime.datetime.now()), message = r['message'] ) return
def _get_next_date(self): for rec in self: _after = fields.Datetime.from_string(datetime.today().strftime('%Y-%m-%d 09:00:00')) rec.next_date = None _next_date = rrule.rrulestr(rec.rrule_str).after(_after, inc=False) if _next_date: rec.next_date = _next_date.strftime('%Y-%m-%d')
def _expand_rrule_all_day(rrule: str, start: date, exclusions: Iterable, start_at: datetime, end_at: datetime) -> Iterable[date]: """Expand an rrule for all-day events. To my mind, these events cannot have changes, just exclusions, because changes only affect the time, which doesn't exist for all-day events. """ rules = rruleset() rules.rrule(rrulestr(rrule, dtstart=start, ignoretz=True)) # add exclusions if exclusions: for xdate in exclusions: rules.exdate(datetime.combine(xdate.dts[0].dt, datetime.min.time())) dates = [] # reduce start and end to datetimes without timezone that just represent a # date at midnight. for candidate in rules.between( datetime.combine(start_at.date(), datetime.min.time()), datetime.combine(end_at.date(), datetime.min.time()), inc=True, ): dates.append(candidate.date()) return dates
def expand_event(event): "expands recurring events into each individual instance" if 'RRULE' in event: event_start = event['DTSTART'].dt dtdelta = event['DTEND'].dt - event_start tz = getattr(event_start, 'tzinfo', None) if isinstance(event_start, datetime): event_start = event_start.replace(tzinfo=None) onerrule = '\n'.join(get_recurrence_lines(event)) ruleset = rrule.rrulestr(onerrule, dtstart=event_start, forceset=True, ignoretz=True) for event_dt_start in ruleset: newdate = event_dt_start.date() if newdate < startdate: continue if newdate > enddate: return newev = deepcopy(event) if isinstance(newev['DTSTART'].dt, datetime): newev['DTSTART'].dt = event_dt_start.replace(tzinfo=tz) newev['DTEND'].dt = (event_dt_start + dtdelta).replace(tzinfo=tz) else: newev['DTSTART'].dt = event_dt_start.date() newev['DTEND'].dt = event_dt_start.date() + dtdelta yield newev else: yield event
def schedule(self, jobid, schedule, after=None, backoff=None): jobid = _e(jobid) schedule = _e(schedule) if schedule is None: self.unset(jobid) elif schedule == 'STOP': self.set_status(jobid, 'STP') elif schedule == 'CONTINUE' and backoff: self.add_to_timeline(jobid, int(time.time()) + backoff) elif schedule == 'CONTINUE': self.enqueue(jobid) elif schedule == 'NOW': self.set_schedule(jobid, 'STOP') self.enqueue(jobid) elif schedule.startswith("AT:"): self.set_schedule(jobid, 'STOP') self.add_to_timeline(jobid, schedule.split(":")[1]) else: after_dt = (datetime.fromtimestamp(after) if after else datetime.utcnow()) try: rrule = rrulestr(schedule) next_run_dt = rrule.after(after_dt) if next_run_dt: next_run = calendar.timegm(next_run_dt.timetuple()) self.add_to_timeline(jobid, next_run) else: self.set_status(jobid, 'STP') except ValueError: self.set_status(jobid, 'BAD') self.log.warn("%s Bad RRULE", jobid)
def init_rule(self): doc = self.doc self.rrule = rrulestr(doc['rule'], dtstart=now()) self.irule = iter(self.rrule) self.queue = self.factory.queue(doc['queue'], tags=doc['tags']) if self.timer is not None: self.set()
def failUnlessParseMatches(self, string, true_rep, canonical, passthru): rule = rrule.rrulestr(string) parsed = CustomRecurrenceDialog.parse_rrule(rule) self.failUnlessEqual(true_rep, parsed[0]) self.failUnlessEqual(canonical, parsed[1]) self.failUnlessEqual(passthru, parsed[2])
def get_wakeup_dates(self): if self.parser.has_section("Wakeup"): for wakeuptimer, rrulestring in self.parser.items('Wakeup'): if rrulestring.startswith("RRULE"): rrulestring = rrulestring+";COUNT=1" self.wakeupTimer[wakeuptimer] = list(rrule.rrulestr(rrulestring))[0] else: self.wakeupTimer[wakeuptimer] = parser.parse(rrulestring)
def create_ami(instance): if not silent and verbose > 0: print "Creating AMI" create_time = datetime.now(pytz.utc) create_time_ISO = create_time.isoformat() name = '%s_Backup_%s' % ((instance.tags['Name'].replace(' ', '_') if instance.tags.has_key('Name') else instance.id), create_time.strftime('%Y%m%dT%H%M%SZ')) desc = '%s Backup on %s (%s)' % ((instance.tags['Name'] if instance.tags.has_key('Name') else instance.id), create_time.ctime(), str(create_time.tzinfo)) reboot_rule_str = instance.tags[REBOOT_RRULE_TAG] if instance.tags.has_key(REBOOT_RRULE_TAG) else None force_reboot = False if reboot_rule_str: last_reboot = parser.parse(instance.tags[REBOOT_STAMP_TAG]) if instance.tags.has_key(REBOOT_STAMP_TAG) else parser.parse(instance.launch_time) try: force_reboot = True if rrulestr(reboot_rule_str+";byhour=0;byminute=0;bysecond=0", dtstart=last_reboot).before(datetime.now(pytz.utc)) else False except ValueError as e: if not silent: print e.message no_reboot = ((not force_reboot) and (instance.tags.has_key(NO_REBOOT_TAG) or instance.tags.has_key(REBOOT_RRULE_TAG))) or (instance.id == self_id) if not no_reboot: if not silent and verbose > 0: print "Tagging instance %s: %s" % (REBOOT_STAMP_TAG, create_time_ISO) instance.add_tag(REBOOT_STAMP_TAG, create_time_ISO) if not silent and verbose > 1: print '''Image parameters: Name: %s Description: %s Source: %s No-Reboot: %s ''' % (name, desc, instance.id, no_reboot) ami_id = instance.create_image(name, description=desc, no_reboot=no_reboot) if not silent: print "Created AMI: %s" % (ami_id) # Wait for the image to appear if not silent and verbose > 0: print "Tagging image" tries_left = MAX_TRIES image = None while not image and tries_left: try: image = instance.connection.get_all_images(image_ids=[ami_id])[0] except exception.EC2ResponseError as e: if not silent: print e.message tries_left -= 1 image.add_tag(STAMP_TAG, create_time_ISO) image.add_tag(SOURCE_TAG, instance.id) if not no_reboot: image.add_tag(CONSISTENT_TAG, "Yes") if not silent and verbose > 1: print "Created AMI tags: %s" % (image.tags) return ami_id
def next_execution(self): if not self.activated: return None rule = rrulestr(self.rrule_string) utc_next_occurrence = rule.after(datetime.utcnow()) if utc_next_occurrence: next_occurrence = utc_next_occurrence.replace(tzinfo=timezone.utc).astimezone(timezone.get_current_timezone()) return next_occurrence return None
def run(self): logging.info('psmqtt using config: %s', self.CONFIG) logging.info('you can set the configuration ' 'with environment variable "PSMQTTCONFIG"') clientid = self.cf.get('mqtt_clientid', 'psmqtt-%s' % os.getpid()) # additonal userdata for mqtt callbacks mqttc_userdata = MqttUserdata( self.request_topic, self.qos, self.run_task) # initialise MQTT broker connection mqttc = paho.Client( clientid, clean_session=True, userdata=mqttc_userdata) self.mqttc = mqttc mqttc.on_message = on_message mqttc.on_connect = on_connect mqttc.on_disconnect = on_disconnect mqttc.will_set('clients/psmqtt', payload="Adios!", qos=0, retain=False) # Delays will be: 3, 6, 12, 24, 30, 30, ... # mqttc.reconnect_delay_set( # delay=3, delay_max=30, exponential_backoff=True) mqttc.username_pw_set(self.cf.get('mqtt_username'), self.cf.get('mqtt_password')) mqttc.connect(self.cf.get('mqtt_broker', 'localhost'), int(self.cf.get('mqtt_port', '1883')), 60) # parse schedule schedule = self.cf.get('schedule', {}) s = sched.scheduler(time.time, time.sleep) now = datetime.now() for t in schedule: r = RecurringEvent() dt = r.parse(t) if not r.is_recurring: logging.error(t + " is not recurring time. Skipping") continue rrule = rrulestr(dt) delay = (rrule.after(now) - now).total_seconds() s.enter(delay, 1, self.on_timer, [s, rrule, schedule[t]]) tt = TimerThread(s) tt.daemon = True tt.start() while True: try: mqttc.loop_forever() except socket.error: print('socket error retrying.') time.sleep(5) except KeyboardInterrupt: sys.exit(0)
def run_remind_recurring (): ''' Run Remind Recurring Find and send all of the recurring reminders that are due -- @return void ''' logger.debug('Running Remind Recurring Job') try: # Get reminders have have not been marked as completed, as well as # have their next_run date ready or not set for reminder in RemindRecurring.select().where(RemindRecurring.sent == 0, ((RemindRecurring.next_run <= datetime.now()) | ( RemindRecurring.next_run >> None))): # If we know the next_run date, send the message. If # we dont know the next_run, this will be skipped # and only the next_run determined if reminder.next_run is not None: logger.debug('Sending recurring reminder message with id {id}'.format( id = reminder.id )) # Send the actual reminder Telegram.send_message( _get_sender_information(reminder.orig_message), 'text', reminder.message) # Lets parse the rrules and update the next_run time for # a message. We will use python-dateutil to help with # determinig the next run based on the parsed RRULE # relative from now. next_run = rrulestr(reminder.rrules, dtstart = datetime.now()).after(datetime.now()) # If there is no next run, consider the # schedule complete and mark it as # sent if not next_run: reminder.sent = 1 reminder.save() continue # Save the next run reminder.next_run = next_run reminder.save() except Exception, e: print traceback.format_exc()
def getActiveEventInstances(self, dt=None): """ Get all event instances active at the given dt. @type dt: L{datetime.datetime} @rtype: list of L{EventInstance} """ if not dt: dt = datetime.datetime.now(tz=UTC) result = [] # handle recurrence events first recurring = self._getRecurringEvent() if recurring: # FIXME: support multiple RRULE; see 4.8.5.4 Recurrence Rule startRecurRule = rrule.rrulestr(recurring.rrules[0], dtstart=recurring.start) dtstart = startRecurRule.before(dt) if dtstart: skip = False # ignore if we have another event with this recurrence-id for event in self._events: if event.recurrenceid: if event.recurrenceid == dtstart: self.log( 'event %r, recurrenceid %r matches dtstart %r', event, event.recurrenceid, dtstart) skip = True # add if it's not on our list of exceptions if recurring.exdates and dtstart in recurring.exdates: self.log('recurring event %r has exdate for %r', recurring, dtstart) skip = True if not skip: delta = recurring.end - recurring.start dtend = dtstart + delta if dtend >= dt: # starts before our dt, and ends after, so add result.append(EventInstance(recurring, dtstart, dtend)) # handle all other events for event in self._events: if event is recurring: continue if event.start < dt < event.end: result.append(EventInstance(event, event.start, event.end)) self.log('events active at %s: %r', str(dt), result) return result