terom@50: """ terom@50: Format LogLines into some other representation terom@50: """ terom@50: terom@69: import re, xml.sax.saxutils terom@50: terom@50: from log_line import LogTypes terom@79: from log_formatter_pil import PILImageFormatter terom@80: from log_formatter_rss import RSSFormatter terom@50: terom@50: class LogFormatter (object) : terom@50: """ terom@65: Provides a method to format series of LogLines into various output formats, with varying themes. terom@50: """ terom@51: terom@72: # machine-readable name terom@51: name = None terom@72: terom@72: # human-readable name terom@72: title = None terom@72: terom@72: ## parameters terom@72: # use a fixed-width font for HTML output terom@72: html_fixedwidth = True terom@72: terom@79: def __init__ (self, tz, timestamp_fmt, img_ttf_path, img_font_size) : terom@50: """ terom@79: Initialize to format timestamps with the given timezone and timestamp. terom@79: terom@79: Use the given TTF font to render image text with the given size, if given, otherwise, a default one. terom@50: """ terom@50: terom@50: self.tz = tz terom@50: self.timestamp_fmt = timestamp_fmt terom@79: self.img_ttf_path = img_ttf_path terom@79: self.img_font_size = img_font_size terom@50: terom@65: def _format_line_text (self, line, template_dict, full_timestamp=False) : terom@50: """ terom@50: Format the given line as text, using the given { type: string template } dict terom@50: """ terom@65: terom@50: # look up the template terom@50: template = template_dict[line.type] terom@65: terom@65: # convert timestamp into display timezone terom@65: dtz = line.timestamp.astimezone(self.tz) terom@65: terom@65: # full timestamps? terom@65: if full_timestamp : terom@73: # XXX: ugly hack terom@65: timestamp_fmt = '%Y-%m-%d ' + self.timestamp_fmt terom@65: terom@65: else : terom@65: timestamp_fmt = self.timestamp_fmt terom@86: terom@86: # breakdown source terom@86: source_nickname, source_username, source_hostname, source_chanflag = line.source terom@86: target_nickname = line.target terom@50: terom@50: # format with dict terom@50: return template % dict( terom@86: channel_name = line.channel.name, terom@86: datetime = dtz.strftime('%a %b %d %H:%M:%S %Y'), terom@86: timestamp = dtz.strftime(timestamp_fmt), terom@86: source_nickname = source_nickname, terom@86: source_username = source_username, terom@86: source_hostname = source_hostname, terom@86: source_chanflag = source_chanflag, terom@86: target_nickname = target_nickname, terom@86: message = line.data, terom@50: ) terom@50: terom@65: def format_txt (self, lines, full_timestamps=False) : terom@50: """ terom@65: Format given lines as plaintext. terom@65: terom@65: If full_timestamps is given, the output will contain full timestamps with both date and time. terom@65: terom@65: No trailing newlines. terom@50: """ terom@50: terom@50: abstract terom@50: terom@65: def format_html (self, lines, full_timestamps=False) : terom@50: """ terom@65: Format as HTML. terom@65: terom@65: See format_txt for information about arguments terom@50: """ terom@50: terom@50: abstract terom@79: terom@80: def format_png (self, lines, full_timestamps=False) : terom@79: """ terom@79: Format as a PNG image, returning the binary PNG data terom@79: """ terom@50: terom@80: abstract terom@80: terom@80: def format_rss (self, lines, full_timestamps=False) : terom@80: """ terom@80: Format as an XML RSS document terom@80: """ terom@80: terom@80: abstract terom@80: terom@86: class BaseHTMLFormatter (LogFormatter) : terom@69: """ terom@69: Implements some HTML-formatting utils terom@69: """ terom@86: terom@86: # parameters terom@86: html_fixedwidth = True terom@69: terom@86: # regexp to match URLs terom@69: URL_REGEXP = re.compile(r"http://\S+") terom@69: terom@69: def _process_links (self, line) : terom@69: """ terom@69: Processed the rendered line, adding in 's for things that look like URLs, returning the new line. terom@69: terom@69: The line should already be escaped terom@69: """ terom@69: terom@69: def _encode_url (match) : terom@69: # encode URL terom@69: url_html = match.group(0) terom@69: url_link = xml.sax.saxutils.unescape(url_html) terom@69: terom@69: return '%(url_html)s' % dict(url_link=url_link, url_html=url_html) terom@69: terom@69: return self.URL_REGEXP.sub(_encode_url, line) terom@86: terom@79: def format_html (self, lines, **kwargs) : terom@50: """ terom@72: Just uses format_txt, but processes links, etc terom@50: """ terom@50: terom@50: # format using IrssiTextFormatter terom@79: for line, txt in self.format_txt(lines, **kwargs) : terom@50: # escape HTML terom@72: html = xml.sax.saxutils.escape(txt) terom@69: terom@69: # process links terom@72: html = self._process_links(html) terom@69: terom@69: # yield terom@72: yield line, html terom@50: terom@86: terom@86: class IrssiTextFormatter (RSSFormatter, PILImageFormatter, LogFormatter) : terom@86: """ terom@86: Implements format_txt for irssi-style output terom@86: """ terom@86: terom@86: # format definitions by type terom@86: __FMT = { terom@86: LogTypes.RAW : "%(timestamp)s %(data)s", terom@86: LogTypes.LOG_OPEN : "--- Log opened %(datetime)s", terom@86: LogTypes.LOG_CLOSE : "--- Log closed %(datetime)s", terom@86: terom@86: LogTypes.MSG : "%(timestamp)s <%(source_chanflag)s%(source_nickname)s> %(message)s", terom@86: LogTypes.NOTICE : "%(timestamp)s -%(source_nickname)s- %(message)s", terom@86: LogTypes.ACTION : "%(timestamp)s * %(source_nickname)s %(message)s", terom@86: terom@86: LogTypes.JOIN : "%(timestamp)s -!- %(source_nickname)s [%(source_username)s@%(source_hostname)s] has joined %(channel_name)s", terom@86: LogTypes.PART : "%(timestamp)s -!- %(source_nickname)s [%(source_username)s@%(source_hostname)s] has left %(channel_name)s [%(message)s]", terom@86: LogTypes.KICK : "%(timestamp)s -!- %(target_nickname)s was kicked from %(channel_name)s by %(source_nickname)s [%(message)s]", terom@86: LogTypes.MODE : "%(timestamp)s -!- mode/%(channel_name)s [%(message)s] by %(source_nickname)s", terom@86: terom@86: LogTypes.NICK : "%(timestamp)s -!- %(source_nickname)s is now known as %(target_nickname)s", terom@86: LogTypes.QUIT : "%(timestamp)s -!- %(source_nickname)s [%(source_username)s@%(source_hostname)s] has quit [%(message)s]", terom@86: terom@86: LogTypes.TOPIC : "%(timestamp)s -!- %(source_nickname)s changed the topic of %(channel_name)s to: %(message)s", terom@86: terom@86: LogTypes.SELF_NOTICE: "%(timestamp)s -%(source_nickname)s- %(message)s", terom@86: LogTypes.SELF_NICK : "%(timestamp)s -!- %(source_nickname)s is now known as %(target_nickname)s", terom@86: } terom@86: terom@86: def format_txt (self, lines, full_timestamps=False) : terom@86: # ...handle each line terom@86: for line in lines : terom@86: # using __TYPES terom@86: yield line, self._format_line_text(line, self.__FMT, full_timestamps) terom@86: terom@86: class IrssiFormatter (BaseHTMLFormatter, IrssiTextFormatter) : terom@86: """ terom@86: Implements plain black-and-white irssi-style formatting terom@86: """ terom@86: terom@86: # name terom@86: name = 'irssi' terom@86: title = "Irssi (plain)" terom@86: terom@86: class DebugFormatter (BaseHTMLFormatter) : terom@86: """ terom@86: Implements a raw debug-style formatting of LogLines terom@86: """ terom@86: terom@86: # name terom@86: name = 'debug' terom@86: title = "Raw debugging format" terom@86: terom@86: def format_txt (self, lines, full_timestamps=False) : terom@86: # iterate terom@86: for line in lines : terom@86: # just dump terom@86: yield line, repr(line) terom@86: terom@50: def by_name (name) : terom@50: """ terom@64: Lookup and return a class LogFormatter by name terom@50: """ terom@50: terom@64: return FORMATTERS[name] terom@50: