示例#1
0
    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"])
示例#2
0
    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"])
示例#3
0
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()
示例#4
0
    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
示例#5
0
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']
示例#6
0
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()
示例#7
0
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']
示例#8
0
    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
示例#9
0
文件: blahfeed.py 项目: akx/blahfeed
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)
示例#10
0
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
示例#11
0
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)
示例#12
0
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']
示例#13
0
    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
示例#14
0
    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()
示例#15
0
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
示例#16
0
    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()
示例#17
0
    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()
示例#18
0
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() == []
示例#19
0
    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()
示例#20
0
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']
示例#21
0
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() == []
示例#22
0
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']
示例#23
0
    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()
示例#24
0
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])))