Package turbofeeds :: Module controllers
[hide private]

Source Code for Module turbofeeds.controllers

  1  # -*- coding: UTF-8 -*- 
  2  """TurboFeed controllers for RSS/Atom feeds handling.""" 
  3  __docformat__ = 'restructuredtext' 
  4   
  5  __all__ = [ 
  6      'FeedController', 
  7  ] 
  8   
  9  __version__ = '1.1' 
 10   
 11  import logging 
 12   
 13  import turbogears 
 14   
 15  from util import xml_stylesheet, absolute_url 
 16   
 17   
 18  log = logging.getLogger('turbofeeds.controllers') 
 19   
 20   
21 -class FeedController(turbogears.controllers.Controller):
22 """Controller for generating feeds in multiple formats. 23 24 Must be subclassed and a ``get_feed_data`` method provided that returns 25 a dict with the feed info (see constructor doc strings) and an ``entries`` 26 member, which should be a list filled with dicts each representing a feed 27 entry (see docstring for the default implementation). 28 """ 29 30 formats = ["atom1.0", "atom0.3", "rss2.0"] 31 32 # Constructor / Intialisation
33 - def __init__(self, default="atom1.0", base_url='/feed', **feed_params):
34 """Constructor - Should be called with ``super()`` if overwritten. 35 36 The ``default`` arguments sets the default feed format when the 37 feed base URL is requested. Currently supported values are 38 ``atom1_0`` (the default), ``atom0_3`` and ''rss1_0``. 39 40 The ``base_url`` sets the URL where the feed controller is mounted 41 to the CherryPy object tree. The default is '/feed'. This is used 42 to construct the full feed URL for the ``link`` element in the feed, 43 if it is not overwritten by the ``link`` keyword argument. 44 45 Any extra keyword arguments will be assigned to the ``feed_params`` 46 attribute and added to the feed data everytime a feed is requested. 47 This can be used to set the feed info in the direct child elements 48 of the ``feed`` (Atom) resp. ``channel`` root element of the feed. 49 Possible names include: 50 51 * author (dict with ``name``, ``email`` and ``uri`` members) 52 * categories (list of strings, used by Atom 1.0 / RSS only) 53 * generator (string) 54 * updated (datetime) 55 * icon (URL, used by Atom 1.0 format only) 56 * id (string/URL, used by Atom formats only) 57 * logo (URL, used by Atom 1.0 / RSS format only) 58 * lang (string, used by RSS format only) 59 * link (URL) 60 * rights (string) 61 * subtitle (string) 62 * stylesheet (URL or dict with members ``href`` and ``type`` or a 63 calable returning either, used place a matching xml-stylesheet 64 processing instruction at the top of the feed XML) 65 * title (string) 66 67 For up-to-date information about supported elements and values, please 68 refer to the templates for the different feed formats in the 69 ``templates`` sub-package. 70 """ 71 72 super(FeedController, self).__init__() 73 assert default in self.formats 74 self.default = default 75 self.base_url = base_url 76 self.feed_params = feed_params
77 78 # Exposed methods 79 @turbogears.expose()
80 - def index(self, *args, **kwargs):
81 """Redirects to the default feed format rendering method.""" 82 83 turbogears.redirect(turbogears.url("%s" % self.default), kwargs)
84 85 @turbogears.expose(template="turbofeeds.templates.atom0_3", 86 format="xml", content_type="application/atom+xml")
87 - def atom0_3(self, *args, **kwargs):
88 """Renders Atom 0.3 XML feed.""" 89 90 feed = self.init_feed() 91 feed.update(self.get_feed_data(*args, **kwargs)) 92 self.format_dates(feed, 3339) 93 feed.setdefault('link', self.get_feed_url("atom0_3", *args, **kwargs)) 94 log.debug(feed) 95 return feed
96 97 @turbogears.expose(template="turbofeeds.templates.atom1_0", 98 format="xml", content_type="application/atom+xml")
99 - def atom1_0(self, *args, **kwargs):
100 """Renders Atom 1.0 XML feed.""" 101 102 feed = self.init_feed() 103 feed.update(self.get_feed_data(*args, **kwargs)) 104 self.format_dates(feed, 3339) 105 feed.setdefault('link', self.get_feed_url("atom1_0", *args, **kwargs)) 106 log.debug(feed) 107 return feed
108 109 @turbogears.expose(template="turbofeeds.templates.rss2_0", 110 format="xml", content_type="application/rss+xml")
111 - def rss2_0(self, *args, **kwargs):
112 """Renders RSS 2.0 XML feed.""" 113 114 feed = self.init_feed() 115 feed.update(self.get_feed_data(*args, **kwargs)) 116 self.format_dates(feed, 822) 117 feed.setdefault('link', self.get_feed_url("rss2_0", *args, **kwargs)) 118 log.debug(feed) 119 return feed
120 121 # Helper methods
122 - def date_to_3339(self, date):
123 """Converts datatime object to RFC 3339 string representation.""" 124 125 date = date.strftime("%Y-%m-%dT%H:%M:%SZ") 126 return date
127
128 - def date_to_822(self, date):
129 """Converts datatime object to RFC 822 string representation.""" 130 131 date = date.strftime("%a, %d %b %Y %H:%M:%S GMT") 132 return date
133
134 - def format_dates(self, feed, format):
135 """Converts datetime objects in the feed data into given format.""" 136 137 if format == 822: 138 convert_date = self.date_to_822 139 else: 140 convert_date = self.date_to_3339 141 for field in ('published', 'updated'): 142 if field in feed: 143 feed[field] = convert_date(feed[field]) 144 145 for entry in feed['entries']: 146 for field in ('published', 'updated'): 147 if field in entry: 148 entry[field] = convert_date(entry[field]) 149 return feed
150
151 - def get_feed_data(self, *args, **kwargs):
152 """Returns dict with feed info and a list of feed entries. 153 154 This method must be overwritten by a subclass. 155 156 It should return a dictionary with members for the feed info (see the 157 constructor doc string) and a member ``entries``, which is a list with 158 dict items for each feed entry. Supported members in each dict include: 159 160 * author (dict with ``name``, ``email`` and ``uri`` members) 161 * categories (list of strings, used by Atom 1.0 / RSS only) 162 * content (string or dict with ``type`` and ``value`` members, 163 Atom formats only) 164 * updated (datetime, Atom only) 165 * id (string/URL, Atom only) 166 * link (URL) 167 * published (datetime) 168 * rights (string, Atom 1.0 only) 169 * summary (string) 170 * title (string) 171 172 For up-to-date information about supported elements and values, please 173 refer to the templates for the different feed formats in the 174 ``templates`` sub-package. 175 """ 176 177 raise NotImplementedError
178
179 - def get_feed_url(self, version='atom1_0', *args, **kwargs):
180 """Returns absolute URL (including server name) for the feed.""" 181 182 if not self.base_url.endswith('/'): 183 base_url = self.base_url + '/' 184 else: 185 base_url = self.base_url + '/' 186 if args: 187 suffix = '/' + '/'.join([str(a) for a in args]) 188 else: 189 suffix = '' 190 feed_url = absolute_url('%s%s%s' % 191 (base_url, version, suffix), **kwargs) 192 return feed_url
193
194 - def init_feed(self):
195 """Initializes feed data with the kwargs given to the constructor.""" 196 197 feed = { 198 'xml_stylesheet': xml_stylesheet, 199 'generator': 'TurboFeeds FeedController version %s' % __version__ 200 } 201 feed.update(self.feed_params) 202 return feed
203