def test_until_wrap(self): date = RecurringEvent(datetime.date(2010, 11, 1)) string = 'daily until Feb' date.parse(string) expected1 = """RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20110201""" expected2 = """RRULE:INTERVAL=1;FREQ=DAILY;UNTIL=20110201""" self.assertIn(date.get_RFC_rrule(), [expected1, expected2])
def test_format_plus(self): date = RecurringEvent() self.assertEqual( date.format('RRULE:INTERVAL=1;FREQ=MONTHLY;BYDAY=+1FR'), '1st Fri of every month') self.assertEqual(date.format(datetime.datetime(2000, 1, 2, 3, 4, 5)), 'Sun Jan 2, 2000 3:04:05am')
def test_rrule_string(self): string = 'every day starting feb 2' date = RecurringEvent(NOW) date.parse(string) expected1 = """DTSTART:20100202\nRRULE:FREQ=DAILY;INTERVAL=1""" expected2 = """DTSTART:20100202\nRRULE:INTERVAL=1;FREQ=DAILY""" self.assertIn(date.get_RFC_rrule(), [expected1, expected2])
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 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 test_date_incrementer(self): date = RecurringEvent(datetime.date(2012, 2, 29)) # Leap year string = 'daily for the next year' date.parse(string) expected1 = """RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20130301""" expected2 = """RRULE:INTERVAL=1;FREQ=DAILY;UNTIL=20130301""" self.assertIn(date.get_RFC_rrule(), [expected1, expected2]) string = 'daily for the next 12 months' date.parse(string) self.assertIn(date.get_RFC_rrule(), [expected1, expected2])
def recurring_date(event, now_date=None, strftime_format='%Y-%m-%d'): if now_date != None: time_struct, parse_status = parsedatetime.Calendar().parse(now_date) if not parse_status: raise InvalidDatetimeException('Failed to parse "%s"' % (now_date)) now_date = datetime(*time_struct[:6]) else: now_date = datetime.today() actually_now = datetime.strptime( datetime.today().strftime(strftime_format), strftime_format) r = RecurringEvent(now_date=now_date) if not r.parse(event): raise InvalidRecurringDateException('Invalid recurring date "%s"' % (event)) rr = rrule.rrulestr(r.get_RFC_rrule()) res = rr.after(actually_now, inc=True) return res.strftime(strftime_format)
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 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) # 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, content='', name='', created=None, modified=None, datestring=None, importance=1000, durationstring=None): current_time = round(time.time()) self.name = name self.content = content self.id = uuid.uuid4().hex if created: self.created = created else: self.created = current_time if modified: self.modified = modified else: self.modified = current_time # self.next = 1 self.datestring = datestring if self.datestring != None and len(self.datestring) > 0: try: now = datetime.datetime.now() # now = datetime.datetime(2010, 1, 1) r = RecurringEvent(now_date=now) self.dateparse = r.parse(self.datestring) self.dateparams = r.get_params() self.datesummary = r.format(self.dateparse) if r.is_recurring: rr = rrule.rrulestr(r.get_RFC_rrule()) self.next = str(rr.after(now)) print(self.dateparse, self.dateparams, self.datesummary, self.next) except Exception as e: print(e) self.durationstring = durationstring if self.durationstring != None and len(self.durationstring) > 0: self.duration = parse_duration(self.durationstring) self.importance = { 'user_defined': importance, 'calculated': 1000., 'history': [], 'ranked': [] } if hasattr(self, 'dateparse'): self.dateparse = str(self.dateparse)
def test_return_recurring(self): string = 'every day' date = RecurringEvent() ret = date.parse(string) self.assertTrue(isinstance(ret, str))
def project_parse_cfg_options(cfg, section): """ Parse project configuration. Use of recurrent lib to parse fuzzy time values :param cfg: Configuration object :param section: Section in the configuration object, i.e. project type :return: Dictionary. Keys are: "duration_text", "duration_dt", "extendable", "finish_text", "finish_dt", "cpu", "finish_notice_text", "acl", "finish_notice_dt", "transform", "description", "evaluation_text", "evaluation_dt", "evaluation_notice_text", "evaluation_notice_dt" """ r = RecurringEvent() cpu = cfg.getint(section, "cpu", fallback=None) description = cfg.get(section, "description", fallback=None) duration = cfg.get(section, "duration", fallback=None) if duration: duration_dt = r.parse(duration).replace(tzinfo=timezone.utc) else: duration_dt = None end = cfg.get(section, "finish_date", fallback=None) if end: end_dt = r.parse(end).replace(tzinfo=timezone.utc) else: end_dt = None end_notice = cfg.get(section, "finish_notice", fallback=None) if end_notice and end_dt: tmp = RecurringEvent(end_dt).parse(end_notice) end_notice_dt = tmp.replace(tzinfo=timezone.utc) else: end_notice_dt = None trans = cfg.get(section, "transform", fallback=None) if trans: transform = list(map(lambda x: x.strip(), trans.split(","))) else: transform = [] acl = cfg.get(section, "acl", fallback=[]) if acl: acl = list(map(lambda x: x.strip(), acl.split(","))) acl.append("admin") eva = cfg.get(section, "evaluation_date", fallback=None) if eva: evaluation = list(map(lambda x: x.strip(), eva.split(","))) else: evaluation = [] if evaluation: tmp = list(map(lambda x: r.parse(x), evaluation)) eva_dt = list(map(lambda x: x.replace(tzinfo=timezone.utc), tmp)) else: eva_dt = None eva_notice = cfg.get(section, "evaluation_notice", fallback=None) if eva_notice and eva_dt: tmp = list(map(lambda x: RecurringEvent(x).parse(eva_notice), eva_dt)) eva_text_dt = list(map(lambda x: x.replace(tzinfo=timezone.utc), tmp)) else: eva_text_dt = None extendable = cfg.get(section, "extendable", fallback=False) visa_names = cfg.get(section, "visa", fallback=None) if visa_names: visa = list(map(lambda x: x.strip(), visa_names.split(","))) else: visa = [] return { "duration_text": duration, "duration_dt": duration_dt, "acl": acl, "finish_text": end, "finish_dt": end_dt, "cpu": cpu, "visa": visa, "finish_notice_text": end_notice, "extendable": extendable, "finish_notice_dt": end_notice_dt, "transform": transform, "description": description, "evaluation_text": evaluation, "evaluation_dt": eva_dt, "evaluation_notice_text": eva_notice, "evaluation_notice_dt": eva_text_dt }
def test_return_non_recurring2(self): string = 'next wednesday' date = RecurringEvent() ret = date.parse(string) self.assertTrue(isinstance(ret, datetime.datetime))
def test_return_non_date(self): string = 'remember to call mitchell' date = RecurringEvent() ret = date.parse(string) self.assertFalse(ret)
def test_rrule_string(self): string = 'every day starting feb 2' date = RecurringEvent(NOW) date.parse(string) expected = """DTSTART:20100202\nRRULE:FREQ=DAILY;INTERVAL=1""" self.assertEqual(expected, date.get_RFC_rrule())
def test_return_non_recurring(self): string = 'march 3rd, 2001' date = RecurringEvent() ret = date.parse(string) self.assertTrue(isinstance(ret, datetime.datetime))
def test_format_errors(self): date = RecurringEvent() self.assertIs(date.format(None), None) self.assertEqual(date.format(''), '') self.assertEqual(date.format('abc'), 'abc') # No RRULE self.assertEqual(date.format('RRULE:INTERVAL=1'), 'RRULE:INTERVAL=1') # No FREQ self.assertEqual(date.format('RRULE:FREQ=WEEKLY'), 'RRULE:FREQ=WEEKLY') # No BYDAY self.assertEqual(date.format('RRULE:FREQ=WEEKLY;BYDAY=XX'), 'RRULE:FREQ=WEEKLY;BYDAY=XX') # Bad BYDAY self.assertEqual(date.format('RRULE:FREQ=MONTHLY'), 'RRULE:FREQ=MONTHLY') # No BYMONTHDAY or BYDAY self.assertEqual( date.format('RRULE:FREQ=YEARLY'), 'RRULE:FREQ=YEARLY') # No BYMONTHDAY or BYDAY or BYMONTH self.assertEqual(date.format('RRULE:FREQ=BADLY'), 'RRULE:FREQ=BADLY')
def parse(s, now=None): return RecurringEvent(now).parse(s)
def format(r, now=None): return RecurringEvent(now).format(r)