Source code for slacklog.formatters

# -*- coding: utf-8 -*-
"""
SlackLog formatters
===================

SlackLog formatter takes an in-memory representation of a Slackware ChangeLog.txt and produces a different
representation of it.

The in-memory representation is an instance of :any:`SlackLog`.

Currently, the following formatters are provided:

  * :py:class:`SlackLogTxtFormatter` tries to reproduce the original ChangeLog.txt.
  * :py:class:`SlackLogRssFormatter` produces an RSS feed.
  * :py:class:`SlackLogAtomFormatter` produces an Atom feed.
  * :py:class:`SlackLogJsonFormatter` produces a JSON representation.
  * :py:class:`SlackLogPyblosxomFormatter` writes the log entries to PyBlosxom HTML entries.

"""
from __future__ import print_function

import codecs
import datetime
import os
import re
import time
from json import dumps, JSONEncoder
from dateutil import tz
from slacklog.models import SlackLog, SlackLogEntry, SlackLogPkg


def readable(d):
    return d.strftime("%a, %d %b %Y %H:%M:%S GMT")


def anchor(d):
    return d.strftime("%Y%m%dT%H%M%SZ")


[docs]class SlackLogFormatter (object): """ Base class for SlackLog formatters. This class is meant for subclassing. """ def __init__(self): self.max_entries = None """If not :py:const:`None`, must be an :py:class:`int` representing how many entries are formatted from the beginning of the log. Rest of the entries are ignored.""" self.max_pkgs = None """If not :py:const:`None`, must be an :py:class:`int` representing how many packages are formatted from the beginning of each entry. Rest of the packages are ignored."""
[docs] def format(self, log): """ Return unicode representation of the in-memory representation of the log. Default implementation calls :py:meth:`format_log_preamble`, followed by a call to :py:meth:`format_entry` for each log entry, and finally calls :py:meth:`format_log_postamble`. The return value is the concatenation of the return values of the mentioned functions. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of the log. """ assert(isinstance(log, SlackLog)) data = u'' data += self.format_log_preamble(log) data += self.format_list(log.entries, self.format_entry, self.max_entries) data += self.format_log_postamble(log) return data
[docs] def format_log_preamble(self, log): """ Return unicode representation of the log preamble, the part before entries. Default implementation returns empty string. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log preamble. """ assert(isinstance(log, SlackLog)) return u''
[docs] def format_log_postamble(self, log): """ Return unicode representation of the log postamble, the part after all entries. Default implementation returns empty string. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log postamble. """ assert(isinstance(log, SlackLog)) return u''
[docs] def format_entry(self, entry, is_first, is_last): """ Return unicode representation of a single log entry. Default implementation calls :py:meth:`format_entry_separator` with arguments `is_first` and `is_last`, followed by a call to :py:meth:`format_entry_preamble`, followed by a call to :py:meth:`format_pkg` for each package in this log entry, finally followed by a call to :py:meth:`format_entry_postamble`. The return value is the concatenation of the return values of the mentioned functions. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :param is_first: :py:class:`bool` -- :py:const:`True` if this is first entry, :py:const:`False` otherwise. :param is_last: :py:class:`bool` -- :py:const:`True` if this is last entry, :py:const:`False` otherwise. :return: :py:class:`unicode` -- Unicode representation of log entry. """ assert(isinstance(entry, SlackLogEntry)) data = u'' data += self.format_entry_separator(is_first, is_last) data += self.format_entry_preamble(entry) data += self.format_list(entry.pkgs, self.format_pkg, self.max_pkgs) data += self.format_entry_postamble(entry) return data
[docs] def format_entry_separator(self, is_first, is_last): """ Return unicode representation of the log entry separator. Default implementation returns an empty string. :param is_first: :py:class:`bool` -- :py:const:`True` if this is first entry, :py:const:`False` otherwise. :param is_last: :py:class:`bool` -- :py:const:`True` if this is last entry, :py:const:`False` otherwise. :return: :py:class:`unicode` -- Unicode representation of log entry separator. """ return u''
[docs] def format_entry_preamble(self, entry): """ Return unicode representation of the log entry preamble, the part before packages. Default implementation returns an empty string. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry preamble. """ assert(isinstance(entry, SlackLogEntry)) return u''
[docs] def format_entry_postamble(self, entry): """ Return unicode representation of the log entry postamble, the part after packages. Default implementation returns an empty string. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry postamble. """ assert(isinstance(entry, SlackLogEntry)) return u''
[docs] def format_pkg(self, pkg, is_first, is_last): """ Return unicode representation of a single log entry package. Default implementation calls :py:meth:`format_pkg_separator`, followed by a call to :py:meth:`format_pkg_preamble`, and finally calls :py:meth:`format_pkg_postamble`. The return value is the concatenation of the return values of the mentioned functions. :param pkg: :py:class:`SlackLogPkg` -- in-memory representation of the log entry package :param is_first: :py:class:`bool` -- :py:const:`True` if this is first package, :py:const:`False` otherwise. :param is_last: :py:class:`bool` -- :py:const:`True` if this is last package, :py:const:`False` otherwise. :return: :py:class:`unicode` -- Unicode representation of log entry package. """ assert(isinstance(pkg, SlackLogPkg)) data = u'' data += self.format_pkg_separator(is_first, is_last) data += self.format_pkg_preamble(pkg) data += self.format_pkg_postamble(pkg) return data
[docs] def format_pkg_separator(self, is_first, is_last): """ Return unicode representation of the log entry package separator. Default implementation returns an empty string. :param is_first: :py:class:`bool` -- :py:const:`True` if this is first package, :py:const:`False` otherwise. :param is_last: :py:class:`bool` -- :py:const:`True` if this is last package, :py:const:`False` otherwise. :return: :py:class:`unicode` -- Unicode representation of log entry package separator. """ return u''
[docs] def format_pkg_preamble(self, pkg): """ Return unicode representation of the log entry package preamble. Default implementation returns an empty string. :param pkg: :any:`SlackLogPkg` -- in-memory representation of the log entry package :return: :py:class:`unicode` -- Unicode representation of log entry package preamble. """ assert(isinstance(pkg, SlackLogPkg)) return u''
[docs] def format_pkg_postamble(self, pkg): """ Return unicode representation of the log entry package postamble. Default implementation returns an empty string. :param pkg: :any:`SlackLogPkg` -- in-memory representation of the log entry package :return: :py:class:`unicode` -- Unicode representation of log entry package postamble. """ assert(isinstance(pkg, SlackLogPkg)) return u''
[docs] def format_list(self, list_of_items, item_formatter, max_items=None): """ Return unicode representation of a list of objects. This method is not meant for subclassing. :param list list_of_items: List of items to format. :param item_formatter: Function that formats one item. A callable that takes one item as the first positional argument, two booleans `is_first` and `is_last` as second and third positional arguments, and returns a :py:class:`unicode` string. :param max_items: :py:class:`int` or falsy -- Maximum number of items to format. If falsy, all items are formatted. :return: :py:class:`unicode` -- Formatted data. """ data = u'' num_items = len(list_of_items) if max_items: assert(isinstance(max_items, int)) if num_items > max_items: num_items = max_items for index in range(num_items): is_first = False is_last = False if index == 0: is_first = True if index == num_items - 1: is_last = True data += item_formatter(list_of_items[index], is_first, is_last) return data
[docs]class SlackLogTxtFormatter (SlackLogFormatter): """ Concrete SlackLog formatter that tries to regenerate the original ChangeLog.txt. """
[docs] def format_log_preamble(self, log): """ Overrides :py:meth:`SlackLogFormatter.format_entry_separator`. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log preamble. """ assert(isinstance(log, SlackLog)) if log.startsWithSeparator: return u'+--------------------------+\n' return u''
[docs] def format_log_postamble(self, log): """ Overrides :py:meth:`SlackLogFormatter.format_entry_separator`. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log postamble. """ assert(isinstance(log, SlackLog)) if log.endsWithSeparator: return u'+--------------------------+\n' return u''
[docs] def format_entry_separator(self, is_first, is_last): """ Overrides :py:meth:`SlackLogFormatter.format_entry_separator`. :param is_first: :py:class:`bool` -- :py:const:`True` if this is first entry, :py:const:`False` otherwise. :param is_last: :py:class:`bool` -- :py:const:`True` if this is last entry, :py:const:`False` otherwise. :return: :py:class:`unicode` -- Unicode representation of log entry separator. """ if not is_first: return u'+--------------------------+\n' return u''
[docs] def format_entry_preamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_preamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry preamble. """ assert(isinstance(entry, SlackLogEntry)) timestamp = entry.timestamp if entry.timezone is not None and not isinstance(entry.timezone, tz.tzutc): timestamp = timestamp.astimezone(entry.timezone) data = u'' # %a -- Weekday as locale’s abbreviated name. # %b -- Month as locale’s abbreviated name. # %d -- Day of the month as a zero-padded decimal number. # %H -- Hour (24-hour clock) as a zero-padded decimal number. # %I -- Hour (12-hour clock) as a zero-padded decimal number. # %M -- Minute as a zero-padded decimal number. # %S -- Second as a zero-padded decimal number. # %p -- Locale’s equivalent of either AM or PM. # %Y -- Year with century as a decimal number. # %Z -- Time zone name (empty string if the object is naive). if entry.twelveHourFormat: # This is the case in one entry in slackware{,64}-current ChangeLog.txt, # whose timestamp was: # Fri 01 Feb 2019 01:26:44 AM UTC data += timestamp.strftime("%a %d %b %Y %I:%M:%S %p %Z") else: # That glitch was corrected in the next entry, whose timestamp was: # Fri Feb 1 05:53:41 UTC 2019 data += timestamp.strftime("%a %b %d %H:%M:%S %Z %Y") # Remove leading zero from the day-of-month only data = re.sub(r' 0(\d) ', r' \1 ', data) data += u'\n' if entry.description: data += entry.description return data
[docs] def format_pkg_preamble(self, pkg): """ Overrides :py:meth:`SlackLogFormatter.format_pkg_preamble`. :param pkg: :any:`SlackLogPkg` -- in-memory representation of the log entry package :return: :py:class:`unicode` -- Unicode representation of log entry package preamble. """ assert(isinstance(pkg, SlackLogPkg)) return u'%s:%s' % (pkg.pkg, pkg.description)
[docs]class SlackLogRssFormatter (SlackLogFormatter): """ Concrete SlackLog formatter that generates an RSS feed. """ def __init__(self): super(SlackLogRssFormatter, self).__init__() self.slackware = None """:py:class:`unicode` description of the distro version. E.g. 'Slackware 13.37' or 'Slackware64 current'.""" self.rssLink = None """:py:class:`unicode`. Full URL of the RSS feed.""" self.webLink = None """:py:class:`unicode`. Full URL of the WWW version of the feed.""" self.description = None """:py:class:`unicode` description of the feed.""" self.language = None """:py:class:`unicode` language identifier. E.g. 'en'.""" self.managingEditor = None """:py:class:`unicode`. Email, and possibly name, of the feed manager. E.g. 'jane@doe.net (Jane Doe)'.""" self.webMaster = None """:py:class:`unicode`. Email, and possibly name, of the webmaster. E.g. 'john@doe.net (John Doe)'. """ self.lastBuildDate = None """:py:class:`datetime.datetime`. Timestamp when this feed was last generated. UTC assumed."""
[docs] def format_log_preamble(self, log): """ Overrides :py:meth:`SlackLogFormatter.format_log_preamble`. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log preamble. """ assert(isinstance(log, SlackLog)) data = u'<?xml version="1.0"?>\n' data += u'<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n' data += u' <channel>\n' data += u' <atom:link href="%s" rel="self" type="application/rss+xml" />\n' % self.rssLink data += u' <title>%s ChangeLog</title>\n' % self.slackware if self.webLink: data += u' <link>%s</link>\n' % self.webLink else: data += u' <link>%s</link>\n' % self.rssLink if self.description: data += u' <description>%s</description>\n' % self.description data += u' <docs>http://www.rssboard.org/rss-specification</docs>\n' data += u' <language>%s</language>\n' % self.language if self.managingEditor: data += u' <managingEditor>%s</managingEditor>\n' % self.managingEditor if self.webMaster: data += u' <webMaster>%s</webMaster>\n' % self.webMaster if len(log.entries) > 0: data += u' <pubDate>%s</pubDate>\n' % readable(log.entries[0].timestamp) elif self.lastBuildDate: data += u' <pubDate>%s</pubDate>\n' % readable(self.lastBuildDate) else: data += u' <pubDate>%s</pubDate>\n' % readable(datetime.datetime.utcnow()) if self.lastBuildDate: data += u' <lastBuildDate>%s</lastBuildDate>\n' % readable(self.lastBuildDate) else: data += u' <lastBuildDate>%s</lastBuildDate>\n' % readable(datetime.datetime.utcnow()) data += u' <generator>SlackLog</generator>\n' return data
[docs] def format_log_postamble(self, log): """ Overrides :py:meth:`SlackLogFormatter.format_log_postamble`. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log postamble. """ assert(isinstance(log, SlackLog)) return u' </channel>\n</rss>\n'
[docs] def format_entry_preamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_preamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry preamble. """ assert(isinstance(entry, SlackLogEntry)) data = u' <item>\n' if self.webLink: perma = u'true' link = u'%s#%s' % (self.webLink, anchor(entry.timestamp)) else: perma = u'false' link = u'%s-%s' % (self.slackware.replace(' ', '-'), anchor(entry.timestamp)) data += u' <guid isPermaLink="%s">%s</guid>\n' % (perma, link) data += u' <title>%s changes for %s</title>\n' % (self.slackware, readable(entry.timestamp)) data += u' <pubDate>%s</pubDate>\n' % readable(entry.timestamp) data += u' <description><![CDATA[<pre>' if entry.description: data += entry.description.replace('<', '&lt;') return data
[docs] def format_entry_postamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_postamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry postamble. """ assert(isinstance(entry, SlackLogEntry)) return u'</pre>]]></description>\n </item>\n'
[docs] def format_pkg_preamble(self, pkg): """ Overrides :py:meth:`SlackLogFormatter.format_pkg_preamble`. :param pkg: :any:`SlackLogPkg` -- in-memory representation of the log entry package :return: :py:class:`unicode` -- Unicode representation of log entry package preamble. """ assert(isinstance(pkg, SlackLogPkg)) return u'%s:%s' % (pkg.pkg, pkg.description.replace('<', '&lt;'))
[docs]class SlackLogAtomFormatter (SlackLogFormatter): """ Concrete SlackLog formatter that generates an Atom feed. """ def __init__(self): super(SlackLogAtomFormatter, self).__init__() self.slackware = None """:py:class:`unicode` description of the distro version. E.g. 'Slackware 13.37' or 'Slackware64 current'.""" self.link = None """:py:class:`unicode`. Full URL of the Atom feed.""" self.webLink = None """:py:class:`unicode`. Full URL of the HTML version.""" self.name = None """:py:class:`unicode`. Name of the feed author.""" self.email = None """:py:class:`unicode`. Email of the feed author.""" self.updated = None """:py:class:`datetime.datetime`. Timestamp when this feed was last generated. UTC assumed."""
[docs] def format_log_preamble(self, log): """ Overrides :py:meth:`SlackLogFormatter.format_log_preamble`. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log preamble. """ assert(isinstance(log, SlackLog)) data = u'<?xml version="1.0"?>\n' data += u'<feed xmlns="http://www.w3.org/2005/Atom">\n' data += u' <link href="%s" rel="self" type="application/rss+xml" />\n' % self.link data += u' <title>%s ChangeLog</title>\n' % self.slackware if self.webLink: data += u' <link href="%s" />\n' % self.webLink else: data += u' <link href="%s" />\n' % self.link if self.updated: data += u' <updated>%s</updated>\n' % self.updated.strftime("%Y-%m-%dT%H:%M:%SZ") else: data += u' <updated>%s</updated>\n' % datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") data += u' <author>\n' data += u' <name>%s</name>\n' % self.name data += u' <email>%s</email>\n' % self.email data += u' </author>\n' data += u' <id>%s</id>\n' % self.link return data
[docs] def format_log_postamble(self, log): """ Overrides :py:meth:`SlackLogFormatter.format_log_postamble`. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of log postamble. """ assert(isinstance(log, SlackLog)) return u'</feed>\n'
[docs] def format_entry_preamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_preamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry preamble. """ assert(isinstance(entry, SlackLogEntry)) data = u' <entry>\n' data += u' <title>%s changes for %s</title>\n' % (self.slackware, readable(entry.timestamp)) if self.webLink: data += u' <link href="%s#%s" />\n' % (self.webLink, anchor(entry.timestamp)) else: data += u' <link href="%s#%s" />\n' % (self.link, anchor(entry.timestamp)) data += u' <updated>%s</updated>\n' % entry.timestamp.strftime("%Y-%m-%dT%H:%M:%SZ") data += u' <id>%s#%s</id>\n' % (self.link, anchor(entry.timestamp)) data += u' <content type="html"><![CDATA[<pre>' return data
[docs] def format_entry_postamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_postamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry postamble. """ assert(isinstance(entry, SlackLogEntry)) return u'</pre>]]></content>\n </entry>\n'
[docs] def format_pkg_preamble(self, pkg): """ Overrides :py:meth:`SlackLogFormatter.format_pkg_preamble`. :param pkg: :any:`SlackLogPkg` -- in-memory representation of the log entry package :return: :py:class:`unicode` -- Unicode representation of log entry package preamble. """ assert(isinstance(pkg, SlackLogPkg)) return u'%s:%s' % (pkg.pkg, pkg.description.replace('<', '&lt;'))
[docs]class SlackLogPyblosxomFormatter (SlackLogFormatter): """ Concrete SlackLog formatter that generates Pyblosxom blog entries. """ def __init__(self): super(SlackLogPyblosxomFormatter, self).__init__() self.quiet = False """If :py:const:`True`, """ self.slackware = None """:py:class:`unicode` description of the distro version. E.g. 'Slackware 13.37' or 'Slackware64 current'.""" self.datadir = None """Blog entry directory.""" self.extension = 'txt' """Blog entry filename extension.""" self.encoding = 'utf-8' """Blog entry file encoding.""" self.tags_separator = ',' """Separator for tags.""" self.pkg_separator = ':' """Separator for packages.""" self.pyfilemtime = False """If :py:const:`True`, a pyfilemtime compatible filenames are generated.""" self.overwrite = False """If :py:const:`True`, already existing blog entries are overwritten.""" self.backup = True """If :py:const:`True`, already existing blog entries are copied to backups before overwriting.""" # Subclass can change these self.entry_preamble = u'<div class="slackLogEntry">\n' """:py:class:`unicode`. HTML to insert before the entry.""" self.entry_postamble = u'</div>\n' """:py:class:`unicode`. HTML to insert after the entry.""" self.entry_desc_preamble = u'<div class="slackLogEntryDesc">' """:py:class:`unicode`. HTML to insert before the entry description.""" self.entry_desc_postamble = u'</div>\n' """:py:class:`unicode`. HTML to insert after the entry description.""" self.entry_pkgs_preamble = u'<div class="slackLogEntryPkgs">\n' """:py:class:`unicode`. HTML to insert before the list of packages.""" self.entry_pkgs_postamble = U'</div>\n' """:py:class:`unicode`. HTML to insert after the list of packages.""" self.pkg_preamble = u'<div class="slackLogPkg">' """:py:class:`unicode`. HTML to insert before a package.""" self.pkg_postamble = u'</div>\n' """:py:class:`unicode`. HTML to insert after a package.""" self.pkg_name_preamble = u'<span class="slackLogPkgName">' """:py:class:`unicode`. HTML to insert before package name.""" self.pkg_name_postamble = u'</span>' """:py:class:`unicode`. HTML to insert after package name.""" self.pkg_desc_preamble = u'<span class="slackLogPkgDesc">' """:py:class:`unicode`. HTML to insert before package description.""" self.pkg_desc_postamble = u'</span>' """:py:class:`unicode`. HTML to insert after package description."""
[docs] def format_entry(self, entry, is_first, is_last): """ Overrides :py:meth:`SlackLogFormatter.format_entry`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :param is_first: :py:class:`bool` -- :py:const:`True` if this is first entry, :py:const:`False` otherwise. :param is_last: :py:class:`bool` -- :py:const:`True` if this is last entry, :py:const:`False` otherwise. :return: :py:class:`unicode` -- Unicode representation of log entry. """ assert(isinstance(entry, SlackLogEntry)) data = super(SlackLogPyblosxomFormatter, self).format_entry(entry, is_first, is_last) # generate filename for this entry filename = '%s%s%s' % (self.datadir, os.path.sep, self.format_entry_basename(entry)) if self.pyfilemtime: filename += entry.timestamp.strftime("-%Y-%m-%d-%H-%M") filename += '.%s' % self.extension filename = os.path.expanduser(filename) # Ensure that the directory exists try: os.makedirs(os.path.dirname(filename)) except: # Directory already exists pass # Handle the entries that already exist if os.path.exists(filename): if self.overwrite: if self.backup: # Make a backup i = 1 backup = "%s~%d~" % (filename, i) while os.path.exists(backup): backup = "%s~%d~" % (filename, i) i += 1 if not self.quiet: print("Backing up entry: %s" % backup) os.rename(filename, backup) else: print("Overwriting entry: %s" % filename) else: if not self.quiet: print("Entry already exists: %s" % filename) return data # Write the entry file = codecs.open(filename, 'w', self.encoding) file.write(data) file.close() # Set the mtime (for those who do not want to use the # pyfilemtime plugin) timestamp = time.mktime(entry.timestamp.timetuple()) os.utime(filename, (timestamp, timestamp)) return data
[docs] def format_entry_preamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_preamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry preamble. """ assert(isinstance(entry, SlackLogEntry)) data = self.format_entry_title(entry) if self.tags_separator: data += u'#tags %s\n' % self.format_entry_tags(entry) data += self.entry_preamble if entry.description: data += u'%s%s%s' % (self.entry_desc_preamble, entry.description, self.entry_desc_postamble) if entry.pkgs: data += self.entry_pkgs_preamble return data
[docs] def format_entry_postamble(self, entry): """ Overrides :py:meth:`SlackLogFormatter.format_entry_postamble`. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry postamble. """ assert(isinstance(entry, SlackLogEntry)) data = u'' if entry.pkgs: data += self.entry_pkgs_postamble data += self.entry_postamble return data
[docs] def format_pkg_preamble(self, pkg): """ Overrides :py:meth:`SlackLogFormatter.format_pkg_preamble`. :param pkg: :any:`SlackLogPkg` -- in-memory representation of the log entry package :return: :py:class:`unicode` -- Unicode representation of log entry package preamble. """ assert(isinstance(pkg, SlackLogPkg)) data = u'%s%s%s%s%s%s%s%s%s' % (self.pkg_preamble, self.pkg_name_preamble, pkg.pkg, self.pkg_name_postamble, self.pkg_separator, self.pkg_desc_preamble, pkg.description, self.pkg_desc_postamble, self.pkg_postamble) return data
[docs] def format_entry_basename(self, entry): """ Return basename for the log entry. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry name """ return self.slackware.replace(' ', '-').replace('.', '_').lower()
[docs] def format_entry_title(self, entry): """ Return log entry title. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry title """ return u'%s changes for %s\n' % (self.slackware, readable(entry.timestamp))
[docs] def format_entry_tags(self, entry): """ Return log entry tags. :param entry: :any:`SlackLogEntry` -- in-memory representation of the log entry. :return: :py:class:`unicode` -- Unicode representation of log entry tags """ return u'%s' % self.slackware.replace(' ', self.tags_separator)
[docs]class SlackLogJsonFormatter (SlackLogFormatter): """ Concrete SlackLog formatter that generates JSON dump. """ class SlackLogEncoder (JSONEncoder): """ JSON encoder that knows how to turn a SlackLog into a dict. """ def default(self, o): if isinstance(o, SlackLog): return {'startsWithSeparator': o.startsWithSeparator, 'endsWithSeparator': o.endsWithSeparator, 'entries': o.entries} if isinstance(o, SlackLogEntry): timezone = None if o.timezone is not None: timezone = o.timezone.tzname(o.timestamp) return {'checksum': o.checksum, 'identifier': o.identifier, 'parent': o.parent, 'timezone': timezone, 'timestamp': o.timestamp.strftime("%Y-%m-%dT%H:%M:%SZ"), 'description': o.description, 'pkgs': o.pkgs} if isinstance(o, SlackLogPkg): return {'pkg': o.pkg, 'description': o.description} # Let the base class default method raise the TypeError return JSONEncoder.default(self, o) def __init__(self): super(SlackLogJsonFormatter, self).__init__() self.indent = None """If not :py:const:`None`, must be an :py:class:`int` representing how many spaces to indent the array elements and object keys."""
[docs] def format(self, log): """ Return unicode representation of the in-memory representation of the log. :param log: :any:`SlackLog` -- in-memory representation of the log. :return: :py:class:`unicode` -- Unicode representation of the log. """ assert(isinstance(log, SlackLog)) if self.indent is None: separators = (',', ':') else: separators = (',', ': ') return dumps(log, ensure_ascii=False, allow_nan=False, sort_keys=True, indent=self.indent, separators=separators, cls=self.SlackLogEncoder)