feedgenerator: atom feed finished (not entries)
This commit is contained in:
parent
27ac219be8
commit
dfc91b66d0
1 changed files with 198 additions and 76 deletions
228
feedgenerator.py
228
feedgenerator.py
|
@ -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,8 +96,7 @@ 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
|
||||||
|
@ -96,8 +110,7 @@ class FeedGenerator:
|
||||||
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']
|
||||||
|
@ -110,6 +123,52 @@ class FeedGenerator:
|
||||||
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)
|
||||||
'''
|
'''
|
||||||
outFile = open('homemade.xml', 'w')
|
outFile = open('homemade.xml', 'w')
|
||||||
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue