From 39a548fd2629f3b6383990264b2e331b3aea99fb Mon Sep 17 00:00:00 2001 From: eliott Date: Sat, 3 Nov 2007 03:45:10 -0400 Subject: Initial import for public release... Special Note Prior to git import, approx 90% of the code was done by Judd Vinet. Thanks Judd! --- packages/__init__.py | 0 packages/models.py | 91 +++++++++++++++++ packages/templatetags/__init__.py | 0 packages/templatetags/package_extras.py | 28 ++++++ packages/views.py | 172 ++++++++++++++++++++++++++++++++ 5 files changed, 291 insertions(+) create mode 100644 packages/__init__.py create mode 100644 packages/models.py create mode 100644 packages/templatetags/__init__.py create mode 100644 packages/templatetags/package_extras.py create mode 100644 packages/views.py (limited to 'packages') diff --git a/packages/__init__.py b/packages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/models.py b/packages/models.py new file mode 100644 index 0000000..c1b6c84 --- /dev/null +++ b/packages/models.py @@ -0,0 +1,91 @@ +from django.db import models +from django.contrib.auth.models import User +import re + +class PackageManager(models.Manager): + def get_flag_stats(self): + results = [] + # first the orphans + unflagged = self.filter(maintainer=0).count() + flagged = self.filter(maintainer=0).filter(needupdate=True).count() + results.append((User(id=0,first_name='Orphans'), unflagged, flagged)) + # now the rest + for maint in User.objects.all().order_by('first_name'): + unflagged = self.filter(maintainer=maint.id).count() + flagged = self.filter(maintainer=maint.id).filter(needupdate=True).count() + results.append((maint, unflagged, flagged)) + return results + +class Category(models.Model): + id = models.AutoField(primary_key=True) + category = models.CharField(maxlength=255) + class Meta: + db_table = 'categories' + verbose_name_plural = 'categories' + +class Repo(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(maxlength=255) + class Meta: + db_table = 'repos' + def last_update(self): + try: + latest = Package.objects.filter(repo__name__exact=self.name).order_by('-last_update')[0] + return latest.last_update + except IndexError: + return "N/A" + +class Package(models.Model): + id = models.AutoField(primary_key=True) + repo = models.ForeignKey(Repo) + maintainer = models.ForeignKey(User) + category = models.ForeignKey(Category) + needupdate = models.BooleanField(default=False) + pkgname = models.CharField(maxlength=255) + pkgver = models.CharField(maxlength=255) + pkgrel = models.CharField(maxlength=255) + pkgdesc = models.CharField(maxlength=255) + url = models.URLField() + sources = models.TextField() + depends = models.TextField() + last_update = models.DateTimeField(null=True, blank=True) + objects = PackageManager() + class Meta: + db_table = 'packages' + get_latest_by = 'last_update' + + def get_absolute_url(self): + return '/packages/%i/' % self.id + + def depends_urlize(self): + urls = '' + for dep in self.depends.split(' '): + # shave off any version qualifiers + nameonly = re.match(r"([a-z0-9-]+)", dep).group(1) + try: + p = Package.objects.filter(pkgname=nameonly)[0] + except IndexError: + # couldn't find a package in the DB -- it might be a virtual depend + urls = urls + '
  • ' + dep + '
  • ' + continue + url = '
  • ' + dep + '
  • ' + urls = urls + url + return urls + + def sources_urlize(self): + urls = '' + for source in self.sources.split(' '): + if re.search('://', source): + url = '
  • ' + source + '
  • ' + else: + url = '
  • ' + source + '
  • ' + urls = urls + url + return urls + +class PackageFile(models.Model): + id = models.AutoField(primary_key=True) + pkg = models.ForeignKey(Package) + path = models.CharField(maxlength=255) + class Meta: + db_table = 'packages_files' + diff --git a/packages/templatetags/__init__.py b/packages/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py new file mode 100644 index 0000000..8b55221 --- /dev/null +++ b/packages/templatetags/package_extras.py @@ -0,0 +1,28 @@ +from django import template + +register = template.Library() + +class BuildQueryStringNode(template.Node): + def __init__(self, sortfield): + self.sortfield = sortfield + def render(self, context): + qs = context['querystring'].copy() + if qs.has_key('sort') and qs['sort'] == self.sortfield: + qs['sort'] = '-' + self.sortfield + else: + qs['sort'] = self.sortfield + return '?' + qs.urlencode() + +@register.tag(name='buildsortqs') +def do_buildsortqs(parser, token): + try: + tagname, sortfield = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires a single argument" % tagname + if not (sortfield[0] == sortfield[-1] and sortfield[0] in ('"', "'")): + raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tagname + return BuildQueryStringNode(sortfield[1:-1]) + +@register.filter(name='space2br') +def space2br(value): + return value.replace(' ', '
    ') diff --git a/packages/views.py b/packages/views.py new file mode 100644 index 0000000..f9cecf1 --- /dev/null +++ b/packages/views.py @@ -0,0 +1,172 @@ +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import get_object_or_404 +from django.core.mail import send_mail +from django.template import Context, loader +from django.core import validators +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User +from archlinux.utils import validate, render_template +from datetime import datetime +from archlinux.packages.models import Package, PackageFile, Repo, Category + +def update(request): + if request.POST.has_key('adopt'): + mode = 'adopt' + message = 'Adoption was successful' + if request.POST.has_key('disown'): + mode = 'disown' + message = 'Disown was successful' + try: + maint = User.objects.get(username=request.user.username) + except User.DoesNotExist: + return render_template('error_page.html', request, {'errmsg':'No maintainer record found! Are you a maintainer?'}) + ids = request.POST.getlist('pkgid') + for id in ids: + pkg = Package.objects.get(id=id) + if mode == 'adopt' and pkg.maintainer_id == 0: + pkg.maintainer = maint + elif mode == 'disown' and pkg.maintainer == maint: + pkg.maintainer_id = 0 + else: + message = "You are not the current maintainer" + pkg.save() + return render_template('status_page.html', request, {'message':message}) + +def details(request, pkgid=0, name='', repo=''): + if pkgid == 0: + p = Package.objects.filter(pkgname=name) + if repo: p = p.filter(repo__name__exact=repo) + # if more then one result, send to the search view + if len(p) > 1: return search(request, name) + if len(p) < 1: return render_template('error_page.html', request, + {'errmsg': 'No matching packages.'}) + pkgid = p[0].id + + pkg = get_object_or_404(Package, id=pkgid) + return render_template('packages/details.html', request, {'pkg':pkg}) + +def search(request, query=''): + if request.GET.has_key('q'): + # take the q GET var over the one passed on the URL + query = request.GET['q'].strip() + + # fetch the form vars + repo = request.GET.get('repo', 'all') + category = request.GET.get('category', 'all') + lastupdate = request.GET.get('lastupdate', '') + limit = int(request.GET.get('limit', '50')) + skip = int(request.GET.get('skip', '0')) + sort = request.GET.get('sort', '') + maint = request.GET.get('maint', 'all') + + # build the form lists + repos = Repo.objects.order_by('name') + cats = Category.objects.order_by('category') + # copy GET data over and add the lists + c = request.GET.copy() + c['repos'], c['categories'] = repos, cats + c['limit'], c['skip'] = limit, skip + c['lastupdate'] = lastupdate + c['sort'] = sort + # 'q' gets renamed to 'query', so it's not in GET + c['query'] = query + + # validate + errors = {} + validate(errors, 'Last Update', lastupdate, validators.isValidANSIDate, True, request) + validate(errors, 'Page Limit', str(limit), validators.isOnlyDigits, True, request) + validate(errors, 'Page Skip', str(skip), validators.isOnlyDigits, True, request) + if errors: + c['errors'] = errors + return render_template('packages/search.html', request, c) + + if query: + res1 = Package.objects.filter(pkgname__icontains=query) + res2 = Package.objects.filter(pkgdesc__icontains=query) + results = res1 | res2 + else: + results = Package.objects.all() + if repo != 'all': results = results.filter(repo__name__exact=repo) + if category != 'all': results = results.filter(category__category__exact=category) + if maint != 'all': results = results.filter(maintainer=maint) + if lastupdate: results = results.filter(last_update__gte=datetime(int(lastupdate[0:4]),int(lastupdate[5:7]),int(lastupdate[8:10]))) + # select_related() shouldn't be needed -- we're working around a Django bug + #results = results.select_related().order_by('repos.name', 'category', 'pkgname') + + # sort results + if sort == '': + results = results.order_by('repo', 'category', 'pkgname') + else: + # duplicate sort fields shouldn't hurt anything + results = results.order_by(sort, 'repo', 'category', 'pkgname') + + qs = request.GET.copy() + # build pagination urls + if results.count() > (skip + limit): + qs['skip'] = skip + limit + c['nextpage'] = '?' + qs.urlencode() + if skip > 0: + qs['skip'] = max(0, skip - limit) + c['prevpage'] = '?' + qs.urlencode() + # pass the querystring to the template so we can build sort queries + c['querystring'] = request.GET + + # if only there's only one result, pass right to the package details view + if results.count() == 1: return details(request, results[0].id) + # limit result set + if limit > 0: results = results[skip:(skip+limit)] + + c['results'] = results + return render_template('packages/search.html', request, c) + +def files(request, pkgid): + pkg = get_object_or_404(Package, id=pkgid) + files = PackageFile.objects.filter(pkg=pkgid) + return render_template('packages/files.html', request, {'pkg':pkg,'files':files}) + +def flaghelp(request): + return render_template('packages/flaghelp.html', request) + +def flag(request, pkgid): + pkg = get_object_or_404(Package, id=pkgid) + context = {'pkg': pkg} + if request.POST.has_key('confirmemail'): + email = request.POST['confirmemail'] + if request.POST.has_key('usermessage'): + message = request.POST['usermessage'] + else: + message = None + # validate + errors = {} + validate(errors, 'Email Address', email, validators.isValidEmail, False, request) + if errors: + context['errors'] = errors + return render_template('packages/flag.html', request, context) + + context['confirmemail'] = email + pkg.needupdate = 1 + pkg.save() + if pkg.maintainer_id > 0: + # send notification email to the maintainer + t = loader.get_template('packages/outofdate.txt') + c = Context({ + 'email': request.POST['confirmemail'], + 'message': message, + 'pkgname': pkg.pkgname, + 'weburl': 'http://www.archlinux.org/packages/' + str(pkg.id) + '/' + }) + send_mail('arch: Package [%s] marked out-of-date' % pkg.pkgname, + t.render(c), + 'Arch Website Notification ', + [pkg.maintainer.email], + fail_silently=True) + return render_template('packages/flag.html', request, context) + +@login_required +def unflag(request, pkgid): + pkg = get_object_or_404(Package, id=pkgid) + if pkg.maintainer.username != request.user.username: + return render_template('error_page.html', request, {'errmsg': 'You do not own this package.'}) + pkg.needupdate = 0 + pkg.save() + return HttpResponseRedirect('/packages/%d/' % (pkg.id)) -- cgit v1.2.3-24-g4f1b