diff options
Diffstat (limited to 'feeds.py')
-rw-r--r-- | feeds.py | 188 |
1 files changed, 148 insertions, 40 deletions
@@ -1,40 +1,69 @@ -import datetime +from datetime import datetime, time +from pytz import utc +from django.conf import settings +from django.contrib.sites.models import Site from django.contrib.syndication.views import Feed +from django.db import connection from django.db.models import Q -from django.utils.hashcompat import md5_constructor +from django.utils.feedgenerator import Rss201rev2Feed from django.views.decorators.http import condition from main.models import Arch, Repo, Package from news.models import News +from releng.models import Release + + +class BatchWritesWrapper(object): + def __init__(self, outfile): + self.outfile = outfile + self.buf = [] + + def write(self, s): + buf = self.buf + buf.append(s) + if len(buf) >= 40: + self.outfile.write(''.join(buf)) + self.buf = [] + + def flush(self): + self.outfile.write(''.join(self.buf)) + self.outfile.flush() + + +class FasterRssFeed(Rss201rev2Feed): + def write(self, outfile, encoding): + ''' + Batch the underlying 'write' calls on the outfile because Python's + default saxutils XmlGenerator is a POS that insists on unbuffered + write/flush calls. This sucks when it is making 1-byte calls to write + '>' closing tags and over 1600 write calls in our package feed. + ''' + wrapper = BatchWritesWrapper(outfile) + super(FasterRssFeed, self).write(wrapper, encoding) + wrapper.flush() -def package_etag(request, *args, **kwargs): - latest = Package.objects.latest('last_update') - if latest: - return md5_constructor( - str(kwargs) + str(latest.last_update)).hexdigest() - return None def package_last_modified(request, *args, **kwargs): - # we could break this down based on the request url, but it would probably - # cost us more in query time to do so. - latest = Package.objects.latest('last_update') - if latest: - return latest.last_update - return None + cursor = connection.cursor() + cursor.execute("SELECT MAX(last_update) FROM packages") + return cursor.fetchone()[0] + class PackageFeed(Feed): + feed_type = FasterRssFeed + link = '/packages/' - title_template = 'feeds/packages_title.html' - description_template = 'feeds/packages_description.html' def __call__(self, request, *args, **kwargs): - wrapper = condition(etag_func=package_etag, last_modified_func=package_last_modified) + wrapper = condition(last_modified_func=package_last_modified) return wrapper(super(PackageFeed, self).__call__)(request, *args, **kwargs) + __name__ = 'package_feed' + def get_object(self, request, arch='', repo=''): obj = dict() - qs = Package.objects.select_related('arch', 'repo').order_by('-last_update') + qs = Package.objects.normal().order_by('-last_update') if arch != '': # feed for a single arch, also include 'any' packages everywhere @@ -43,22 +72,26 @@ class PackageFeed(Feed): obj['arch'] = a if repo != '': # feed for a single arch AND repo - r = Repo.objects.get(name=repo) + r = Repo.objects.get(name__iexact=repo) qs = qs.filter(repo=r) obj['repo'] = r + else: + qs = qs.filter(repo__staging=False) obj['qs'] = qs[:50] return obj def title(self, obj): - s = 'Arch Linux: Recent package updates' - if 'repo' in obj: + s = settings.BRANDING_DISTRONAME+': Recent package updates' + if 'repo' in obj and 'arch' in obj: s += ' (%s [%s])' % (obj['arch'].name, obj['repo'].name.lower()) + elif 'repo' in obj: + s += ' [%s]' % (obj['repo'].name.lower()) elif 'arch' in obj: s += ' (%s)' % (obj['arch'].name) return s def description(self, obj): - s = 'Recently updated packages in the Arch Linux package repositories' + s = 'Recently updated packages in the '+settings.BRANDING_DISTRONAME+' package repositories' if 'arch' in obj: s += ' for the \'%s\' architecture' % obj['arch'].name.lower() if not obj['arch'].agnostic: @@ -68,47 +101,122 @@ class PackageFeed(Feed): s += '.' return s + subtitle = description + def items(self, obj): return obj['qs'] + item_guid_is_permalink = False + + def item_guid(self, item): + # http://diveintomark.org/archives/2004/05/28/howto-atom-id + date = item.last_update + return 'tag:%s,%s:%s%s' % (Site.objects.get_current().domain, + date.strftime('%Y-%m-%d'), item.get_absolute_url(), + date.strftime('%Y%m%d%H%M')) + def item_pubdate(self, item): return item.last_update + def item_title(self, item): + return '%s %s %s' % (item.pkgname, item.full_version, item.arch.name) + + def item_description(self, item): + return item.pkgdesc + def item_categories(self, item): return (item.repo.name, item.arch.name) -def news_etag(request, *args, **kwargs): - latest = News.objects.latest('last_modified') - if latest: - return md5_constructor(str(latest.last_modified)).hexdigest() - return None +def news_last_modified(request, *args, **kwargs): + cursor = connection.cursor() + cursor.execute("SELECT MAX(last_modified) FROM news") + return cursor.fetchone()[0] -def news_last_modified(request): - latest = News.objects.latest('last_modified') - if latest: - return latest.last_modified - return None class NewsFeed(Feed): - title = 'Arch Linux: Recent news updates' + feed_type = FasterRssFeed + + title = settings.BRANDING_DISTRONAME+': Recent news updates' link = '/news/' - description = 'The latest and greatest news from the Arch Linux distribution.' - title_template = 'feeds/news_title.html' - description_template = 'feeds/news_description.html' + description = 'The latest and greatest news from the '+settings.BRANDING_DISTRONAME+' distribution.' + subtitle = description def __call__(self, request, *args, **kwargs): - wrapper = condition(etag_func=news_etag, last_modified_func=news_last_modified) + wrapper = condition(last_modified_func=news_last_modified) return wrapper(super(NewsFeed, self).__call__)(request, *args, **kwargs) + __name__ = 'news_feed' + def items(self): - return News.objects.select_related('author').order_by('-postdate', '-id')[:10] + return News.objects.select_related('author').order_by( + '-postdate', '-id')[:10] + + item_guid_is_permalink = False + + def item_guid(self, item): + return item.guid def item_pubdate(self, item): - d = item.postdate - return datetime.datetime(d.year, d.month, d.day) + return item.postdate + + def item_updateddate(self, item): + return item.last_modified def item_author_name(self, item): return item.author.get_full_name() + def item_title(self, item): + return item.title + + def item_description(self, item): + return item.html() + + +class ReleaseFeed(Feed): + feed_type = FasterRssFeed + + title = settings.BRANDING_DISTRONAME+': Releases' + link = '/download/' + description = 'Release ISOs' + subtitle = description + + __name__ = 'release_feed' + + def items(self): + return Release.objects.filter(available=True)[:10] + + def item_title(self, item): + return item.version + + def item_description(self, item): + return item.info_html() + + def item_pubdate(self, item): + return datetime.combine(item.release_date, time()).replace(tzinfo=utc) + + def item_updateddate(self, item): + return item.last_modified + + item_guid_is_permalink = False + + def item_guid(self, item): + # http://diveintomark.org/archives/2004/05/28/howto-atom-id + date = item.release_date + return 'tag:%s,%s:%s' % (Site.objects.get_current().domain, + date.strftime('%Y-%m-%d'), item.get_absolute_url()) + + def item_enclosure_url(self, item): + domain = Site.objects.get_current().domain + proto = 'https' + return "%s://%s/%s.torrent" % (proto, domain, item.iso_url()) + + def item_enclosure_length(self, item): + if item.torrent_data: + torrent = item.torrent() + return torrent['file_length'] or "" + return "" + + item_enclosure_mime_type = 'application/x-bittorrent' + # vim: set ts=4 sw=4 et: |