def get_agenda(self, horizon_incoming, horizon_unfinished, list_unfinished_appointments, relative_to): "Generate agenda in a text format" log.info("Getting agenda from %r to %r", horizon_unfinished, horizon_incoming) # Open and read when needed so the file can be updated # without restarting bot. template = helpers.get_template(self.agenda_path, self.agenda_content) since = relative_to.replace(hour=0, minute=0) if (relative_to - since).total_seconds() < 4 * 60 * 60: # Include more past since -= dt.timedelta(hours=4) ctx = { # TODO: planned 'planned': [], 'unfinished': self.get_unfinished(horizon_unfinished, list_unfinished_appointments, relative_to), # Incoming - from the whole day 'appointments': self.get_appointments(since=since, horizon=horizon_incoming), 'now': relative_to, } return template.render(ctx)
def refresh_events(self): """ Read events from exchange, convert and update calendar. """ log.info("Periodic operation executed") now = self.time.now() start_of_day = now.replace(hour=0, minute=0) horizon_end = now + dt.timedelta(hours=self.horizon_incoming) try: events = self.exch_calendar.list_events(start=start_of_day, end=horizon_end, details=True) except AttributeError: # Module is badly written. In case of connection errors it # throws Attribute Error. Show error in case something weird # happened, but don't kill bot. log.exception("Connection (probably) error within exch module.") return None calendar_events = [] for event in events.events: converted = self.convert_event(event) calendar_events.append(converted) log.info('Read %d events from exchange', len(calendar_events)) # Use shared state to talk to core plugins self.state['calendar'].update_events(calendar_events, 'exch') return calendar_events
def refresh_db(self): "Refresh/load DB with org entries" log.info('Refreshed/read org-mode data') db = helpers.load_orgnode(self.parsed_config) events = [ helpers.orgnode_to_event(node, self.parsed_config) for node in db ] self.state['calendar'].update_events(events, 'org') return events
def initialize(self): """ Called third, after all plugins are registered. Use it to initialize the plugin. It's a good place to register periodic callbacks using self.scheduler. You can also use other plugins public API via the self.state object. """ log.info("3. Initialize the plugin") # There might be some operation you need to do periodically: self.scheduler.every(30).seconds.do(self.periodic)
def register(self): """ Called second: Use this method to register any commands/callbacks via the self.assistant API. """ log.info("2. Register plugin commands") # You can register some commands here commands = [ (['owa_refresh'], self.handle_refresh), ] for aliases, callback in commands: self.assistant.command.register(aliases, callback)
def validate_config(self): """ Called first: Use this method to read config parameters and substitute defaults were appropriate. You should touch all your config variables here so we can inform the user if he have mistyped something in the config file (even when --test parameter is given) Available API: self.config, self.assistant, self.scheduler. self.state might not be fully populated and ready yet. """ log.info("1. Validate and read config") self.parameter = self.config.get('parameter', assert_type=int, default=42)
def schedule_notifications(self): "Schedule incoming notifications" # Program may hang for indefinite amount of time and we still should # not miss any notifications. At the same time calendar may get updated, # tasks added or removed. We can't send duplicates. # Prepare incoming notifications up to 5 minutes before window_size = 5 now = self.time.now() for notify_period in self.notify_periods: # Calculate window wnd_start = now + dt.timedelta(minutes=notify_period) wnd_end = wnd_start + dt.timedelta(minutes=window_size) wnd_start = max(wnd_start, self.notify_positions[notify_period]) last_scheduled = wnd_start for event in self.calendar.events: # Ignore date-less events if event.relevant_date is None: continue date = event.relevant_date.sort_date # We want to schedule an event if it lies between now+notify_period and # now+notify_period+prepare_before if not event.relevant_date.appointment: continue if date <= wnd_start: continue if date > wnd_end: continue log.info("Scheduling %dm notification for event %r", notify_period, event) delta = (date - self.time.now()).total_seconds() - notify_period * 60 self.scheduler.every(delta).seconds.do(self.send_notice, event) last_scheduled = max(last_scheduled, date) self.notify_positions[notify_period] = last_scheduled
def _initialize_plugins(self): "Create instances of plugins" # {name: handler1, name2: handler1, name3: handler2, ...} plugins = self.config.get('plugins', assert_type=dict) for plugin_name, plugin_config in plugins.items(): plugin_cls = Assistant.registered_plugins.get(plugin_name, None) if plugin_cls is None: raise ConfigError("Configured plugin '%s' is not registered" % plugin_name) plugin = plugin_cls(self, plugin_config, self.scheduler, self.time, self.state) plugin.validate_config() plugin.register() self.plugins[plugin_name] = plugin log.info('Plugin %s instantiated', plugin_name) # After all plugins are created - initialize plugins for plugin in self.plugins.values(): plugin.initialize()
def periodic(self): "Example: Periodic operations" log.info("Periodic operation executed") # Use shared state to talk to core plugins self.state['calendar'].add_events([], 'owa')