summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/migrations/0007_auto__add_field_packagerelation_created.py135
-rw-r--r--packages/models.py13
-rw-r--r--packages/templatetags/package_extras.py37
-rw-r--r--packages/urls.py5
-rw-r--r--packages/views.py64
5 files changed, 229 insertions, 25 deletions
diff --git a/packages/migrations/0007_auto__add_field_packagerelation_created.py b/packages/migrations/0007_auto__add_field_packagerelation_created.py
new file mode 100644
index 00000000..37321fdb
--- /dev/null
+++ b/packages/migrations/0007_auto__add_field_packagerelation_created.py
@@ -0,0 +1,135 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.add_column('packages_packagerelation', 'created', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.utcnow()), keep_default=False)
+
+ def backwards(self, orm):
+ db.delete_column('packages_packagerelation', 'created')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'main.arch': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Arch', 'db_table': "'arches'"},
+ 'agnostic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Repo']"}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'main.repo': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'staging': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'packages.conflict': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Conflict'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'conflicts'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.license': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'License'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': "orm['main.Package']"})
+ },
+ 'packages.packagegroup': {
+ 'Meta': {'object_name': 'PackageGroup'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Package']"})
+ },
+ 'packages.packagerelation': {
+ 'Meta': {'unique_together': "(('pkgbase', 'user', 'type'),)", 'object_name': 'PackageRelation'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'type': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_relations'", 'to': "orm['auth.User']"})
+ },
+ 'packages.provision': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Provision'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'provides'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.replacement': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Replacement'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'replaces'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 79e8abca..a950bddb 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -1,5 +1,7 @@
+from datetime import datetime
+
from django.db import models
-from django.db.models.signals import post_save
+from django.db.models.signals import pre_save, post_save
from django.contrib.auth.models import User
class PackageRelation(models.Model):
@@ -18,6 +20,7 @@ class PackageRelation(models.Model):
pkgbase = models.CharField(max_length=255)
user = models.ForeignKey(User, related_name="package_relations")
type = models.PositiveIntegerField(choices=TYPE_CHOICES, default=MAINTAINER)
+ created = models.DateTimeField(editable=False)
def get_associated_packages(self):
# TODO: delayed import to avoid circular reference
@@ -109,7 +112,15 @@ def remove_inactive_maintainers(sender, instance, created, **kwargs):
type=PackageRelation.MAINTAINER)
maint_relations.delete()
+def set_created_field(sender, **kwargs):
+ # We use this same callback for both Isos and Tests
+ obj = kwargs['instance']
+ if not obj.created:
+ obj.created = datetime.utcnow()
+
post_save.connect(remove_inactive_maintainers, sender=User,
dispatch_uid="packages.models")
+pre_save.connect(set_created_field, sender=PackageRelation,
+ dispatch_uid="packages.models")
# vim: set ts=4 sw=4 et:
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index dd5b9347..e089b723 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -1,4 +1,4 @@
-import urllib
+from urllib import urlencode, quote as urlquote
try:
from urlparse import parse_qs
except ImportError:
@@ -22,7 +22,7 @@ class BuildQueryStringNode(template.Node):
qs['sort'] = ['-' + self.sortfield]
else:
qs['sort'] = [self.sortfield]
- return urllib.urlencode(qs, True)
+ return urlencode(qs, True)
@register.tag(name='buildsortqs')
def do_buildsortqs(parser, token):
@@ -48,4 +48,37 @@ def userpkgs(user):
)
return ''
+
+def svn_link(package, svnpath):
+ '''Helper function for the two real SVN link methods.'''
+ parts = (package.repo.svn_root, package.pkgbase, svnpath)
+ linkbase = "http://projects.archlinux.org/svntogit/%s.git/tree/%s/%s/"
+ return linkbase % tuple(urlquote(part) for part in parts)
+
+@register.simple_tag
+def svn_arch(package):
+ repo = package.repo.name.lower()
+ return svn_link(package, "repos/%s-%s" % (repo, package.arch.name))
+
+@register.simple_tag
+def svn_trunk(package):
+ return svn_link(package, "trunk")
+
+@register.simple_tag
+def bugs_list(package):
+ data = {
+ 'project': package.repo.bugs_project,
+ 'string': package.pkgname,
+ }
+ return "https://bugs.archlinux.org/?%s" % urlencode(data)
+
+@register.simple_tag
+def bug_report(package):
+ data = {
+ 'project': package.repo.bugs_project,
+ 'product_category': package.repo.bugs_category,
+ 'item_summary': '[%s]' % package.pkgname,
+ }
+ return "https://bugs.archlinux.org/newtask?%s" % urlencode(data)
+
# vim: set ts=4 sw=4 et:
diff --git a/packages/urls.py b/packages/urls.py
index 638a370a..e0362fa2 100644
--- a/packages/urls.py
+++ b/packages/urls.py
@@ -5,6 +5,7 @@ package_patterns = patterns('packages.views',
(r'^files/$', 'files'),
(r'^maintainer/$', 'getmaintainer'),
(r'^flag/$', 'flag'),
+ (r'^flag/done/$', 'flag_confirmed', {}, 'package-flag-confirmed'),
(r'^unflag/$', 'unflag'),
(r'^unflag/all/$', 'unflag_all'),
(r'^download/$', 'download'),
@@ -17,10 +18,6 @@ urlpatterns = patterns('packages.views',
'signoff_package'),
(r'^update/$', 'update'),
- # Preference is for the non-search url below, but search is kept
- # because other projects link to it
- (r'^search/$', 'search'),
- (r'^search/(?P<page>\d+)/$', 'search'),
(r'^$', 'search'),
(r'^(?P<page>\d+)/$', 'search'),
diff --git a/packages/views.py b/packages/views.py
index 73594507..adf6c0af 100644
--- a/packages/views.py
+++ b/packages/views.py
@@ -18,6 +18,7 @@ from django.views.generic.simple import direct_to_template
from datetime import datetime
import string
+from urllib import urlencode
from main.models import Package, PackageFile
from main.models import Arch, Repo, Signoff
@@ -84,7 +85,8 @@ def update(request):
def details(request, name='', repo='', arch=''):
if all([name, repo, arch]):
try:
- pkg = Package.objects.get(pkgname=name,
+ pkg = Package.objects.select_related(
+ 'arch', 'repo', 'packager').get(pkgname=name,
repo__name__iexact=repo, arch__name=arch)
return direct_to_template(request, 'packages/details.html',
{'pkg': pkg, })
@@ -107,8 +109,14 @@ def details(request, name='', repo='', arch=''):
return direct_to_template(request, 'packages/packages_list.html',
context)
else:
- return redirect("/packages/?arch=%s&repo=%s&q=%s" % (
- arch.lower(), repo.title(), name))
+ pkg_data = [
+ ('arch', arch.lower()),
+ ('repo', repo.lower()),
+ ('q', name),
+ ]
+ # only include non-blank values in the query we generate
+ pkg_data = [(x, y) for x, y in pkg_data if y]
+ return redirect("/packages/?%s" % urlencode(pkg_data))
def groups(request, arch=None):
arches = []
@@ -163,7 +171,7 @@ class LimitTypedChoiceField(forms.TypedChoiceField):
try:
coerce_limit_value(value)
return True
- except ValueError, TypeError:
+ except (ValueError, TypeError):
return False
class PackageSearchForm(forms.Form):
@@ -218,7 +226,7 @@ def search(request, page=None):
packages = packages.filter(pkgbase__in=inner_q)
if form.cleaned_data['flagged'] == 'Flagged':
- packages=packages.filter(flag_date__isnull=False)
+ packages = packages.filter(flag_date__isnull=False)
elif form.cleaned_data['flagged'] == 'Not Flagged':
packages = packages.filter(flag_date__isnull=True)
@@ -359,18 +367,21 @@ class FlagForm(forms.Form):
def flag(request, name, repo, arch):
pkg = get_object_or_404(Package,
pkgname=name, repo__name__iexact=repo, arch__name=arch)
- context = {'pkg': pkg}
if pkg.flag_date is not None:
# already flagged. do nothing.
- return direct_to_template(request, 'packages/flagged.html', context)
+ return direct_to_template(request, 'packages/flagged.html', {'pkg': pkg})
+ # find all packages from (hopefully) the same PKGBUILD
+ pkgs = Package.objects.select_related('arch', 'repo').filter(
+ pkgbase=pkg.pkgbase, flag_date__isnull=True,
+ repo__testing=pkg.repo.testing).order_by(
+ 'pkgname', 'repo__name', 'arch__name')
if request.POST:
form = FlagForm(request.POST)
if form.is_valid() and form.cleaned_data['website'] == '':
- # find all packages from (hopefully) the same PKGBUILD
- pkgs = Package.objects.filter(
- pkgbase=pkg.pkgbase, repo__testing=pkg.repo.testing)
- pkgs.update(flag_date=datetime.now())
+ # save the package list for later use
+ flagged_pkgs = list(pkgs)
+ pkgs.update(flag_date=datetime.utcnow())
maints = pkg.maintainers
if not maints:
@@ -386,13 +397,13 @@ def flag(request, name, repo, arch):
toemail.append(maint.email)
if toemail:
- # send notification email to the maintainer
+ # send notification email to the maintainers
t = loader.get_template('packages/outofdate.txt')
c = Context({
'email': form.cleaned_data['email'],
'message': form.cleaned_data['usermessage'],
'pkg': pkg,
- 'weburl': pkg.get_full_url(),
+ 'packages': flagged_pkgs,
})
send_mail(subject,
t.render(c),
@@ -400,14 +411,30 @@ def flag(request, name, repo, arch):
toemail,
fail_silently=True)
- context['confirmed'] = True
+ return redirect('package-flag-confirmed', name=name, repo=repo,
+ arch=arch)
else:
form = FlagForm()
- context['form'] = form
-
+ context = {
+ 'package': pkg,
+ 'packages': pkgs,
+ 'form': form
+ }
return direct_to_template(request, 'packages/flag.html', context)
+def flag_confirmed(request, name, repo, arch):
+ pkg = get_object_or_404(Package,
+ pkgname=name, repo__name__iexact=repo, arch__name=arch)
+ pkgs = Package.objects.select_related('arch', 'repo').filter(
+ pkgbase=pkg.pkgbase, flag_date=pkg.flag_date,
+ repo__testing=pkg.repo.testing).order_by(
+ 'pkgname', 'repo__name', 'arch__name')
+
+ context = {'package': pkg, 'packages': pkgs}
+
+ return direct_to_template(request, 'packages/flag_confirmed.html', context)
+
def download(request, name, repo, arch):
pkg = get_object_or_404(Package,
pkgname=name, repo__name__iexact=repo, arch__name=arch)
@@ -418,13 +445,13 @@ def download(request, name, repo, arch):
if pkg.arch.agnostic:
# grab the first non-any arch to fake the download path
arch = Arch.objects.exclude(agnostic=True)[0].name
- details = {
+ values = {
'host': mirrorurl.url,
'arch': arch,
'repo': pkg.repo.name.lower(),
'file': pkg.filename,
}
- url = string.Template('${host}${repo}/os/${arch}/${file}').substitute(details)
+ url = string.Template('${host}${repo}/os/${arch}/${file}').substitute(values)
return redirect(url)
def arch_differences(request):
@@ -440,6 +467,7 @@ def arch_differences(request):
return direct_to_template(request, 'packages/differences.html', context)
@permission_required('main.change_package')
+@never_cache
def stale_relations(request):
relations = PackageRelation.objects.select_related('user')
pkgbases = Package.objects.all().values('pkgbase')