feedgenerator: atom feed finished (not entries)

This commit is contained in:
Lars Kiesow 2013-04-21 14:34:52 +02:00
parent 27ac219be8
commit dfc91b66d0

View file

@ -10,7 +10,7 @@
''' '''
from lxml import etree from lxml import etree
from email.Utils import formatdate from datetime import datetime
class FeedGenerator: class FeedGenerator:
@ -18,30 +18,29 @@ class FeedGenerator:
## ATOM ## ATOM
# http://www.atomenabled.org/developers/syndication/ # http://www.atomenabled.org/developers/syndication/
# required # required
__atom_id = '' __atom_id = None
__atom_title = '' __atom_title = None
__atom_updated = '' __atom_updated = datetime.utcnow().isoformat('T')+'Z'
# recommended # recommended
__atom_author = '' # {name*, uri, email} __atom_author = None # {name*, uri, email}
__atom_link = '' __atom_link = None # {href*, rel, type, hreflang, title, length}
'''
# optional # optional
category = '' __atom_category = None # {term*, schema, label}
contributor = '' __atom_contributor = None
generator = '' __atom_generator = {'value':'Lernfunk3 FeedGenerator'} #{value*,uri,version}
icon = '' __atom_icon = None
logo = '' __atom_logo = None
rights = '' __atom_rights = None
subtitle = '' __atom_subtitle = None
## RSS ## RSS
title __rss_title = None
link __rss_link = None
description __rss_description = None
'''
category category
cloud cloud
copyright copyright
@ -64,8 +63,24 @@ class FeedGenerator:
# rss -> channel # rss -> channel
''' '''
def __init__(self):
self.__atom_updated = formatdate() def __ensure_format(self, val, allowed, required, allowed_values={}):
if not val:
return None
# Make shure that we have a list of dicts. Even if there is only one.
if not isinstance(val, list):
val = [val]
for elem in val:
if not isinstance(elem, dict):
raise ValueError('Invalid data (value is no dictionary)')
if not set(elem.keys()) <= allowed:
raise ValueError('Data contains invalid keys')
if not set(elem.keys()) >= required:
raise ValueError('Data contains not all required keys')
for k,v in allowed_values.iteritems():
if elem.get(k) and not elem[k] in v:
raise ValueError('Invalid value for %s' % k )
return val
def atom_str(self): def atom_str(self):
@ -81,34 +96,78 @@ class FeedGenerator:
updated.text = self.__atom_updated updated.text = self.__atom_updated
# Add author elements # Add author elements
if self.__atom_author: for a in self.__atom_author or []:
for a in self.__atom_author: # Atom requires a name. Skip elements without.
# Atom requires a name. Skip elements without. if not a.get('name'):
if not a.get('name'): continue
continue author = etree.SubElement(feed, 'author')
author = etree.SubElement(feed, 'author') name = etree.SubElement(author, 'name')
name = etree.SubElement(author, 'name') name.text = a.get('name')
name.text = a.get('name') if a.get('email'):
if a.get('email'): email = etree.SubElement(author, 'email')
email = etree.SubElement(author, 'email') email.text = a.get('email')
email.text = a.get('email') if a.get('uri'):
if a.get('uri'): email = etree.SubElement(author, 'url')
email = etree.SubElement(author, 'url') email.text = a.get('uri')
email.text = a.get('uri')
if self.__atom_link: for l in self.__atom_link or []:
for l in self.__atom_link: link = etree.SubElement(feed, 'link', href=l['href'])
link = etree.SubElement(feed, 'link', href=l['href']) if l.get('rel'):
if l.get('rel'): link.attrib['rel'] = l['rel']
link.attrib['rel'] = l['rel'] if l.get('type'):
if l.get('type'): link.attrib['type'] = l['type']
link.attrib['type'] = l['type'] if l.get('hreflang'):
if l.get('hreflang'): link.attrib['hreflang'] = l['hreflang']
link.attrib['hreflang'] = l['hreflang'] if l.get('title'):
if l.get('title'): link.attrib['title'] = l['title']
link.attrib['title'] = l['title'] if l.get('length'):
if l.get('length'): link.attrib['length'] = l['length']
link.attrib['length'] = l['length']
for c in self.__atom_category or []:
cat = etree.SubElement(feed, 'category', term=c['term'])
if c.get('schema'):
cat.attrib['schema'] = c['schema']
if c.get('label'):
cat.attrib['label'] = c['label']
# Add author elements
for c in self.__atom_contributor or []:
# Atom requires a name. Skip elements without.
if not c.get('name'):
continue
contrib = etree.SubElement(feed, 'contributor')
name = etree.SubElement(contrib, 'name')
name.text = c.get('name')
if c.get('email'):
email = etree.SubElement(contrib, 'email')
email.text = c.get('email')
if c.get('uri'):
email = etree.SubElement(contrib, 'url')
email.text = c.get('uri')
if self.__atom_generator:
generator = etree.SubElement(feed, 'generator')
generator.text = self.__atom_generator['value']
if self.__atom_generator.get('uri'):
generator.attrib['uri'] = self.__atom_generator['uri']
if self.__atom_generator.get('version'):
generator.attrib['version'] = self.__atom_generator['version']
if self.__atom_icon:
icon = etree.SubElement(feed, 'icon')
icon.text = self.__atom_icon
if self.__atom_logo:
logo = etree.SubElement(feed, 'logo')
logo.text = self.__atom_logo
if self.__atom_rights:
rights = etree.SubElement(feed, 'rights')
rights.text = self.__atom_rights
if self.__atom_subtitle:
subtitle = etree.SubElement(feed, 'subtitle')
subtitle.text = self.__atom_subtitle
return etree.tostring(feed, pretty_print=True) return etree.tostring(feed, pretty_print=True)
''' '''
@ -129,7 +188,7 @@ class FeedGenerator:
return self.__atom_id return self.__atom_id
def author(self, author=None, replace=False): def author(self, author=None, replace=False, **kwargs):
'''Get or set autor data. An author element is a dict containing a name, '''Get or set autor data. An author element is a dict containing a name,
an email adress and a uri. Name is mandatory for ATOM, email is mandatory an email adress and a uri. Name is mandatory for ATOM, email is mandatory
for RSS. for RSS.
@ -139,26 +198,28 @@ class FeedGenerator:
Example:: Example::
>>> author( { 'name'='John Doe', 'email'='jdoe@example.com' } ) >>> author( { 'name':'John Doe', 'email':'jdoe@example.com' } )
[{'name'='John Doe','email'='jdoe@example.com'}] [{'name':'John Doe','email':'jdoe@example.com'}]
>>> author([{'name'='John Doe'},{'name'='Max'}]) >>> author([{'name':'Mr. X'},{'name':'Max'}])
[{'name'='John Doe'},{'name'='Max'}] [{'name':'John Doe','email':'jdoe@example.com'},
{'name':'John Doe'}, {'name':'Max'}]
>>> author( name='John Doe', email='jdoe@example.com', replace=True )
[{'name':'John Doe','email':'jdoe@example.com'}]
''' '''
if author is None and kwargs:
author = kwargs
if not author is None: if not author is None:
if not isinstance(author, list): if replace or self.__atom_author is None:
author = [ author ] self.__atom_author = []
for a in author: self.__atom_author += self.__ensure_format( author,
if not isinstance(a, dict): set(['name', 'email', 'uri']), set(['name']))
raise ValueError('Invalid author data (author is no dict)')
if not set(a.keys()) <= set(['name', 'email', 'uri']):
raise ValueError('Invalid author data')
self.__atom_author = author
return self.__atom_author return self.__atom_author
def link(self, link=None, replace=False): def link(self, link=None, replace=False, **kwargs):
'''Get or set link data. An link element is a dict with the fields href, '''Get or set link data. An link element is a dict with the fields href,
rel, type, hreflang, title, and length. Href is mandatory for ATOM. rel, type, hreflang, title, and length. Href is mandatory for ATOM.
@ -170,23 +231,76 @@ class FeedGenerator:
link(...) link(...)
''' '''
if link is None and kwargs:
link = kwargs
if not link is None: if not link is None:
if not isinstance(link, list): if replace or self.__atom_link is None:
link = [ link ] self.__atom_link = []
for l in link: self.__atom_link += self.__ensure_format( link,
if not isinstance(l, dict): set(['href', 'rel', 'type', 'hreflang', 'title', 'length']),
raise ValueError('Invalid link data (link is no dict)') set(['href']),
if not set(l.keys()) <= set(['href', 'rel', 'type', 'hreflang', 'title', 'length']): {'rel':['alternate', 'enclosure', 'related', 'self', 'via']} )
raise ValueError('Invalid link data')
if not set(l.keys()) >= set(['href']):
raise ValueError('Invalid link data (no href)')
if l.get('rel') and l['rel'] not in \
['alternate', 'enclosure', 'related', 'self', 'via']:
raise ValueError('Invalid rel type')
self.__atom_link = link
return self.__atom_link return self.__atom_link
def category(self, category=None, replace=False, **kwargs):
if category is None and kwargs:
category = kwargs
if not category is None:
if replace or self.__atom_category is None:
self.__atom_category = []
self.__atom_category += self.__ensure_format(
category,
set(['term', 'schema', 'label']),
set(['term']) )
return self.__atom_category
def contributor(self, contributor=None, replace=False, **kwargs):
if contributor is None and kwargs:
contributor = kwargs
if not contributor is None:
if replace or self.__atom_contributor is None:
self.__atom_contributor = []
self.__atom_contributor += self.__ensure_format( contributor,
set(['name', 'email', 'uri']), set(['name']))
return self.__atom_contributor
def generator(self, generator=None, version=None, uri=None):
if not generator is None:
self.__atom_generator = {'value':generator}
if not version in None:
self.__atom_generator['version'] = version
if not uri in None:
self.__atom_generator['uri'] = uri
return self.__atom_generator
def icon(self, icon=None):
if not icon is None:
self.__atom_icon = icon
return self.__atom_icon
def logo(self, logo=None):
if not logo is None:
self.__atom_logo = logo
return self.__atom_logo
def rights(self, rights=None):
if not rights is None:
self.__atom_rights = rights
return self.__atom_rights
def subtitle(self, subtitle=None):
if not subtitle is None:
self.__atom_subtitle = subtitle
return self.__atom_subtitle
class FeedEntry: class FeedEntry:
''' '''
@ -229,8 +343,16 @@ class FeedEntry:
if __name__ == '__main__': if __name__ == '__main__':
fg = FeedGenerator() fg = FeedGenerator()
fg.id('123') fg.id('http://lernfunk.de/_MEDIAID_123')
fg.title('Testfeed') fg.title('Testfeed')
fg.author( {'name':'Lars Kiesow','email':'lkiesow@uos.de'} ) fg.author( {'name':'Lars Kiesow','email':'lkiesow@uos.de'} )
fg.link( {'href':'http://example.com', 'rel':'alternate'} ) fg.link( href='http://example.com', rel='alternate' )
fg.category(term='test')
fg.contributor( name='Lars Kiesow', email='lkiesow@uos.de' )
fg.contributor( name='John Doe', email='jdoe@example.com' )
fg.icon('http://ex.com/icon.jpg')
fg.logo('http://ex.com/logo.jpg')
fg.rights('cc-by')
fg.subtitle('This is a cool feed!')
fg.link( href='http://larskiesow.de/test.atom', rel='self' )
print fg.atom_str() print fg.atom_str()