def get(self): fg = FeedGenerator() fg.id("http://test.ts") fg.title("My Test Feed") fg.icon("https://avatars1.githubusercontent.com/u/715660?v=3&s=32") fg.author({'name': "The Author", 'email': "*****@*****.**"}) fg.link(href="http://example.org/index.atom?page=2", rel="next") fg.link(href="http://test.ts", rel="alternate") fg.logo("https://avatars1.githubusercontent.com/u/715660?v=3&s=32") fg.description("Este é o monstro do lago 1") fg.subtitle("This is an example feed!") fg.language("en-us") # Handle this: #< sy:updatePeriod > hourly < / sy:updatePeriod > #< sy:updateFrequency > 1 < / sy:updateFrequency > fg.lastBuildDate(datetime.now(pytz.timezone("America/Sao_Paulo"))) fi = fg.add_item() fi.id("http://test.ts/id/1", ) #fi.link(link="http://test.ts/id/1") fi.title("Monstro do Lago 1") fi.description("Este é o monstro do lago 1") fi.comments("http://test.ts/id/1/comments") fi.pubdate(datetime.now(pytz.timezone("America/Sao_Paulo"))) fi = fg.add_item() fi.id("http://test.ts/id/2") fi.title("Monstro do Lago 2") fi.description("Este é o monstro do lago 2") fi.pubdate(datetime.now(pytz.timezone("America/Sao_Paulo"))) #test = fg.atom_str(pretty=True) rss_str = fg.rss_str(pretty=True) self.set_header("Content-Type", 'application/xml; charset="utf-8"') #self.set_header("Content-Disposition", # "attachment; filename='test.xml'") self.write(rss_str) #if regexp.search(word) is not None: # print # 'matched' if self.is_browser_mobile(): print("buu") else: print(self.request.headers["User-Agent"])
class TestExtensionDc(unittest.TestCase): def setUp(self): self.fg = FeedGenerator() self.fg.load_extension('dc') self.fg.title('title') self.fg.link(href='http://example.com', rel='self') self.fg.description('description') def test_entryLoadExtension(self): fe = self.fg.add_item() try: fe.load_extension('dc') except ImportError: pass # Extension already loaded def test_elements(self): for method in dir(self.fg.dc): if method.startswith('dc_'): m = getattr(self.fg.dc, method) m(method) assert m() == [method] self.fg.id('123') assert self.fg.atom_str() assert self.fg.rss_str()
def setUp(self): fg = FeedGenerator() self.feedId = 'http://example.com' self.title = 'Some Testfeed' fg.id(self.feedId) fg.title(self.title) fg.link(href='http://lkiesow.de', rel='alternate')[0] fg.description('...') fe = fg.add_entry() fe.id('http://lernfunk.de/media/654321/1') fe.title('The First Episode') fe.content(u'…') # Use also the different name add_item fe = fg.add_item() fe.id('http://lernfunk.de/media/654321/1') fe.title('The Second Episode') fe.content(u'…') fe = fg.add_entry() fe.id('http://lernfunk.de/media/654321/1') fe.title('The Third Episode') fe.content(u'…') self.fg = fg
class TestExtensionTorrent(unittest.TestCase): def setUp(self): self.fg = FeedGenerator() self.fg.load_extension('torrent') self.fg.title('title') self.fg.link(href='http://example.com', rel='self') self.fg.description('description') def test_podcastEntryItems(self): fe = self.fg.add_item() fe.title('y') fe.torrent.filename('file.xy') fe.torrent.infohash('123') fe.torrent.contentlength('23') fe.torrent.seeds('1') fe.torrent.peers('2') fe.torrent.verified('1') assert fe.torrent.filename() == 'file.xy' assert fe.torrent.infohash() == '123' assert fe.torrent.contentlength() == '23' assert fe.torrent.seeds() == '1' assert fe.torrent.peers() == '2' assert fe.torrent.verified() == '1' # Check that we have the item in the resulting XML ns = {'torrent': 'http://xmlns.ezrss.it/0.1/dtd/'} root = etree.fromstring(self.fg.rss_str()) filename = root.xpath('/rss/channel/item/torrent:filename/text()', namespaces=ns) assert filename == ['file.xy']
async def generate_hybrid_feed(feed_urls: List[str], max_age: int = 3600) -> bytes: digest = hashlib.sha256("\n".join(sorted(feed_urls)).encode()).hexdigest() async with aiohttp.ClientSession() as sess: feeds = await asyncio.gather( *[parse_feed(sess, url, max_age=max_age) for url in feed_urls]) sorted_entries = sorted( chain(*(f["entries"] for f in feeds)), key=lambda item: item.get("updated_parsed") or item.get( "published_parsed"), reverse=True, ) fg = FeedGenerator() fg.title("Hybrid Feed") fg.id(f"feed-{digest}") for entry in sorted_entries: item = fg.add_item() item.title(entry["title"]) item.guid(entry["guid"]) # TODO: ensure uniqueness across feeds? item.link(href=entry["link"]) item.published(entry.get("published")) item.updated(entry.get("updated")) # TODO: add e.g. category, tags, image, ...? return fg.atom_str(pretty=True)
def create_feed(title, stories): feed = FeedGenerator() feed.id('https://bostonglobe.com/today') feed.title(title) feed.link(href='https://bostonglobe.com') feed.description('Today\'s Boston Globe') for story in reversed(stories): item = feed.add_item() item.id(story['url']) item.title(story['title']) item.link(href=story['url']) downloaded = web.get_article(story['url']) if downloaded['metadata']: item.author(author={ 'name': downloaded['metadata'].get('author', 'BostonGlobe.com')}) item.summary(summary=downloaded['metadata'].get('description')) return feed
def generate_atom(page_id): """ Generate atom feed. return it as str page_id can be both in string form (like 'darcor' in https://vk.com/darcor) and in number form (like 'id210700286' in https://vk.com/id210700286). """ log.debug('generate atom for %s', page_id) #vk.com discriminates its output depending on user-agent resp = requests_get(SITE + page_id, headers={'user-agent': 'Mozilla/5.0 (Linux x86_64)', 'Accept-Charset': 'utf-8', 'Accept-Language': 'ru'}) posts = parse_posts(resp.text) if not posts: log.warning('no posts - parsing failed or empty page') return '' _fg = FeedGenerator() _fg.id('https://vk.com/' + str(page_id)) _fg.title(str(page_id)) _fg.language('ru') for post in posts: _fe = _fg.add_item() _fe.id(post['id']) post['title'] = ' ' if post['is_repost'] and post['orig_post']['text_content']: post['title'] = get_first_sentence(post['orig_post']['text_content']) elif post['text_content']: post['title'] = get_first_sentence(post['text_content']) _fe.title(post['title'] or ' ') _fe.link(href='https://vk.com/' + post['href'], rel='alternate') _fe.updated(datetime.fromtimestamp(post['ts'], tz=timezone.utc)) author = None if post['is_repost'] and post['orig_post']['text_content']: _fe.description((post['text_content'] or '') + '\n\n' + post['orig_post']['text_content']) author = post['orig_post']['author'] else: _fe.description(post['text_content']) author = post['author'] _fe.author(name='=?UTF-8?B?' + b64encode(author.encode()).decode() + '=?=') if 'image_url' in post.keys(): _fe.enclosure(url=post['image_url'], type='image/jpeg') return _fg.atom_str(pretty=True)
class TestExtensionGeo(unittest.TestCase): def setUp(self): self.fg = FeedGenerator() self.fg.load_extension('geo') self.fg.title('title') self.fg.link(href='http://example.com', rel='self') self.fg.description('description') def test_geoEntryItems(self): fe = self.fg.add_item() fe.title('y') fe.geo.point('42.36 -71.05') assert fe.geo.point() == '42.36 -71.05' # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) point = root.xpath('/rss/channel/item/georss:point/text()', namespaces=ns) assert point == ['42.36 -71.05']
def setUp(self): fg = FeedGenerator() self.feedId = 'http://example.com' self.title = 'Some Testfeed' fg.id(self.feedId) fg.title(self.title) fe = fg.add_entry() fe.id('http://lernfunk.de/media/654321/1') fe.title('The First Episode') # Use also the different name add_item fe = fg.add_item() fe.id('http://lernfunk.de/media/654321/1') fe.title('The Second Episode') fe = fg.add_entry() fe.id('http://lernfunk.de/media/654321/1') fe.title('The Third Episode') self.fg = fg
def render(data, system): request = system.get('request') if request is not None: response = request.response ct = response.content_type if ct == response.default_content_type: response.content_type = 'application/rss+xml' if 'updates' in data: key = 'updates' elif 'users' in data: key = 'users' elif 'comments' in data: key = 'comments' elif 'overrides' in data: key = 'overrides' else: raise HTTPNotFound("RSS not implemented for this service") feed = FeedGenerator() feed.title(key) feed.link(href=request.url, rel='self') feed.description(key) feed.language(u'en') def linker(route, param, key): def link_dict(obj): return dict(href=request.route_url(route, **{param: obj[key]})) return link_dict getters = { 'updates': { 'title': operator.itemgetter('title'), 'link': linker('update', 'id', 'title'), 'description': operator.itemgetter('notes'), 'pubdate': lambda obj: utc.localize(obj['date_submitted']), }, 'users': { 'title': operator.itemgetter('name'), 'link': linker('user', 'name', 'name'), 'description': operator.itemgetter('name'), }, 'comments': { 'title': operator.itemgetter('text'), 'link': linker('comment', 'id', 'id'), 'description': operator.itemgetter('text'), 'pubdate': lambda obj: utc.localize(obj['timestamp']), }, 'overrides': { 'title': operator.itemgetter('nvr'), 'link': linker('override', 'nvr', 'nvr'), 'description': operator.itemgetter('notes'), 'pubdate': lambda obj: utc.localize(obj['submission_date']), }, } for value in data[key]: feed_item = feed.add_item() for name, getter in getters[key].items(): # Because we have to use methods to fill feed entry attributes, # it's done by getting methods by name and calling them # on the same line getattr(feed_item, name)(getter(value)) return feed.rss_str()
def get_feed(): # required ENV variable: LETTERBOXD_USER letterboxd_user = os.environ['LETTERBOXD_USER'] feedlen = os.environ.get('FEED_LENGTH', 100) watchlist_url = f'https://letterboxd.com/{letterboxd_user}/watchlist/' output_file = 'feed.xml' base_url = 'https://letterboxd.com/' page_title = 'Watchlist' feed = FeedGenerator() feed.title(page_title) feed.id(watchlist_url) feed.link(href=watchlist_url, rel='alternate') feed.description(page_title + ' from Letterboxd') s = session() page = 0 total_movies = 0 while True: page += 1 r = s.get(watchlist_url + '/page/%i/' % page) soup = BeautifulSoup(r.text, 'html.parser') if page == 1: watchlist_title = soup.find('meta', attrs={'property': 'og:title'}) if watchlist_title is not None: page_title = watchlist_title.attrs['content'] posters = soup.findAll('div', attrs={'class', 'poster'}) if len(posters) == 0: print('No more movies on page %i' % (page, )) break elif total_movies + len(posters) > feedlen: posters = posters[:feedlen - total_movies] print('Adding %i movies from page %i' % (len(posters), page)) total_movies += len(posters) for movie in posters: movie_page = s.get(base_url + movie.attrs['data-film-slug']) movie_soup = BeautifulSoup(movie_page.text, 'html.parser') try: movie_title = movie_soup.find('meta', attrs={ 'property': 'og:title' }).attrs['content'] movie_link = movie_soup.find('a', attrs={ 'href': match_imdb }).attrs['href'] movie_link = movie_link[:-11] except AttributeError: print('Parsing failed') movie_description = movie_soup.find('div', attrs={'class': 'truncate'}) movie_description = movie_description.text.strip() item = feed.add_item() item.title(movie_title) item.description(movie_description) item.link(href=movie_link, rel='alternate') item.guid(movie_link) print(movie_title) if total_movies > 0: return feed
def render(data, system): """ Render the given data as an RSS view. If the request's content type is set to the default, this function will change it to application/rss+xml. Args: data (dict): A dictionary describing the information to be rendered. The information can be different types of objects, such as updates, users, comments, or overrides. system (pyramid.events.BeforeRender): Used to get the current request. Returns: str: An RSS document representing the given data. """ request = system.get('request') if request is not None: response = request.response ct = response.content_type if ct == response.default_content_type: response.content_type = 'application/rss+xml' if 'updates' in data: key = 'updates' feed_title = 'Released updates' elif 'users' in data: key = 'users' feed_title = 'Bodhi users' elif 'comments' in data: key = 'comments' feed_title = 'User comments' elif 'overrides' in data: key = 'overrides' feed_title = 'Update overrides' else: # This is a request we don't know how to render. Let's return BadRequest and log. log.debug('Unable to render RSS feed for data: %s', data) # See if we have a request so we can set a code without raising an Exception if request is not None: response.status = HTTPBadRequest.code return 'Invalid RSS feed request' else: raise HTTPBadRequest('Invalid RSS feed request') feed_description_list = [] for k in request.GET.keys(): feed_description_list.append('%s(%s)' % (k, request.GET[k])) if feed_description_list: feed_description = 'Filtered on: ' + ', '.join( feed_description_list) else: feed_description = "All %s" % (key) feed = FeedGenerator() feed.title(feed_title) feed.link(href=request.url, rel='self') feed.description(feed_description) feed.language('en') def linker(route, param, key): def link_dict(obj): return dict(href=request.route_url(route, **{param: obj[key]})) return link_dict def describe_update(alias, notes, builds): """ Wrap calls to operator.itemgetter to retrieve notes and builds list. Methods are used to fill feed entry values, so we must use a wrapper to get an HTML formatted description from the `notes` and the `builds` properties of the update. For example: getter = describe_update(operator.itemgetter('notes'),operator.itemgetter('builds')) description_value = getter(update_data) Args: alias (operator.itemgetter): A callable object which returns update alias as string. notes (operator.itemgetter): A callable object which returns update notes as string. builds (operator.itemgetter): A callable object which returns a list of builds associated to the update. Returns: function: A function which accepts a dict representing an update as parameter. """ def describe(*args, **kwargs): text = f'# {alias(*args, **kwargs)}\n' text += f'## Packages in this update:\n' for p in builds(*args, **kwargs): text += f'* {p.nvr}\n' text += f'## Update description:\n{notes(*args, **kwargs)}' return markup(None, text) return describe getters = { 'updates': { 'title': operator.itemgetter('title'), 'link': linker('update', 'id', 'alias'), 'description': describe_update(operator.itemgetter('alias'), operator.itemgetter('notes'), operator.itemgetter('builds')), 'pubDate': lambda obj: utc.localize(obj['date_submitted']), }, 'users': { 'title': operator.itemgetter('name'), 'link': linker('user', 'name', 'name'), 'description': operator.itemgetter('name'), }, 'comments': { 'title': operator.itemgetter('rss_title'), 'link': linker('comment', 'id', 'id'), 'description': operator.itemgetter('text'), 'pubDate': lambda obj: utc.localize(obj['timestamp']), }, 'overrides': { 'title': operator.itemgetter('nvr'), 'link': linker('override', 'nvr', 'nvr'), 'description': operator.itemgetter('notes'), 'pubDate': lambda obj: utc.localize(obj['submission_date']), }, } for value in reversed(data[key]): feed_item = feed.add_item() for name, getter in getters[key].items(): # Because we have to use methods to fill feed entry attributes, # it's done by getting methods by name and calling them # on the same line. getattr(feed_item, name)(getter(value)) return feed.rss_str()
def render(data, system): """ Render the given data as an RSS view. If the request's content type is set to the default, this function will change it to application/rss+xml. Args: data (dict): A dictionary describing the information to be rendered. The information can be different types of objects, such as updates, users, comments, or overrides. system (pyramid.events.BeforeRender): Used to get the current request. Returns: str: An RSS document representing the given data. """ request = system.get('request') if request is not None: response = request.response ct = response.content_type if ct == response.default_content_type: response.content_type = 'application/rss+xml' if 'updates' in data: key = 'updates' feed_title = 'Released updates' elif 'users' in data: key = 'users' feed_title = 'Bodhi users' elif 'comments' in data: key = 'comments' feed_title = 'User comments' elif 'overrides' in data: key = 'overrides' feed_title = 'Update overrides' else: # This is a request we don't know how to render. Let's return BadRequest and log. log.debug('Unable to render RSS feed for data: %s', data) # See if we have a request so we can set a code without raising an Exception if request is not None: response.status = HTTPBadRequest.code return 'Invalid RSS feed request' else: raise HTTPBadRequest('Invalid RSS feed request') feed_description_list = [] for k in request.GET.keys(): feed_description_list.append('%s(%s)' % (k, request.GET[k])) if feed_description_list: feed_description = 'Filtered on: ' + ', '.join( feed_description_list) else: feed_description = "All %s" % (key) feed = FeedGenerator() feed.title(feed_title) feed.link(href=request.url, rel='self') feed.description(feed_description) feed.language('en') def linker(route, param, key): def link_dict(obj): return dict(href=request.route_url(route, **{param: obj[key]})) return link_dict getters = { 'updates': { 'title': operator.itemgetter('alias'), 'link': linker('update', 'id', 'alias'), 'description': operator.itemgetter('notes'), 'pubdate': lambda obj: utc.localize(obj['date_submitted']), }, 'users': { 'title': operator.itemgetter('name'), 'link': linker('user', 'name', 'name'), 'description': operator.itemgetter('name'), }, 'comments': { 'title': operator.itemgetter('rss_title'), 'link': linker('comment', 'id', 'id'), 'description': operator.itemgetter('text'), 'pubdate': lambda obj: utc.localize(obj['timestamp']), }, 'overrides': { 'title': operator.itemgetter('nvr'), 'link': linker('override', 'nvr', 'nvr'), 'description': operator.itemgetter('notes'), 'pubdate': lambda obj: utc.localize(obj['submission_date']), }, } for value in data[key]: feed_item = feed.add_item() for name, getter in getters[key].items(): # Because we have to use methods to fill feed entry attributes, # it's done by getting methods by name and calling them # on the same line. getattr(feed_item, name)(getter(value)) return feed.rss_str()
class TestExtensionMedia(unittest.TestCase): def setUp(self): self.fg = FeedGenerator() self.fg.load_extension('media') self.fg.id('id') self.fg.title('title') self.fg.link(href='http://example.com', rel='self') self.fg.description('description') def test_media_content(self): fe = self.fg.add_item() fe.id('id') fe.title('title') fe.content('content') fe.media.content(url='file1.xy') fe.media.content(url='file2.xy') fe.media.content(url='file1.xy', group=2) fe.media.content(url='file2.xy', group=2) fe.media.content(url='file.xy', group=None) ns = {'media': 'http://search.yahoo.com/mrss/', 'a': 'http://www.w3.org/2005/Atom'} # Check that we have the item in the resulting RSS root = etree.fromstring(self.fg.rss_str()) url = root.xpath('/rss/channel/item/media:group/media:content[1]/@url', namespaces=ns) assert url == ['file1.xy', 'file1.xy'] # There is one without a group url = root.xpath('/rss/channel/item/media:content[1]/@url', namespaces=ns) assert url == ['file.xy'] # Check that we have the item in the resulting Atom feed root = etree.fromstring(self.fg.atom_str()) url = root.xpath('/a:feed/a:entry/media:group/media:content[1]/@url', namespaces=ns) assert url == ['file1.xy', 'file1.xy'] fe.media.content(content=[], replace=True) assert fe.media.content() == [] def test_media_thumbnail(self): fe = self.fg.add_item() fe.id('id') fe.title('title') fe.content('content') fe.media.thumbnail(url='file1.xy') fe.media.thumbnail(url='file2.xy') fe.media.thumbnail(url='file1.xy', group=2) fe.media.thumbnail(url='file2.xy', group=2) fe.media.thumbnail(url='file.xy', group=None) ns = {'media': 'http://search.yahoo.com/mrss/', 'a': 'http://www.w3.org/2005/Atom'} # Check that we have the item in the resulting RSS root = etree.fromstring(self.fg.rss_str()) url = root.xpath( '/rss/channel/item/media:group/media:thumbnail[1]/@url', namespaces=ns) assert url == ['file1.xy', 'file1.xy'] # There is one without a group url = root.xpath('/rss/channel/item/media:thumbnail[1]/@url', namespaces=ns) assert url == ['file.xy'] # Check that we have the item in the resulting Atom feed root = etree.fromstring(self.fg.atom_str()) url = root.xpath('/a:feed/a:entry/media:group/media:thumbnail[1]/@url', namespaces=ns) assert url == ['file1.xy', 'file1.xy'] fe.media.thumbnail(thumbnail=[], replace=True) assert fe.media.thumbnail() == []
if not (repos_arg := request.args.get('repos')): return api_usage() try: # https://github.com/lkiesow/python-feedgen#create-a-feed fg = FeedGenerator() # ValueError: Required fields not set (title, link, description) fg.title('GitHub multi-repo releases tracker') fg.link(href='https://github.com/VergeDX/rss_filter_server') fg.description('Tracker more repo\'s release in one rss link! ' 'Written by HyDEV, thanks for using. ') # repos arg list to set, filtered same items. for repo_str in set(repos_arg.split(', ')): if 'message' not in (r_json := requests.get(REPOS_API + repo_str + LATEST).json()): # https://github.com/lkiesow/python-feedgen#add-feed-entries fe = fg.add_item() fe.title('[%s] %s' % (repo_str, r_json['name'])) fe.link(href=r_json['html_url']) return Response(fg.rss_str(), mimetype='text/xml') except Exception as e: return Response('Error: ' + e.__str__()) if __name__ == '__main__': app.run()
class TestExtensionPodcast(unittest.TestCase): def setUp(self): self.fg = FeedGenerator() self.fg.load_extension('podcast') self.fg.title('title') self.fg.link(href='http://example.com', rel='self') self.fg.description('description') def test_category_new(self): self.fg.podcast.itunes_category([{'cat': 'Technology', 'sub': 'Podcasting'}]) self.fg.podcast.itunes_explicit('no') self.fg.podcast.itunes_complete('no') self.fg.podcast.itunes_new_feed_url('http://example.com/new-feed.rss') self.fg.podcast.itunes_owner('John Doe', '*****@*****.**') ns = {'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'} root = etree.fromstring(self.fg.rss_str()) cat = root.xpath('/rss/channel/itunes:category/@text', namespaces=ns) scat = root.xpath('/rss/channel/itunes:category/itunes:category/@text', namespaces=ns) assert cat[0] == 'Technology' assert scat[0] == 'Podcasting' def test_category(self): self.fg.podcast.itunes_category('Technology', 'Podcasting') self.fg.podcast.itunes_explicit('no') self.fg.podcast.itunes_complete('no') self.fg.podcast.itunes_new_feed_url('http://example.com/new-feed.rss') self.fg.podcast.itunes_owner('John Doe', '*****@*****.**') ns = {'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'} root = etree.fromstring(self.fg.rss_str()) cat = root.xpath('/rss/channel/itunes:category/@text', namespaces=ns) scat = root.xpath('/rss/channel/itunes:category/itunes:category/@text', namespaces=ns) assert cat[0] == 'Technology' assert scat[0] == 'Podcasting' def test_podcastItems(self): fg = self.fg fg.podcast.itunes_author('Lars Kiesow') fg.podcast.itunes_block('x') fg.podcast.itunes_complete(False) fg.podcast.itunes_explicit('no') fg.podcast.itunes_image('x.png') fg.podcast.itunes_subtitle('x') fg.podcast.itunes_summary('x') assert fg.podcast.itunes_author() == 'Lars Kiesow' assert fg.podcast.itunes_block() == 'x' assert fg.podcast.itunes_complete() == 'no' assert fg.podcast.itunes_explicit() == 'no' assert fg.podcast.itunes_image() == 'x.png' assert fg.podcast.itunes_subtitle() == 'x' assert fg.podcast.itunes_summary() == 'x' # Check that we have the item in the resulting XML ns = {'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'} root = etree.fromstring(self.fg.rss_str()) author = root.xpath('/rss/channel/itunes:author/text()', namespaces=ns) assert author == ['Lars Kiesow'] def test_podcastEntryItems(self): fe = self.fg.add_item() fe.title('y') fe.podcast.itunes_author('Lars Kiesow') fe.podcast.itunes_block('x') fe.podcast.itunes_duration('00:01:30') fe.podcast.itunes_explicit('no') fe.podcast.itunes_image('x.png') fe.podcast.itunes_is_closed_captioned('yes') fe.podcast.itunes_order(1) fe.podcast.itunes_subtitle('x') fe.podcast.itunes_summary('x') assert fe.podcast.itunes_author() == 'Lars Kiesow' assert fe.podcast.itunes_block() == 'x' assert fe.podcast.itunes_duration() == '00:01:30' assert fe.podcast.itunes_explicit() == 'no' assert fe.podcast.itunes_image() == 'x.png' assert fe.podcast.itunes_is_closed_captioned() assert fe.podcast.itunes_order() == 1 assert fe.podcast.itunes_subtitle() == 'x' assert fe.podcast.itunes_summary() == 'x' # Check that we have the item in the resulting XML ns = {'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'} root = etree.fromstring(self.fg.rss_str()) author = root.xpath('/rss/channel/item/itunes:author/text()', namespaces=ns) assert author == ['Lars Kiesow']
def render(data, system): """ Render the given data as an RSS view. If the request's content type is set to the default, this function will change it to application/rss+xml. Args: data (dict): A dictionary describing the information to be rendered. The information can be different types of objects, such as updates, users, comments, or overrides. system (pyramid.events.BeforeRender): Used to get the current request. Returns: basestring: An RSS document representing the given data. """ request = system.get('request') if request is not None: response = request.response ct = response.content_type if ct == response.default_content_type: response.content_type = 'application/rss+xml' if 'updates' in data: key = 'updates' elif 'users' in data: key = 'users' elif 'comments' in data: key = 'comments' elif 'overrides' in data: key = 'overrides' feed = FeedGenerator() feed.title(key) feed.link(href=request.url, rel='self') feed.description(key) feed.language(u'en') def linker(route, param, key): def link_dict(obj): return dict(href=request.route_url(route, **{param: obj[key]})) return link_dict getters = { 'updates': { 'title': operator.itemgetter('title'), 'link': linker('update', 'id', 'title'), 'description': operator.itemgetter('notes'), 'pubdate': lambda obj: utc.localize(obj['date_submitted']), }, 'users': { 'title': operator.itemgetter('name'), 'link': linker('user', 'name', 'name'), 'description': operator.itemgetter('name'), }, 'comments': { 'title': operator.itemgetter('text'), 'link': linker('comment', 'id', 'id'), 'description': operator.itemgetter('text'), 'pubdate': lambda obj: utc.localize(obj['timestamp']), }, 'overrides': { 'title': operator.itemgetter('nvr'), 'link': linker('override', 'nvr', 'nvr'), 'description': operator.itemgetter('notes'), 'pubdate': lambda obj: utc.localize(obj['submission_date']), }, } for value in data[key]: feed_item = feed.add_item() for name, getter in getters[key].items(): # Because we have to use methods to fill feed entry attributes, # it's done by getting methods by name and calling them # on the same line getattr(feed_item, name)(getter(value)) return feed.rss_str()
class TestExtensionGeo(unittest.TestCase): @classmethod def setUpClass(cls): cls.point = Geom('Point', [-71.05, 42.36]) cls.line = Geom('LineString', [[-71.05, 42.36], [-71.15, 42.46]]) cls.polygon = Geom( 'Polygon', [[[-71.05, 42.36], [-71.15, 42.46], [-71.15, 42.36]]]) cls.box = Geom('Box', [[-71.05, 42.36], [-71.15, 42.46]]) cls.polygon_with_interior = Geom( 'Polygon', [ [ # exterior [0, 0], [0, 1], [1, 1], [1, 0], [0, 0] ], [ # interior [0.25, 0.25], [0.25, 0.75], [0.75, 0.75], [0.75, 0.25], [0.25, 0.25] ] ]) def setUp(self): self.fg = FeedGenerator() self.fg.load_extension('geo') self.fg.title('title') self.fg.link(href='http://example.com', rel='self') self.fg.description('description') def test_point(self): fe = self.fg.add_item() fe.title('y') fe.geo.point(str(self.point)) self.assertEqual(fe.geo.point(), str(self.point)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) point = root.xpath('/rss/channel/item/georss:point/text()', namespaces=ns) self.assertEqual(point, [str(self.point)]) def test_line(self): fe = self.fg.add_item() fe.title('y') fe.geo.line(str(self.line)) self.assertEqual(fe.geo.line(), str(self.line)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) line = root.xpath('/rss/channel/item/georss:line/text()', namespaces=ns) self.assertEqual(line, [str(self.line)]) def test_polygon(self): fe = self.fg.add_item() fe.title('y') fe.geo.polygon(str(self.polygon)) self.assertEqual(fe.geo.polygon(), str(self.polygon)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) poly = root.xpath('/rss/channel/item/georss:polygon/text()', namespaces=ns) self.assertEqual(poly, [str(self.polygon)]) def test_box(self): fe = self.fg.add_item() fe.title('y') fe.geo.box(str(self.box)) self.assertEqual(fe.geo.box(), str(self.box)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) box = root.xpath('/rss/channel/item/georss:box/text()', namespaces=ns) self.assertEqual(box, [str(self.box)]) def test_featuretypetag(self): fe = self.fg.add_item() fe.title('y') fe.geo.featuretypetag('city') self.assertEqual(fe.geo.featuretypetag(), 'city') # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) featuretypetag = root.xpath( '/rss/channel/item/georss:featuretypetag/text()', namespaces=ns) self.assertEqual(featuretypetag, ['city']) def test_relationshiptag(self): fe = self.fg.add_item() fe.title('y') fe.geo.relationshiptag('is-centred-at') self.assertEqual(fe.geo.relationshiptag(), 'is-centred-at') # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) relationshiptag = root.xpath( '/rss/channel/item/georss:relationshiptag/text()', namespaces=ns) self.assertEqual(relationshiptag, ['is-centred-at']) def test_featurename(self): fe = self.fg.add_item() fe.title('y') fe.geo.featurename('Footscray') self.assertEqual(fe.geo.featurename(), 'Footscray') # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) featurename = root.xpath('/rss/channel/item/georss:featurename/text()', namespaces=ns) self.assertEqual(featurename, ['Footscray']) def test_elev(self): fe = self.fg.add_item() fe.title('y') fe.geo.elev(100.3) self.assertEqual(fe.geo.elev(), 100.3) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) elev = root.xpath('/rss/channel/item/georss:elev/text()', namespaces=ns) self.assertEqual(elev, ['100.3']) def test_elev_fails_nonnumeric(self): fe = self.fg.add_item() fe.title('y') with self.assertRaises(ValueError): fe.geo.elev('100.3') def test_floor(self): fe = self.fg.add_item() fe.title('y') fe.geo.floor(4) self.assertEqual(fe.geo.floor(), 4) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) floor = root.xpath('/rss/channel/item/georss:floor/text()', namespaces=ns) self.assertEqual(floor, ['4']) def test_floor_fails_nonint(self): fe = self.fg.add_item() fe.title('y') with self.assertRaises(ValueError): fe.geo.floor(100.3) with self.assertRaises(ValueError): fe.geo.floor('4') def test_radius(self): fe = self.fg.add_item() fe.title('y') fe.geo.radius(100.3) self.assertEqual(fe.geo.radius(), 100.3) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) radius = root.xpath('/rss/channel/item/georss:radius/text()', namespaces=ns) self.assertEqual(radius, ['100.3']) def test_radius_fails_nonnumeric(self): fe = self.fg.add_item() fe.title('y') with self.assertRaises(ValueError): fe.geo.radius('100.3') def test_geom_from_geointerface_point(self): fe = self.fg.add_item() fe.title('y') fe.geo.geom_from_geo_interface(self.point) self.assertEqual(fe.geo.point(), str(self.point)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) point = root.xpath('/rss/channel/item/georss:point/text()', namespaces=ns) self.assertEqual(point, [str(self.point)]) coords = [float(c) for c in point[0].split()] try: self.assertCountEqual(coords, self.point.coords) except AttributeError: # was assertItemsEqual in Python 2.7 self.assertItemsEqual(coords, self.point.coords) def test_geom_from_geointerface_line(self): fe = self.fg.add_item() fe.title('y') fe.geo.geom_from_geo_interface(self.line) self.assertEqual(fe.geo.line(), str(self.line)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) line = root.xpath('/rss/channel/item/georss:line/text()', namespaces=ns) self.assertEqual(line, [str(self.line)]) coords = [float(c) for c in line[0].split()] try: self.assertCountEqual(coords, list(chain.from_iterable(self.line.coords))) except AttributeError: # was assertItemsEqual in Python 2.7 self.assertItemsEqual(coords, list(chain.from_iterable(self.line.coords))) def test_geom_from_geointerface_poly(self): fe = self.fg.add_item() fe.title('y') fe.geo.geom_from_geo_interface(self.polygon) self.assertEqual(fe.geo.polygon(), str(self.polygon)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) poly = root.xpath('/rss/channel/item/georss:polygon/text()', namespaces=ns) self.assertEqual(poly, [str(self.polygon)]) coords = [float(c) for c in poly[0].split()] try: self.assertCountEqual( coords, list(chain.from_iterable(self.polygon.coords[0]))) except AttributeError: # was assertItemsEqual in Python 2.7 self.assertItemsEqual( coords, list(chain.from_iterable(self.polygon.coords[0]))) def test_geom_from_geointerface_fail_other_geom(self): fe = self.fg.add_item() fe.title('y') with self.assertRaises(GeoRSSGeometryError): fe.geo.geom_from_geo_interface(self.box) def test_geom_from_geointerface_fail_requires_geo_interface(self): fe = self.fg.add_item() fe.title('y') with self.assertRaises(AttributeError): fe.geo.geom_from_geo_interface(str(self.box)) def test_geom_from_geointerface_warn_poly_interior(self): """ Test complex polygons warn as expected. Taken from https://stackoverflow.com/a/3892301/379566 and https://docs.python.org/2.7/library/warnings.html#testing-warnings """ fe = self.fg.add_item() fe.title('y') with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") # Trigger a warning. fe.geo.geom_from_geo_interface(self.polygon_with_interior) # Verify some things assert len(w) == 1 assert issubclass(w[-1].category, GeoRSSPolygonInteriorWarning) self.assertEqual(fe.geo.polygon(), str(self.polygon_with_interior)) # Check that we have the item in the resulting XML ns = {'georss': 'http://www.georss.org/georss'} root = etree.fromstring(self.fg.rss_str()) poly = root.xpath('/rss/channel/item/georss:polygon/text()', namespaces=ns) self.assertEqual(poly, [str(self.polygon_with_interior)]) coords = [float(c) for c in poly[0].split()] try: self.assertCountEqual( coords, list(chain.from_iterable( self.polygon_with_interior.coords[0]))) except AttributeError: # was assertItemsEqual in Python 2.7 self.assertItemsEqual( coords, list(chain.from_iterable( self.polygon_with_interior.coords[0])))