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 __init__(self, start, end): """ :param start: earliest time schedule events may occur, inclusive, and reference time for relative time descriptions :param end: latest time schedule events may occur, inclusive :return: new `Scheduler` instance """ self.start, self.end = start, end self.events = [] self.recurrent_parser = RecurringEvent(now_date=start)
def parse_next_event_from_string(s): r = RecurringEvent(now_date=datetime.datetime.now()) r.parse(s) if r.is_recurring: rr = rrule.rrulestr(r.get_RFC_rrule()) return time.mktime(rr.after(datetime.datetime.now()).timetuple()) else: cal = parsedatetime.Calendar() if time.mktime(datetime.datetime.now().timetuple()) == time.mktime( cal.parse(s)[0]): raise Exception("Can't understand this time expression") return time.mktime(cal.parse(s)[0])
def get_dates(frequency, today, end): try: return [pd.Timestamp(frequency).normalize()] except ValueError: pass try: r = RecurringEvent() r.parse(frequency) rr = rrule.rrulestr(r.get_RFC_rrule()) return [ pd.to_datetime(date).normalize() for date in rr.between(today, end) ] except ValueError as e: raise ValueError('Invalid Frequecy')
def __call__(self, parser, namespace, values, option_string=None): if namespace.add is None: raise argparse.ArgumentError(self, 'This argument must be provided along with --add. E.g.: --add facebook.com --playtime_days="every weekend"') if len(values) != 1: raise argparse.ArgumentError(self, 'This argument must be provided only once and contain a single recurrence string. E.g.: --playtime_days="every weekend"') play_days = values[0] if RecurringEvent().parse(play_days) is None: raise argparse.ArgumentError(self, "Invalid recurrence input %s" % play_days) if getattr(namespace, self.dest) is not None: raise argparse.ArgumentError(self, "--playtime_days can only be provided once") setattr(namespace, self.dest, play_days)
def next_pickup(self): r = RecurringEvent(now_date=datetime.now()) r.parse(self.pickup_time) rr = rrule.rrulestr(r.get_RFC_rrule()) next_date = rr.after(datetime.now()) try: date_exception = DistrictExceptions.objects.get(date=next_date, district=self) except: date_exception = None if date_exception: new_date = date_exception.new_date if not new_date: next_date = rr.after(next_date) else: next_date = new_date return next_date.date()
def insert(self, value): ''' A new thought to be inserted, parses thought input and extracts hashtags, people, reminder and location ''' with shelve.open(self.dbf, writeback=True) as db: thought = value # Get Location location = None if ';' in value: thought, _, location = value.rpartition(';') location = location.strip() g = geocoder.google(location) if g.latlng is None: print('Trying OSM') g = geocoder.osm(location) location = (location, g.latlng) # Prase Recurring event r = RecurringEvent() # read more here https://github.com/kvh/recurrent print(r.parse(thought)) # Colour Hashtags hashtags = {tag for tag in thought.split() if tag.startswith("#")} thought = self.highlight_terms(thought, hashtags, 'ffdc00') # Colour People people = {tag for tag in thought.split() if tag.startswith("@")} thought = self.highlight_terms(thought, people, '7fdbff') # Create People who don't exist for person in people: if person not in db: db[person] = Person(person) # Cleanup value = thought if location: value += '; ' + location # Save Thought to DB db['known_terms'] = list(set(db.get('known_terms', []) + list(hashtags) + list(people))) t = Thought(value, hashtags, people, r, location) db['thoughts'] = db.get('thoughts', []) db['thoughts'].insert(0, t) # Insert into UI self.rv.data.insert(0, {'value': value or 'empty thought'})
def get_dates(freq, Start, End): try: return [pd.Timestamp(freq).normalize()] except ValueError: pass try: return pd.date_range(start=Start, end=End, freq=freq) except ValueError: pass try: r = RecurringEvent() r.parse(freq) rr = rrule.rrulestr(r.get_RFC_rrule()) return [ pd.to_datetime(date).normalize() for date in rr.between(Start, End) ] except ValueError as e: raise ValueError('Invalid frequency')
mqttc.username_pw_set(cf.get('mqtt_username'), cf.get('mqtt_password')) mqtt_port = int(cf.get('mqtt_port', '1883')) ssl = (mqtt_port == 8883) if ssl: mqttc.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=paho.ssl.CERT_REQUIRED, tls_version=paho.ssl.PROTOCOL_TLS, ciphers=None) mqttc.connect(cf.get('mqtt_broker', 'localhost'), mqtt_port, 60) # parse schedule schedule = 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 delay = (rrulestr(dt).after(now) - now).total_seconds() s.enter(delay, 1, on_timer, [s, dt, schedule[t]]) tt = TimerThread(s) tt.daemon = True tt.start() while True: try: mqttc.loop_forever() except socket.error:
def get_rrule(human_string): r = RecurringEvent(now_date=datetime.datetime.now()) return r.parse(human_string)
def is_day_in_recurrence(recurrence_string, day): recurrence = rrule.rrulestr(RecurringEvent().parse(recurrence_string)) return (recurrence[0].date() - day.date()).days == 0
def test_convert_recurring_event_to_trigger_format(frequency, expected): event = RecurringEvent() event.parse(frequency) assert parser.convert_recurring_event_to_trigger_format(event) == expected # validate with ApsSchedule cron trigger CronTrigger(**expected)
def is_event_recurring(s): r = RecurringEvent(now_date=datetime.datetime.now()) r.parse(s) return r.is_recurring
def slack_events(request): bod = request.json_body bot_token = os.environ.get('SLACK_BOT_TOKEN') if bod['token'] != bot_token: return Response(status=304) evt = bod['event'] msg = evt['text'] channel = evt['channel'] user = evt['user'] edited = evt.get('edited') if edited: user_ts = edited['ts'] else: user_ts = evt['ts'] print(user_ts) retry_count = request.headers.get('X-Slack-Retry-Num') if retry_count is not None and int(retry_count) > 0: thread = evt['ts'] else: thread = None slack_api_key = os.environ.get('SLACK_API_KEY') slack_client = slack.WebClient(token=slack_api_key) user_ts = float(user_ts) # Parsing natural language with regex...we can add a context free grammar later... md = MSG_RGX.search(msg) if md: tgts = re.split(AND_OR_COMMA_RGX_STR, md.group('targets')) found_tgts = [] query_str = md.group('query') # A list of available targets for this requester, a subset of all # possible targets (those in the message regex) potential_targets = get_potential_targets(request) for t in tgts: for s in potential_targets: if s.lower() == t.lower(): found_tgts.append(s) queries = [] for t in found_tgts: queries.append(SEARCH_TARGETS[t]['query_type'](query_str)) user_now = datetime.utcfromtimestamp(user_ts) sched = RecurringEvent(now_date=user_now) sched_str = md.group('schedule') if not sched_str: sched_str = 'daily' rrule_str = sched.parse(sched_str) if rrule_str: schedule = rrulestr(rrule_str) schedule.dtstart = user_now reply = ( 'OK, <@{}>, I will search for "{}" on {} with a schedule of "{}". ' 'The next query will be at {}') reply = reply.format(user, query_str, ", ".join(found_tgts), str(rrule_str), schedule.after(user_now)) # TODO: Make this logic also account for per-user schedule requests, # org-level event handlers and storage key = ('slack_channel', channel) if SCHEDULER_KEY not in request.context: # TODO: Put this in a different place and use a remote search scheduler request.context[SCHEDULER_KEY] = PersistentDict() if key not in request.context[SCHEDULER_KEY]: request.context[SCHEDULER_KEY][key] = ListSearchScheduler() if HANDLER_KEY not in request.context: # TODO: Put this in a different place and use a remote event handler request.context[HANDLER_KEY] = PersistentDict() if key not in request.context[HANDLER_KEY]: request.context[HANDLER_KEY][key] = SlackMessageEventHandler( channel, user) scheduler = request.context[SCHEDULER_KEY][key] event_handler = request.context[HANDLER_KEY][key] for q in queries: scheduler.add_schedule(q, schedule, event_handler) else: reply = f'Sorry, <@{user}>, but I don\'t understand this search schedule: {sched_str}' else: reply = f'Sorry, <@{user}>, I don\'t know about that' send_message(slack_client, evt['channel'], reply, thread) return Response('')
def _extract_parts(text): ''' Extract Parts Extract and parse the message, returning a dictionary describing the actions that should be taken -- @param text:string The message sent by the user @return dict ''' # Examples: # remind me once monday at 4pm, Help! # remind me once two weeks from now, Help! # # remind me every 5 minutes, Help! # remind me every 1 day, Help! # # remind show # remind stop #1 # With the above examples, we can say that the # format should follow the following: # # [script init] [set / show /stop / help] [once/every] [time]: [message] parts = { 'recipient': None, 'action': 'set', 'recurrence': 'once', 'time': None, 'parsed_time': None, 'message': None, 'error': False, 'error_message': None } # Check the action_recipient action_recipient = text.split(' ')[0].strip() # Ensure that the action is something we understand if action_recipient not in ['set', 'show', 'stop', 'help']: parts['error'] = True parts['error_message'] = 'Unknown command: {c}'.format( c=action_recipient) return parts # We are happy with the action. show and stop # actions dont care about the rest if action_recipient in ['show', 'stop', 'help']: parts['action'] = action_recipient return parts # Set the recipient and remove it from the text text = text.replace(action_recipient, '', 1).strip() parts['recipient'] = action_recipient # Next part is to check the recurrence. recurrence = text.split(' ')[0].strip() # Check that the recurrence is valid if recurrence not in ['once', 'every']: parts['error'] = True parts['error_message'] = 'Unknown recurrence: {r}'.format(r=recurrence) return parts # Set the recurrence and remove it from the text # only of it is set to 'once' text = text.replace('once', '', 1).strip() parts['recurrence'] = recurrence # Next, we parse the time. For this we should be # able to split by , if len(text.split(',', 1)) != 2: parts['error'] = True parts[ 'error_message'] = 'Time and message should be seperated by a comma ( , ) character' return parts # Set the time and the message time = text.split(',', 1)[0].strip() message = text.split(',', 1)[1].strip() # Resolve the human time to something we can work # with r = RecurringEvent() parsed_time = r.parse(time) # If the time parsing failed, oh well. if parsed_time is None: parts['error'] = True parts['error_message'] = 'Unable to parse time: {t}'.format(t=time) return parts # Set the time and parsed time, as well as the message # that should be used parts['time'] = time parts['parsed_time'] = parsed_time parts['message'] = message return parts
def do_GET(self): self.send_response(200) self.send_header('Content-type', 'text/plain') self.end_headers() query = urlparse(self.path).query query_components = dict(qc.split("=") for qc in query.split("&")) msg = ' '.join(query_components["message"].split('%20')) msg = '\''.join(msg.split('%27')) intent = clf.predict([msg])[0] if intent == 'create_event': r = RecurringEvent(now_date=datetime.now()) dt = r.parse(msg) if "p.m." in msg and dt.hour < 12: dt += timedelta(hours=12) if "a.m." in msg and dt.hour > 12: dt -= timedelta(hours=12) reply = random.choice(create_replies) event = { 'summary': 'Meeting', 'start': { 'dateTime': dt.isoformat(), 'timeZone': 'America/Denver', }, 'end': { 'dateTime': (dt + timedelta(hours=1)).isoformat(), 'timeZone': 'America/Denver', } } event = service.events().insert(calendarId='primary', body=event).execute() else: #query_events now = datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time reply = 'Your Agenda:<br>' eventsResult = service.events().list( calendarId='primary', timeMin=now, maxResults=10, singleEvents=True, orderBy='startTime').execute() events = eventsResult.get('items', []) if not events: reply = 'No upcoming events found.' for event in events: start = event['start'] if start.get('dateTime'): dt = dateutil.parser.parse(start.get('dateTime')) hour = str( dt.hour) if dt.hour <= 12 and dt.hour > 0 else str( abs(dt.hour - 12)) minute = str( dt.minute) if dt.minute > 9 else '0' + str(dt.minute) meridiem_indicator = ' AM' if dt.hour < 12 else ' PM' date = dt.date() time = hour + ':' + minute + meridiem_indicator else: dt = dateutil.parser.parse(start.get('date')) date = dt.date() time = 'All-Day' reply += '<br>' + str(dt.month) + '/' + str( dt.day) + ' ' + time + ' ' + event['summary'] self.wfile.write({"intent": intent, "reply": reply})