Commits

Marc-Alexandre Chan committed 2f2d4a2

Completed logging in events.py

  • Participants
  • Parent commits a8df299

Comments (0)

Files changed (3)

File minibot/config.py

 from ConfigParser import SafeConfigParser
 from logging import DEBUG, INFO, WARN, ERROR
 
-
 class Section(object):
     """ Abstract class representing a configuration section. Defines a generic
     constructor that loads a dictionary, as well as a method to collapse the

File minibot/events.py

 # - owner: set by the scheduler upon registration. Reference to the scheduler.
 # - res: set by the scheduler upon registration. A dict of resources listed in
 #       required_res. This is NOT available upon construction.
-# -
+#
+# Note that res and owner are NOT available upon construction. Initialisation
+# relying on these two attributes should be executed in start().
+#
 # Required methods:
 # - start() - Called upon the first run of the event. Note that run() is NOT
 #       called at this run---if needed, call it in start(). For one-off events,
 #   considering the event corrupt and dropping/logging it, or it may re-throw
 #   it). This method must not raise an exception.
 #
-# Note that res and owner are NOT available upon construction. Initialisation
-# relying on these two attributes should be executed in start().
+# Other methods:
+# __repr__(): The Python object representation will be used for the purpose of
+# logging events. It is suggested that you specify this method and return a
+# meaningful string identifying a particular event object. This should
+# be a relatively short single-line string.
 #
 # Valid resources:
 # - reddit - A reddit.Reddit() instance
 
 
 from minibot import MinibotError
+import minibot.logger
 from minibot.db import Prompt, User, SuggestionThread
-from minibot.util import DateParseMixin
+from minibot.util import DateParseMixin, MarkdownMixin, DataFormatMixin,\
+    classname, get_reddit_url
 
 import reddit.objects.Inboxable
 
 from calendar import timegm
 from urlparse import urlparse
 
-# TODO: logging
 # TODO: error handling
+# TODO: repr
 
 # this is in all superscript (renders equiv. small text) ^(syntax is like this)
 _BOT_MSG = ("^(Having a problem with DailyPromptBot? Want to comment? You can "
 "DailyPromptBot? It's open-source! Here's [the DailyPromptBot "
 "repository](https://bitbucket.org/Laogeodritt/dailypromptbot).)")
 
-_evlog = logging.getLog('dailyprompt.minibot.events')
+_evlog = minibot.logger.getChild('events')
 
 
 class CommandError(MinibotError):
                 # if bad PM, log; don't care about replies/comments though
                 if msg.subreddit is None:
                     _evlog.info(
-                        ("CheckMessageEvent: Ignoring bad PM from "
-                        "{user} ({id_:d})").ormat(user=msg.author, id_=msg.id))
+                        ("%s: Ignoring bad PM from %s (%d)"),
+                        classname(self), msg.author, msg.id)
                 msg.mark_as_read()
 
             if msg_command is not None:
         No validation of the data is performed.
 
         """
-        _evlog.debug('CheckMessageEvent:Parsing message {id_:d} from {user}.'.
-            format(id_=msg.id, user=msg.author))
+        _evlog.debug('%s: Parsing message %d from %s.',
+            classname(self), msg.id, msg.author)
         msg_data = {}
         text_data = []
 
         #    raise CommandParseError(
         #            ''.join(["No parameter value in line: \"", line, "\""]))
 
+        _evlog.debug('%s: Parsed argument line: %s = %s',
+            classname(self), field_data[0], field_data[1])
+
         return field_data
 
     def _make_command(self, msg, data):
         data. If the command name is invalid, raises a CommandNameError. If the
         command parameters are invalid or incomplete, raises a
         CommandParametersError (from the command constructor). """
-        _evlog.info('CheckMessageEvent: PM {id_:d}: building command: {info}'.
-                    format(id_=msg.id, info='; '.join(data)))
+        _evlog.info('%s: PM %d: building command: %s',
+            classname(self), msg.id, '; '.join(data))
         if data['action'] in self.cmd_actions.keys():
             CommandClass = self.cmd_actions[data.pop('action')]
             cmd_obj = CommandClass(msg, **data)
                 filter(Prompt.status == Prompt.STATUS_QUEUED).\
                 order_by(Prompt.post_time)[0:int(self.queue_rate)]:
             if id_ not in queued_id:
+                _evlog.info("%s: Queuing prompt %s at %s", classname(self),
+                id_, t_post.strftime("%Y-%m-%d %H:%M:%S UTC"))
                 new_cmd = PostPromptCommand(id_, t_post)
                 self.owner.queue_event(new_cmd)
 
                 filter(SuggestionThread.status==SuggestionThread.STATUS_QUEUED).\
                 order_by(SuggestionThread.time)[0:int(self.queue_rate)]:
             if id_ not in queued_ids:
+                _evlog.info(
+                    "%s: Queued thread %s at %s", classname(self),
+                    id_, t_post.strftime("%Y-%m-%d %H:%M:%S UTC"))
                 new_cmd = PostSuggestionThreadCommand(id_, t_post)
                 self.owner.queue_event(new_cmd)
 
         day_next = dt_now.date() + delta_next
         dt_next = dt_now + delta_next
         dt_next_end = dt_next + timedelta(days=1)
+        _evlog.debug("%s: Checking %s to %s",
+            classname(self), dt_next.strftime("%Y-%m-%d %H:%M:%S"))
 
-        # find any thread on the next post date on the post date
-        thread = db.query(SuggestionThread).\
-                 filter(SuggestionThread.time >= dt_next).\
-                 filter(SuggestionThread.time <  dt_next_end).\
-                 limit(1).first()
+        # find any thread on the next post date
+        num_threads = db.query(SuggestionThread).\
+                    filter(SuggestionThread.time >= dt_next).\
+                    filter(SuggestionThread.time <  dt_next_end).\
+                    count()
 
         # if no thread is found on that day, queue one up
-        if thread is None:
-            thread = SuggestionThread(datetime.combine(day_next, post_time))
-            db.add(thread)
+        if num_threads == 0:
+            new_post_dt = datetime.combine(day_next, post_time)
+            new_thread = SuggestionThread(new_post_dt)
+            db.add(new_thread)
+            _evlog.info("%s: Added thread for %s to database", classname(self),
+            new_post_dt.strftime("%Y-%m-%d %H:%M:%S"))
+            _evlog.debug("%s: %s", classname(self), repr(new_thread))
             db.commit()
         else: # if a thread is found on that day, no action to take
             pass
         * ``duration`` = 0
         * ``interval`` = 0
 
-        """
+    """
     # TODO: Clean up repetition of UpdatePromptCommand & very long methods
     required_res = ['reddit', 'dbsession', 'config.events', 'config.reddit']
     start_time = 0 # execute ASAP
         self.title = kwargs.pop('title', '')
 
         str_date = kwargs.pop('date', None)
+        if hasattr(str_date, 'strip'): str_date = str_date.strip()
         str_time = kwargs.pop('time', None)
-        if hasattr(str_date, 'strip'): str_date = str_date.strip()
         if has_attr(str_time, 'strip'): str_time = str_time.strip()
         self.datetime = _parse_datetime(str_date, str_time)
         self.date_default = (str_date is None)
         r_approver_id = self.msg.author.id
         approver_name = self.msg.author.name
 
+        # check approver in User table
         if not db.query(User).filter(User.r_id==r_approver_id).count():
+            _evlog.info(
+                "%s: User %s not found. Adding to database `user` table.",
+                classname(self), approver_name)
             new_user = User(r_approver_id, approver_name)
             db.add(new_user) # set as unregistered by default
+        else:
+            _evlog.debug(
+                "%s: User %s (%s) found in database `user` table.",
+                classname(self), r_approver_id, approver_name)
 
         new_prompt = Prompt(
                             self.title, self.text, r_approver_id,
         new_prompt.post_time = post_time
         db.add(new_prompt)
 
+        _evlog.info("%s: Added prompt '%s' (%d) to database queue.",
+            classname(self), self.title, new_prompt.id)
+        _evlog.debug("%s: %s", classname(self), repr(new_prompt))
+
         db.commit()
+        self.msg.mark_as_read()
 
-        self.msg.mark_as_read()
+        _evlog.info("%s: Marked message %s as read.",
+            classname(self), self.msg.name)
 
         reply_topic = ''.join([target_reddit, ": Prompt added"])
         reply_text  = ''.join([
                 break
             else:
                 continue
+        _evlog.debug("%s: Nearest unqueued date found: %s.",
+            classname(self), current_date.stftime('%Y-%m-%d'))
         return current_date
 
 
         if del_prompt is None:
             raise CommandParameterError(
                 ''.join(["Prompt ", self.id, " not found."]))
-        db.delete(del_prompt)
+        del_prompt.status = del_prompt.STATUS_REJECTED
+
+        _evlog.info("%s: Removed prompt '%s' (%d) from database queue.",
+            classname(self), self.title, del_prompt.id)
+
         db.commit()
         self.msg.mark_as_read()
 
+        _evlog.info("%s: Marked message %s as read.",
+                    classname(self), self.msg.name)
+
         reply_title = ''.join([target_reddit, ": Prompt deleted"])
         reply_text = u"Prompt {id_:d} has been deleted.\n\n___\n\n{prompt}".\
-            format(id_=upd_prompt.id, prompt=self._format_prompt(del_prompt))
+            format(id_=del_prompt.id, prompt=self._format_prompt(del_prompt))
         self.owner.queue_command(
             SendReplyCommand(self.msg, reply_title, reply_text))
 
             new_user = User(upd_prompt.r_approver_id, approver_name)
             db.add(new_user) # set as unregistered by default
 
+        _evlog.info(
+            "%s: Updated prompt '%s' (%d) in database queue.",
+            classname(self), self.title, upd_prompt.id)
+        _evlog.debug("%s: %s", classname(self), repr(upd_prompt))
+
         db.commit()
+        self.msg.mark_as_read()
 
-        self.msg.mark_as_read()
+        _evlog.info("%s: Marked message %s as read.",
+                    classname(self), self.msg.name)
 
         reply_title = ''.join([target_reddit, ": Prompt updated"])
         reply_text = u"Prompt {id_:d} has been updated.\n\n___\n\n{prompt}".\
                 break
             else:
                 continue
+        _evlog.debug("%s: Nearest unqueued date found: %s.",
+            classname(self), current_date.stftime('%Y-%m-%d'))
         return current_date
 
 
 
     def start(self):
         self.res['reddit'].compose_message(self.user, self.title, self.text)
+        _evlog.info("%s: Sent PM titled '%s' to %s.", classname(self),
+            self.title, self.user)
 
     def run(self):
         pass
 
     def start(self):
         self.msg.reply(self.text)
+        _evlog.info("%s: Sent Reddit reply to %s from %s.", classname(self),
+            self.msg.name, self.msg.user)
 
     def run(self):
         pass
             query = query.order_by(Prompt.id)
 
         prompt_list = [self._header_prompt(self.short_entry)]
+        prompt_ids = []
         has_prompt = False
 
         for prompt in query[0:self.limit]:
             has_prompt = True
             prompt_list.append(self._format_prompt(prompt, self.short_entry))
+            prompt_ids.append(prompt.id)
 
         if not has_prompt:
             prompt_list = ["No results found."]
         else: # long list = newlines between entries
             self.msg.reply(u'\n'.join(prompt_list))
 
+        _evlog.info("%s: Sent PM list to %s for prompts %s.",
+            classname(self), self.msg.author, ', '.join(prompt_ids))
+
         self.msg.mark_as_read()
+        _evlog.info("%s: Marked message %s as read.",
+            classname(self), self.msg.name)
 
     def run(self):
         pass
 
     def _format_url(self, post_id):
         """ Overrides ``DataFormatMixin._format_url()``. """
-        return _get_reddit_url(post_id, self.res['reddit'])
+        return get_reddit_url(post_id, self.res['reddit'])
 
 
 class SendHelpMessageCommand(SendReplyCommand):
     def start(self):
         with open('minibot/commands.txt') as help_file:
             self.msg.reply(help_file.read())
+        _evlog.info("%s: Sent help message to",
+            classname(self), self.msg.author)
 
     def run(self):
         pass
 
         prompt.r_post_id = r_sub.id
         prompt.status = prompt.STATUS_POSTED
+        _evlog.info("%s: Posted prompt %d to /r/%s", classname(self),
+            prompt.id, target)
         db.commit()
 
     def run(self):
 
     def end(self):
         self.res['dbsession'].close()
-        del se.fres['dbsession']
+        del self.res['dbsession']
 
     def _get_title(self):
         """ Return the submission title for the prompt stored in
                 "it enough to queue it up later on!")
     NAV_MENU = (u"| | | |\n"
                 u"| :-: | :-: | :-: |\n"
-                u"| {prev} | {cur} | {next} |\n"
+                u"| « {prev} | .:{cur}:. | {next} » |\n"
                 u"| | | |\n")
 
     required_res = ['reddit', 'dbsession', 'config.reddit']
         thread.status = thread.STATUS_ACTIVE
         self._archive_older_threads()
         self._add_link_to_last_thread()
+        _evlog.info("%s: Posted suggestion thread %d to /r/%s", classname(self),
+            thread.id, target)
         db.commit()
 
     def run(self):
 
         if self.prev_thread is not None and\
                 self.prev_thread.r_post_id is not None:
-            prev_url = _get_reddit_url(self.prev_thread.r_post_id, reddit)
+            prev_url = get_reddit_url(self.prev_thread.r_post_id, reddit)
             prev_text = self.md_link(prev_date_str, prev_url)
 
         text = ''.join([
                     filter(SuggestionThread.time >= prev_start).\
                     filter(SuggestionThread.time < prev_end).\
                     order_by(SuggestionThread.time).limit(1).first()
+        if thread:
+            _evlog.info("%s: Found previous thread %d (%s) <%s>",
+                classname(self), thread.id,
+                thread.time.strftime("%Y-%m-%d %H:%M:%S"),
+                get_reddit_url(thread.r_post_id, self.res['reddit']))
+        else:
+            _evlog.warn("%s: No previous week's thread found.", classname(self))
         return thread
 
     def _archive_older_threads(self):
         """ Sets all older active threads to archived status in database. Does
         not commit the transaction. """
         db = self.res['dbsession']
+        threads = []
         for other_thread in db.query(SuggestionThread).\
                 filter(SuggestionThread.id != self.post_id).\
                 filter(SuggestionThread.time <= self.thread.time).\
                 filter(SuggestionThread.status == self.thread.STATUS_ACTIVE):
             other_thread.status = other_thread.STATUS_ARCHIV
+            threads.append(other_thread.r_post_id)
+        _evlog.info("%s: Archived %d other threads: %s", classname(self),
+            len(threads), ', '.join(threads))
 
     def _add_link_to_last_thread(self):
         """ Adds a 'next week's suggestion thread' link to the previous
         suggestion thread. """
         if self.prev_thread is not None:
             curr_date_str = self.post_time.date().strftime('%d %B %Y')
-            post_url = _get_reddit_url(
+            post_url = get_reddit_url(
                                         self.prev_thread.r_post_id,
                                         self.res['reddit'])
-             self.prev_thread.text.replace(
-                ' '.join(['|', curr_date_str, '|']),
-                ' '.join(['|', self.md_link(curr_date_str, post_url), '|')])
+            self.prev_thread.text = self.prev_thread.text.replace(
+                ' '.join(['|', curr_date_str, '» |']),
+                ' '.join(['|', self.md_link(curr_date_str, post_url), '» |')])
 
-
-def _get_reddit_url(id, reddit):
-    """ Returns the URL to a reddit submission, given the submission id and a
-    Reddit API object. """
-    return ''.join(['http://', reddit.config.domain, '/', id])
+            r_thread = self.res['reddit'].\
+                       get_submission(submission_id=self.prev_thread.r_post_id)
+            r_thread.edit(self.prev_thread.text)
+            _evlog.info("%s: Edited navigation link to thread %s in previous "
+                "thread %s", classname(self), self.thread.r_post_id,
+                self.prev_thread.r_post_id)
 
 
 CheckMessageEvent.cmd_actions = {'add'    : AddPromptCommand,

File minibot/util.py

 import re
 import copy
 
+def get_reddit_url(id, reddit):
+    """ Returns the URL to a reddit submission, given the submission id and a
+    Reddit API object. """
+    return ''.join(['http://', reddit.config.domain, '/', id])
+
+def classname(obj):
+    return obj.__class__.__name__
+
 class MarkdownMixin(object):
     """ Mixin providing markdown syntax shortcut methods. """