From 4e1e28729f97eb694cdcae2f3fe51b5178963069 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 27 Apr 2012 09:07:26 -0500
Subject: Use GenericIPAddressField in flag request ip_address
New (and slightly odd with regards to verbose_name) in Django 1.4. This
simply ensures a deployment in an IPv6 environment actually works as
expected. If you were using PostgreSQL as a database backend, you won't
be affected by this as the 'inet' type was already used, but at least
now you can edit the values in the admin without getting an error.
Signed-off-by: Dan McGee
---
.../0014_auto__chg_field_flagrequest_ip_address.py | 181 +++++++++++++++++++++
packages/models.py | 4 +-
2 files changed, 184 insertions(+), 1 deletion(-)
create mode 100644 packages/migrations/0014_auto__chg_field_flagrequest_ip_address.py
(limited to 'packages')
diff --git a/packages/migrations/0014_auto__chg_field_flagrequest_ip_address.py b/packages/migrations/0014_auto__chg_field_flagrequest_ip_address.py
new file mode 100644
index 00000000..351b9985
--- /dev/null
+++ b/packages/migrations/0014_auto__chg_field_flagrequest_ip_address.py
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.alter_column('packages_flagrequest', 'ip_address', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39))
+
+ def backwards(self, orm):
+ db.alter_column('packages_flagrequest', 'ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15))
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 820e61ba..7a7a81cd 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -173,7 +173,9 @@ class FlagRequest(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
user_email = models.EmailField('email address')
created = models.DateTimeField(editable=False)
- ip_address = models.IPAddressField('IP address')
+ # Great work, Django... https://code.djangoproject.com/ticket/18212
+ ip_address = models.GenericIPAddressField(verbose_name='IP address',
+ unpack_ipv4=True)
pkgbase = models.CharField(max_length=255, db_index=True)
version = models.CharField(max_length=255, default='')
repo = models.ForeignKey(Repo)
--
cgit v1.2.3-54-g00ecf
From b59e79f3878d59b83c6867eb5c6196f8f003dcd9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 1 May 2012 17:13:33 -0500
Subject: Opensearch enhancements
* Add a 64x64 icon as indicated in the Opensearch specification.
* Add suggestions capability and a new view providing suggestions based
on package name starting with the typed value.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 18 +++++++++++++++++-
sitestatic/logos/icon-transparent-64x64.png | Bin 0 -> 1430 bytes
templates/packages/opensearch.xml | 17 +++++++++++------
urls.py | 2 ++
4 files changed, 30 insertions(+), 7 deletions(-)
create mode 100644 sitestatic/logos/icon-transparent-64x64.png
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 6a9c5275..21d17470 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -7,7 +7,7 @@
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, redirect
from django.utils import simplejson
-from django.views.decorators.http import require_POST
+from django.views.decorators.http import require_GET, require_POST
from django.views.decorators.vary import vary_on_headers
from django.views.generic.simple import direct_to_template
@@ -24,6 +24,7 @@
from .signoff import signoffs, signoff_package, signoff_options, signoffs_json
+@require_GET
def opensearch(request):
if request.is_secure():
domain = "https://%s" % request.META['HTTP_HOST']
@@ -34,6 +35,21 @@ def opensearch(request):
{'domain': domain},
mimetype='application/opensearchdescription+xml')
+
+@require_GET
+def opensearch_suggest(request):
+ search_term = request.GET.get('q', '')
+ if search_term == '':
+ return HttpResponse('', mimetype='application/x-suggestions+json')
+
+ names = Package.objects.filter(
+ pkgname__startswith=search_term).values_list(
+ 'pkgname', flat=True).order_by('pkgname').distinct()[:10]
+ results = [search_term, list(names)]
+ to_json = simplejson.dumps(results, ensure_ascii=False)
+ return HttpResponse(to_json, mimetype='application/x-suggestions+json')
+
+
@permission_required('main.change_package')
@require_POST
def update(request):
diff --git a/sitestatic/logos/icon-transparent-64x64.png b/sitestatic/logos/icon-transparent-64x64.png
new file mode 100644
index 00000000..0318f183
Binary files /dev/null and b/sitestatic/logos/icon-transparent-64x64.png differ
diff --git a/templates/packages/opensearch.xml b/templates/packages/opensearch.xml
index 216be3e9..6c50d991 100644
--- a/templates/packages/opensearch.xml
+++ b/templates/packages/opensearch.xml
@@ -1,13 +1,18 @@
-
+{% load static from staticfiles %}
- Arch Linux Packages
- Search the Arch Linux package repositories.
+ Arch Packages
+ Arch Linux Package Repository Search
+ Search the Arch Linux package repositories by keyword in package names and descriptions.
linux archlinux package software
- {{domain}}/static/favicon.ico
+ {{ domain }}{% static "favicon.ico" %}
+ {{ domain }}{% static "logos/icon-transparent-64x64.png" %}
en-us
UTF-8
UTF-8
-
-
+
+
+
diff --git a/urls.py b/urls.py
index 0f2e07af..c0ce8c6f 100644
--- a/urls.py
+++ b/urls.py
@@ -91,6 +91,8 @@
(r'^visualize/', include('visualize.urls')),
(r'^opensearch/packages/$', 'packages.views.opensearch',
{}, 'opensearch-packages'),
+ (r'^opensearch/packages/suggest$', 'packages.views.opensearch_suggest',
+ {}, 'opensearch-packages-suggest'),
(r'^todolists/$','todolists.views.public_list'),
)
--
cgit v1.2.3-54-g00ecf
From f3e0adcb2fc9a26e2ad9337a47550a37590074d9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 1 May 2012 18:12:50 -0500
Subject: Add some caching to the Opensearch-related views
Both some simple cache headers as well as low-level results caching on
search terms suggestions.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 21d17470..0f1dc799 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -4,9 +4,11 @@
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
+from django.core.cache import cache
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, redirect
from django.utils import simplejson
+from django.views.decorators.cache import cache_control
from django.views.decorators.http import require_GET, require_POST
from django.views.decorators.vary import vary_on_headers
from django.views.generic.simple import direct_to_template
@@ -25,6 +27,7 @@
@require_GET
+@cache_control(public=True, max_age=86400)
def opensearch(request):
if request.is_secure():
domain = "https://%s" % request.META['HTTP_HOST']
@@ -37,16 +40,21 @@ def opensearch(request):
@require_GET
+@cache_control(public=True, max_age=300)
def opensearch_suggest(request):
search_term = request.GET.get('q', '')
if search_term == '':
return HttpResponse('', mimetype='application/x-suggestions+json')
- names = Package.objects.filter(
- pkgname__startswith=search_term).values_list(
- 'pkgname', flat=True).order_by('pkgname').distinct()[:10]
- results = [search_term, list(names)]
- to_json = simplejson.dumps(results, ensure_ascii=False)
+ cache_key = 'opensearch:packages:' + search_term
+ to_json = cache.get(cache_key, None)
+ if to_json is None:
+ names = Package.objects.filter(
+ pkgname__startswith=search_term).values_list(
+ 'pkgname', flat=True).order_by('pkgname').distinct()[:10]
+ results = [search_term, list(names)]
+ to_json = simplejson.dumps(results, ensure_ascii=False)
+ cache.set('opensearch:packages:%s' % search_term, to_json, 300)
return HttpResponse(to_json, mimetype='application/x-suggestions+json')
--
cgit v1.2.3-54-g00ecf
From badc535aeb1d310a9b8aa59aade07045e6eae653 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 18 Apr 2012 15:05:43 -0500
Subject: Ensure order_by default value is cleared when using distinct()
Otherwise the queryset returns nonsensical results. I find the design of
this less than obvious but so be it; we can ensure the results work
regardless of a default ordering on the model.
Signed-off-by: Dan McGee
---
devel/views.py | 6 ++++--
main/models.py | 7 ++++---
news/views.py | 3 ++-
packages/utils.py | 3 ++-
todolists/views.py | 4 ++--
5 files changed, 14 insertions(+), 9 deletions(-)
(limited to 'packages')
diff --git a/devel/views.py b/devel/views.py
index 85acda74..7ef33362 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -249,7 +249,8 @@ def report(request, report_name, username=None):
if username:
pkg_ids = set(packages.values_list('id', flat=True))
bad_files = bad_files.filter(pkg__in=pkg_ids)
- bad_files = bad_files.values_list('pkg_id', flat=True).distinct()
+ bad_files = bad_files.values_list(
+ 'pkg_id', flat=True).order_by().distinct()
packages = packages.filter(id__in=set(bad_files))
elif report_name == 'uncompressed-info':
title = 'Packages with uncompressed infopages'
@@ -260,7 +261,8 @@ def report(request, report_name, username=None):
if username:
pkg_ids = set(packages.values_list('id', flat=True))
bad_files = bad_files.filter(pkg__in=pkg_ids)
- bad_files = bad_files.values_list('pkg_id', flat=True).distinct()
+ bad_files = bad_files.values_list(
+ 'pkg_id', flat=True).order_by().distinct()
packages = packages.filter(id__in=set(bad_files))
elif report_name == 'unneeded-orphans':
title = 'Orphan packages required by no other packages'
diff --git a/main/models.py b/main/models.py
index c532ed56..398cb88b 100644
--- a/main/models.py
+++ b/main/models.py
@@ -13,7 +13,7 @@
class TodolistManager(models.Manager):
def incomplete(self):
- return self.filter(todolistpkg__complete=False).distinct()
+ return self.filter(todolistpkg__complete=False).order_by().distinct()
class PackageManager(models.Manager):
def flagged(self):
@@ -378,7 +378,7 @@ def get_providers(self, arches=None, testing=None, staging=None):
'''Return providers of this dep. Does *not* include exact matches as it
checks the Provision names only, use get_best_satisfier() instead.'''
pkgs = Package.objects.normal().filter(
- provides__name=self.depname).distinct()
+ provides__name=self.depname).order_by().distinct()
if arches is not None:
pkgs = pkgs.filter(arch__in=arches)
@@ -432,7 +432,8 @@ def packages(self):
@property
def package_names(self):
# depends on packages property returning a queryset
- return self.packages.values_list('pkg__pkgname', flat=True).distinct()
+ return self.packages.values_list(
+ 'pkg__pkgname', flat=True).order_by().distinct()
class Meta:
db_table = 'todolists'
diff --git a/news/views.py b/news/views.py
index 268f0523..03f3b0ac 100644
--- a/news/views.py
+++ b/news/views.py
@@ -13,7 +13,8 @@
def find_unique_slug(newsitem):
'''Attempt to find a unique slug for this news item.'''
- existing = list(News.objects.values_list('slug', flat=True).distinct())
+ existing = list(News.objects.values_list(
+ 'slug', flat=True).order_by().distinct())
suffixed = slug = slugify(newsitem.title)
suffix = 0
diff --git a/packages/utils.py b/packages/utils.py
index a3c13b17..8d00bd68 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -218,7 +218,8 @@ def attach_maintainers(packages):
packages = list(packages)
pkgbases = set(p.pkgbase for p in packages)
rels = PackageRelation.objects.filter(type=PackageRelation.MAINTAINER,
- pkgbase__in=pkgbases).values_list('pkgbase', 'user_id').distinct()
+ pkgbase__in=pkgbases).values_list(
+ 'pkgbase', 'user_id').order_by().distinct()
# get all the user objects we will need
user_ids = set(rel[1] for rel in rels)
diff --git a/todolists/views.py b/todolists/views.py
index e5cc0823..70209b6d 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -49,8 +49,8 @@ def flag(request, list_id, pkg_id):
@login_required
def view(request, list_id):
todolist = get_object_or_404(Todolist, id=list_id)
- svn_roots = Repo.objects.order_by().values_list(
- 'svn_root', flat=True).distinct()
+ svn_roots = Repo.objects.values_list(
+ 'svn_root', flat=True).order_by().distinct()
# we don't hold onto the result, but the objects are the same here,
# so accessing maintainers in the template is now cheap
attach_maintainers(tp.pkg for tp in todolist.packages)
--
cgit v1.2.3-54-g00ecf
From 768bc688aab844cf9fdf9809b9381aaf0042f2fc Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 2 May 2012 10:21:30 -0500
Subject: Flagging related cleanups and improvements
Touch up the style slightly on the flag help popup to match the main
site style more closely.
When a logged-in user is flagging a package out of date, we have no need
for them to fill in the email field since we already have an email
address on file.
Signed-off-by: Dan McGee
---
packages/views/flag.py | 34 ++++++++++++++++++++++++----------
templates/packages/flaghelp.html | 20 +++++++++-----------
2 files changed, 33 insertions(+), 21 deletions(-)
(limited to 'packages')
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 0d2f9009..7fa2d508 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -6,16 +6,13 @@
from django.shortcuts import get_object_or_404, redirect
from django.template import loader, Context
from django.views.generic.simple import direct_to_template
-from django.views.decorators.cache import never_cache
+from django.views.decorators.cache import cache_page, never_cache
from ..models import FlagRequest
from main.models import Package
from main.utils import utc_now
-def flaghelp(request):
- return direct_to_template(request, 'packages/flaghelp.html')
-
class FlagForm(forms.Form):
email = forms.EmailField(label='E-mail Address')
message = forms.CharField(label='Message To Developer',
@@ -26,6 +23,20 @@ class FlagForm(forms.Form):
widget=forms.TextInput(attrs={'style': 'display:none;'}),
required=False)
+ def __init__(self, *args, **kwargs):
+ # we remove the 'email' field if this form is being shown to a
+ # logged-in user, e.g., a developer.
+ auth = kwargs.pop('authenticated', False)
+ super(FlagForm, self).__init__(*args, **kwargs)
+ if auth:
+ del self.fields['email']
+
+
+@cache_page(3600)
+def flaghelp(request):
+ return direct_to_template(request, 'packages/flaghelp.html')
+
+
@never_cache
def flag(request, name, repo, arch):
pkg = get_object_or_404(Package,
@@ -41,8 +52,10 @@ def flag(request, name, repo, arch):
repo__staging=pkg.repo.staging).order_by(
'pkgname', 'repo__name', 'arch__name')
+ authenticated = request.user.is_authenticated()
+
if request.POST:
- form = FlagForm(request.POST)
+ form = FlagForm(request.POST, authenticated=authenticated)
if form.is_valid() and form.cleaned_data['website'] == '':
# save the package list for later use
flagged_pkgs = list(pkgs)
@@ -54,9 +67,12 @@ def flag(request, name, repo, arch):
else:
version = ''
- email = form.cleaned_data['email']
message = form.cleaned_data['message']
ip_addr = request.META.get('REMOTE_ADDR')
+ if authenticated:
+ email = request.user.email
+ else:
+ email = form.cleaned_data['email']
@transaction.commit_on_success
def perform_updates():
@@ -68,7 +84,7 @@ def perform_updates():
ip_address=ip_addr, pkgbase=pkg.pkgbase,
version=version, repo=pkg.repo,
num_packages=len(flagged_pkgs))
- if request.user.is_authenticated():
+ if authenticated:
flag_request.user = request.user
flag_request.save()
@@ -106,9 +122,7 @@ def perform_updates():
arch=arch)
else:
initial = {}
- if request.user.is_authenticated():
- initial['email'] = request.user.email
- form = FlagForm(initial=initial)
+ form = FlagForm(authenticated=authenticated)
context = {
'package': pkg,
diff --git a/templates/packages/flaghelp.html b/templates/packages/flaghelp.html
index 819a2f01..399b7e01 100644
--- a/templates/packages/flaghelp.html
+++ b/templates/packages/flaghelp.html
@@ -1,20 +1,19 @@
-
+{% load static from staticfiles %}
Flagging Packages
-
-
Flagging Packages
-
If you notice that a package is out-of-date (i.e., there is a newer
stable release available), then please notify us by
using the Flag button in the Package Details
@@ -29,8 +28,7 @@
Flagging Packages
with your additional text.
Note: Please do not use this facility if the
- package is broken! Use the bugtracker instead.
-
--
cgit v1.2.3-54-g00ecf
From 86f8efaeb1f67138c194d0c373f9d91e2999c5dd Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 4 May 2012 10:57:59 -0500
Subject: Fix search suggestions for invalid cache keys
Unfortunately, "invalid" in this case includes spaces, which is a bit
crazy. MD5 the provided search term before using it as a cache key to be
safe.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 0f1dc799..559368b9 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -1,3 +1,4 @@
+import hashlib
from string import Template
from urllib import urlencode
@@ -46,7 +47,7 @@ def opensearch_suggest(request):
if search_term == '':
return HttpResponse('', mimetype='application/x-suggestions+json')
- cache_key = 'opensearch:packages:' + search_term
+ cache_key = 'opensearch:packages:' + hashlib.md5(search_term).hexdigest()
to_json = cache.get(cache_key, None)
if to_json is None:
names = Package.objects.filter(
@@ -54,7 +55,7 @@ def opensearch_suggest(request):
'pkgname', flat=True).order_by('pkgname').distinct()[:10]
results = [search_term, list(names)]
to_json = simplejson.dumps(results, ensure_ascii=False)
- cache.set('opensearch:packages:%s' % search_term, to_json, 300)
+ cache.set(cache_key, to_json, 300)
return HttpResponse(to_json, mimetype='application/x-suggestions+json')
--
cgit v1.2.3-54-g00ecf
From d4c7a48623f90cdc508d1824bf47ce3e398dd820 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 5 May 2012 10:19:49 -0500
Subject: Fix suggestion caching again for non-ASCII characters
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is why you should test this stuff with random input before rolling
it out. Whoops. URL that caught this problem:
/opensearch/packages/suggest?q=%D7%A0%D7%9F%D7%92%D7%9F
aka
/opensearch/packages/suggest?q=נןגן
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 559368b9..60c3a46b 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -47,7 +47,8 @@ def opensearch_suggest(request):
if search_term == '':
return HttpResponse('', mimetype='application/x-suggestions+json')
- cache_key = 'opensearch:packages:' + hashlib.md5(search_term).hexdigest()
+ cache_key = 'opensearch:packages:' + \
+ hashlib.md5(search_term.encode('utf-8')).hexdigest()
to_json = cache.get(cache_key, None)
if to_json is None:
names = Package.objects.filter(
--
cgit v1.2.3-54-g00ecf
From a5f5557493446bede78adb0584c88208234f874e Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 12 May 2012 09:32:30 -0500
Subject: Use python json module directly in place of simplejson
As of Python 2.6, this is a builtin module that has all the same
functions and capabilities of the Django simplejson module. Additionally
simplejson is deprecated in the upcoming Django 1.5 release.
Signed-off-by: Dan McGee
---
mirrors/views.py | 5 ++---
packages/views/__init__.py | 10 ++++------
packages/views/search.py | 5 ++---
packages/views/signoff.py | 7 +++----
todolists/views.py | 6 +++---
visualize/views.py | 8 ++++----
6 files changed, 18 insertions(+), 23 deletions(-)
(limited to 'packages')
diff --git a/mirrors/views.py b/mirrors/views.py
index eac78ff2..b0be6238 100644
--- a/mirrors/views.py
+++ b/mirrors/views.py
@@ -1,5 +1,6 @@
from datetime import timedelta
from itertools import groupby
+import json
from operator import attrgetter, itemgetter
from django import forms
@@ -10,7 +11,6 @@
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.simple import direct_to_template
-from django.utils import simplejson
from django_countries.countries import COUNTRIES
from .models import Mirror, MirrorUrl, MirrorProtocol
@@ -237,8 +237,7 @@ def status_json(request):
status_info = get_mirror_statuses()
data = status_info.copy()
data['version'] = 3
- to_json = simplejson.dumps(data, ensure_ascii=False,
- cls=MirrorStatusJSONEncoder)
+ to_json = json.dumps(data, ensure_ascii=False, cls=MirrorStatusJSONEncoder)
response = HttpResponse(to_json, mimetype='application/json')
return response
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 60c3a46b..c1a035d0 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -1,4 +1,5 @@
import hashlib
+import json
from string import Template
from urllib import urlencode
@@ -8,7 +9,6 @@
from django.core.cache import cache
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, redirect
-from django.utils import simplejson
from django.views.decorators.cache import cache_control
from django.views.decorators.http import require_GET, require_POST
from django.views.decorators.vary import vary_on_headers
@@ -55,7 +55,7 @@ def opensearch_suggest(request):
pkgname__startswith=search_term).values_list(
'pkgname', flat=True).order_by('pkgname').distinct()[:10]
results = [search_term, list(names)]
- to_json = simplejson.dumps(results, ensure_ascii=False)
+ to_json = json.dumps(results, ensure_ascii=False)
cache.set(cache_key, to_json, 300)
return HttpResponse(to_json, mimetype='application/x-suggestions+json')
@@ -197,8 +197,7 @@ def files(request, name, repo, arch):
def details_json(request, name, repo, arch):
pkg = get_object_or_404(Package,
pkgname=name, repo__name__iexact=repo, arch__name=arch)
- to_json = simplejson.dumps(pkg, ensure_ascii=False,
- cls=PackageJSONEncoder)
+ to_json = json.dumps(pkg, ensure_ascii=False, cls=PackageJSONEncoder)
return HttpResponse(to_json, mimetype='application/json')
def files_json(request, name, repo, arch):
@@ -212,8 +211,7 @@ def files_json(request, name, repo, arch):
'arch': pkg.arch.name.lower(),
'files': fileslist,
}
- to_json = simplejson.dumps(data, ensure_ascii=False,
- cls=PackageJSONEncoder)
+ to_json = json.dumps(data, ensure_ascii=False, cls=PackageJSONEncoder)
return HttpResponse(to_json, mimetype='application/json')
def download(request, name, repo, arch):
diff --git a/packages/views/search.py b/packages/views/search.py
index a09de0a7..a89822be 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -1,4 +1,5 @@
from datetime import datetime
+import json
from django import forms
from django.contrib.admin.widgets import AdminDateWidget
@@ -6,7 +7,6 @@
from django.db.models import Q
from django.http import HttpResponse
from django.views.generic import list_detail
-from django.utils import simplejson
from main.models import Package, Arch, Repo
from main.utils import make_choice
@@ -179,8 +179,7 @@ def search_json(request):
container['results'] = packages
container['valid'] = True
- to_json = simplejson.dumps(container, ensure_ascii=False,
- cls=PackageJSONEncoder)
+ to_json = json.dumps(container, ensure_ascii=False, cls=PackageJSONEncoder)
return HttpResponse(to_json, mimetype='application/json')
# vim: set ts=4 sw=4 et:
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 63341a1d..61d949fc 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -1,3 +1,4 @@
+import json
from operator import attrgetter
from django import forms
@@ -7,7 +8,6 @@
from django.db import transaction
from django.http import HttpResponse, Http404
from django.shortcuts import get_list_or_404, redirect, render
-from django.utils import simplejson
from django.views.decorators.cache import never_cache
from django.views.generic.simple import direct_to_template
@@ -67,7 +67,7 @@ def signoff_package(request, name, repo, arch, revoke=False):
'known_bad': spec.known_bad,
'user': str(request.user),
}
- return HttpResponse(simplejson.dumps(data, ensure_ascii=False),
+ return HttpResponse(json.dumps(data, ensure_ascii=False),
mimetype='application/json')
return redirect('package-signoffs')
@@ -183,8 +183,7 @@ def signoffs_json(request):
'version': 2,
'signoff_groups': signoff_groups,
}
- to_json = simplejson.dumps(data, ensure_ascii=False,
- cls=SignoffJSONEncoder)
+ to_json = json.dumps(data, ensure_ascii=False, cls=SignoffJSONEncoder)
response = HttpResponse(to_json, mimetype='application/json')
return response
diff --git a/todolists/views.py b/todolists/views.py
index 70209b6d..b9ba0903 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -1,5 +1,6 @@
-from django import forms
+import json
+from django import forms
from django.http import HttpResponse
from django.core.mail import send_mail
from django.shortcuts import get_list_or_404, get_object_or_404, redirect
@@ -9,7 +10,6 @@
from django.views.generic import DeleteView
from django.views.generic.simple import direct_to_template
from django.template import Context, loader
-from django.utils import simplejson
from main.models import Todolist, TodolistPkg, Package, Repo
from packages.utils import attach_maintainers
@@ -42,7 +42,7 @@ def flag(request, list_id, pkg_id):
pkg.save()
if request.is_ajax():
return HttpResponse(
- simplejson.dumps({'complete': pkg.complete}),
+ json.dumps({'complete': pkg.complete}),
mimetype='application/json')
return redirect(todolist)
diff --git a/visualize/views.py b/visualize/views.py
index afc5429d..95f8c314 100644
--- a/visualize/views.py
+++ b/visualize/views.py
@@ -1,9 +1,9 @@
from datetime import datetime
+import json
from django.contrib.auth.models import User
from django.db.models import Count, Sum, Q
from django.http import HttpResponse
-from django.utils import simplejson
from django.views.decorators.cache import cache_page
from django.views.generic.simple import direct_to_template
@@ -61,13 +61,13 @@ def build_map(name, arch, repo):
@cache_page(1800)
def by_arch(request):
data = arch_repo_data()
- to_json = simplejson.dumps(data['by_arch'], ensure_ascii=False)
+ to_json = json.dumps(data['by_arch'], ensure_ascii=False)
return HttpResponse(to_json, mimetype='application/json')
@cache_page(1800)
def by_repo(request):
data = arch_repo_data()
- to_json = simplejson.dumps(data['by_repo'], ensure_ascii=False)
+ to_json = json.dumps(data['by_repo'], ensure_ascii=False)
return HttpResponse(to_json, mimetype='application/json')
@@ -109,7 +109,7 @@ def pgp_keys(request):
data = { 'nodes': node_list, 'edges': edge_list }
- to_json = simplejson.dumps(data, ensure_ascii=False)
+ to_json = json.dumps(data, ensure_ascii=False)
return HttpResponse(to_json, mimetype='application/json')
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From 17e33f9118e9749b1e3fdfd76686e85dbcecfb00 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 19:17:30 -0500
Subject: Refactor an abstract base class out of conflicts/provides/replaces
Signed-off-by: Dan McGee
---
packages/models.py | 41 +++++++++++++++++------------------------
1 file changed, 17 insertions(+), 24 deletions(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index 7a7a81cd..f57c9f3c 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -218,10 +218,10 @@ def __unicode__(self):
class Meta:
ordering = ['name']
-class Conflict(models.Model):
- pkg = models.ForeignKey('main.Package', related_name='conflicts')
+
+class RelatedToBase(models.Model):
+ '''A base class for conflicts/provides/replaces/etc.'''
name = models.CharField(max_length=255, db_index=True)
- comparison = models.CharField(max_length=255, default='')
version = models.CharField(max_length=255, default='')
def __unicode__(self):
@@ -230,36 +230,29 @@ def __unicode__(self):
return self.name
class Meta:
+ abstract = True
ordering = ['name']
-class Provision(models.Model):
+
+class Conflict(RelatedToBase):
+ pkg = models.ForeignKey('main.Package', related_name='conflicts')
+ comparison = models.CharField(max_length=255, default='')
+
+
+class Provision(RelatedToBase):
pkg = models.ForeignKey('main.Package', related_name='provides')
- name = models.CharField(max_length=255, db_index=True)
# comparison must be '=' for provides
- comparison = '='
- version = models.CharField(max_length=255, default='')
- def __unicode__(self):
- if self.version:
- return u'%s=%s' % (self.name, self.version)
- return self.name
+ @property
+ def comparison(self):
+ if self.version is not None and self.version != '':
+ return '='
+ return None
- class Meta:
- ordering = ['name']
-class Replacement(models.Model):
+class Replacement(RelatedToBase):
pkg = models.ForeignKey('main.Package', related_name='replaces')
- name = models.CharField(max_length=255, db_index=True)
comparison = models.CharField(max_length=255, default='')
- version = models.CharField(max_length=255, default='')
-
- def __unicode__(self):
- if self.version:
- return u'%s%s%s' % (self.name, self.comparison, self.version)
- return self.name
-
- class Meta:
- ordering = ['name']
# hook up some signals
--
cgit v1.2.3-54-g00ecf
From 158be107e4ad682de0c9360658dfa5a72c21ee58 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 19:57:12 -0500
Subject: Add a get_best_satisfier method to RelatedToBase
This is basically what we do in PackageDepend already.
Signed-off-by: Dan McGee
---
packages/models.py | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index f57c9f3c..a4d2a556 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -4,7 +4,7 @@
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
-from main.models import Arch, Repo
+from main.models import Arch, Repo, Package
from main.utils import set_created_field
class PackageRelation(models.Model):
@@ -224,6 +224,40 @@ class RelatedToBase(models.Model):
name = models.CharField(max_length=255, db_index=True)
version = models.CharField(max_length=255, default='')
+ def get_best_satisfier(self):
+ '''Find a satisfier for this related package that best matches the
+ given criteria. It will not search provisions, but will find packages
+ named and matching repo characteristics if possible.'''
+ # NOTE: this is cribbed directly from the PackageDepend method of the
+ # same name. Really, all of these things could use the same method if
+ # the PackageDepend class was moved here and field names were changed
+ # to match the layout we use here.
+ pkgs = Package.objects.normal().filter(pkgname=self.name)
+ if not self.pkg.arch.agnostic:
+ # make sure we match architectures if possible
+ arches = self.pkg.applicable_arches()
+ pkgs = pkgs.filter(arch__in=arches)
+ if len(pkgs) == 0:
+ # couldn't find a package in the DB
+ # it should be a virtual depend (or a removed package)
+ return None
+ if len(pkgs) == 1:
+ return pkgs[0]
+ # more than one package, see if we can't shrink it down
+ # grab the first though in case we fail
+ pkg = pkgs[0]
+ # prevents yet more DB queries, these lists should be short;
+ # after each grab the best available in case we remove all entries
+ pkgs = [p for p in pkgs if p.repo.staging == self.pkg.repo.staging]
+ if len(pkgs) > 0:
+ pkg = pkgs[0]
+
+ pkgs = [p for p in pkgs if p.repo.testing == self.pkg.repo.testing]
+ if len(pkgs) > 0:
+ pkg = pkgs[0]
+
+ return pkg
+
def __unicode__(self):
if self.version:
return u'%s%s%s' % (self.name, self.comparison, self.version)
--
cgit v1.2.3-54-g00ecf
From cf67e7952396121d3f7190195d812ea3f5fc7dcf Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 20:26:16 -0500
Subject: Issue redirects from non-agnostic to agnostic URLs if unambiguous
For something like "/extra/i686/apache-ant/", we can redirect to
"/extra/any/apache-ant/" without ambiguity. Previously this redirected
to the split packages listing with a single package, which was neither
correct nor really expected.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 10 ++++++++++
1 file changed, 10 insertions(+)
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index c1a035d0..3e574c26 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -136,6 +136,16 @@ def details(request, name='', repo='', arch=''):
return direct_to_template(request, 'packages/details.html',
{'pkg': pkg, })
except Package.DoesNotExist:
+ arch_obj = get_object_or_404(Arch, name=arch)
+ # for arch='any' packages, we can issue a redirect to them if we
+ # have a single non-ambiguous option by changing the arch to match
+ # any arch-agnostic package
+ if not arch_obj.agnostic:
+ pkgs = Package.objects.select_related(
+ 'arch', 'repo', 'packager').filter(pkgname=name,
+ repo__name__iexact=repo, arch__agnostic=True)
+ if len(pkgs) == 1:
+ return redirect(pkgs[0], permanent=True)
return split_package_details(request, name, repo, arch)
else:
pkg_data = [
--
cgit v1.2.3-54-g00ecf
From ef561bcd705e1047b1a81364ac143ce52c60defa Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 20:54:39 -0500
Subject: Add new depends model
Signed-off-by: Dan McGee
---
packages/migrations/0015_auto__add_depend.py | 199 +++++++++++++++++++++++++++
packages/models.py | 7 +
2 files changed, 206 insertions(+)
create mode 100644 packages/migrations/0015_auto__add_depend.py
(limited to 'packages')
diff --git a/packages/migrations/0015_auto__add_depend.py b/packages/migrations/0015_auto__add_depend.py
new file mode 100644
index 00000000..c9685ecb
--- /dev/null
+++ b/packages/migrations/0015_auto__add_depend.py
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+ def forwards(self, orm):
+ db.create_table('packages_depend', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
+ ('version', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
+ ('pkg', self.gf('django.db.models.fields.related.ForeignKey')(related_name='depends_new', to=orm['main.Package'])),
+ ('comparison', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
+ ('optional', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('packages', ['Depend'])
+
+ def backwards(self, orm):
+ db.delete_table('packages_depend')
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends_new'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index a4d2a556..c7b1cab4 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -268,6 +268,13 @@ class Meta:
ordering = ['name']
+class Depend(RelatedToBase):
+ pkg = models.ForeignKey('main.Package', related_name='depends_new')
+ comparison = models.CharField(max_length=255, default='')
+ optional = models.BooleanField(default=False)
+ description = models.TextField(null=True, blank=True)
+
+
class Conflict(RelatedToBase):
pkg = models.ForeignKey('main.Package', related_name='conflicts')
comparison = models.CharField(max_length=255, default='')
--
cgit v1.2.3-54-g00ecf
From cc44fdbea59596daf106e48acdb3f4137988d0d9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 21:10:14 -0500
Subject: Migrate package depends data into new model
Signed-off-by: Dan McGee
---
packages/migrations/0016_copy_depends_data.py | 246 ++++++++++++++++++++++++++
1 file changed, 246 insertions(+)
create mode 100644 packages/migrations/0016_copy_depends_data.py
(limited to 'packages')
diff --git a/packages/migrations/0016_copy_depends_data.py b/packages/migrations/0016_copy_depends_data.py
new file mode 100644
index 00000000..a4b55d4e
--- /dev/null
+++ b/packages/migrations/0016_copy_depends_data.py
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+import re
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ depends_on = (
+ ('main', '0038_add_depends_optional_description.py'),
+ )
+
+ def forwards(self, orm):
+ Depend = orm['packages.Depend']
+ vcmp_re = re.compile(r"^(>=|<=|=|>|<)(.*)$")
+ for old in orm['main.PackageDepend'].objects.all():
+ comp = ver = ''
+ m = vcmp_re.match(old.depvcmp)
+ if m:
+ comp = m.group(1)
+ ver = m.group(2)
+ new_dep = Depend(pkg_id=old.pkg_id, name=old.depname,
+ comparison=comp, version=ver, optional=old.optional,
+ description=old.description)
+ new_dep.save(force_insert=True)
+
+ def backwards(self, orm):
+ orm['packages.Depend'].objects.all().delete()
+
+ 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.donor': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Repo']"}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'main.packagedepend': {
+ 'Meta': {'object_name': 'PackageDepend', 'db_table': "'package_depends'"},
+ 'depname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'depvcmp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends'", 'to': "orm['main.Package']"})
+ },
+ 'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ },
+ 'main.repo': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ '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'})
+ },
+ 'main.todolist': {
+ 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"},
+ 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'on_delete': 'models.PROTECT'}),
+ 'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'main.todolistpkg': {
+ 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"},
+ 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ },
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends_new'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ }
+ }
+
+ complete_apps = ['main', 'packages']
+ symmetrical = True
--
cgit v1.2.3-54-g00ecf
From 72a92102df4999dbcc370064707c9026d51c4fe7 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 21:29:03 -0500
Subject: Switch to usage of new Depend object
Signed-off-by: Dan McGee
---
devel/management/commands/reporead.py | 18 +++++++++-------
devel/views.py | 6 +++---
main/models.py | 7 +++---
packages/models.py | 34 ++++++++++++++++++++++++++----
packages/utils.py | 6 +++---
templates/packages/details_depend.html | 6 +++---
templates/packages/details_requiredby.html | 2 +-
7 files changed, 54 insertions(+), 25 deletions(-)
(limited to 'packages')
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index fd8e3979..47294d9a 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -29,9 +29,9 @@
from django.db.utils import IntegrityError
from devel.utils import UserFinder
-from main.models import Arch, Package, PackageDepend, PackageFile, Repo
+from main.models import Arch, Package, PackageFile, Repo
from main.utils import utc_now
-from packages.models import Conflict, Provision, Replacement
+from packages.models import Depend, Conflict, Provision, Replacement
logging.basicConfig(
@@ -141,19 +141,21 @@ def full_version(self):
return u'%s-%s' % (self.ver, self.rel)
-DEPEND_RE = re.compile(r"^(.+?)((>=|<=|=|>|<)(.*))?$")
+DEPEND_RE = re.compile(r"^(.+?)((>=|<=|=|>|<)(.+))?$")
def create_depend(package, dep_str, optional=False):
- depend = PackageDepend(pkg=package, optional=optional)
+ depend = Depend(pkg=package, optional=optional)
# lop off any description first
parts = dep_str.split(':', 1)
if len(parts) > 1:
depend.description = parts[1].strip()
match = DEPEND_RE.match(parts[0].strip())
if match:
- depend.depname = match.group(1)
- if match.group(2):
- depend.depvcmp = match.group(2)
+ depend.name = match.group(1)
+ if match.group(3):
+ depend.comparison = match.group(3)
+ if match.group(4):
+ related.version = match.group(4)
else:
logger.warning('Package %s had unparsable depend string %s',
package.pkgname, dep_str)
@@ -232,7 +234,7 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None):
dbpkg.depends.all().delete()
deps = [create_depend(dbpkg, y) for y in repopkg.depends]
deps += [create_depend(dbpkg, y, True) for y in repopkg.optdepends]
- PackageDepend.objects.bulk_create(deps)
+ Depend.objects.bulk_create(deps)
dbpkg.conflicts.all().delete()
conflicts = [create_related(Conflict, dbpkg, y) for y in repopkg.conflicts]
diff --git a/devel/views.py b/devel/views.py
index 0f1c8d15..16b6acc6 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -26,11 +26,11 @@
from django.utils.http import http_date
from .models import UserProfile
-from main.models import Package, PackageDepend, PackageFile, TodolistPkg
+from main.models import Package, PackageFile, TodolistPkg
from main.models import Arch, Repo
from main.utils import utc_now
from news.models import News
-from packages.models import PackageRelation, Signoff
+from packages.models import PackageRelation, Signoff, Depend
from packages.utils import get_signoff_groups
from todolists.utils import get_annotated_todolists
from .utils import get_annotated_maintainers, UserFinder
@@ -267,7 +267,7 @@ def report(request, report_name, username=None):
elif report_name == 'unneeded-orphans':
title = 'Orphan packages required by no other packages'
owned = PackageRelation.objects.all().values('pkgbase')
- required = PackageDepend.objects.all().values('depname')
+ required = Depend.objects.all().values('name')
# The two separate calls to exclude is required to do the right thing
packages = packages.exclude(pkgbase__in=owned).exclude(
pkgname__in=required)
diff --git a/main/models.py b/main/models.py
index 4b445dd0..f17d4a4d 100644
--- a/main/models.py
+++ b/main/models.py
@@ -180,11 +180,12 @@ def get_requiredby(self):
list slim by including the corresponding package in the same testing
category as this package if that check makes sense.
"""
+ from packages.models import Depend
provides = set(self.provides.values_list('name', flat=True))
provides.add(self.pkgname)
- requiredby = PackageDepend.objects.select_related('pkg',
+ requiredby = Depend.objects.select_related('pkg',
'pkg__arch', 'pkg__repo').filter(
- depname__in=provides).order_by(
+ name__in=provides).order_by(
'pkg__pkgname', 'pkg__arch__name', 'pkg__repo__name')
if not self.arch.agnostic:
# make sure we match architectures if possible
@@ -232,7 +233,7 @@ def get_depends(self):
deps = []
arches = None
# TODO: we can use list comprehension and an 'in' query to make this more effective
- for dep in self.depends.order_by('optional', 'depname'):
+ for dep in self.depends.order_by('optional', 'name'):
pkg = dep.get_best_satisfier()
providers = None
if not pkg:
diff --git a/packages/models.py b/packages/models.py
index c7b1cab4..cb65f1f1 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -228,10 +228,6 @@ def get_best_satisfier(self):
'''Find a satisfier for this related package that best matches the
given criteria. It will not search provisions, but will find packages
named and matching repo characteristics if possible.'''
- # NOTE: this is cribbed directly from the PackageDepend method of the
- # same name. Really, all of these things could use the same method if
- # the PackageDepend class was moved here and field names were changed
- # to match the layout we use here.
pkgs = Package.objects.normal().filter(pkgname=self.name)
if not self.pkg.arch.agnostic:
# make sure we match architectures if possible
@@ -258,6 +254,36 @@ def get_best_satisfier(self):
return pkg
+ def get_providers(self):
+ '''Return providers of this related package. Does *not* include exact
+ matches as it checks the Provision names only, use get_best_satisfier()
+ instead for exact matches.'''
+ pkgs = Package.objects.normal().filter(
+ provides__name=self.name).order_by().distinct()
+ if not self.pkg.arch.agnostic:
+ # make sure we match architectures if possible
+ arches = self.pkg.applicable_arches()
+ pkgs = pkgs.filter(arch__in=arches)
+
+ # Logic here is to filter out packages that are in multiple repos if
+ # they are not requested. For example, if testing is False, only show a
+ # testing package if it doesn't exist in a non-testing repo.
+ filtered = {}
+ for package in pkgs:
+ if package.pkgname not in filtered or \
+ package.repo.staging == self.pkg.repo.staging:
+ filtered[package.pkgname] = package
+ pkgs = filtered.values()
+
+ filtered = {}
+ for package in pkgs:
+ if package.pkgname not in filtered or \
+ package.repo.testing == self.pkg.repo.testing:
+ filtered[package.pkgname] = package
+ pkgs = filtered.values()
+
+ return pkgs
+
def __unicode__(self):
if self.version:
return u'%s%s%s' % (self.name, self.comparison, self.version)
diff --git a/packages/utils.py b/packages/utils.py
index 8d00bd68..82313472 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -7,10 +7,10 @@
from django.db.models import Count, Max, F
from django.contrib.auth.models import User
-from main.models import Package, PackageDepend, PackageFile, Arch, Repo
+from main.models import Package, PackageFile, Arch, Repo
from main.utils import cache_function, groupby_preserve_order, PackageStandin
from .models import (PackageGroup, PackageRelation,
- License, Conflict, Provision, Replacement,
+ License, Depend, Conflict, Provision, Replacement,
SignoffSpecification, Signoff, DEFAULT_SIGNOFF_SPEC)
@cache_function(127)
@@ -451,7 +451,7 @@ def default(self, obj):
return obj.name.lower()
if isinstance(obj, (PackageGroup, License)):
return obj.name
- if isinstance(obj, (Conflict, Provision, Replacement, PackageDepend)):
+ if isinstance(obj, (Depend, Conflict, Provision, Replacement)):
return unicode(obj)
elif isinstance(obj, User):
return obj.username
diff --git a/templates/packages/details_depend.html b/templates/packages/details_depend.html
index 8b6e85c9..0cf2c36a 100644
--- a/templates/packages/details_depend.html
+++ b/templates/packages/details_depend.html
@@ -2,12 +2,12 @@
{% ifequal depend.pkg None %}
{% if depend.providers %}
-{{ depend.dep.depname }} ({% multi_pkg_details depend.providers %})
+{{ depend.dep.name }}{{ depend.dep.comparison|default:"" }}{{ depend.dep.version|default:"" }} ({% multi_pkg_details depend.providers %})
{% else %}
-{{ depend.dep.depname }} (virtual)
+{{ depend.dep.name }}{{ depend.dep.comparison|default:"" }}{{ depend.dep.version|default:"" }} (virtual)
{% endif %}
{% else %}
-{% pkg_details_link depend.pkg %}{{ depend.dep.depvcmp|default:"" }}
+{% pkg_details_link depend.pkg %}{{ depend.dep.comparison|default:"" }}{{ depend.dep.version|default:"" }}
{% if depend.pkg.repo.testing %} (testing) {% endif %}
{% if depend.pkg.repo.staging %} (staging) {% endif %}
{% endifequal %}
diff --git a/templates/packages/details_requiredby.html b/templates/packages/details_requiredby.html
index c7697289..ecc92b29 100644
--- a/templates/packages/details_requiredby.html
+++ b/templates/packages/details_requiredby.html
@@ -1,6 +1,6 @@
{% load package_extras %}
{% pkg_details_link req.pkg %}
-{% if req.depname != pkg.pkgname %}(requires {{ req.depname }}) {% endif %}
+{% if req.name != pkg.pkgname %}(requires {{ req.name }}) {% endif %}
{% if req.pkg.repo.testing %}(testing) {% endif %}
{% if req.pkg.repo.staging %}(staging) {% endif %}
{% if req.optional %}(optional) {% endif %}
--
cgit v1.2.3-54-g00ecf
From e1f9a3c44a1449a36f3981b868814f3d1f65d70d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 May 2012 21:35:28 -0500
Subject: Drop old PackageDepend model
Signed-off-by: Dan McGee
---
main/migrations/0061_auto__del_packagedepend.py | 135 ++++++++++++++++++++++++
main/models.py | 71 -------------
packages/models.py | 2 +-
3 files changed, 136 insertions(+), 72 deletions(-)
create mode 100644 main/migrations/0061_auto__del_packagedepend.py
(limited to 'packages')
diff --git a/main/migrations/0061_auto__del_packagedepend.py b/main/migrations/0061_auto__del_packagedepend.py
new file mode 100644
index 00000000..6cb1f68f
--- /dev/null
+++ b/main/migrations/0061_auto__del_packagedepend.py
@@ -0,0 +1,135 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ depends_on = (
+ ('packages', '0016_copy_depends_data.py'),
+ )
+
+ def forwards(self, orm):
+ db.delete_table('package_depends')
+
+ def backwards(self, orm):
+ db.create_table('package_depends', (
+ ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ('depvcmp', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
+ ('pkg', self.gf('django.db.models.fields.related.ForeignKey')(related_name='depends', to=orm['main.Package'])),
+ ('depname', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
+ ('optional', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ))
+ db.send_create_signal('main', ['PackageDepend'])
+
+ 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.donor': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Repo']"}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ },
+ 'main.repo': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ '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'})
+ },
+ 'main.todolist': {
+ 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"},
+ 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'on_delete': 'models.PROTECT'}),
+ 'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'main.todolistpkg': {
+ 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"},
+ 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ }
+ }
+
+ complete_apps = ['main']
diff --git a/main/models.py b/main/models.py
index f17d4a4d..04d8da8f 100644
--- a/main/models.py
+++ b/main/models.py
@@ -333,77 +333,6 @@ def __unicode__(self):
class Meta:
db_table = 'package_files'
-class PackageDepend(models.Model):
- pkg = models.ForeignKey(Package, related_name='depends')
- depname = models.CharField(max_length=255, db_index=True)
- depvcmp = models.CharField(max_length=255, default='')
- optional = models.BooleanField(default=False)
- description = models.TextField(null=True, blank=True)
-
- def get_best_satisfier(self):
- '''Find a satisfier for this dependency that best matches the given
- criteria. It will not search provisions, but will find packages named
- and matching repo characteristics if possible.'''
- pkgs = Package.objects.normal().filter(pkgname=self.depname)
- if not self.pkg.arch.agnostic:
- # make sure we match architectures if possible
- arches = self.pkg.applicable_arches()
- pkgs = pkgs.filter(arch__in=arches)
- if len(pkgs) == 0:
- # couldn't find a package in the DB
- # it should be a virtual depend (or a removed package)
- return None
- if len(pkgs) == 1:
- return pkgs[0]
- # more than one package, see if we can't shrink it down
- # grab the first though in case we fail
- pkg = pkgs[0]
- # prevents yet more DB queries, these lists should be short;
- # after each grab the best available in case we remove all entries
- pkgs = [p for p in pkgs if p.repo.staging == self.pkg.repo.staging]
- if len(pkgs) > 0:
- pkg = pkgs[0]
-
- pkgs = [p for p in pkgs if p.repo.testing == self.pkg.repo.testing]
- if len(pkgs) > 0:
- pkg = pkgs[0]
-
- return pkg
-
- def get_providers(self):
- '''Return providers of this dep. Does *not* include exact matches as it
- checks the Provision names only, use get_best_satisfier() instead.'''
- pkgs = Package.objects.normal().filter(
- provides__name=self.depname).order_by().distinct()
- if not self.pkg.arch.agnostic:
- # make sure we match architectures if possible
- arches = self.pkg.applicable_arches()
- pkgs = pkgs.filter(arch__in=arches)
-
- # Logic here is to filter out packages that are in multiple repos if
- # they are not requested. For example, if testing is False, only show a
- # testing package if it doesn't exist in a non-testing repo.
- filtered = {}
- for package in pkgs:
- if package.pkgname not in filtered or \
- package.repo.staging == self.pkg.repo.staging:
- filtered[package.pkgname] = package
- pkgs = filtered.values()
-
- filtered = {}
- for package in pkgs:
- if package.pkgname not in filtered or \
- package.repo.testing == self.pkg.repo.testing:
- filtered[package.pkgname] = package
- pkgs = filtered.values()
-
- return pkgs
-
- def __unicode__(self):
- return "%s%s" % (self.depname, self.depvcmp)
-
- class Meta:
- db_table = 'package_depends'
class Todolist(models.Model):
creator = models.ForeignKey(User, on_delete=models.PROTECT)
diff --git a/packages/models.py b/packages/models.py
index cb65f1f1..c3a16fc5 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -295,7 +295,7 @@ class Meta:
class Depend(RelatedToBase):
- pkg = models.ForeignKey('main.Package', related_name='depends_new')
+ pkg = models.ForeignKey('main.Package', related_name='depends')
comparison = models.CharField(max_length=255, default='')
optional = models.BooleanField(default=False)
description = models.TextField(null=True, blank=True)
--
cgit v1.2.3-54-g00ecf
From b547d41dbf338fb75eb2c6ae05da143a5cd32c74 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 1 Jul 2012 19:40:42 -0500
Subject: Remove no-longer necessary delayed imports of Package
Since commit 158be107e4ad6, we have been importing the Package model at
the top-level in this file, so we can kill this code that was never
updated. This should also give us back any performance hit we were
seeing from the delayed imports.
Signed-off-by: Dan McGee
---
packages/models.py | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index c3a16fc5..2c6ad43c 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -26,8 +26,6 @@ class PackageRelation(models.Model):
created = models.DateTimeField(editable=False)
def get_associated_packages(self):
- # TODO: delayed import to avoid circular reference
- from main.models import Package
return Package.objects.normal().filter(pkgbase=self.pkgbase)
def repositories(self):
@@ -146,8 +144,6 @@ class Signoff(models.Model):
@property
def packages(self):
- # TODO: delayed import to avoid circular reference
- from main.models import Package
return Package.objects.normal().filter(pkgbase=self.pkgbase,
pkgver=self.pkgver, pkgrel=self.pkgrel, epoch=self.epoch,
arch=self.arch, repo=self.repo)
@@ -202,14 +198,15 @@ class PackageGroup(models.Model):
Represents a group a package is in. There is no actual group entity,
only names that link to given packages.
'''
- pkg = models.ForeignKey('main.Package', related_name='groups')
+ pkg = models.ForeignKey(Package, related_name='groups')
name = models.CharField(max_length=255, db_index=True)
def __unicode__(self):
return "%s: %s" % (self.name, self.pkg)
+
class License(models.Model):
- pkg = models.ForeignKey('main.Package', related_name='licenses')
+ pkg = models.ForeignKey(Package, related_name='licenses')
name = models.CharField(max_length=255)
def __unicode__(self):
@@ -295,19 +292,19 @@ class Meta:
class Depend(RelatedToBase):
- pkg = models.ForeignKey('main.Package', related_name='depends')
+ pkg = models.ForeignKey(Package, related_name='depends')
comparison = models.CharField(max_length=255, default='')
optional = models.BooleanField(default=False)
description = models.TextField(null=True, blank=True)
class Conflict(RelatedToBase):
- pkg = models.ForeignKey('main.Package', related_name='conflicts')
+ pkg = models.ForeignKey(Package, related_name='conflicts')
comparison = models.CharField(max_length=255, default='')
class Provision(RelatedToBase):
- pkg = models.ForeignKey('main.Package', related_name='provides')
+ pkg = models.ForeignKey(Package, related_name='provides')
# comparison must be '=' for provides
@property
@@ -318,7 +315,7 @@ def comparison(self):
class Replacement(RelatedToBase):
- pkg = models.ForeignKey('main.Package', related_name='replaces')
+ pkg = models.ForeignKey(Package, related_name='replaces')
comparison = models.CharField(max_length=255, default='')
--
cgit v1.2.3-54-g00ecf
From 43b5c29b3d89cc2e7e7109bb3c7717a87cfc67b5 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 1 Jul 2012 19:57:16 -0500
Subject: Add new package Update model
This will be used to track updates to package as we do them during
reporead. By storing enough relevant fields from the package object, we
should be able to produce a useful report on a regular basis of what has
been happening in the repositories.
Signed-off-by: Dan McGee
---
packages/admin.py | 14 +-
packages/migrations/0017_auto__add_update.py | 226 +++++++++++++++++++++++++++
packages/models.py | 64 +++++++-
3 files changed, 302 insertions(+), 2 deletions(-)
create mode 100644 packages/migrations/0017_auto__add_update.py
(limited to 'packages')
diff --git a/packages/admin.py b/packages/admin.py
index 0589209f..d43cecce 100644
--- a/packages/admin.py
+++ b/packages/admin.py
@@ -1,6 +1,7 @@
from django.contrib import admin
-from .models import PackageRelation, FlagRequest, Signoff, SignoffSpecification
+from .models import (PackageRelation, FlagRequest,
+ Signoff, SignoffSpecification, Update)
class PackageRelationAdmin(admin.ModelAdmin):
list_display = ('pkgbase', 'user', 'type', 'created')
@@ -9,6 +10,7 @@ class PackageRelationAdmin(admin.ModelAdmin):
ordering = ('pkgbase', 'user')
date_hierarchy = 'created'
+
class FlagRequestAdmin(admin.ModelAdmin):
list_display = ('pkgbase', 'version', 'repo', 'created', 'who', 'is_spam',
'is_legitimate', 'message')
@@ -35,9 +37,19 @@ class SignoffSpecificationAdmin(admin.ModelAdmin):
date_hierarchy = 'created'
+class UpdateAdmin(admin.ModelAdmin):
+ list_display = ('pkgname', 'repo', 'arch', 'action_flag',
+ 'old_version', 'new_version', 'created')
+ list_filter = ('action_flag', 'repo', 'arch')
+ search_fields = ('pkgname',)
+ ordering = ('-created',)
+ date_hierarchy = 'created'
+
+
admin.site.register(PackageRelation, PackageRelationAdmin)
admin.site.register(FlagRequest, FlagRequestAdmin)
admin.site.register(Signoff, SignoffAdmin)
admin.site.register(SignoffSpecification, SignoffSpecificationAdmin)
+admin.site.register(Update, UpdateAdmin)
# vim: set ts=4 sw=4 et:
diff --git a/packages/migrations/0017_auto__add_update.py b/packages/migrations/0017_auto__add_update.py
new file mode 100644
index 00000000..c7c16d4e
--- /dev/null
+++ b/packages/migrations/0017_auto__add_update.py
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.create_table('packages_update', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('package', self.gf('django.db.models.fields.related.ForeignKey')(related_name='updates', null=True, on_delete=models.SET_NULL, to=orm['main.Package'])),
+ ('repo', self.gf('django.db.models.fields.related.ForeignKey')(related_name='updates', to=orm['main.Repo'])),
+ ('arch', self.gf('django.db.models.fields.related.ForeignKey')(related_name='updates', to=orm['main.Arch'])),
+ ('pkgname', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('pkgbase', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('action_flag', self.gf('django.db.models.fields.PositiveSmallIntegerField')()),
+ ('created', self.gf('django.db.models.fields.DateTimeField')()),
+ ('old_pkgver', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)),
+ ('old_pkgrel', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)),
+ ('old_epoch', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
+ ('new_pkgver', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)),
+ ('new_pkgrel', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)),
+ ('new_epoch', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
+ ))
+ db.send_create_signal('packages', ['Update'])
+
+
+ def backwards(self, orm):
+ db.delete_table('packages_update')
+
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 2c6ad43c..5ee06575 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -2,11 +2,13 @@
from django.db import models
from django.db.models.signals import pre_save
+from django.contrib.admin.models import ADDITION, CHANGE, DELETION
from django.contrib.auth.models import User
from main.models import Arch, Repo, Package
from main.utils import set_created_field
+
class PackageRelation(models.Model):
'''
Represents maintainership (or interest) in a package by a given developer.
@@ -193,6 +195,66 @@ def who(self):
def __unicode__(self):
return u'%s from %s on %s' % (self.pkgbase, self.who(), self.created)
+
+UPDATE_ACTION_CHOICES = (
+ (ADDITION, 'Addition'),
+ (CHANGE, 'Change'),
+ (DELETION, 'Deletion'),
+)
+
+
+class Update(models.Model):
+ package = models.ForeignKey(Package, related_name="updates",
+ null=True, on_delete=models.SET_NULL)
+ repo = models.ForeignKey(Repo, related_name="updates")
+ arch = models.ForeignKey(Arch, related_name="updates")
+ pkgname = models.CharField(max_length=255)
+ pkgbase = models.CharField(max_length=255)
+ action_flag = models.PositiveSmallIntegerField('action flag',
+ choices=UPDATE_ACTION_CHOICES)
+ created = models.DateTimeField(editable=False)
+
+ old_pkgver = models.CharField(max_length=255, null=True)
+ old_pkgrel = models.CharField(max_length=255, null=True)
+ old_epoch = models.PositiveIntegerField(null=True)
+
+ new_pkgver = models.CharField(max_length=255, null=True)
+ new_pkgrel = models.CharField(max_length=255, null=True)
+ new_epoch = models.PositiveIntegerField(null=True)
+
+ class Meta:
+ get_latest_by = 'created'
+
+ def is_addition(self):
+ return self.action_flag == ADDITION
+
+ def is_change(self):
+ return self.action_flag == CHANGE
+
+ def is_deletion(self):
+ return self.action_flag == DELETION
+
+ @property
+ def old_version(self):
+ if self.action_flag == ADDITION:
+ return None
+ if self.old_epoch > 0:
+ return u'%d:%s-%s' % (self.old_epoch, self.old_pkgver, self.old_pkgrel)
+ return u'%s-%s' % (self.old_pkgver, self.old_pkgrel)
+
+ @property
+ def new_version(self):
+ if self.action_flag == DELETION:
+ return None
+ if self.new_epoch > 0:
+ return u'%d:%s-%s' % (self.new_epoch, self.new_pkgver, self.new_pkgrel)
+ return u'%s-%s' % (self.new_pkgver, self.new_pkgrel)
+
+ def __unicode__(self):
+ return u'%s of %s on %s' % (self.get_action_flag_display(),
+ self.pkgname, self.created)
+
+
class PackageGroup(models.Model):
'''
Represents a group a package is in. There is no actual group entity,
@@ -320,7 +382,7 @@ class Replacement(RelatedToBase):
# hook up some signals
-for sender in (PackageRelation, SignoffSpecification, Signoff):
+for sender in (PackageRelation, SignoffSpecification, Signoff, Update):
pre_save.connect(set_created_field, sender=sender,
dispatch_uid="packages.models")
--
cgit v1.2.3-54-g00ecf
From a87fe016d1a1bf7fdcd2b19f515aa72a5b93db2b Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 1 Jul 2012 20:21:34 -0500
Subject: Log package updates during reporead invocation
This adds a Manager and log_update method to help log all updates made
to the packages table during reporead runs.
Signed-off-by: Dan McGee
---
devel/management/commands/reporead.py | 7 ++++++-
packages/models.py | 36 +++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index 2e8c4625..4e242af1 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -14,6 +14,7 @@
"""
from collections import defaultdict
+from copy import copy
import io
import os
import re
@@ -31,7 +32,7 @@
from devel.utils import UserFinder
from main.models import Arch, Package, PackageFile, Repo
from main.utils import utc_now
-from packages.models import Depend, Conflict, Provision, Replacement
+from packages.models import Depend, Conflict, Provision, Replacement, Update
logging.basicConfig(
@@ -362,6 +363,7 @@ def db_update(archname, reponame, pkgs, force=False):
try:
with transaction.commit_on_success():
populate_pkg(dbpkg, pkg, timestamp=utc_now())
+ Update.objects.log_update(None, dbpkg)
except IntegrityError:
logger.warning("Could not add package %s; "
"not fatal if another thread beat us to it.",
@@ -372,6 +374,7 @@ def db_update(archname, reponame, pkgs, force=False):
logger.info("Removing package %s", pkgname)
dbpkg = dbdict[pkgname]
with transaction.commit_on_success():
+ Update.objects.log_update(dbpkg, None)
# no race condition here as long as simultaneous threads both
# issue deletes; second delete will be a no-op
delete_pkg_files(dbpkg)
@@ -399,7 +402,9 @@ def db_update(archname, reponame, pkgs, force=False):
logger.debug("Package %s was already updated", pkg.name)
continue
logger.info("Updating package %s", pkg.name)
+ prevpkg = copy(dbpkg)
populate_pkg(dbpkg, pkg, force=force, timestamp=timestamp)
+ Update.objects.log_update(prevpkg, dbpkg)
logger.info('Finished updating arch: %s', archname)
diff --git a/packages/models.py b/packages/models.py
index 5ee06575..04f35f9d 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -203,6 +203,40 @@ def __unicode__(self):
)
+class UpdateManager(models.Manager):
+ def log_update(self, old_pkg, new_pkg):
+ '''Utility method to help log an update. This will determine the type
+ based on how many packages are passed in, and will pull the relevant
+ necesary fields off the given packages.'''
+ update = Update()
+ if new_pkg:
+ update.action_flag = ADDITION
+ update.package = new_pkg
+ update.arch = new_pkg.arch
+ update.repo = new_pkg.repo
+ update.pkgname = new_pkg.pkgname
+ update.pkgbase = new_pkg.pkgbase
+ update.new_pkgver = new_pkg.pkgver
+ update.new_pkgrel = new_pkg.pkgrel
+ update.new_epoch = new_pkg.epoch
+ if old_pkg:
+ if new_pkg:
+ update.action_flag = CHANGE
+ else:
+ update.action_flag = DELETION
+ update.arch = old_pkg.arch
+ update.repo = old_pkg.repo
+ update.pkgname = old_pkg.pkgname
+ update.pkgbase = old_pkg.pkgbase
+
+ update.old_pkgver = old_pkg.pkgver
+ update.old_pkgrel = old_pkg.pkgrel
+ update.old_epoch = old_pkg.epoch
+
+ update.save(force_insert=True)
+ return update
+
+
class Update(models.Model):
package = models.ForeignKey(Package, related_name="updates",
null=True, on_delete=models.SET_NULL)
@@ -222,6 +256,8 @@ class Update(models.Model):
new_pkgrel = models.CharField(max_length=255, null=True)
new_epoch = models.PositiveIntegerField(null=True)
+ objects = UpdateManager()
+
class Meta:
get_latest_by = 'created'
--
cgit v1.2.3-54-g00ecf
From 34e877d3328edd93b24bc82e63a89781d4350658 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 1 Jul 2012 20:35:34 -0500
Subject: Add indexes on 'created' field to several package-related models
These models regularly sort by or limit by the created field, so adding
a index on the created database column makes sense.
Signed-off-by: Dan McGee
---
packages/migrations/0018_create_created_indexes.py | 214 +++++++++++++++++++++
packages/models.py | 6 +-
2 files changed, 217 insertions(+), 3 deletions(-)
create mode 100644 packages/migrations/0018_create_created_indexes.py
(limited to 'packages')
diff --git a/packages/migrations/0018_create_created_indexes.py b/packages/migrations/0018_create_created_indexes.py
new file mode 100644
index 00000000..678a04d4
--- /dev/null
+++ b/packages/migrations/0018_create_created_indexes.py
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.create_index('packages_flagrequest', ['created'])
+ db.create_index('packages_update', ['created'])
+ db.create_index('packages_signoff', ['created'])
+
+
+ def backwards(self, orm):
+ db.delete_index('packages_signoff', ['created'])
+ db.delete_index('packages_update', ['created'])
+ db.delete_index('packages_flagrequest', ['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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 04f35f9d..2f03a28b 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -138,7 +138,7 @@ class Signoff(models.Model):
arch = models.ForeignKey(Arch)
repo = models.ForeignKey(Repo)
user = models.ForeignKey(User, related_name="package_signoffs")
- created = models.DateTimeField(editable=False)
+ created = models.DateTimeField(editable=False, db_index=True)
revoked = models.DateTimeField(null=True)
comments = models.TextField(null=True, blank=True)
@@ -170,7 +170,7 @@ class FlagRequest(models.Model):
'''
user = models.ForeignKey(User, blank=True, null=True)
user_email = models.EmailField('email address')
- created = models.DateTimeField(editable=False)
+ created = models.DateTimeField(editable=False, db_index=True)
# Great work, Django... https://code.djangoproject.com/ticket/18212
ip_address = models.GenericIPAddressField(verbose_name='IP address',
unpack_ipv4=True)
@@ -246,7 +246,7 @@ class Update(models.Model):
pkgbase = models.CharField(max_length=255)
action_flag = models.PositiveSmallIntegerField('action flag',
choices=UPDATE_ACTION_CHOICES)
- created = models.DateTimeField(editable=False)
+ created = models.DateTimeField(editable=False, db_index=True)
old_pkgver = models.CharField(max_length=255, null=True)
old_pkgrel = models.CharField(max_length=255, null=True)
--
cgit v1.2.3-54-g00ecf
From b95b0cd4197d70831754a7e81b40388c37ab1a3d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 8 Jul 2012 20:51:23 -0500
Subject: Use a set instead of list when gathering package IDs to fetch
If we have duplicates in this list, it makes no sense to include them in
the list we send to the database.
Signed-off-by: Dan McGee
---
packages/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index 82313472..b86b6eba 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -123,7 +123,7 @@ def get_differences_info(arch_a, arch_b):
cursor.execute(sql, [arch_a.id, arch_b.id])
results = cursor.fetchall()
# column A will always have a value, column B might be NULL
- to_fetch = [row[0] for row in results]
+ to_fetch = set(row[0] for row in results)
# fetch all of the necessary packages
pkgs = Package.objects.normal().in_bulk(to_fetch)
# now build a list of tuples containing differences
--
cgit v1.2.3-54-g00ecf
From 4cd588ae898c2abc8035cf0165ccdd038f2321bd Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 7 Jul 2012 16:50:06 -0500
Subject: Add triggers for adding package update rows
This will be done instead of doing this logic at the application level,
which has some subtle race conditions. When two simultaneous threads
attempt to delete the same package, two update rows for the delete
action are inserted. When done at the database level, we can ensure a
one-to-one mapping between row operations and entries in this table.
Signed-off-by: Dan McGee
---
packages/sql/update.postgresql_psycopg2.sql | 45 +++++++++++++++++++++++++++++
packages/sql/update.sqlite3.sql | 30 +++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 packages/sql/update.postgresql_psycopg2.sql
create mode 100644 packages/sql/update.sqlite3.sql
(limited to 'packages')
diff --git a/packages/sql/update.postgresql_psycopg2.sql b/packages/sql/update.postgresql_psycopg2.sql
new file mode 100644
index 00000000..6d678387
--- /dev/null
+++ b/packages/sql/update.postgresql_psycopg2.sql
@@ -0,0 +1,45 @@
+CREATE OR REPLACE FUNCTION packages_on_insert() RETURNS trigger AS $body$
+BEGIN
+ INSERT INTO packages_update
+ (action_flag, created, package_id, arch_id, repo_id, pkgname, pkgbase, new_pkgver, new_pkgrel, new_epoch)
+ VALUES (1, now(), NEW.id, NEW.arch_id, NEW.repo_id, NEW.pkgname, NEW.pkgbase, NEW.pkgver, NEW.pkgrel, NEW.epoch);
+ RETURN NULL;
+END;
+$body$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION packages_on_update() RETURNS trigger AS $body$
+BEGIN
+ INSERT INTO packages_update
+ (action_flag, created, package_id, arch_id, repo_id, pkgname, pkgbase, old_pkgver, old_pkgrel, old_epoch, new_pkgver, new_pkgrel, new_epoch)
+ VALUES (2, now(), NEW.id, NEW.arch_id, NEW.repo_id, NEW.pkgname, NEW.pkgbase, OLD.pkgver, OLD.pkgrel, OLD.epoch, NEW.pkgver, NEW.pkgrel, NEW.epoch);
+ RETURN NULL;
+END;
+$body$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION packages_on_delete() RETURNS trigger AS $body$
+BEGIN
+ INSERT INTO packages_update
+ (action_flag, created, arch_id, repo_id, pkgname, pkgbase, old_pkgver, old_pkgrel, old_epoch)
+ VALUES (3, now(), OLD.arch_id, OLD.repo_id, OLD.pkgname, OLD.pkgbase, OLD.pkgver, OLD.pkgrel, OLD.epoch);
+ RETURN NULL;
+END;
+$body$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS packages_insert ON packages;
+CREATE TRIGGER packages_insert
+ AFTER INSERT ON packages
+ FOR EACH ROW
+ EXECUTE PROCEDURE packages_on_insert();
+
+DROP TRIGGER IF EXISTS packages_update ON packages;
+CREATE TRIGGER packages_update
+ AFTER UPDATE ON packages
+ FOR EACH ROW
+ WHEN (OLD.pkgver != NEW.pkgver OR OLD.pkgrel != NEW.pkgrel OR OLD.epoch != NEW.epoch)
+ EXECUTE PROCEDURE packages_on_update();
+
+DROP TRIGGER IF EXISTS packages_delete ON packages;
+CREATE TRIGGER packages_delete
+ AFTER DELETE ON packages
+ FOR EACH ROW
+ EXECUTE PROCEDURE packages_on_delete();
diff --git a/packages/sql/update.sqlite3.sql b/packages/sql/update.sqlite3.sql
new file mode 100644
index 00000000..6f151bdd
--- /dev/null
+++ b/packages/sql/update.sqlite3.sql
@@ -0,0 +1,30 @@
+DROP TRIGGER IF EXISTS packages_insert;
+CREATE TRIGGER packages_insert
+ AFTER INSERT ON packages
+ FOR EACH ROW
+ BEGIN
+ INSERT INTO packages_update
+ (action_flag, created, package_id, arch_id, repo_id, pkgname, pkgbase, new_pkgver, new_pkgrel, new_epoch)
+ VALUES (1, strftime('%Y-%m-%d %H:%M:%f', 'now'), NEW.id, NEW.arch_id, NEW.repo_id, NEW.pkgname, NEW.pkgbase, NEW.pkgver, NEW.pkgrel, NEW.epoch);
+ END;
+
+DROP TRIGGER IF EXISTS packages_update;
+CREATE TRIGGER packages_update
+ AFTER UPDATE ON packages
+ FOR EACH ROW
+ WHEN (OLD.pkgver != NEW.pkgver OR OLD.pkgrel != NEW.pkgrel OR OLD.epoch != NEW.epoch)
+ BEGIN
+ INSERT INTO packages_update
+ (action_flag, created, package_id, arch_id, repo_id, pkgname, pkgbase, old_pkgver, old_pkgrel, old_epoch, new_pkgver, new_pkgrel, new_epoch)
+ VALUES (2, strftime('%Y-%m-%d %H:%M:%f', 'now'), NEW.id, NEW.arch_id, NEW.repo_id, NEW.pkgname, NEW.pkgbase, OLD.pkgver, OLD.pkgrel, OLD.epoch, NEW.pkgver, NEW.pkgrel, NEW.epoch);
+ END;
+
+DROP TRIGGER IF EXISTS packages_delete;
+CREATE TRIGGER packages_delete
+ AFTER DELETE ON packages
+ FOR EACH ROW
+ BEGIN
+ INSERT INTO packages_update
+ (action_flag, created, arch_id, repo_id, pkgname, pkgbase, old_pkgver, old_pkgrel, old_epoch)
+ VALUES (3, strftime('%Y-%m-%d %H:%M:%f', 'now'), OLD.arch_id, OLD.repo_id, OLD.pkgname, OLD.pkgbase, OLD.pkgver, OLD.pkgrel, OLD.epoch);
+ END;
--
cgit v1.2.3-54-g00ecf
From 26a00cadcebc0b37775954d261ec73f927ceca12 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 7 Jul 2012 17:28:02 -0500
Subject: Don't log package updates in Python when we have DB trigger support
This adds a helper method to find the database engine in use, and then
skips code we shouldn't execute if we are doing this another way.
Note that this helper method could be useful for backend-specific code
paths elsewhere, such as custom SQL being called or lack of StdDev() in
sqlite3 out of the box.
Signed-off-by: Dan McGee
---
main/utils.py | 11 +++++++++++
packages/models.py | 15 +++++++++++++--
2 files changed, 24 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/main/utils.py b/main/utils.py
index b7cb19f4..879abfb9 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -8,6 +8,7 @@
from pytz import utc
from django.core.cache import cache
+from django.db import connections, router
CACHE_TIMEOUT = 1800
@@ -106,6 +107,16 @@ def set_created_field(sender, **kwargs):
obj.created = utc_now()
+def database_vendor(model, mode='read'):
+ if mode == 'read':
+ database = router.db_for_read(model)
+ elif mode == 'write':
+ database = router.db_for_write(model)
+ else:
+ raise Exception('Invalid database mode specified')
+ return connections[database].vendor
+
+
def groupby_preserve_order(iterable, keyfunc):
'''Take an iterable and regroup using keyfunc to determine whether items
belong to the same group. The order of the iterable is preserved and
diff --git a/packages/models.py b/packages/models.py
index 2f03a28b..45ff3c08 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -6,7 +6,7 @@
from django.contrib.auth.models import User
from main.models import Arch, Repo, Package
-from main.utils import set_created_field
+from main.utils import set_created_field, database_vendor
class PackageRelation(models.Model):
@@ -207,7 +207,12 @@ class UpdateManager(models.Manager):
def log_update(self, old_pkg, new_pkg):
'''Utility method to help log an update. This will determine the type
based on how many packages are passed in, and will pull the relevant
- necesary fields off the given packages.'''
+ necesary fields off the given packages.
+ Note that in some cases, this is a no-op if we know this database type
+ supports triggers to add these rows instead.'''
+ if database_vendor(Package, 'write') in ('sqlite', 'postgresql'):
+ # we log updates using database triggers for these backends
+ return
update = Update()
if new_pkg:
update.action_flag = ADDITION
@@ -222,6 +227,12 @@ def log_update(self, old_pkg, new_pkg):
if old_pkg:
if new_pkg:
update.action_flag = CHANGE
+ # ensure we should even be logging this
+ if (old_pkg.pkgver == new_pkg.pkgver and
+ old_pkg.pkgrel == new_pkg.pkgrel and
+ old_pkg.epoch == new_pkg.epoch):
+ # all relevant fields were the same; e.g. a force update
+ return
else:
update.action_flag = DELETION
update.arch = old_pkg.arch
--
cgit v1.2.3-54-g00ecf
From 3c906888e2ba9e55cef00dfc61667fb383c9754d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 8 Jul 2012 20:44:07 -0500
Subject: Get multilib package differences query working on sqlite3
Thank you database engines for all implementing such simple operations
as substring() and length() in different ways.
Signed-off-by: Dan McGee
---
packages/utils.py | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index b86b6eba..6d54d71a 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -8,7 +8,8 @@
from django.contrib.auth.models import User
from main.models import Package, PackageFile, Arch, Repo
-from main.utils import cache_function, groupby_preserve_order, PackageStandin
+from main.utils import (cache_function, database_vendor,
+ groupby_preserve_order, PackageStandin)
from .models import (PackageGroup, PackageRelation,
License, Depend, Conflict, Provision, Replacement,
SignoffSpecification, Signoff, DEFAULT_SIGNOFF_SPEC)
@@ -150,12 +151,18 @@ def get_differences_info(arch_a, arch_b):
def multilib_differences():
# Query for checking multilib out of date-ness
- sql = """
-SELECT ml.id, reg.id
- FROM packages ml
- JOIN packages reg
- ON (
- reg.pkgname = (
+ if database_vendor(Package) == 'sqlite':
+ pkgname_sql = """
+ CASE WHEN ml.pkgname LIKE %s
+ THEN SUBSTR(ml.pkgname, 7)
+ WHEN ml.pkgname LIKE %s
+ THEN SUBSTR(ml.pkgname, 1, LENGTH(ml.pkgname) - 9)
+ ELSE
+ ml.pkgname
+ END
+ """
+ else:
+ pkgname_sql = """
CASE WHEN ml.pkgname LIKE %s
THEN SUBSTRING(ml.pkgname, 7)
WHEN ml.pkgname LIKE %s
@@ -163,7 +170,13 @@ def multilib_differences():
ELSE
ml.pkgname
END
- )
+ """
+ sql = """
+SELECT ml.id, reg.id
+ FROM packages ml
+ JOIN packages reg
+ ON (
+ reg.pkgname = (""" + pkgname_sql + """)
AND reg.pkgver != ml.pkgver
)
JOIN repos r ON reg.repo_id = r.id
@@ -172,7 +185,7 @@ def multilib_differences():
AND r.staging = %s
AND reg.arch_id = %s
ORDER BY ml.last_update
-"""
+ """
multilib = Repo.objects.get(name__iexact='multilib')
i686 = Arch.objects.get(name='i686')
params = ['lib32-%', '%-multilib', multilib.id, False, False, i686.id]
--
cgit v1.2.3-54-g00ecf
From 872d4bcaa2ce85d2d319a1146e0fc05ab6808eb9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 21 Jul 2012 11:26:34 -0500
Subject: Split details/display package views into new module
This moves a lot of the package and group display logic into a new view
module, similar to what we already did earlier with a bunch of other
views.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 154 ++-----------------------------------------
packages/views/display.py | 160 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 167 insertions(+), 147 deletions(-)
create mode 100644 packages/views/display.py
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 3e574c26..fa67daa8 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -1,27 +1,24 @@
import hashlib
import json
-from string import Template
-from urllib import urlencode
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
from django.core.cache import cache
-from django.http import HttpResponse, Http404
-from django.shortcuts import get_object_or_404, redirect
+from django.http import HttpResponse
+from django.shortcuts import redirect
from django.views.decorators.cache import cache_control
from django.views.decorators.http import require_GET, require_POST
-from django.views.decorators.vary import vary_on_headers
from django.views.generic.simple import direct_to_template
-from main.models import Package, PackageFile, Arch, Repo
-from mirrors.models import MirrorUrl
-from mirrors.utils import get_mirror_url_for_download
+from main.models import Package, Arch
from ..models import PackageRelation
-from ..utils import (get_group_info, get_differences_info,
- multilib_differences, get_wrong_permissions, PackageJSONEncoder)
+from ..utils import (get_differences_info,
+ multilib_differences, get_wrong_permissions)
# make other views available from this same package
+from .display import (details, groups, group_details, files, details_json,
+ files_json, download)
from .flag import flaghelp, flag, flag_confirmed, unflag, unflag_all
from .search import search, search_json
from .signoff import signoffs, signoff_package, signoff_options, signoffs_json
@@ -105,143 +102,6 @@ def update(request):
messages.error(request, "Are you trying to adopt or disown?")
return redirect('/packages/')
-def split_package_details(request, name='', repo='', arch=''):
- arch = get_object_or_404(Arch, name=arch)
- arches = [ arch ]
- arches.extend(Arch.objects.filter(agnostic=True))
- repo = get_object_or_404(Repo, name__iexact=repo)
- pkgs = Package.objects.normal().filter(pkgbase=name,
- repo__testing=repo.testing, repo__staging=repo.staging,
- arch__in=arches).order_by('pkgname')
- if len(pkgs) == 0:
- raise Http404
- # we have packages, but ensure at least one is in the given repo
- if not any(True for pkg in pkgs if pkg.repo == repo):
- raise Http404
- context = {
- 'list_title': 'Split Package Details',
- 'name': name,
- 'arch': arch,
- 'packages': pkgs,
- }
- return direct_to_template(request, 'packages/packages_list.html',
- context)
-
-def details(request, name='', repo='', arch=''):
- if all([name, repo, arch]):
- try:
- 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, })
- except Package.DoesNotExist:
- arch_obj = get_object_or_404(Arch, name=arch)
- # for arch='any' packages, we can issue a redirect to them if we
- # have a single non-ambiguous option by changing the arch to match
- # any arch-agnostic package
- if not arch_obj.agnostic:
- pkgs = Package.objects.select_related(
- 'arch', 'repo', 'packager').filter(pkgname=name,
- repo__name__iexact=repo, arch__agnostic=True)
- if len(pkgs) == 1:
- return redirect(pkgs[0], permanent=True)
- return split_package_details(request, name, repo, arch)
- else:
- pkg_data = [
- ('arch', arch.lower()),
- ('repo', repo.lower()),
- ('q', name),
- ]
- # only include non-blank values in the query we generate
- pkg_data = [(x, y.encode('utf-8')) for x, y in pkg_data if y]
- return redirect("/packages/?%s" % urlencode(pkg_data))
-
-def groups(request, arch=None):
- arches = []
- if arch:
- get_object_or_404(Arch, name=arch, agnostic=False)
- arches.append(arch)
- grps = get_group_info(arches)
- context = {
- 'groups': grps,
- 'arch': arch,
- }
- return direct_to_template(request, 'packages/groups.html', context)
-
-def group_details(request, arch, name):
- arch = get_object_or_404(Arch, name=arch)
- arches = [ arch ]
- arches.extend(Arch.objects.filter(agnostic=True))
- pkgs = Package.objects.normal().filter(
- groups__name=name, arch__in=arches).order_by('pkgname')
- if len(pkgs) == 0:
- raise Http404
- context = {
- 'list_title': 'Group Details',
- 'name': name,
- 'arch': arch,
- 'packages': pkgs,
- }
- return direct_to_template(request, 'packages/packages_list.html', context)
-
-@vary_on_headers('X-Requested-With')
-def files(request, name, repo, arch):
- pkg = get_object_or_404(Package,
- pkgname=name, repo__name__iexact=repo, arch__name=arch)
- # files are inserted in sorted order, so preserve that
- fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
- dir_count = sum(1 for f in fileslist if f.is_directory)
- files_count = len(fileslist) - dir_count
- context = {
- 'pkg': pkg,
- 'files': fileslist,
- 'files_count': files_count,
- 'dir_count': dir_count,
- }
- template = 'packages/files.html'
- if request.is_ajax():
- template = 'packages/files_list.html'
- return direct_to_template(request, template, context)
-
-def details_json(request, name, repo, arch):
- pkg = get_object_or_404(Package,
- pkgname=name, repo__name__iexact=repo, arch__name=arch)
- to_json = json.dumps(pkg, ensure_ascii=False, cls=PackageJSONEncoder)
- return HttpResponse(to_json, mimetype='application/json')
-
-def files_json(request, name, repo, arch):
- pkg = get_object_or_404(Package,
- pkgname=name, repo__name__iexact=repo, arch__name=arch)
- # files are inserted in sorted order, so preserve that
- fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
- data = {
- 'pkgname': pkg.pkgname,
- 'repo': pkg.repo.name.lower(),
- 'arch': pkg.arch.name.lower(),
- 'files': fileslist,
- }
- to_json = json.dumps(data, ensure_ascii=False, cls=PackageJSONEncoder)
- return HttpResponse(to_json, mimetype='application/json')
-
-def download(request, name, repo, arch):
- pkg = get_object_or_404(Package,
- pkgname=name, repo__name__iexact=repo, arch__name=arch)
- url = get_mirror_url_for_download()
- if not url:
- raise Http404
- arch = pkg.arch.name
- if pkg.arch.agnostic:
- # grab the first non-any arch to fake the download path
- arch = Arch.objects.exclude(agnostic=True)[0].name
- values = {
- 'host': url.url,
- 'arch': arch,
- 'repo': pkg.repo.name.lower(),
- 'file': pkg.filename,
- }
- url = Template('${host}${repo}/os/${arch}/${file}').substitute(values)
- return redirect(url)
def arch_differences(request):
# TODO: we have some hardcoded magic here with respect to the arches.
diff --git a/packages/views/display.py b/packages/views/display.py
new file mode 100644
index 00000000..dd57571a
--- /dev/null
+++ b/packages/views/display.py
@@ -0,0 +1,160 @@
+import json
+from string import Template
+from urllib import urlencode
+
+from django.http import HttpResponse, Http404
+from django.shortcuts import get_object_or_404, redirect
+from django.views.generic.simple import direct_to_template
+from django.views.decorators.vary import vary_on_headers
+
+from main.models import Package, PackageFile, Arch, Repo
+from mirrors.utils import get_mirror_url_for_download
+from ..utils import get_group_info, PackageJSONEncoder
+
+
+def split_package_details(request, name='', repo='', arch=''):
+ arch = get_object_or_404(Arch, name=arch)
+ arches = [ arch ]
+ arches.extend(Arch.objects.filter(agnostic=True))
+ repo = get_object_or_404(Repo, name__iexact=repo)
+ pkgs = Package.objects.normal().filter(pkgbase=name,
+ repo__testing=repo.testing, repo__staging=repo.staging,
+ arch__in=arches).order_by('pkgname')
+ if len(pkgs) == 0:
+ raise Http404
+ # we have packages, but ensure at least one is in the given repo
+ if not any(True for pkg in pkgs if pkg.repo == repo):
+ raise Http404
+ context = {
+ 'list_title': 'Split Package Details',
+ 'name': name,
+ 'arch': arch,
+ 'packages': pkgs,
+ }
+ return direct_to_template(request, 'packages/packages_list.html',
+ context)
+
+
+def details(request, name='', repo='', arch=''):
+ if all([name, repo, arch]):
+ try:
+ 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, })
+ except Package.DoesNotExist:
+ arch_obj = get_object_or_404(Arch, name=arch)
+ # for arch='any' packages, we can issue a redirect to them if we
+ # have a single non-ambiguous option by changing the arch to match
+ # any arch-agnostic package
+ if not arch_obj.agnostic:
+ pkgs = Package.objects.select_related(
+ 'arch', 'repo', 'packager').filter(pkgname=name,
+ repo__name__iexact=repo, arch__agnostic=True)
+ if len(pkgs) == 1:
+ return redirect(pkgs[0], permanent=True)
+ return split_package_details(request, name, repo, arch)
+ else:
+ pkg_data = [
+ ('arch', arch.lower()),
+ ('repo', repo.lower()),
+ ('q', name),
+ ]
+ # only include non-blank values in the query we generate
+ pkg_data = [(x, y.encode('utf-8')) for x, y in pkg_data if y]
+ return redirect("/packages/?%s" % urlencode(pkg_data))
+
+
+def groups(request, arch=None):
+ arches = []
+ if arch:
+ get_object_or_404(Arch, name=arch, agnostic=False)
+ arches.append(arch)
+ grps = get_group_info(arches)
+ context = {
+ 'groups': grps,
+ 'arch': arch,
+ }
+ return direct_to_template(request, 'packages/groups.html', context)
+
+
+def group_details(request, arch, name):
+ arch = get_object_or_404(Arch, name=arch)
+ arches = [ arch ]
+ arches.extend(Arch.objects.filter(agnostic=True))
+ pkgs = Package.objects.normal().filter(
+ groups__name=name, arch__in=arches).order_by('pkgname')
+ if len(pkgs) == 0:
+ raise Http404
+ context = {
+ 'list_title': 'Group Details',
+ 'name': name,
+ 'arch': arch,
+ 'packages': pkgs,
+ }
+ return direct_to_template(request, 'packages/packages_list.html', context)
+
+
+@vary_on_headers('X-Requested-With')
+def files(request, name, repo, arch):
+ pkg = get_object_or_404(Package,
+ pkgname=name, repo__name__iexact=repo, arch__name=arch)
+ # files are inserted in sorted order, so preserve that
+ fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
+ dir_count = sum(1 for f in fileslist if f.is_directory)
+ files_count = len(fileslist) - dir_count
+ context = {
+ 'pkg': pkg,
+ 'files': fileslist,
+ 'files_count': files_count,
+ 'dir_count': dir_count,
+ }
+ template = 'packages/files.html'
+ if request.is_ajax():
+ template = 'packages/files_list.html'
+ return direct_to_template(request, template, context)
+
+
+def details_json(request, name, repo, arch):
+ pkg = get_object_or_404(Package,
+ pkgname=name, repo__name__iexact=repo, arch__name=arch)
+ to_json = json.dumps(pkg, ensure_ascii=False, cls=PackageJSONEncoder)
+ return HttpResponse(to_json, mimetype='application/json')
+
+
+def files_json(request, name, repo, arch):
+ pkg = get_object_or_404(Package,
+ pkgname=name, repo__name__iexact=repo, arch__name=arch)
+ # files are inserted in sorted order, so preserve that
+ fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
+ data = {
+ 'pkgname': pkg.pkgname,
+ 'repo': pkg.repo.name.lower(),
+ 'arch': pkg.arch.name.lower(),
+ 'files': fileslist,
+ }
+ to_json = json.dumps(data, ensure_ascii=False, cls=PackageJSONEncoder)
+ return HttpResponse(to_json, mimetype='application/json')
+
+
+def download(request, name, repo, arch):
+ pkg = get_object_or_404(Package,
+ pkgname=name, repo__name__iexact=repo, arch__name=arch)
+ url = get_mirror_url_for_download()
+ if not url:
+ raise Http404
+ arch = pkg.arch.name
+ if pkg.arch.agnostic:
+ # grab the first non-any arch to fake the download path
+ arch = Arch.objects.exclude(agnostic=True)[0].name
+ values = {
+ 'host': url.url,
+ 'arch': arch,
+ 'repo': pkg.repo.name.lower(),
+ 'file': pkg.filename,
+ }
+ url = Template('${host}${repo}/os/${arch}/${file}').substitute(values)
+ return redirect(url)
+
+# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From 946d90d08f29094153142056a1778cd595e568a3 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 23 Jul 2012 09:45:44 -0500
Subject: Add '410 Gone' support for packages moved out of repositories
This allows us to do better than a generic 404 handler when we know a
package previously existed in a given repository, and should also make
things a bit nicer when getting sent in from a search engine to a page
that no longer exists.
Signed-off-by: Dan McGee
---
packages/models.py | 3 +++
packages/views/display.py | 46 ++++++++++++++++++++++++++++++++++++-----
templates/packages/removed.html | 28 +++++++++++++++++++++++++
3 files changed, 72 insertions(+), 5 deletions(-)
create mode 100644 templates/packages/removed.html
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index 45ff3c08..18a62a3c 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -297,6 +297,9 @@ def new_version(self):
return u'%d:%s-%s' % (self.new_epoch, self.new_pkgver, self.new_pkgrel)
return u'%s-%s' % (self.new_pkgver, self.new_pkgrel)
+ def elsewhere(self):
+ return Package.objects.filter(pkgname=self.pkgname, arch=self.arch)
+
def __unicode__(self):
return u'%s of %s on %s' % (self.get_action_flag_display(),
self.pkgname, self.created)
diff --git a/packages/views/display.py b/packages/views/display.py
index dd57571a..5332dc13 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -1,18 +1,21 @@
+import datetime
import json
from string import Template
from urllib import urlencode
from django.http import HttpResponse, Http404
-from django.shortcuts import get_object_or_404, redirect
+from django.shortcuts import get_object_or_404, redirect, render
+from django.utils.timezone import now
from django.views.generic.simple import direct_to_template
from django.views.decorators.vary import vary_on_headers
from main.models import Package, PackageFile, Arch, Repo
from mirrors.utils import get_mirror_url_for_download
+from ..models import Update
from ..utils import get_group_info, PackageJSONEncoder
-def split_package_details(request, name='', repo='', arch=''):
+def split_package_details(request, name, repo, arch):
arch = get_object_or_404(Arch, name=arch)
arches = [ arch ]
arches.extend(Arch.objects.filter(agnostic=True))
@@ -21,10 +24,10 @@ def split_package_details(request, name='', repo='', arch=''):
repo__testing=repo.testing, repo__staging=repo.staging,
arch__in=arches).order_by('pkgname')
if len(pkgs) == 0:
- raise Http404
+ return None
# we have packages, but ensure at least one is in the given repo
if not any(True for pkg in pkgs if pkg.repo == repo):
- raise Http404
+ return None
context = {
'list_title': 'Split Package Details',
'name': name,
@@ -35,6 +38,30 @@ def split_package_details(request, name='', repo='', arch=''):
context)
+CUTOFF = datetime.timedelta(days=60)
+
+
+def recently_removed_package(request, name, repo, arch, cutoff=CUTOFF):
+ '''We're just steps away from raising a 404, but check our packages update
+ table first to see if this package has existed in this repo before. If so,
+ we can show a 410 Gone page and point the requester in the right
+ direction.'''
+ arch = get_object_or_404(Arch, name=arch)
+ arches = [ arch ]
+ arches.extend(Arch.objects.filter(agnostic=True))
+ match = Update.objects.select_related('arch', 'repo').filter(
+ pkgname=name, repo__name__iexact=repo, arch__in=arches)
+ if cutoff is not None:
+ when = now() - cutoff
+ match = match.filter(created__gte=when)
+ try:
+ match = match.latest()
+ return render(request, 'packages/removed.html',
+ {'update': match, }, status=410)
+ except Update.DoesNotExist:
+ return None
+
+
def details(request, name='', repo='', arch=''):
if all([name, repo, arch]):
try:
@@ -54,7 +81,16 @@ def details(request, name='', repo='', arch=''):
repo__name__iexact=repo, arch__agnostic=True)
if len(pkgs) == 1:
return redirect(pkgs[0], permanent=True)
- return split_package_details(request, name, repo, arch)
+ # do we have a split package matching this criteria?
+ ret = split_package_details(request, name, repo, arch)
+ if ret is None:
+ # maybe we have a recently-removed package?
+ ret = recently_removed_package(request, name, repo, arch)
+ if ret is not None:
+ return ret
+ else:
+ # we've tried everything at this point, nothing to see
+ raise Http404
else:
pkg_data = [
('arch', arch.lower()),
diff --git a/templates/packages/removed.html b/templates/packages/removed.html
new file mode 100644
index 00000000..17b1f989
--- /dev/null
+++ b/templates/packages/removed.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% load url from future %}
+{% load package_extras %}
+
+{% block title %}Arch Linux - Not Available - {{ update.pkgname }} {{ update.old_version }} ({{ update.arch.name }}){% endblock %}
+{% block navbarclass %}anb-packages{% endblock %}
+
+{% block content %}
+
+
{{ update.pkgname }} {{ update.old_version }} is no longer available
+
+
{{ update.pkgname }} {{ update.old_version }} has been removed from the [{{ update.repo.name|lower }}] repository.
+
+ {% with update.elsewhere as elsewhere %}{% if elsewhere %}
+
However, this package is available in other repositories:
+
+ {% for pkg in elsewhere %}
+ {% pkg_details_link pkg %} {{ pkg.full_version }} [{{ pkg.repo.name|lower }}] ({{ pkg.arch.name }})
+ {% endfor %}
+
+ {% else %}
+
Unfortunately, this package cannot be found in any other repositories.
+ Try using the package search page ,
+ or try searching the AUR
+ to see if the package can be found there.
+ {% endif %}{% endwith %}
+
+{% endblock %}
--
cgit v1.2.3-54-g00ecf
From 374bf53505480eebd675e95f975dc908c485a6fa Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 23 Jul 2012 21:17:02 -0500
Subject: Remove files list AJAX conditionals
Now that we just generate this list in JS, we don't need this separate
code.
Signed-off-by: Dan McGee
---
packages/views/display.py | 4 ----
1 file changed, 4 deletions(-)
(limited to 'packages')
diff --git a/packages/views/display.py b/packages/views/display.py
index 5332dc13..d5aa2c20 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -7,7 +7,6 @@
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.timezone import now
from django.views.generic.simple import direct_to_template
-from django.views.decorators.vary import vary_on_headers
from main.models import Package, PackageFile, Arch, Repo
from mirrors.utils import get_mirror_url_for_download
@@ -132,7 +131,6 @@ def group_details(request, arch, name):
return direct_to_template(request, 'packages/packages_list.html', context)
-@vary_on_headers('X-Requested-With')
def files(request, name, repo, arch):
pkg = get_object_or_404(Package,
pkgname=name, repo__name__iexact=repo, arch__name=arch)
@@ -147,8 +145,6 @@ def files(request, name, repo, arch):
'dir_count': dir_count,
}
template = 'packages/files.html'
- if request.is_ajax():
- template = 'packages/files_list.html'
return direct_to_template(request, template, context)
--
cgit v1.2.3-54-g00ecf
From 211340c8bd6ccd6b16f3115a71fce4abedcc4c06 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 23 Jul 2012 21:31:17 -0500
Subject: Ensure package files JS can support corner cases
We should handle the cases dealing with no filelist available, outdated
filelist, or a package without files, just as the HTML server-side page
does. Add a bit more info to the JSON returned so we can do so.
Signed-off-by: Dan McGee
---
packages/views/display.py | 6 ++++++
sitestatic/archweb.js | 12 +++++++++++-
2 files changed, 17 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/views/display.py b/packages/views/display.py
index d5aa2c20..02f5a5b2 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -160,10 +160,16 @@ def files_json(request, name, repo, arch):
pkgname=name, repo__name__iexact=repo, arch__name=arch)
# files are inserted in sorted order, so preserve that
fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
+ dir_count = sum(1 for f in fileslist if f.is_directory)
+ files_count = len(fileslist) - dir_count
data = {
'pkgname': pkg.pkgname,
'repo': pkg.repo.name.lower(),
'arch': pkg.arch.name.lower(),
+ 'pkg_last_update': pkg.last_update,
+ 'files_last_update': pkg.files_last_update,
+ 'files_count': files_count,
+ 'dir_count': dir_count,
'files': fileslist,
}
to_json = json.dumps(data, ensure_ascii=False, cls=PackageJSONEncoder)
diff --git a/sitestatic/archweb.js b/sitestatic/archweb.js
index d17cc68f..b04b1d01 100644
--- a/sitestatic/archweb.js
+++ b/sitestatic/archweb.js
@@ -170,7 +170,17 @@ function ajaxifyFiles() {
var cls = value.match(/\/$/) ? 'd' : 'f';
return ['', value, ' '];
});
- $('#pkgfilelist').html('' + list_items.join('') + ' ');
+ $('#pkgfilelist').empty();
+ if (data.pkg_last_update > data.files_last_update) {
+ $('#pkgfilelist').append('Note: This file list was generated from a previous version of the package; it may be out of date.
');
+ }
+ if (list_items.length > 0) {
+ $('#pkgfilelist').append('' + list_items.join('') + ' ');
+ } else if (data.files_last_update == null) {
+ $('#pkgfilelist').append('No file list available.
');
+ } else {
+ $('#pkgfilelist').append('Package has no files.
');
+ }
});
});
}
--
cgit v1.2.3-54-g00ecf
From 9ab460c53a1ac4c79da6f05f2850ee21beedbab2 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 23 Jul 2012 21:47:43 -0500
Subject: Fall back to 410 Gone for package files view as well
This is another thing that Google and other search engines try to crawl
that no longer exists at times, so we should handle it gracefully.
Signed-off-by: Dan McGee
---
packages/views/display.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/views/display.py b/packages/views/display.py
index 02f5a5b2..31f18c79 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -132,8 +132,16 @@ def group_details(request, arch, name):
def files(request, name, repo, arch):
- pkg = get_object_or_404(Package,
- pkgname=name, repo__name__iexact=repo, arch__name=arch)
+ try:
+ pkg = Package.objects.get(pkgname=name,
+ repo__name__iexact=repo, arch__name=arch)
+ except Package.DoesNotExist:
+ # this may have been deleted recently, so follow the same logic as we
+ # do on the package details page if possible
+ ret = recently_removed_package(request, name, repo, arch)
+ if ret is not None:
+ return ret
+ raise Http404
# files are inserted in sorted order, so preserve that
fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
dir_count = sum(1 for f in fileslist if f.is_directory)
--
cgit v1.2.3-54-g00ecf
From 61b4098c611592d62b40a9ee941976a869dff4fc Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 24 Jul 2012 08:55:48 -0500
Subject: Add index on package updates pkgname field
Signed-off-by: Dan McGee
---
.../0019_package_update_pkgname_index.py | 208 +++++++++++++++++++++
packages/models.py | 2 +-
2 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 packages/migrations/0019_package_update_pkgname_index.py
(limited to 'packages')
diff --git a/packages/migrations/0019_package_update_pkgname_index.py b/packages/migrations/0019_package_update_pkgname_index.py
new file mode 100644
index 00000000..047f11ec
--- /dev/null
+++ b/packages/migrations/0019_package_update_pkgname_index.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.create_index('packages_update', ['pkgname'])
+
+ def backwards(self, orm):
+ db.delete_index('packages_update', ['pkgname'])
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 18a62a3c..5b48b30f 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -253,7 +253,7 @@ class Update(models.Model):
null=True, on_delete=models.SET_NULL)
repo = models.ForeignKey(Repo, related_name="updates")
arch = models.ForeignKey(Arch, related_name="updates")
- pkgname = models.CharField(max_length=255)
+ pkgname = models.CharField(max_length=255, db_index=True)
pkgbase = models.CharField(max_length=255)
action_flag = models.PositiveSmallIntegerField('action flag',
choices=UPDATE_ACTION_CHOICES)
--
cgit v1.2.3-54-g00ecf
From c0bf9e20660cfae7ea8994472555bba23398b598 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 24 Jul 2012 09:19:48 -0500
Subject: Remove custom utc_now() function, use django.utils.timezone.now()
This was around from the time when we handled timezones sanely and
Django did not; now that we are on 1.4 we no longer need our own code to
handle this.
Signed-off-by: Dan McGee
---
devel/management/commands/reporead.py | 9 +++++----
devel/models.py | 5 +++--
devel/views.py | 12 ++++++------
main/models.py | 5 +++--
main/utils.py | 9 ++-------
mirrors/management/commands/mirrorcheck.py | 7 ++++---
mirrors/utils.py | 17 +++++++++--------
news/models.py | 11 +++++------
packages/management/commands/signoff_report.py | 8 ++++----
packages/views/flag.py | 8 ++++----
packages/views/signoff.py | 4 ++--
releng/management/commands/syncisos.py | 5 ++---
12 files changed, 49 insertions(+), 51 deletions(-)
(limited to 'packages')
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index 2d9b68b2..e69691db 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -28,10 +28,11 @@
from django.core.management.base import BaseCommand, CommandError
from django.db import connections, router, transaction
from django.db.utils import IntegrityError
+from django.utils.timezone import now
from devel.utils import UserFinder
from main.models import Arch, Package, PackageFile, Repo
-from main.utils import utc_now, database_vendor
+from main.utils import database_vendor
from packages.models import Depend, Conflict, Provision, Replacement, Update
@@ -318,7 +319,7 @@ def populate_files(dbpkg, repopkg, force=False):
filename=filename)
pkg_files.append(pkgfile)
batched_bulk_create(PackageFile, pkg_files)
- dbpkg.files_last_update = utc_now()
+ dbpkg.files_last_update = now()
dbpkg.save()
@@ -388,7 +389,7 @@ def db_update(archname, reponame, pkgs, force=False):
dbpkg = Package(pkgname=pkg.name, arch=architecture, repo=repository)
try:
with transaction.commit_on_success():
- populate_pkg(dbpkg, pkg, timestamp=utc_now())
+ populate_pkg(dbpkg, pkg, timestamp=now())
Update.objects.log_update(None, dbpkg)
except IntegrityError:
logger.warning("Could not add package %s; "
@@ -417,7 +418,7 @@ def db_update(archname, reponame, pkgs, force=False):
if not force and pkg_same_version(pkg, dbpkg):
continue
elif not force:
- timestamp = utc_now()
+ timestamp = now()
# The odd select_for_update song and dance here are to ensure
# simultaneous updates don't happen on a package, causing
diff --git a/devel/models.py b/devel/models.py
index fd5df00a..9b6f07a7 100644
--- a/devel/models.py
+++ b/devel/models.py
@@ -4,10 +4,11 @@
from django.db import models
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
+from django.utils.timezone import now
from django_countries import CountryField
from .fields import PGPKeyField
-from main.utils import make_choice, utc_now
+from main.utils import make_choice
class UserProfile(models.Model):
@@ -105,7 +106,7 @@ def set_last_modified(sender, **kwargs):
signal handler.'''
obj = kwargs['instance']
if hasattr(obj, 'last_modified'):
- obj.last_modified = utc_now()
+ obj.last_modified = now()
# connect signals needed to keep cache in line with reality
diff --git a/devel/views.py b/devel/views.py
index 78ed26f2..143b12bf 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -24,11 +24,11 @@
from django.views.generic.simple import direct_to_template
from django.utils.encoding import force_unicode
from django.utils.http import http_date
+from django.utils.timezone import now
from .models import UserProfile
from main.models import Package, PackageFile, TodolistPkg
from main.models import Arch, Repo
-from main.utils import utc_now
from news.models import News
from packages.models import PackageRelation, Signoff, Depend
from packages.utils import get_signoff_groups
@@ -122,15 +122,15 @@ def clock(request):
else:
dev.last_action = None
- now = utc_now()
+ current_time = now()
page_dict = {
'developers': devs,
- 'utc_now': now,
+ 'utc_now': current_time,
}
response = direct_to_template(request, 'devel/clock.html', page_dict)
if not response.has_header('Expires'):
- expire_time = now.replace(second=0, microsecond=0)
+ expire_time = current_time.replace(second=0, microsecond=0)
expire_time += timedelta(minutes=1)
expire_time = time.mktime(expire_time.timetuple())
response['Expires'] = http_date(expire_time)
@@ -198,12 +198,12 @@ def report(request, report_name, username=None):
if report_name == 'old':
title = 'Packages last built more than one year ago'
- cutoff = utc_now() - timedelta(days=365)
+ cutoff = now() - timedelta(days=365)
packages = packages.filter(
build_date__lt=cutoff).order_by('build_date')
elif report_name == 'long-out-of-date':
title = 'Packages marked out-of-date more than 90 days ago'
- cutoff = utc_now() - timedelta(days=90)
+ cutoff = now() - timedelta(days=90)
packages = packages.filter(
flag_date__lt=cutoff).order_by('flag_date')
elif report_name == 'big':
diff --git a/main/models.py b/main/models.py
index 04d8da8f..6c9dfe4d 100644
--- a/main/models.py
+++ b/main/models.py
@@ -6,9 +6,10 @@
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
+from django.utils.timezone import now
from .fields import PositiveBigIntegerField
-from .utils import cache_function, set_created_field, utc_now
+from .utils import cache_function, set_created_field
class TodolistManager(models.Manager):
@@ -385,7 +386,7 @@ class Meta:
def set_todolist_fields(sender, **kwargs):
todolist = kwargs['instance']
if not todolist.date_added:
- todolist.date_added = utc_now()
+ todolist.date_added = now()
# connect signals needed to keep cache in line with reality
from main.utils import refresh_latest
diff --git a/main/utils.py b/main/utils.py
index 879abfb9..0b6849a4 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -5,10 +5,10 @@
from datetime import datetime
import hashlib
-from pytz import utc
from django.core.cache import cache
from django.db import connections, router
+from django.utils.timezone import now
CACHE_TIMEOUT = 1800
@@ -94,17 +94,12 @@ def retrieve_latest(sender, latest_by=None):
return None
-def utc_now():
- '''Returns a timezone-aware UTC date representing now.'''
- return datetime.utcnow().replace(tzinfo=utc)
-
-
def set_created_field(sender, **kwargs):
'''This will set the 'created' field on any object to the current UTC time
if it is unset. For use as a pre_save signal handler.'''
obj = kwargs['instance']
if hasattr(obj, 'created') and not obj.created:
- obj.created = utc_now()
+ obj.created = now()
def database_vendor(model, mode='read'):
diff --git a/mirrors/management/commands/mirrorcheck.py b/mirrors/management/commands/mirrorcheck.py
index 7a133cbf..e09ea680 100644
--- a/mirrors/management/commands/mirrorcheck.py
+++ b/mirrors/management/commands/mirrorcheck.py
@@ -29,8 +29,9 @@
from django.core.management.base import NoArgsCommand
from django.db import transaction
+from django.utils.timezone import now
-from main.utils import utc_now, database_vendor
+from main.utils import database_vendor
from mirrors.models import MirrorUrl, MirrorLog
logging.basicConfig(
@@ -83,7 +84,7 @@ def parse_lastsync(log, data):
def check_mirror_url(mirror_url, timeout):
url = mirror_url.url + 'lastsync'
logger.info("checking URL %s", url)
- log = MirrorLog(url=mirror_url, check_time=utc_now())
+ log = MirrorLog(url=mirror_url, check_time=now())
headers = {'User-Agent': 'archweb/1.0'}
req = urllib2.Request(url, None, headers)
try:
@@ -136,7 +137,7 @@ def check_mirror_url(mirror_url, timeout):
def check_rsync_url(mirror_url, timeout):
url = mirror_url.url + 'lastsync'
logger.info("checking URL %s", url)
- log = MirrorLog(url=mirror_url, check_time=utc_now())
+ log = MirrorLog(url=mirror_url, check_time=now())
tempdir = tempfile.mkdtemp()
lastsync_path = os.path.join(tempdir, 'lastsync')
diff --git a/mirrors/utils.py b/mirrors/utils.py
index f2c98ee0..bf030d39 100644
--- a/mirrors/utils.py
+++ b/mirrors/utils.py
@@ -1,13 +1,14 @@
from datetime import timedelta
from django.db.models import Avg, Count, Max, Min, StdDev
+from django.utils.timezone import now
from django_countries.fields import Country
-from main.utils import cache_function, utc_now, database_vendor
+from main.utils import cache_function, database_vendor
from .models import MirrorLog, MirrorProtocol, MirrorUrl
-default_cutoff = timedelta(hours=24)
+DEFAULT_CUTOFF = timedelta(hours=24)
def annotate_url(url, delays):
'''Given a MirrorURL object, add a few more attributes to it regarding
@@ -30,8 +31,8 @@ def annotate_url(url, delays):
@cache_function(123)
-def get_mirror_statuses(cutoff=default_cutoff):
- cutoff_time = utc_now() - cutoff
+def get_mirror_statuses(cutoff=DEFAULT_CUTOFF):
+ cutoff_time = now() - cutoff
# I swear, this actually has decent performance...
urls = MirrorUrl.objects.select_related('mirror', 'protocol').filter(
mirror__active=True, mirror__public=True,
@@ -88,8 +89,8 @@ def get_mirror_statuses(cutoff=default_cutoff):
@cache_function(117)
-def get_mirror_errors(cutoff=default_cutoff):
- cutoff_time = utc_now() - cutoff
+def get_mirror_errors(cutoff=DEFAULT_CUTOFF):
+ cutoff_time = now() - cutoff
errors = MirrorLog.objects.filter(
is_success=False, check_time__gte=cutoff_time,
url__mirror__active=True, url__mirror__public=True).values(
@@ -105,11 +106,11 @@ def get_mirror_errors(cutoff=default_cutoff):
@cache_function(295)
-def get_mirror_url_for_download(cutoff=default_cutoff):
+def get_mirror_url_for_download(cutoff=DEFAULT_CUTOFF):
'''Find a good mirror URL to use for package downloads. If we have mirror
status data available, it is used to determine a good choice by looking at
the last batch of status rows.'''
- cutoff_time = utc_now() - cutoff
+ cutoff_time = now() - cutoff
status_data = MirrorLog.objects.filter(
check_time__gte=cutoff_time).aggregate(
Max('check_time'), Max('last_sync'))
diff --git a/news/models.py b/news/models.py
index 95026e1d..2efea579 100644
--- a/news/models.py
+++ b/news/models.py
@@ -1,8 +1,7 @@
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
-
-from main.utils import utc_now
+from django.utils.timezone import now
class News(models.Model):
@@ -29,13 +28,13 @@ class Meta:
def set_news_fields(sender, **kwargs):
news = kwargs['instance']
- now = utc_now()
- news.last_modified = now
+ current_time = now()
+ news.last_modified = current_time
if not news.postdate:
- news.postdate = now
+ news.postdate = current_time
# http://diveintomark.org/archives/2004/05/28/howto-atom-id
news.guid = 'tag:%s,%s:%s' % (Site.objects.get_current(),
- now.strftime('%Y-%m-%d'), news.get_absolute_url())
+ current_time.strftime('%Y-%m-%d'), news.get_absolute_url())
# connect signals needed to keep cache in line with reality
from main.utils import refresh_latest
diff --git a/packages/management/commands/signoff_report.py b/packages/management/commands/signoff_report.py
index ddf930db..72fcbe1e 100644
--- a/packages/management/commands/signoff_report.py
+++ b/packages/management/commands/signoff_report.py
@@ -15,6 +15,7 @@
from django.contrib.sites.models import Site
from django.db.models import Count
from django.template import loader, Context
+from django.utils.timezone import now
from collections import namedtuple
from datetime import timedelta
@@ -23,7 +24,6 @@
import sys
from main.models import Repo
-from main.utils import utc_now
from packages.models import Signoff
from packages.utils import get_signoff_groups
@@ -66,9 +66,9 @@ def generate_report(email, repo_name):
new_hours = 24
old_days = 14
- now = utc_now()
- new_cutoff = now - timedelta(hours=new_hours)
- old_cutoff = now - timedelta(days=old_days)
+ current_time = now()
+ new_cutoff = current_time - timedelta(hours=new_hours)
+ old_cutoff = current_time - timedelta(days=old_days)
if len(signoff_groups) == 0:
# no need to send an email at all
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 7fa2d508..f3db93b3 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -5,12 +5,12 @@
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect
from django.template import loader, Context
+from django.utils.timezone import now
from django.views.generic.simple import direct_to_template
from django.views.decorators.cache import cache_page, never_cache
from ..models import FlagRequest
from main.models import Package
-from main.utils import utc_now
class FlagForm(forms.Form):
@@ -76,10 +76,10 @@ def flag(request, name, repo, arch):
@transaction.commit_on_success
def perform_updates():
- now = utc_now()
- pkgs.update(flag_date=now)
+ current_time = now()
+ pkgs.update(flag_date=current_time)
# store our flag request
- flag_request = FlagRequest(created=now,
+ flag_request = FlagRequest(created=current_time,
user_email=email, message=message,
ip_address=ip_addr, pkgbase=pkg.pkgbase,
version=version, repo=pkg.repo,
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 61d949fc..7aa39106 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -8,11 +8,11 @@
from django.db import transaction
from django.http import HttpResponse, Http404
from django.shortcuts import get_list_or_404, redirect, render
+from django.utils.timezone import now
from django.views.decorators.cache import never_cache
from django.views.generic.simple import direct_to_template
from main.models import Package, Arch, Repo
-from main.utils import utc_now
from ..models import SignoffSpecification, Signoff
from ..utils import (get_signoff_groups, approved_by_signoffs,
PackageSignoffGroup)
@@ -45,7 +45,7 @@ def signoff_package(request, name, repo, arch, revoke=False):
package, request.user, False)
except Signoff.DoesNotExist:
raise Http404
- signoff.revoked = utc_now()
+ signoff.revoked = now()
signoff.save()
created = False
else:
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index 62f005ff..223c771b 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -4,8 +4,8 @@
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
+from django.utils.timezone import now
-from main.utils import utc_now
from releng.models import Iso
@@ -54,9 +54,8 @@ def handle(self, *args, **options):
existing.active = True
existing.removed = None
existing.save()
- now = utc_now()
# and then mark all other names as no longer active
Iso.objects.filter(active=True).exclude(name__in=active_isos).update(
- active=False, removed=now)
+ active=False, removed=now())
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From 76c37ce3acc7a4af0271c7535d4a33042f7749b5 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 24 Jul 2012 09:35:55 -0500
Subject: Replace deprecated direct_to_template() with render() shortcut
Now that Django actually provides a concise way to use a RequestContext
object without instantiating it, we can use that rather than the old
function-based generic view that worked well to do the same.
Additionally, these function-based generic views will be gone in Django
1.5, so might as well make the move now.
Signed-off-by: Dan McGee
---
devel/views.py | 15 +++++++--------
mirrors/views.py | 19 +++++++++----------
packages/views/__init__.py | 11 +++++------
packages/views/display.py | 13 +++++--------
packages/views/flag.py | 12 +++++-------
packages/views/signoff.py | 5 ++---
public/views.py | 14 +++++++-------
releng/views.py | 15 +++++++--------
retro/views.py | 4 ++--
todolists/views.py | 13 ++++++-------
visualize/views.py | 4 ++--
11 files changed, 57 insertions(+), 68 deletions(-)
(limited to 'packages')
diff --git a/devel/views.py b/devel/views.py
index 143b12bf..f877bc84 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -17,11 +17,10 @@
from django.db import transaction
from django.db.models import F, Count, Max
from django.http import Http404
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, render
from django.template import loader, Context
from django.template.defaultfilters import filesizeformat
from django.views.decorators.cache import never_cache
-from django.views.generic.simple import direct_to_template
from django.utils.encoding import force_unicode
from django.utils.http import http_date
from django.utils.timezone import now
@@ -88,7 +87,7 @@ def index(request):
'signoffs': signoffs
}
- return direct_to_template(request, 'devel/index.html', page_dict)
+ return render(request, 'devel/index.html', page_dict)
@login_required
def clock(request):
@@ -128,7 +127,7 @@ def clock(request):
'utc_now': current_time,
}
- response = direct_to_template(request, 'devel/clock.html', page_dict)
+ response = render(request, 'devel/clock.html', page_dict)
if not response.has_header('Expires'):
expire_time = current_time.replace(second=0, microsecond=0)
expire_time += timedelta(minutes=1)
@@ -178,7 +177,7 @@ def change_profile(request):
else:
form = ProfileForm(initial={'email': request.user.email})
profile_form = UserProfileForm(instance=request.user.get_profile())
- return direct_to_template(request, 'devel/profile.html',
+ return render(request, 'devel/profile.html',
{'form': form, 'profile_form': profile_form})
@login_required
@@ -301,7 +300,7 @@ def report(request, report_name, username=None):
'column_names': names,
'column_attrs': attrs,
}
- return direct_to_template(request, 'devel/packages.html', context)
+ return render(request, 'devel/packages.html', context)
class NewUserForm(forms.ModelForm):
@@ -399,7 +398,7 @@ def inner_save():
'title': 'Create User',
'submit_text': 'Create User'
}
- return direct_to_template(request, 'general_form.html', context)
+ return render(request, 'general_form.html', context)
@user_passes_test(lambda u: u.is_superuser)
def admin_log(request, username=None):
@@ -410,6 +409,6 @@ def admin_log(request, username=None):
'title': "Admin Action Log",
'log_user': user,
}
- return direct_to_template(request, 'devel/admin_log.html', context)
+ return render(request, 'devel/admin_log.html', context)
# vim: set ts=4 sw=4 et:
diff --git a/mirrors/views.py b/mirrors/views.py
index 8f092be7..400c084d 100644
--- a/mirrors/views.py
+++ b/mirrors/views.py
@@ -8,9 +8,8 @@
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q
from django.http import Http404, HttpResponse
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, render
from django.views.decorators.csrf import csrf_exempt
-from django.views.generic.simple import direct_to_template
from django_countries.countries import COUNTRIES
from .models import Mirror, MirrorUrl, MirrorProtocol, TIER_CHOICES
@@ -76,7 +75,7 @@ def generate_mirrorlist(request):
else:
form = MirrorlistForm()
- return direct_to_template(request, 'mirrors/mirrorlist_generate.html',
+ return render(request, 'mirrors/mirrorlist_generate.html',
{'mirrorlist_form': form})
@@ -142,10 +141,10 @@ def find_mirrors(request, countries=None, protocols=None, use_status=False,
urls = status_filter(urls)
template = 'mirrors/mirrorlist_status.txt'
- return direct_to_template(request, template, {
- 'mirror_urls': urls,
- },
- mimetype='text/plain')
+ context = {
+ 'mirror_urls': urls,
+ }
+ return render(request, template, context, content_type='text/plain')
def find_mirrors_simple(request, protocol):
@@ -161,7 +160,7 @@ def mirrors(request):
mirror_list = Mirror.objects.select_related().order_by('tier', 'country')
if not request.user.is_authenticated():
mirror_list = mirror_list.filter(public=True, active=True)
- return direct_to_template(request, 'mirrors/mirrors.html',
+ return render(request, 'mirrors/mirrors.html',
{'mirror_list': mirror_list})
@@ -180,7 +179,7 @@ def mirror_details(request, name):
all_urls = set(checked_urls).union(all_urls)
all_urls = sorted(all_urls, key=attrgetter('url'))
- return direct_to_template(request, 'mirrors/mirror_details.html',
+ return render(request, 'mirrors/mirror_details.html',
{'mirror': mirror, 'urls': all_urls})
@@ -217,7 +216,7 @@ def status(request, tier=None):
'error_logs': error_logs,
'tier': tier,
})
- return direct_to_template(request, 'mirrors/status.html', context)
+ return render(request, 'mirrors/status.html', context)
class MirrorStatusJSONEncoder(DjangoJSONEncoder):
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index fa67daa8..19ea9103 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -6,10 +6,9 @@
from django.contrib.auth.models import User
from django.core.cache import cache
from django.http import HttpResponse
-from django.shortcuts import redirect
+from django.shortcuts import redirect, render
from django.views.decorators.cache import cache_control
from django.views.decorators.http import require_GET, require_POST
-from django.views.generic.simple import direct_to_template
from main.models import Package, Arch
from ..models import PackageRelation
@@ -32,9 +31,9 @@ def opensearch(request):
else:
domain = "http://%s" % request.META['HTTP_HOST']
- return direct_to_template(request, 'packages/opensearch.xml',
+ return render(request, 'packages/opensearch.xml',
{'domain': domain},
- mimetype='application/opensearchdescription+xml')
+ content_type='application/opensearchdescription+xml')
@require_GET
@@ -115,7 +114,7 @@ def arch_differences(request):
'differences': differences,
'multilib_differences': multilib_diffs
}
- return direct_to_template(request, 'packages/differences.html', context)
+ return render(request, 'packages/differences.html', context)
@permission_required('main.change_package')
def stale_relations(request):
@@ -132,7 +131,7 @@ def stale_relations(request):
'missing_pkgbase': missing_pkgbase,
'wrong_permissions': wrong_permissions,
}
- return direct_to_template(request, 'packages/stale_relations.html', context)
+ return render(request, 'packages/stale_relations.html', context)
@permission_required('packages.delete_packagerelation')
@require_POST
diff --git a/packages/views/display.py b/packages/views/display.py
index 31f18c79..585e0e4e 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -6,7 +6,6 @@
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.timezone import now
-from django.views.generic.simple import direct_to_template
from main.models import Package, PackageFile, Arch, Repo
from mirrors.utils import get_mirror_url_for_download
@@ -33,8 +32,7 @@ def split_package_details(request, name, repo, arch):
'arch': arch,
'packages': pkgs,
}
- return direct_to_template(request, 'packages/packages_list.html',
- context)
+ return render(request, 'packages/packages_list.html', context)
CUTOFF = datetime.timedelta(days=60)
@@ -67,8 +65,7 @@ def details(request, name='', repo='', arch=''):
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, })
+ return render(request, 'packages/details.html', {'pkg': pkg})
except Package.DoesNotExist:
arch_obj = get_object_or_404(Arch, name=arch)
# for arch='any' packages, we can issue a redirect to them if we
@@ -111,7 +108,7 @@ def groups(request, arch=None):
'groups': grps,
'arch': arch,
}
- return direct_to_template(request, 'packages/groups.html', context)
+ return render(request, 'packages/groups.html', context)
def group_details(request, arch, name):
@@ -128,7 +125,7 @@ def group_details(request, arch, name):
'arch': arch,
'packages': pkgs,
}
- return direct_to_template(request, 'packages/packages_list.html', context)
+ return render(request, 'packages/packages_list.html', context)
def files(request, name, repo, arch):
@@ -153,7 +150,7 @@ def files(request, name, repo, arch):
'dir_count': dir_count,
}
template = 'packages/files.html'
- return direct_to_template(request, template, context)
+ return render(request, template, context)
def details_json(request, name, repo, arch):
diff --git a/packages/views/flag.py b/packages/views/flag.py
index f3db93b3..b9542a62 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -3,10 +3,9 @@
from django.contrib.auth.decorators import permission_required
from django.core.mail import send_mail
from django.db import transaction
-from django.shortcuts import get_object_or_404, redirect
+from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader, Context
from django.utils.timezone import now
-from django.views.generic.simple import direct_to_template
from django.views.decorators.cache import cache_page, never_cache
from ..models import FlagRequest
@@ -34,7 +33,7 @@ def __init__(self, *args, **kwargs):
@cache_page(3600)
def flaghelp(request):
- return direct_to_template(request, 'packages/flaghelp.html')
+ return render(request, 'packages/flaghelp.html')
@never_cache
@@ -43,8 +42,7 @@ def flag(request, name, repo, arch):
pkgname=name, repo__name__iexact=repo, arch__name=arch)
if pkg.flag_date is not None:
# already flagged. do nothing.
- return direct_to_template(request, 'packages/flagged.html',
- {'pkg': pkg})
+ return render(request, 'packages/flagged.html', {'pkg': pkg})
# find all packages from (hopefully) the same PKGBUILD
pkgs = Package.objects.normal().filter(
pkgbase=pkg.pkgbase, flag_date__isnull=True,
@@ -129,7 +127,7 @@ def perform_updates():
'packages': pkgs,
'form': form
}
- return direct_to_template(request, 'packages/flag.html', context)
+ return render(request, 'packages/flag.html', context)
def flag_confirmed(request, name, repo, arch):
pkg = get_object_or_404(Package,
@@ -142,7 +140,7 @@ def flag_confirmed(request, name, repo, arch):
context = {'package': pkg, 'packages': pkgs}
- return direct_to_template(request, 'packages/flag_confirmed.html', context)
+ return render(request, 'packages/flag_confirmed.html', context)
@permission_required('main.change_package')
def unflag(request, name, repo, arch):
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 7aa39106..56eb060c 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -10,7 +10,6 @@
from django.shortcuts import get_list_or_404, redirect, render
from django.utils.timezone import now
from django.views.decorators.cache import never_cache
-from django.views.generic.simple import direct_to_template
from main.models import Package, Arch, Repo
from ..models import SignoffSpecification, Signoff
@@ -28,7 +27,7 @@ def signoffs(request):
'arches': Arch.objects.all(),
'repo_names': sorted(set(g.target_repo for g in signoff_groups)),
}
- return direct_to_template(request, 'packages/signoffs.html', context)
+ return render(request, 'packages/signoffs.html', context)
@permission_required('main.change_package')
@never_cache
@@ -144,7 +143,7 @@ def signoff_options(request, name, repo, arch):
'package': package,
'form': form,
}
- return direct_to_template(request, 'packages/signoff_options.html', context)
+ return render(request, 'packages/signoff_options.html', context)
class SignoffJSONEncoder(DjangoJSONEncoder):
'''Base JSONEncoder extended to handle all serialization of all classes
diff --git a/public/views.py b/public/views.py
index 3ea8f841..3f68545c 100644
--- a/public/views.py
+++ b/public/views.py
@@ -5,8 +5,8 @@
from django.contrib.auth.models import User
from django.db.models import Count, Q
from django.http import Http404
+from django.shortcuts import render
from django.views.decorators.cache import cache_control
-from django.views.generic.simple import direct_to_template
from devel.models import MasterKey, PGPSignature
from main.models import Arch, Repo, Donor
@@ -21,7 +21,7 @@ def index(request):
'news_updates': News.objects.order_by('-postdate', '-id')[:15],
'pkg_updates': pkgs,
}
- return direct_to_template(request, 'public/index.html', context)
+ return render(request, 'public/index.html', context)
USER_LISTS = {
'devs': {
@@ -55,14 +55,14 @@ def userlist(request, user_type='devs'):
users = users.distinct()
context = USER_LISTS[user_type].copy()
context['users'] = users
- return direct_to_template(request, 'public/userlist.html', context)
+ return render(request, 'public/userlist.html', context)
@cache_control(max_age=300)
def donate(request):
context = {
'donors': Donor.objects.filter(visible=True).order_by('name'),
}
- return direct_to_template(request, 'public/donate.html', context)
+ return render(request, 'public/donate.html', context)
@cache_control(max_age=300)
def download(request):
@@ -76,7 +76,7 @@ def download(request):
'releng_pxeboot_url': settings.PXEBOOT_URL,
'mirror_urls': mirror_urls,
}
- return direct_to_template(request, 'public/download.html', context)
+ return render(request, 'public/download.html', context)
@cache_control(max_age=300)
def feeds(request):
@@ -84,7 +84,7 @@ def feeds(request):
'arches': Arch.objects.all(),
'repos': Repo.objects.all(),
}
- return direct_to_template(request, 'public/feeds.html', context)
+ return render(request, 'public/feeds.html', context)
@cache_control(max_age=300)
def keys(request):
@@ -113,6 +113,6 @@ def keys(request):
'active_users': users,
'signatures': signatures,
}
- return direct_to_template(request, 'public/keys.html', context)
+ return render(request, 'public/keys.html', context)
# vim: set ts=4 sw=4 et:
diff --git a/releng/views.py b/releng/views.py
index fc81d410..a1bc3b81 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -2,8 +2,7 @@
from django.conf import settings
from django.db.models import Count, Max
from django.http import Http404
-from django.shortcuts import get_object_or_404, redirect
-from django.views.generic.simple import direct_to_template
+from django.shortcuts import get_object_or_404, redirect, render
from .models import (Architecture, BootType, Bootloader, ClockChoice,
Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source,
@@ -73,7 +72,7 @@ def submit_test_result(request):
form = TestForm()
context = {'form': form}
- return direct_to_template(request, 'releng/add.html', context)
+ return render(request, 'releng/add.html', context)
def calculate_option_overview(field_name):
field = Test._meta.get_field(field_name)
@@ -145,7 +144,7 @@ def test_results_overview(request):
'options': all_options,
'iso_url': settings.ISO_LIST_URL,
}
- return direct_to_template(request, 'releng/results.html', context)
+ return render(request, 'releng/results.html', context)
def test_results_iso(request, iso_id):
iso = get_object_or_404(Iso, pk=iso_id)
@@ -154,7 +153,7 @@ def test_results_iso(request, iso_id):
'iso_name': iso.name,
'test_list': test_list
}
- return direct_to_template(request, 'releng/result_list.html', context)
+ return render(request, 'releng/result_list.html', context)
def test_results_for(request, option, value):
if option not in Test._meta.get_all_field_names():
@@ -170,10 +169,10 @@ def test_results_for(request, option, value):
'value_id': value,
'test_list': test_list
}
- return direct_to_template(request, 'releng/result_list.html', context)
+ return render(request, 'releng/result_list.html', context)
def submit_test_thanks(request):
- return direct_to_template(request, "releng/thanks.html", None)
+ return render(request, "releng/thanks.html", None)
def iso_overview(request):
isos = Iso.objects.all().order_by('-pk')
@@ -192,6 +191,6 @@ def iso_overview(request):
context = {
'isos': isos
}
- return direct_to_template(request, 'releng/iso_overview.html', context)
+ return render(request, 'releng/iso_overview.html', context)
# vim: set ts=4 sw=4 et:
diff --git a/retro/views.py b/retro/views.py
index 3bc59e9f..31226deb 100644
--- a/retro/views.py
+++ b/retro/views.py
@@ -1,6 +1,6 @@
from django.http import Http404
+from django.shortcuts import render
from django.views.decorators.cache import cache_page
-from django.views.generic.simple import direct_to_template
RETRO_YEAR_MAP = {
@@ -26,6 +26,6 @@ def retro_homepage(request, year):
context = {
'year': year,
}
- return direct_to_template(request, 'retro/%s' % template, context)
+ return render(request, 'retro/%s' % template, context)
# vim: set ts=4 sw=4 et:
diff --git a/todolists/views.py b/todolists/views.py
index 580ec00f..c7ba2560 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -3,12 +3,11 @@
from django import forms
from django.http import HttpResponse
from django.core.mail import send_mail
-from django.shortcuts import get_list_or_404, get_object_or_404, redirect
+from django.shortcuts import get_list_or_404, get_object_or_404, redirect, render
from django.contrib.auth.decorators import login_required, permission_required
from django.db import transaction
from django.views.decorators.cache import never_cache
from django.views.generic import DeleteView
-from django.views.generic.simple import direct_to_template
from django.template import Context, loader
from main.models import Todolist, TodolistPkg, Package, Repo
@@ -54,7 +53,7 @@ def view(request, list_id):
# we don't hold onto the result, but the objects are the same here,
# so accessing maintainers in the template is now cheap
attach_maintainers(tp.pkg for tp in todolist.packages)
- return direct_to_template(request, 'todolists/view.html', {
+ return render(request, 'todolists/view.html', {
'list': todolist,
'svn_roots': svn_roots,
})
@@ -72,7 +71,7 @@ def list_pkgbases(request, list_id, svn_root):
@login_required
def todolist_list(request):
lists = get_annotated_todolists()
- return direct_to_template(request, 'todolists/list.html', {'lists': lists})
+ return render(request, 'todolists/list.html', {'lists': lists})
@permission_required('main.add_todolist')
@never_cache
@@ -92,7 +91,7 @@ def add(request):
'form': form,
'submit_text': 'Create List'
}
- return direct_to_template(request, 'general_form.html', page_dict)
+ return render(request, 'general_form.html', page_dict)
# TODO: this calls for transaction management and async emailing
@permission_required('main.change_todolist')
@@ -115,7 +114,7 @@ def edit(request, list_id):
'form': form,
'submit_text': 'Save List'
}
- return direct_to_template(request, 'general_form.html', page_dict)
+ return render(request, 'general_form.html', page_dict)
class DeleteTodolist(DeleteView):
model = Todolist
@@ -185,7 +184,7 @@ def public_list(request):
# total hackjob, but it makes this a lot less query-intensive.
all_pkgs = [tp for tl in todo_lists for tp in tl.packages]
attach_maintainers([tp.pkg for tp in all_pkgs])
- return direct_to_template(request, "todolists/public_list.html",
+ return render(request, "todolists/public_list.html",
{"todo_lists": todo_lists})
# vim: set ts=4 sw=4 et:
diff --git a/visualize/views.py b/visualize/views.py
index 95f8c314..44e60472 100644
--- a/visualize/views.py
+++ b/visualize/views.py
@@ -4,14 +4,14 @@
from django.contrib.auth.models import User
from django.db.models import Count, Sum, Q
from django.http import HttpResponse
+from django.shortcuts import render
from django.views.decorators.cache import cache_page
-from django.views.generic.simple import direct_to_template
from main.models import Package, Arch, Repo
from devel.models import MasterKey, PGPSignature
def index(request):
- return direct_to_template(request, 'visualize/index.html', {})
+ return render(request, 'visualize/index.html')
def arch_repo_data():
qs = Package.objects.select_related().values(
--
cgit v1.2.3-54-g00ecf
From 24b28a504cabcf077882aa95cfa0edbc6a8d4569 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 24 Jul 2012 21:01:31 -0500
Subject: Replace deprecated list_detail usage in search with class-based view
We can convert the entire search view to a generic class-based ListView.
This is still one of the more disgusting views in the application and
has a ton of logic scattered buckshot across several methods, but this
commit is not meant to address all of that in one go.
This is the last of the deprecated pieces I know of we are still using
in the codebase, so we should be relatively safe in the long run now for
an upgrade to the eventual next major Django release.
Signed-off-by: Dan McGee
---
packages/urls.py | 8 +++--
packages/views/__init__.py | 2 +-
packages/views/search.py | 77 +++++++++++++++++++++++-----------------------
3 files changed, 45 insertions(+), 42 deletions(-)
(limited to 'packages')
diff --git a/packages/urls.py b/packages/urls.py
index 6eddc5fe..9a151b4b 100644
--- a/packages/urls.py
+++ b/packages/urls.py
@@ -1,5 +1,7 @@
from django.conf.urls import include, patterns
+from .views.search import SearchListView
+
package_patterns = patterns('packages.views',
(r'^$', 'details'),
(r'^json/$', 'details_json'),
@@ -21,9 +23,9 @@
(r'^signoffs/json/$', 'signoffs_json', {}, 'package-signoffs-json'),
(r'^update/$', 'update'),
- (r'^$', 'search', {}, 'packages-search'),
- (r'^search/json/$', 'search_json'),
- (r'^(?P\d+)/$', 'search'),
+ (r'^$', SearchListView.as_view(), {}, 'packages-search'),
+ (r'^(?P\d+)/$', SearchListView.as_view()),
+ (r'^search/json/$', 'search_json'),
(r'^differences/$', 'arch_differences', {}, 'packages-differences'),
(r'^stale_relations/$', 'stale_relations'),
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 19ea9103..038d40ac 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -19,7 +19,7 @@
from .display import (details, groups, group_details, files, details_json,
files_json, download)
from .flag import flaghelp, flag, flag_confirmed, unflag, unflag_all
-from .search import search, search_json
+from .search import search_json
from .signoff import signoffs, signoff_package, signoff_options, signoffs_json
diff --git a/packages/views/search.py b/packages/views/search.py
index a89822be..9750894a 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -6,7 +6,7 @@
from django.contrib.auth.models import User
from django.db.models import Q
from django.http import HttpResponse
-from django.views.generic import list_detail
+from django.views.generic import ListView
from main.models import Package, Arch, Repo
from main.utils import make_choice
@@ -33,6 +33,7 @@ def valid_value(self, value):
except (ValueError, TypeError):
return False
+
class PackageSearchForm(forms.Form):
repo = forms.MultipleChoiceField(required=False)
arch = forms.MultipleChoiceField(required=False)
@@ -69,6 +70,7 @@ def __init__(self, *args, **kwargs):
[('', 'All'), ('unknown', 'Unknown')] + \
[(m.username, m.get_full_name()) for m in maints]
+
def parse_form(form, packages):
if form.cleaned_data['repo']:
packages = packages.filter(
@@ -117,48 +119,47 @@ def parse_form(form, packages):
return packages
-def search(request, page=None):
- limit = 50
- sort = None
- packages = Package.objects.normal()
- if request.GET:
- form = PackageSearchForm(data=request.GET)
- if form.is_valid():
- packages = parse_form(form, packages)
- asked_limit = form.cleaned_data['limit']
+class SearchListView(ListView):
+ template_name = "packages/search.html"
+
+ sort_fields = ("arch", "repo", "pkgname", "pkgbase", "compressed_size",
+ "installed_size", "build_date", "last_update", "flag_date")
+ allowed_sort = list(sort_fields) + ["-" + s for s in sort_fields]
+
+ def get(self, request, *args, **kwargs):
+ self.form = PackageSearchForm(data=request.GET)
+ return super(SearchListView, self).get(request, *args, **kwargs)
+
+ def get_queryset(self):
+ packages = Package.objects.normal()
+ if self.form.is_valid():
+ packages = parse_form(self.form, packages)
+ sort = self.form.cleaned_data['sort']
+ if sort in self.allowed_sort:
+ packages = packages.order_by(sort)
+ else:
+ packages = packages.order_by('pkgname')
+ return packages
+
+ # Form had errors so don't return any results
+ return Package.objects.none()
+
+ def get_paginate_by(self, queryset):
+ limit = 50
+ if self.form.is_valid():
+ asked_limit = self.form.cleaned_data['limit']
if asked_limit and asked_limit < 0:
limit = None
elif asked_limit:
limit = asked_limit
- sort = form.cleaned_data['sort']
- else:
- # Form had errors, don't return any results, just the busted form
- packages = Package.objects.none()
- else:
- form = PackageSearchForm()
-
- current_query = request.GET.urlencode()
- page_dict = {
- 'search_form': form,
- 'current_query': current_query
- }
- allowed_sort = ["arch", "repo", "pkgname", "pkgbase",
- "compressed_size", "installed_size",
- "build_date", "last_update", "flag_date"]
- allowed_sort += ["-" + s for s in allowed_sort]
- if sort in allowed_sort:
- packages = packages.order_by(sort)
- page_dict['sort'] = sort
- else:
- packages = packages.order_by('pkgname')
-
- return list_detail.object_list(request, packages,
- template_name="packages/search.html",
- page=page,
- paginate_by=limit,
- template_object_name="package",
- extra_context=page_dict)
+ return limit
+
+ def get_context_data(self, **kwargs):
+ context = super(SearchListView, self).get_context_data(**kwargs)
+ context['current_query'] = self.request.GET.urlencode()
+ context['search_form'] = self.form
+ return context
def search_json(request):
--
cgit v1.2.3-54-g00ecf
From 4ad43fd8165834b26914ff8ba0666ce96267205b Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 31 Jul 2012 00:25:19 -0500
Subject: Fix broken hidden input sort field on search form
Signed-off-by: Dan McGee
---
packages/views/search.py | 2 +-
templates/packages/search.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 9750894a..497d9bca 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -40,7 +40,7 @@ class PackageSearchForm(forms.Form):
name = forms.CharField(required=False)
desc = forms.CharField(required=False)
q = forms.CharField(required=False)
- sort = forms.CharField(required=False)
+ sort = forms.CharField(required=False, widget=forms.HiddenInput())
maintainer = forms.ChoiceField(required=False)
packager = forms.ChoiceField(required=False)
last_update = forms.DateField(required=False, widget=AdminDateWidget(),
diff --git a/templates/packages/search.html b/templates/packages/search.html
index 9d86cedf..ab9b6d32 100644
--- a/templates/packages/search.html
+++ b/templates/packages/search.html
@@ -19,7 +19,7 @@ Package Database
Package Search
diff --git a/templates/packages/details_requiredby.html b/templates/packages/details_requiredby.html
index ecc92b29..15c62c61 100644
--- a/templates/packages/details_requiredby.html
+++ b/templates/packages/details_requiredby.html
@@ -3,5 +3,5 @@
{% if req.name != pkg.pkgname %}(requires {{ req.name }}) {% endif %}
{% if req.pkg.repo.testing %}(testing) {% endif %}
{% if req.pkg.repo.staging %}(staging) {% endif %}
-{% if req.optional %}(optional) {% endif %}
+{% if req.deptype == 'O' %}(optional) {% endif %}
--
cgit v1.2.3-54-g00ecf
From 4c02b11cd6c53e122e1c919b64e28646960c5eda Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 31 Jul 2012 20:29:53 -0500
Subject: Remove optional package depends column
This is now completely replaced by the deptype column.
Signed-off-by: Dan McGee
---
.../0022_auto__del_field_depend_optional.py | 211 +++++++++++++++++++++
packages/models.py | 1 -
2 files changed, 211 insertions(+), 1 deletion(-)
create mode 100644 packages/migrations/0022_auto__del_field_depend_optional.py
(limited to 'packages')
diff --git a/packages/migrations/0022_auto__del_field_depend_optional.py b/packages/migrations/0022_auto__del_field_depend_optional.py
new file mode 100644
index 00000000..8e65ccb1
--- /dev/null
+++ b/packages/migrations/0022_auto__del_field_depend_optional.py
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.delete_column('packages_depend', 'optional')
+
+ def backwards(self, orm):
+ db.add_column('packages_depend', 'optional',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'deptype': ('django.db.models.fields.CharField', [], {'default': "'D'", 'max_length': '1'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ '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': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index b3752b6c..65aa8f4a 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -412,7 +412,6 @@ class Depend(RelatedToBase):
pkg = models.ForeignKey(Package, related_name='depends')
comparison = models.CharField(max_length=255, default='')
- optional = models.BooleanField(default=False)
description = models.TextField(null=True, blank=True)
deptype = models.CharField(max_length=1, default='D',
choices=DEPTYPE_CHOICES)
--
cgit v1.2.3-54-g00ecf
From f61e61c8a6fc0753359963a836bf65a3a8b1981e Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 4 Aug 2012 15:07:03 -0500
Subject: Include description in Depend unicode() output
This overrides the base class __unicode__ method.
Signed-off-by: Dan McGee
---
packages/models.py | 7 +++++++
1 file changed, 7 insertions(+)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index 65aa8f4a..1959183f 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -416,6 +416,13 @@ class Depend(RelatedToBase):
deptype = models.CharField(max_length=1, default='D',
choices=DEPTYPE_CHOICES)
+ def __unicode__(self):
+ '''For depends, we may also have a description and a modifier.'''
+ to_str = super(Depend, self).__unicode__()
+ if self.description:
+ return u'%s: %s' % (to_str, self.description)
+ return to_str
+
class Conflict(RelatedToBase):
pkg = models.ForeignKey(Package, related_name='conflicts')
--
cgit v1.2.3-54-g00ecf
From 2cb15547b0d3dfee46f3d51341bebd0b1251c0f9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 7 Aug 2012 01:53:26 -0500
Subject: Add ctypes-based wrapper to ALPM vercmp API
This will allow us to do some additional stuff on systems that have
libalpm available; namely we can use the version comparison logic it
provides to do smarter filtering etc. of fields that use comparsion
operations.
Signed-off-by: Dan McGee
---
packages/alpm.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 packages/alpm.py
(limited to 'packages')
diff --git a/packages/alpm.py b/packages/alpm.py
new file mode 100644
index 00000000..a7f4c3b5
--- /dev/null
+++ b/packages/alpm.py
@@ -0,0 +1,68 @@
+import ctypes
+from ctypes.util import find_library
+import operator
+
+
+def load_alpm(name=None):
+ # Load the alpm library and set up some of the functions we might use
+ if name == None:
+ name = find_library('alpm')
+ try:
+ alpm = ctypes.cdll.LoadLibrary(name)
+ except OSError:
+ return None
+ alpm.alpm_version.argtypes = ()
+ alpm.alpm_version.restype = ctypes.c_char_p
+ alpm.alpm_pkg_vercmp.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
+ alpm.alpm_pkg_vercmp.restype = ctypes.c_int
+ return alpm
+
+
+ALPM = load_alpm()
+
+class AlpmAPI(object):
+ OPERATOR_MAP = {
+ '=': operator.eq,
+ '==': operator.eq,
+ '!=': operator.ne,
+ '<': operator.lt,
+ '<=': operator.le,
+ '>': operator.gt,
+ '>=': operator.ge,
+ }
+
+ def __init__(self):
+ self.alpm = ALPM
+ self.available = ALPM is not None
+
+ def version(self):
+ if not self.available:
+ return None
+ return ALPM.alpm_version()
+
+ def vercmp(self, ver1, ver2):
+ if not self.available:
+ return None
+ return ALPM.alpm_pkg_vercmp(str(ver1), str(ver2))
+
+ def compare_versions(self, ver1, oper, ver2):
+ func = self.OPERATOR_MAP.get(oper, None)
+ if func is None:
+ raise Exception("Invalid operator %s specified" % oper)
+ if not self.available:
+ return None
+ res = self.vercmp(ver1, ver2)
+ return func(res, 0)
+
+
+def main():
+ api = AlpmAPI()
+ print api.version()
+ print api.vercmp(1, 2)
+ print api.compare_versions(1, '<', 2)
+
+
+if __name__ == '__main__':
+ main()
+
+# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From f63a70f5118781fe34d82ae0d59c911f0ea28d1d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 7 Aug 2012 19:36:07 -0500
Subject: Make use of new ctypes ALPM API
We can use this when filtering down lists of depends, required by,
conflicts, etc. to ensure we are honoring the version specifications the
same way pacman would.
Signed-off-by: Dan McGee
---
main/models.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++-------
packages/models.py | 23 +++++++++++++++++++++++
2 files changed, 71 insertions(+), 7 deletions(-)
(limited to 'packages')
diff --git a/main/models.py b/main/models.py
index b49edde3..bb2320e8 100644
--- a/main/models.py
+++ b/main/models.py
@@ -11,6 +11,7 @@
from .fields import PositiveBigIntegerField
from .utils import cache_function, set_created_field
+from packages.alpm import AlpmAPI
class TodolistManager(models.Manager):
@@ -189,16 +190,42 @@ def get_requiredby(self):
category as this package if that check makes sense.
"""
from packages.models import Depend
- provides = set(self.provides.values_list('name', flat=True))
- provides.add(self.pkgname)
+ provides = self.provides.all()
+ provide_names = set(provide.name for provide in provides)
+ provide_names.add(self.pkgname)
requiredby = Depend.objects.select_related('pkg',
'pkg__arch', 'pkg__repo').filter(
- name__in=provides).order_by(
+ name__in=provide_names).order_by(
'pkg__pkgname', 'pkg__arch__name', 'pkg__repo__name')
if not self.arch.agnostic:
# make sure we match architectures if possible
requiredby = requiredby.filter(
pkg__arch__in=self.applicable_arches())
+
+ # if we can use ALPM, ensure our returned Depend objects abide by the
+ # version comparison operators they may specify
+ alpm = AlpmAPI()
+ if alpm.available:
+ new_rqd = []
+ for dep in requiredby:
+ if not dep.comparison or not dep.version:
+ # no comparisson/version, so always let it through
+ new_rqd.append(dep)
+ elif self.pkgname == dep.name:
+ # depends on this package, so check it directly
+ if alpm.compare_versions(self.full_version,
+ dep.comparison, dep.version):
+ new_rqd.append(dep)
+ else:
+ # it must be a provision of ours at this point
+ for provide in (p for p in provides if p.name == dep.name):
+ print(provide.version, dep.comparison, dep.version)
+ if alpm.compare_versions(provide.version,
+ dep.comparison, dep.version):
+ new_rqd.append(dep)
+ break
+ requiredby = new_rqd
+
# sort out duplicate packages; this happens if something has a double
# versioned depend such as a kernel module
requiredby = [list(vals)[0] for _, vals in
@@ -263,10 +290,24 @@ def reverse_conflicts(self):
"""
Returns a list of packages with conflicts against this package.
"""
- # TODO: fix this; right now we cheat since we can't do proper version
- # number checking without using alpm or vercmp directly.
- return Package.objects.filter(conflicts__name=self.pkgname,
- conflicts__comparison='').distinct()
+ pkgs = Package.objects.filter(conflicts__name=self.pkgname)
+ alpm = AlpmAPI()
+ if not alpm.available:
+ return pkgs
+
+ # If we can use ALPM, we can filter out items that don't actually
+ # conflict due to the version specification.
+ pkgs = pkgs.prefetch_related('conflicts')
+ new_pkgs = []
+ for package in pkgs:
+ for conflict in package.conflicts.all():
+ if conflict.name != self.pkgname:
+ continue
+ if not conflict.comparison or not conflict.version \
+ or alpm.compare_versions(self.full_version,
+ conflict.comparison, conflict.version):
+ new_pkgs.append(package)
+ return new_pkgs
@cache_function(125)
def base_package(self):
diff --git a/packages/models.py b/packages/models.py
index 1959183f..403dfef0 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -7,6 +7,7 @@
from main.models import Arch, Repo, Package
from main.utils import set_created_field, database_vendor
+from packages.alpm import AlpmAPI
class PackageRelation(models.Model):
@@ -341,6 +342,13 @@ def get_best_satisfier(self):
# make sure we match architectures if possible
arches = self.pkg.applicable_arches()
pkgs = pkgs.filter(arch__in=arches)
+ # if we have a comparison operation, make sure the packages we grab
+ # actually satisfy the requirements
+ if self.comparison and self.version:
+ alpm = AlpmAPI()
+ pkgs = [pkg for pkg in pkgs if not alpm.available or
+ alpm.compare_versions(pkg.full_version, self.comparison,
+ self.version)]
if len(pkgs) == 0:
# couldn't find a package in the DB
# it should be a virtual depend (or a removed package)
@@ -373,6 +381,21 @@ def get_providers(self):
arches = self.pkg.applicable_arches()
pkgs = pkgs.filter(arch__in=arches)
+ # If we have a comparison operation, make sure the packages we grab
+ # actually satisfy the requirements.
+ alpm = AlpmAPI()
+ if alpm.available and self.comparison and self.version:
+ pkgs = pkgs.prefetch_related('provides')
+ new_pkgs = []
+ for package in pkgs:
+ for provide in package.provides.all():
+ if provide.name != self.name:
+ continue
+ if alpm.compare_versions(provide.version,
+ self.comparison, self.version):
+ new_pkgs.append(package)
+ pkgs = new_pkgs
+
# Logic here is to filter out packages that are in multiple repos if
# they are not requested. For example, if testing is False, only show a
# testing package if it doesn't exist in a non-testing repo.
--
cgit v1.2.3-54-g00ecf
From b7a03d89d126989bf53005404759482e17163991 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 7 Aug 2012 23:22:18 -0500
Subject: Push more default values down into the database
This makes it easier to do manual manipulation/insertion/etc. at the
database level, as well as just making things act more sane from an
overall software stack perspective.
Signed-off-by: Dan McGee
---
main/migrations/0032_auto__add_field_arch_agnostic.py | 2 +-
main/migrations/0037_auto__add_field_userprofile_time_zone.py | 2 +-
main/migrations/0043_auto__add_field_package_epoch.py | 2 +-
main/migrations/0046_auto__add_field_repo_staging.py | 2 +-
main/migrations/0048_auto__add_field_repo_bugs_category.py | 2 +-
mirrors/migrations/0004_auto__add_field_mirrorprotocol_is_download.py | 2 +-
...uto__add_field_mirrorurl_has_ipv4__add_field_mirrorurl_has_ipv6.py | 4 ++--
mirrors/migrations/0010_auto__add_field_mirrorprotocol_default.py | 2 +-
mirrors/migrations/0017_auto__chg_field_mirrorlog_error.py | 2 +-
packages/models.py | 2 +-
10 files changed, 11 insertions(+), 11 deletions(-)
(limited to 'packages')
diff --git a/main/migrations/0032_auto__add_field_arch_agnostic.py b/main/migrations/0032_auto__add_field_arch_agnostic.py
index ab9b9159..9ccf059d 100644
--- a/main/migrations/0032_auto__add_field_arch_agnostic.py
+++ b/main/migrations/0032_auto__add_field_arch_agnostic.py
@@ -8,7 +8,7 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Arch.agnostic'
- db.add_column('arches', 'agnostic', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+ db.add_column('arches', 'agnostic', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=True)
def backwards(self, orm):
# Deleting field 'Arch.agnostic'
diff --git a/main/migrations/0037_auto__add_field_userprofile_time_zone.py b/main/migrations/0037_auto__add_field_userprofile_time_zone.py
index 9b9b8beb..3a65eacc 100644
--- a/main/migrations/0037_auto__add_field_userprofile_time_zone.py
+++ b/main/migrations/0037_auto__add_field_userprofile_time_zone.py
@@ -8,7 +8,7 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'UserProfile.time_zone'
- db.add_column('user_profiles', 'time_zone', self.gf('django.db.models.fields.CharField')(default='UTC', max_length=100), keep_default=False)
+ db.add_column('user_profiles', 'time_zone', self.gf('django.db.models.fields.CharField')(default='UTC', max_length=100), keep_default=True)
def backwards(self, orm):
# Deleting field 'UserProfile.time_zone'
diff --git a/main/migrations/0043_auto__add_field_package_epoch.py b/main/migrations/0043_auto__add_field_package_epoch.py
index 77cd9b49..1c6ae9db 100644
--- a/main/migrations/0043_auto__add_field_package_epoch.py
+++ b/main/migrations/0043_auto__add_field_package_epoch.py
@@ -9,7 +9,7 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Package.epoch'
- db.add_column('packages', 'epoch', self.gf('django.db.models.fields.PositiveIntegerField')(default=0), keep_default=False)
+ db.add_column('packages', 'epoch', self.gf('django.db.models.fields.PositiveIntegerField')(default=0), keep_default=True)
def backwards(self, orm):
diff --git a/main/migrations/0046_auto__add_field_repo_staging.py b/main/migrations/0046_auto__add_field_repo_staging.py
index 40c3cb20..0daaf69b 100644
--- a/main/migrations/0046_auto__add_field_repo_staging.py
+++ b/main/migrations/0046_auto__add_field_repo_staging.py
@@ -7,7 +7,7 @@
class Migration(SchemaMigration):
def forwards(self, orm):
- db.add_column('repos', 'staging', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+ db.add_column('repos', 'staging', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=True)
def backwards(self, orm):
db.delete_column('repos', 'staging')
diff --git a/main/migrations/0048_auto__add_field_repo_bugs_category.py b/main/migrations/0048_auto__add_field_repo_bugs_category.py
index 30575126..3e61f7ed 100644
--- a/main/migrations/0048_auto__add_field_repo_bugs_category.py
+++ b/main/migrations/0048_auto__add_field_repo_bugs_category.py
@@ -7,7 +7,7 @@
class Migration(SchemaMigration):
def forwards(self, orm):
- db.add_column('repos', 'bugs_category', self.gf('django.db.models.fields.SmallIntegerField')(default=0), keep_default=False)
+ db.add_column('repos', 'bugs_category', self.gf('django.db.models.fields.SmallIntegerField')(default=2), keep_default=False)
def backwards(self, orm):
db.delete_column('repos', 'bugs_category')
diff --git a/mirrors/migrations/0004_auto__add_field_mirrorprotocol_is_download.py b/mirrors/migrations/0004_auto__add_field_mirrorprotocol_is_download.py
index 5e44d211..0506e2cd 100644
--- a/mirrors/migrations/0004_auto__add_field_mirrorprotocol_is_download.py
+++ b/mirrors/migrations/0004_auto__add_field_mirrorprotocol_is_download.py
@@ -7,7 +7,7 @@
class Migration(SchemaMigration):
def forwards(self, orm):
- db.add_column('mirrors_mirrorprotocol', 'is_download', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
+ db.add_column('mirrors_mirrorprotocol', 'is_download', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=True)
def backwards(self, orm):
db.delete_column('mirrors_mirrorprotocol', 'is_download')
diff --git a/mirrors/migrations/0006_auto__add_field_mirrorurl_has_ipv4__add_field_mirrorurl_has_ipv6.py b/mirrors/migrations/0006_auto__add_field_mirrorurl_has_ipv4__add_field_mirrorurl_has_ipv6.py
index a5e34589..5a40207d 100644
--- a/mirrors/migrations/0006_auto__add_field_mirrorurl_has_ipv4__add_field_mirrorurl_has_ipv6.py
+++ b/mirrors/migrations/0006_auto__add_field_mirrorurl_has_ipv4__add_field_mirrorurl_has_ipv6.py
@@ -7,8 +7,8 @@
class Migration(SchemaMigration):
def forwards(self, orm):
- db.add_column('mirrors_mirrorurl', 'has_ipv4', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
- db.add_column('mirrors_mirrorurl', 'has_ipv6', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+ db.add_column('mirrors_mirrorurl', 'has_ipv4', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=True)
+ db.add_column('mirrors_mirrorurl', 'has_ipv6', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=True)
def backwards(self, orm):
db.delete_column('mirrors_mirrorurl', 'has_ipv4')
diff --git a/mirrors/migrations/0010_auto__add_field_mirrorprotocol_default.py b/mirrors/migrations/0010_auto__add_field_mirrorprotocol_default.py
index d30c78c7..66e60090 100644
--- a/mirrors/migrations/0010_auto__add_field_mirrorprotocol_default.py
+++ b/mirrors/migrations/0010_auto__add_field_mirrorprotocol_default.py
@@ -6,7 +6,7 @@
class Migration(SchemaMigration):
def forwards(self, orm):
- db.add_column('mirrors_mirrorprotocol', 'default', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
+ db.add_column('mirrors_mirrorprotocol', 'default', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=True)
def backwards(self, orm):
db.delete_column('mirrors_mirrorprotocol', 'default')
diff --git a/mirrors/migrations/0017_auto__chg_field_mirrorlog_error.py b/mirrors/migrations/0017_auto__chg_field_mirrorlog_error.py
index 2f76c099..60c4ec26 100644
--- a/mirrors/migrations/0017_auto__chg_field_mirrorlog_error.py
+++ b/mirrors/migrations/0017_auto__chg_field_mirrorlog_error.py
@@ -7,7 +7,7 @@
class Migration(SchemaMigration):
def forwards(self, orm):
- db.alter_column('mirrors_mirrorlog', 'error', self.gf('django.db.models.fields.TextField')())
+ db.alter_column('mirrors_mirrorlog', 'error', self.gf('django.db.models.fields.TextField')(default=''))
def backwards(self, orm):
db.alter_column('mirrors_mirrorlog', 'error', self.gf('django.db.models.fields.CharField')(max_length=255))
diff --git a/packages/models.py b/packages/models.py
index 403dfef0..11fd0a66 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -176,7 +176,7 @@ class FlagRequest(models.Model):
ip_address = models.GenericIPAddressField(verbose_name='IP address',
unpack_ipv4=True)
pkgbase = models.CharField(max_length=255, db_index=True)
- version = models.CharField(max_length=255, default='')
+ version = models.CharField(max_length=255)
repo = models.ForeignKey(Repo)
num_packages = models.PositiveIntegerField('number of packages', default=1)
message = models.TextField('message to developer', blank=True)
--
cgit v1.2.3-54-g00ecf
From 064a1b7699ef8f65ee1c123f93cd8da02e843dea Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 8 Aug 2012 19:12:06 -0500
Subject: Fix alpm ctypes interface on systems not having alpm
Signed-off-by: Dan McGee
---
packages/alpm.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
(limited to 'packages')
diff --git a/packages/alpm.py b/packages/alpm.py
index a7f4c3b5..3762ea68 100644
--- a/packages/alpm.py
+++ b/packages/alpm.py
@@ -5,16 +5,23 @@
def load_alpm(name=None):
# Load the alpm library and set up some of the functions we might use
- if name == None:
+ if name is None:
name = find_library('alpm')
+ if name is None:
+ # couldn't locate the correct library
+ return None
try:
alpm = ctypes.cdll.LoadLibrary(name)
except OSError:
return None
- alpm.alpm_version.argtypes = ()
- alpm.alpm_version.restype = ctypes.c_char_p
- alpm.alpm_pkg_vercmp.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
- alpm.alpm_pkg_vercmp.restype = ctypes.c_int
+ try:
+ alpm.alpm_version.argtypes = ()
+ alpm.alpm_version.restype = ctypes.c_char_p
+ alpm.alpm_pkg_vercmp.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
+ alpm.alpm_pkg_vercmp.restype = ctypes.c_int
+ except AttributeError:
+ return None
+
return alpm
--
cgit v1.2.3-54-g00ecf
From 411ccfb3c74c521969ca6b68459289134976547d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 8 Aug 2012 22:04:07 -0500
Subject: Begin split of flag request version column into parts
Not sure why on only this one I decided to put all three parts in the
same column. We don't do this anywhere else.
Signed-off-by: Dan McGee
---
.../0023_split_flag_req_version_field.py | 222 +++++++++++++++++++++
packages/models.py | 14 ++
2 files changed, 236 insertions(+)
create mode 100644 packages/migrations/0023_split_flag_req_version_field.py
(limited to 'packages')
diff --git a/packages/migrations/0023_split_flag_req_version_field.py b/packages/migrations/0023_split_flag_req_version_field.py
new file mode 100644
index 00000000..b3d6c05c
--- /dev/null
+++ b/packages/migrations/0023_split_flag_req_version_field.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+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_flagrequest', 'pkgver',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=255),
+ keep_default=False)
+ db.add_column('packages_flagrequest', 'pkgrel',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=255),
+ keep_default=False)
+ db.add_column('packages_flagrequest', 'epoch',
+ self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
+ keep_default=True)
+
+ def backwards(self, orm):
+ db.delete_column('packages_flagrequest', 'pkgver')
+ db.delete_column('packages_flagrequest', 'pkgrel')
+ db.delete_column('packages_flagrequest', 'epoch')
+
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'deptype': ('django.db.models.fields.CharField', [], {'default': "'D'", 'max_length': '1'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ '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': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'version': ('django.db.models.fields.CharField', [], {'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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 11fd0a66..03da27ec 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -177,6 +177,9 @@ class FlagRequest(models.Model):
unpack_ipv4=True)
pkgbase = models.CharField(max_length=255, db_index=True)
version = models.CharField(max_length=255)
+ pkgver = models.CharField(max_length=255)
+ pkgrel = models.CharField(max_length=255)
+ epoch = models.PositiveIntegerField(default=0)
repo = models.ForeignKey(Repo)
num_packages = models.PositiveIntegerField('number of packages', default=1)
message = models.TextField('message to developer', blank=True)
@@ -193,6 +196,17 @@ def who(self):
return self.user.get_full_name()
return self.user_email
+ @property
+ def full_version(self):
+ # Difference here from other implementations at the moment: we need to
+ # handle the case of pkgver and pkgrel being null as this table didn't
+ # originally have version columns.
+ if self.pkgver == '' and self.pkgrel == '':
+ return u''
+ if self.epoch > 0:
+ return u'%d:%s-%s' % (self.epoch, self.pkgver, self.pkgrel)
+ return u'%s-%s' % (self.pkgver, self.pkgrel)
+
def __unicode__(self):
return u'%s from %s on %s' % (self.pkgbase, self.who(), self.created)
--
cgit v1.2.3-54-g00ecf
From 241ff8fbd79f9f17cd326a34eb39096851f630ba Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 8 Aug 2012 22:07:06 -0500
Subject: Extract parse_version function from reporead logic
Signed-off-by: Dan McGee
---
devel/management/commands/reporead.py | 9 ++-------
packages/utils.py | 18 ++++++++++++++++++
2 files changed, 20 insertions(+), 7 deletions(-)
(limited to 'packages')
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index 8b55b09a..af0a2dc0 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -34,6 +34,7 @@
from main.models import Arch, Package, PackageFile, Repo
from main.utils import database_vendor
from packages.models import Depend, Conflict, Provision, Replacement, Update
+from packages.utils import parse_version
logging.basicConfig(
@@ -84,8 +85,6 @@ class RepoPackage(object):
'conflicts', 'provides', 'replaces', 'groups', 'license',
'files' )
- version_re = re.compile(r'^((\d+):)?(.+)-([^-]+)$')
-
def __init__(self, repo):
self.repo = repo
self.ver = None
@@ -112,11 +111,7 @@ def populate(self, values):
# do NOT prune these values at all
setattr(self, k, v[0])
elif k == 'version':
- match = self.version_re.match(v[0])
- self.ver = match.group(3)
- self.rel = match.group(4)
- if match.group(2):
- self.epoch = int(match.group(2))
+ self.ver, self.rel, self.epoch = parse_version(v[0])
elif k == 'builddate':
try:
builddate = datetime.utcfromtimestamp(int(v[0]))
diff --git a/packages/utils.py b/packages/utils.py
index 6d54d71a..d4b4e611 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -1,6 +1,7 @@
from collections import defaultdict
from itertools import chain
from operator import itemgetter
+import re
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection
@@ -14,6 +15,23 @@
License, Depend, Conflict, Provision, Replacement,
SignoffSpecification, Signoff, DEFAULT_SIGNOFF_SPEC)
+
+VERSION_RE = re.compile(r'^((\d+):)?(.+)-([^-]+)$')
+
+
+def parse_version(version):
+ match = VERSION_RE.match(version)
+ if not match:
+ return None, None, 0
+ ver = match.group(3)
+ rel = match.group(4)
+ if match.group(2):
+ epoch = int(match.group(2))
+ else:
+ epoch = 0
+ return ver, rel, epoch
+
+
@cache_function(127)
def get_group_info(include_arches=None):
raw_groups = PackageGroup.objects.values_list(
--
cgit v1.2.3-54-g00ecf
From ad05f3eb2c8511c63dbdc9378bf3561ab949e940 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 8 Aug 2012 22:07:38 -0500
Subject: PEP8 cleanups in package utils
Signed-off-by: Dan McGee
---
packages/utils.py | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index d4b4e611..d95c015f 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -36,7 +36,7 @@ def parse_version(version):
def get_group_info(include_arches=None):
raw_groups = PackageGroup.objects.values_list(
'name', 'pkg__arch__name').order_by('name').annotate(
- cnt=Count('pkg'), last_update=Max('pkg__last_update'))
+ cnt=Count('pkg'), last_update=Max('pkg__last_update'))
# now for post_processing. we need to seperate things out and add
# the count in for 'any' to all of the other architectures.
group_mapping = {}
@@ -71,6 +71,7 @@ def get_group_info(include_arches=None):
groups.extend(val.itervalues())
return sorted(groups, key=itemgetter('name', 'arch'))
+
def get_split_packages_info():
'''Return info on split packages that do not have an actual package name
matching the split pkgbase.'''
@@ -276,6 +277,7 @@ def approved_by_signoffs(signoffs, spec):
return good_signoffs >= spec.required
return False
+
class PackageSignoffGroup(object):
'''Encompasses all packages in testing with the same pkgbase.'''
def __init__(self, packages):
@@ -375,6 +377,7 @@ def __unicode__(self):
AND p.repo_id IN (%s)
"""
+
def get_current_signoffs(repos):
'''Returns a mapping of pkgbase -> signoff objects for the given repos.'''
cursor = connection.cursor()
@@ -389,6 +392,7 @@ def get_current_signoffs(repos):
signoffs = Signoff.objects.select_related('user').in_bulk(to_fetch)
return signoffs.values()
+
def get_current_specifications(repos):
'''Returns a mapping of pkgbase -> signoff specification objects for the
given repos.'''
@@ -401,6 +405,7 @@ def get_current_specifications(repos):
to_fetch = [row[0] for row in results]
return SignoffSpecification.objects.in_bulk(to_fetch).values()
+
def get_target_repo_map(repos):
sql = """
SELECT DISTINCT p1.pkgbase, r.name
@@ -421,6 +426,7 @@ def get_target_repo_map(repos):
cursor.execute(sql, params)
return dict(cursor.fetchall())
+
def get_signoff_groups(repos=None, user=None):
if repos is None:
repos = Repo.objects.filter(testing=True)
@@ -458,12 +464,12 @@ def get_signoff_groups(repos=None, user=None):
class PackageJSONEncoder(DjangoJSONEncoder):
- pkg_attributes = [ 'pkgname', 'pkgbase', 'repo', 'arch', 'pkgver',
+ pkg_attributes = ['pkgname', 'pkgbase', 'repo', 'arch', 'pkgver',
'pkgrel', 'epoch', 'pkgdesc', 'url', 'filename', 'compressed_size',
'installed_size', 'build_date', 'last_update', 'flag_date',
- 'maintainers', 'packager' ]
- pkg_list_attributes = [ 'groups', 'licenses', 'conflicts',
- 'provides', 'replaces', 'depends' ]
+ 'maintainers', 'packager']
+ pkg_list_attributes = ['groups', 'licenses', 'conflicts',
+ 'provides', 'replaces', 'depends']
def default(self, obj):
if hasattr(obj, '__iter__'):
@@ -488,5 +494,4 @@ def default(self, obj):
return obj.username
return super(PackageJSONEncoder, self).default(obj)
-
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From b425b192e12afd0584bbffc9ff1d997a330bcd5a Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 8 Aug 2012 22:21:05 -0500
Subject: Migrate flag request version info to new format
Signed-off-by: Dan McGee
---
main/models.py | 8 +-
packages/admin.py | 4 +-
.../migrations/0024_move_flag_req_version_info.py | 218 +++++++++++++++++++++
packages/views/flag.py | 10 +-
4 files changed, 232 insertions(+), 8 deletions(-)
create mode 100644 packages/migrations/0024_move_flag_req_version_info.py
(limited to 'packages')
diff --git a/main/models.py b/main/models.py
index bb2320e8..95e3c42b 100644
--- a/main/models.py
+++ b/main/models.py
@@ -345,12 +345,16 @@ def split_packages(self):
pkgbase=self.pkgbase).exclude(id=self.id)
def flag_request(self):
- if not self.flag_date:
+ if self.flag_date is None:
return None
from packages.models import FlagRequest
try:
+ # Note that we don't match on pkgrel here; this is because a pkgrel
+ # bump does not unflag a package so we can still show the same flag
+ # request from a different pkgrel.
request = FlagRequest.objects.filter(pkgbase=self.pkgbase,
- repo=self.repo).latest()
+ repo=self.repo, pkgver=self.pkgver,
+ epoch=self.epoch).latest()
return request
except FlagRequest.DoesNotExist:
return None
diff --git a/packages/admin.py b/packages/admin.py
index 51c6fb06..5e32dbb4 100644
--- a/packages/admin.py
+++ b/packages/admin.py
@@ -12,8 +12,8 @@ class PackageRelationAdmin(admin.ModelAdmin):
class FlagRequestAdmin(admin.ModelAdmin):
- list_display = ('pkgbase', 'version', 'repo', 'created', 'who', 'is_spam',
- 'is_legitimate', 'message')
+ list_display = ('pkgbase', 'full_version', 'repo', 'created', 'who',
+ 'is_spam', 'is_legitimate', 'message')
list_filter = ('is_spam', 'is_legitimate', 'repo')
search_fields = ('pkgbase', 'user_email', 'message')
ordering = ('-created',)
diff --git a/packages/migrations/0024_move_flag_req_version_info.py b/packages/migrations/0024_move_flag_req_version_info.py
new file mode 100644
index 00000000..3be4654a
--- /dev/null
+++ b/packages/migrations/0024_move_flag_req_version_info.py
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+from packages.utils import parse_version
+
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ for pk, version in orm.FlagRequest.objects.exclude(
+ version='').values_list('pk', 'version'):
+ ver, rel, epoch = parse_version(version)
+ orm.FlagRequest.objects.filter(pk=pk).update(
+ pkgver=ver, pkgrel=rel, epoch=epoch)
+
+ def backwards(self, orm):
+ orm.FlagRequest.objects.all().update(pkgver='', pkgrel='', epoch=0)
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'deptype': ('django.db.models.fields.CharField', [], {'default': "'D'", 'max_length': '1'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ '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': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'version': ('django.db.models.fields.CharField', [], {'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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
+ symmetrical = True
diff --git a/packages/views/flag.py b/packages/views/flag.py
index b9542a62..16f5f202 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -59,11 +59,12 @@ def flag(request, name, repo, arch):
flagged_pkgs = list(pkgs)
# find a common version if there is one available to store
- versions = set(pkg.full_version for pkg in flagged_pkgs)
+ versions = set((pkg.pkgver, pkg.pkgrel, pkg.epoch)
+ for pkg in flagged_pkgs)
if len(versions) == 1:
version = versions.pop()
else:
- version = ''
+ version = ('', '', 0)
message = form.cleaned_data['message']
ip_addr = request.META.get('REMOTE_ADDR')
@@ -77,11 +78,12 @@ def perform_updates():
current_time = now()
pkgs.update(flag_date=current_time)
# store our flag request
+ # TODO
flag_request = FlagRequest(created=current_time,
user_email=email, message=message,
ip_address=ip_addr, pkgbase=pkg.pkgbase,
- version=version, repo=pkg.repo,
- num_packages=len(flagged_pkgs))
+ repo=pkg.repo, pkgver=version[0], pkgrel=version[1],
+ epoch=version[2], num_packages=len(flagged_pkgs))
if authenticated:
flag_request.user = request.user
flag_request.save()
--
cgit v1.2.3-54-g00ecf
From 32e3e24bbc26a44cbf6fce06cf802ee26f81aa48 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 8 Aug 2012 22:22:24 -0500
Subject: Drop old flag request version column
Signed-off-by: Dan McGee
---
.../0025_auto__del_field_flagrequest_version.py | 213 +++++++++++++++++++++
packages/models.py | 1 -
2 files changed, 213 insertions(+), 1 deletion(-)
create mode 100644 packages/migrations/0025_auto__del_field_flagrequest_version.py
(limited to 'packages')
diff --git a/packages/migrations/0025_auto__del_field_flagrequest_version.py b/packages/migrations/0025_auto__del_field_flagrequest_version.py
new file mode 100644
index 00000000..963b0b12
--- /dev/null
+++ b/packages/migrations/0025_auto__del_field_flagrequest_version.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.delete_column('packages_flagrequest', 'version')
+
+ def backwards(self, orm):
+ db.add_column('packages_flagrequest', 'version',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=255),
+ keep_default=False)
+
+
+ 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',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ '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': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ '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'", 'on_delete': 'models.PROTECT', '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': '2'}),
+ '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.depend': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Depend'},
+ 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'deptype': ('django.db.models.fields.CharField', [], {'default': "'D'", 'max_length': '1'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ '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': "'depends'", 'to': "orm['main.Package']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'})
+ },
+ 'packages.flagrequest': {
+ 'Meta': {'object_name': 'FlagRequest'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'})
+ },
+ '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', 'db_index': 'True'}),
+ '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'})
+ },
+ 'packages.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"})
+ },
+ 'packages.signoffspecification': {
+ 'Meta': {'object_name': 'SignoffSpecification'},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkgbase': ('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', [], {'to': "orm['main.Repo']"}),
+ 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
+ },
+ 'packages.update': {
+ 'Meta': {'object_name': 'Update'},
+ 'action_flag': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Arch']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'new_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'new_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_epoch': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'old_pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'old_pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Package']"}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updates'", 'to': "orm['main.Repo']"})
+ }
+ }
+
+ complete_apps = ['packages']
diff --git a/packages/models.py b/packages/models.py
index 03da27ec..af428a77 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -176,7 +176,6 @@ class FlagRequest(models.Model):
ip_address = models.GenericIPAddressField(verbose_name='IP address',
unpack_ipv4=True)
pkgbase = models.CharField(max_length=255, db_index=True)
- version = models.CharField(max_length=255)
pkgver = models.CharField(max_length=255)
pkgrel = models.CharField(max_length=255)
epoch = models.PositiveIntegerField(default=0)
--
cgit v1.2.3-54-g00ecf
From 3cb16e4784f492c50555e879ea6b07fd898b1c3d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 13 Aug 2012 09:34:11 -0500
Subject: Attempt to screen for useless out-of-date messages
Things like ' ', '-', '.', etc. will no longer be accepted in this
field.
Signed-off-by: Dan McGee
---
packages/views/flag.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 16f5f202..33cec006 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -1,3 +1,5 @@
+import re
+
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import permission_required
@@ -30,6 +32,15 @@ def __init__(self, *args, **kwargs):
if auth:
del self.fields['email']
+ def clean_message(self):
+ data = self.cleaned_data['message']
+ # make sure the message isn't garbage (only punctuation or whitespace)
+ # and ensure a certain minimum length
+ if re.match(r'^[^0-9A-Za-z]+$', data) or len(data) < 3:
+ raise forms.ValidationError(
+ "Enter a valid and useful out-of-date message.")
+ return data
+
@cache_page(3600)
def flaghelp(request):
@@ -78,7 +89,6 @@ def perform_updates():
current_time = now()
pkgs.update(flag_date=current_time)
# store our flag request
- # TODO
flag_request = FlagRequest(created=current_time,
user_email=email, message=message,
ip_address=ip_addr, pkgbase=pkg.pkgbase,
--
cgit v1.2.3-54-g00ecf
From a071d800c6a26d3efcdc0d32fe1adb1cde7e6f31 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 15 Aug 2012 08:22:01 -0500
Subject: Fix signoffs SQL query
Although the old query returned the same results, the repos IN clause
should really be a part of the WHERE, not the JOIN condition.
Signed-off-by: Dan McGee
---
packages/utils.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index d95c015f..ee1b56b3 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -363,6 +363,7 @@ def __unicode__(self):
return u'%s-%s (%s): %d' % (
self.pkgbase, self.version, self.arch, len(self.signoffs))
+
_SQL_SPEC_OR_SIGNOFF = """
SELECT DISTINCT s.id
FROM %s s
@@ -374,7 +375,7 @@ def __unicode__(self):
AND s.arch_id = p.arch_id
AND s.repo_id = p.repo_id
)
- AND p.repo_id IN (%s)
+ WHERE p.repo_id IN (%s)
"""
--
cgit v1.2.3-54-g00ecf
From 9189665c2c64c13a59b25b981633ae9e12cfcc92 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 15 Aug 2012 08:24:10 -0500
Subject: Ensure created is set when creating flag request via admin
Signed-off-by: Dan McGee
---
packages/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index af428a77..0bea21b1 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -482,7 +482,8 @@ class Replacement(RelatedToBase):
# hook up some signals
-for sender in (PackageRelation, SignoffSpecification, Signoff, Update):
+for sender in (FlagRequest, PackageRelation,
+ SignoffSpecification, Signoff, Update):
pre_save.connect(set_created_field, sender=sender,
dispatch_uid="packages.models")
--
cgit v1.2.3-54-g00ecf
From f7289625000d0f83675fc5a70650b49707338dca Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 20 Aug 2012 21:19:02 -0500
Subject: Use case-insensitive search in opensearch suggestions
There is no real good reason not to do this, since our packages are
lowercased by convention.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 038d40ac..f7952255 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -5,6 +5,7 @@
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
from django.core.cache import cache
+from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.views.decorators.cache import cache_control
@@ -47,8 +48,13 @@ def opensearch_suggest(request):
hashlib.md5(search_term.encode('utf-8')).hexdigest()
to_json = cache.get(cache_key, None)
if to_json is None:
- names = Package.objects.filter(
- pkgname__startswith=search_term).values_list(
+ q = Q(pkgname__startswith=search_term)
+ lookup = search_term.lower()
+ if search_term != lookup:
+ # package names are lowercase by convention, so include that in
+ # search if original wasn't lowercase already
+ q |= Q(pkgname__startswith=lookup)
+ names = Package.objects.filter(q).values_list(
'pkgname', flat=True).order_by('pkgname').distinct()[:10]
results = [search_term, list(names)]
to_json = json.dumps(results, ensure_ascii=False)
--
cgit v1.2.3-54-g00ecf
From cd51842ce86c44eef4e3c3d5334aca13e234151a Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 20 Aug 2012 23:50:21 -0500
Subject: Fix scm_link for Unicode characters
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This blew up with non-ASCII due to us trying to stuff 8-bit characters
into the URL parameters where they aren't allowed.
Tested with a package entered via the admin with pkgname and pkgbase set
to 'αναζήτη'.
Signed-off-by: Dan McGee
---
packages/templatetags/package_extras.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index 9daecd96..a0ee8bee 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -91,11 +91,12 @@ def packager_link(user):
@register.simple_tag
def scm_link(package, operation):
- parts = (package.repo.svn_root, operation, package.pkgbase)
- linkbase = (
- "https://projects.archlinux.org/svntogit/%s.git/%s/trunk?"
- "h=packages/%s")
- return linkbase % tuple(urlquote(part) for part in parts)
+ parts = (package.repo.svn_root, operation)
+ url = "https://projects.archlinux.org/svntogit/%s.git/%s/trunk" % parts
+ data = {
+ 'h': 'packages/%s' % package.pkgbase
+ }
+ return link_encode(url, data)
@register.simple_tag
def get_wiki_link(package):
--
cgit v1.2.3-54-g00ecf
From 1c416b97e6a9f3d6bf5f8747bd10ed1ca9cad68c Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 21 Aug 2012 00:02:57 -0500
Subject: Revert "Fix scm_link for Unicode characters"
This reverts commit cd51842ce86c44eef4e3c3d5334aca13e234151a. It turns
out cgit doesn't like it if you escape the '/' in the URL parameter when
viewing the log (a clear upstream bug), but we need to be able to work
around this.
Example: 'extra%2Fkdelibs' and 'extra/kdelibs' are handled differently.
---
packages/templatetags/package_extras.py | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index a0ee8bee..9daecd96 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -91,12 +91,11 @@ def packager_link(user):
@register.simple_tag
def scm_link(package, operation):
- parts = (package.repo.svn_root, operation)
- url = "https://projects.archlinux.org/svntogit/%s.git/%s/trunk" % parts
- data = {
- 'h': 'packages/%s' % package.pkgbase
- }
- return link_encode(url, data)
+ parts = (package.repo.svn_root, operation, package.pkgbase)
+ linkbase = (
+ "https://projects.archlinux.org/svntogit/%s.git/%s/trunk?"
+ "h=packages/%s")
+ return linkbase % tuple(urlquote(part) for part in parts)
@register.simple_tag
def get_wiki_link(package):
--
cgit v1.2.3-54-g00ecf
From 90001d9ac781d07feba6120bfe64112481b9a5fb Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 21 Aug 2012 00:10:12 -0500
Subject: Style cleanups in package_extras
Signed-off-by: Dan McGee
---
packages/templatetags/package_extras.py | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index 9daecd96..1aedf677 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -9,6 +9,7 @@
register = template.Library()
+
def link_encode(url, query):
# massage the data into all utf-8 encoded strings first, so urlencode
# doesn't barf at the data we pass it
@@ -16,6 +17,7 @@ def link_encode(url, query):
data = urlencode(query).replace('&', '&')
return "%s?%s" % (url, data)
+
@register.filter
def url_unquote(original_url):
try:
@@ -27,6 +29,7 @@ def url_unquote(original_url):
except UnicodeError:
return original_url
+
class BuildQueryStringNode(template.Node):
def __init__(self, sortfield):
self.sortfield = sortfield
@@ -34,7 +37,7 @@ def __init__(self, sortfield):
def render(self, context):
qs = parse_qs(context['current_query'])
- if qs.has_key('sort') and self.sortfield in qs['sort']:
+ if 'sort' in qs and self.sortfield in qs['sort']:
if self.sortfield.startswith('-'):
qs['sort'] = [self.sortfield[1:]]
else:
@@ -43,6 +46,7 @@ def render(self, context):
qs['sort'] = [self.sortfield]
return urlencode(qs, True).replace('&', '&')
+
@register.tag(name='buildsortqs')
def do_buildsortqs(parser, token):
try:
@@ -55,15 +59,18 @@ def do_buildsortqs(parser, token):
"%r tag's argument should be in quotes" % tagname)
return BuildQueryStringNode(sortfield[1:-1])
+
@register.simple_tag
def pkg_details_link(pkg):
link = '%s '
return link % (pkg.get_absolute_url(), pkg.pkgname, pkg.pkgname)
+
@register.simple_tag
def multi_pkg_details(pkgs):
return ', '.join([pkg_details_link(pkg) for pkg in pkgs])
+
@register.simple_tag
def maintainer_link(user):
if user:
@@ -76,6 +83,7 @@ def maintainer_link(user):
)
return ''
+
@register.simple_tag
def packager_link(user):
if user:
@@ -97,6 +105,7 @@ def scm_link(package, operation):
"h=packages/%s")
return linkbase % tuple(urlquote(part) for part in parts)
+
@register.simple_tag
def get_wiki_link(package):
url = "https://wiki.archlinux.org/index.php/Special:Search"
@@ -105,6 +114,7 @@ def get_wiki_link(package):
}
return link_encode(url, data)
+
@register.simple_tag
def bugs_list(package):
url = "https://bugs.archlinux.org/"
@@ -115,6 +125,7 @@ def bugs_list(package):
}
return link_encode(url, data)
+
@register.simple_tag
def bug_report(package):
url = "https://bugs.archlinux.org/newtask"
--
cgit v1.2.3-54-g00ecf
From eb44404a17ae9feacdf2948a5cda958510ac2fed Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 21 Aug 2012 00:12:38 -0500
Subject: Fix scm_link in a way that doesn't make cgit barf
This does what commit cd51842ce86 set out to do in a way that doesn't
break cgit's not-so-hit query string parsing.
Signed-off-by: Dan McGee
---
packages/templatetags/package_extras.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index 1aedf677..d87a74a5 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -103,7 +103,7 @@ def scm_link(package, operation):
linkbase = (
"https://projects.archlinux.org/svntogit/%s.git/%s/trunk?"
"h=packages/%s")
- return linkbase % tuple(urlquote(part) for part in parts)
+ return linkbase % tuple(urlquote(part.encode('utf-8')) for part in parts)
@register.simple_tag
--
cgit v1.2.3-54-g00ecf
From f2f00b3c0474c4776e7a7f0e58162dc67ce2ca18 Mon Sep 17 00:00:00 2001
From: Florian Pritz
Date: Sat, 8 Sep 2012 17:08:21 +0200
Subject: p/v/flag: Add reply-to to out-of-date notifications
Signed-off-by: Florian Pritz
Signed-off-by: Dan McGee
---
packages/views/flag.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
(limited to 'packages')
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 33cec006..d7302f72 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -3,7 +3,7 @@
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import permission_required
-from django.core.mail import send_mail
+from django.core.mail import send_mail, EmailMessage
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader, Context
@@ -122,11 +122,13 @@ def perform_updates():
'pkg': pkg,
'packages': flagged_pkgs,
})
- send_mail(subject,
+ msg = EmailMessage(subject,
tmpl.render(ctx),
'Arch Website Notification ',
toemail,
- fail_silently=True)
+ headers={"Reply-To": email }
+ )
+ msg.send(fail_silently=True)
return redirect('package-flag-confirmed', name=name, repo=repo,
arch=arch)
--
cgit v1.2.3-54-g00ecf
From 10f5c27bf8124a3ccffb94930283b5062ad96cce Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 10 Sep 2012 08:27:23 -0500
Subject: Remove now unnecessary import
Signed-off-by: Dan McGee
---
packages/views/flag.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/views/flag.py b/packages/views/flag.py
index d7302f72..dadadd19 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -3,7 +3,7 @@
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import permission_required
-from django.core.mail import send_mail, EmailMessage
+from django.core.mail import EmailMessage
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader, Context
--
cgit v1.2.3-54-g00ecf
From 7a0e6620c9c8782fbef37db15afc3ccebc642d19 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 25 Sep 2012 18:22:16 -0500
Subject: Don't show staging in package search repo listing
This is temporary or at least a quick way to ensure regular users aren't
confused by staging packages; later updates should re-enable display of
this for logged in developers and trusted users.
Signed-off-by: Dan McGee
---
packages/views/search.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 497d9bca..1fbe5694 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -56,8 +56,9 @@ class PackageSearchForm(forms.Form):
def __init__(self, *args, **kwargs):
super(PackageSearchForm, self).__init__(*args, **kwargs)
+ repos = Repo.objects.filter(staging=False)
self.fields['repo'].choices = make_choice(
- [repo.name for repo in Repo.objects.all()])
+ [repo.name for repo in repos])
self.fields['arch'].choices = make_choice(
[arch.name for arch in Arch.objects.all()])
self.fields['q'].widget.attrs.update({"size": "30"})
--
cgit v1.2.3-54-g00ecf
From 44dc458a2b8ca500acda72b437a720dd4f57bedf Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 30 Sep 2012 01:40:25 -0500
Subject: Hide staging packages in search results
This is for users that aren't logged in; developers will still see them.
Signed-off-by: Dan McGee
---
packages/views/search.py | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 1fbe5694..99bf703a 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -55,8 +55,11 @@ class PackageSearchForm(forms.Form):
initial=50)
def __init__(self, *args, **kwargs):
+ show_staging = kwargs.pop('show_staging', False)
super(PackageSearchForm, self).__init__(*args, **kwargs)
- repos = Repo.objects.filter(staging=False)
+ repos = Repo.objects.all()
+ if not show_staging:
+ repos = repos.filter(staging=False)
self.fields['repo'].choices = make_choice(
[repo.name for repo in repos])
self.fields['arch'].choices = make_choice(
@@ -129,11 +132,14 @@ class SearchListView(ListView):
allowed_sort = list(sort_fields) + ["-" + s for s in sort_fields]
def get(self, request, *args, **kwargs):
- self.form = PackageSearchForm(data=request.GET)
+ self.form = PackageSearchForm(data=request.GET,
+ show_staging=self.request.user.is_authenticated())
return super(SearchListView, self).get(request, *args, **kwargs)
def get_queryset(self):
packages = Package.objects.normal()
+ if not self.request.user.is_authenticated():
+ packages = packages.filter(repo__staging=False)
if self.form.is_valid():
packages = parse_form(self.form, packages)
sort = self.form.cleaned_data['sort']
@@ -174,9 +180,12 @@ def search_json(request):
}
if request.GET:
- form = PackageSearchForm(data=request.GET)
+ form = PackageSearchForm(data=request.GET,
+ show_staging=request.user.is_authenticated())
if form.is_valid():
packages = Package.objects.normal()
+ if not request.user.is_authenticated():
+ packages = packages.filter(repo__staging=False)
packages = parse_form(form, packages)[:limit]
container['results'] = packages
container['valid'] = True
--
cgit v1.2.3-54-g00ecf
From feabc12d384a448614dbc8a9a51cd39ee63b4a83 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 30 Sep 2012 10:11:19 -0500
Subject: Fix usage of naïve datetime object
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Dan McGee
---
packages/views/search.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 99bf703a..f7b8ed1d 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -1,5 +1,6 @@
from datetime import datetime
import json
+from pytz import utc
from django import forms
from django.contrib.admin.widgets import AdminDateWidget
@@ -105,8 +106,9 @@ def parse_form(form, packages):
if form.cleaned_data['last_update']:
lu = form.cleaned_data['last_update']
- packages = packages.filter(last_update__gte=
- datetime(lu.year, lu.month, lu.day, 0, 0))
+ cutoff = datetime(lu.year, lu.month, lu.day, 0, 0)
+ cutoff = cutoff.replace(tzinfo=utc)
+ packages = packages.filter(last_update__gte=cutoff)
if form.cleaned_data['name']:
name = form.cleaned_data['name']
--
cgit v1.2.3-54-g00ecf
From 50daf33d080c9fa85bf10e98b975cc44f6f72314 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 30 Sep 2012 13:00:41 -0500
Subject: pkg_details_link template tag enhancements
* Output the blank string when passed None for pkg argument
* Allow override of the link text if optional argument passed
Signed-off-by: Dan McGee
---
packages/templatetags/package_extras.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index d87a74a5..994265d8 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -61,9 +61,13 @@ def do_buildsortqs(parser, token):
@register.simple_tag
-def pkg_details_link(pkg):
+def pkg_details_link(pkg, link_title=None):
+ if not pkg:
+ return ''
+ if link_title is None:
+ link_title = pkg.pkgname
link = '%s '
- return link % (pkg.get_absolute_url(), pkg.pkgname, pkg.pkgname)
+ return link % (pkg.get_absolute_url(), pkg.pkgname, link_title)
@register.simple_tag
--
cgit v1.2.3-54-g00ecf
From a71aa2e354599950f4bd464f0f19215f1c581141 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 12 Oct 2012 11:34:49 -0500
Subject: Make wrong permissions query more efficient
This removes the subplan and per-row query in favor of a LEFT JOIN where
we look for non-matching rows. Tested in sqlite3 and PostgreSQL.
Signed-off-by: Dan McGee
---
packages/utils.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index ee1b56b3..c29e2297 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -228,12 +228,13 @@ def get_wrong_permissions():
FROM packages p
JOIN packages_packagerelation pr ON p.pkgbase = pr.pkgbase
WHERE pr.type = %s
- ) pkgs
- WHERE pkgs.repo_id NOT IN (
- SELECT repo_id FROM user_profiles_allowed_repos ar
+ ) mp
+ LEFT JOIN (
+ SELECT user_id, repo_id FROM user_profiles_allowed_repos ar
INNER JOIN user_profiles up ON ar.userprofile_id = up.id
- WHERE up.user_id = pkgs.user_id
- )
+ ) ur
+ ON mp.user_id = ur.user_id AND mp.repo_id = ur.repo_id
+ WHERE ur.user_id IS NULL;
"""
cursor = connection.cursor()
cursor.execute(sql, [PackageRelation.MAINTAINER])
--
cgit v1.2.3-54-g00ecf
From 0b3aa29cb63c6ca07f066a4a68fa3df9b92f6216 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 14 Oct 2012 15:42:15 -0500
Subject: Refactor signoff-grabbing queries
Make them a bit more efficient by adding an explicit condition on both
the packages and signoff table for the repo ID, and move the common code
into a shared function both can use.
Signed-off-by: Dan McGee
---
packages/utils.py | 40 ++++++++++++++++++----------------------
1 file changed, 18 insertions(+), 22 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index c29e2297..051fed8e 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -365,7 +365,8 @@ def __unicode__(self):
self.pkgbase, self.version, self.arch, len(self.signoffs))
-_SQL_SPEC_OR_SIGNOFF = """
+def signoffs_id_query(model, repos):
+ sql = """
SELECT DISTINCT s.id
FROM %s s
JOIN packages p ON (
@@ -377,34 +378,29 @@ def __unicode__(self):
AND s.repo_id = p.repo_id
)
WHERE p.repo_id IN (%s)
-"""
-
-
-def get_current_signoffs(repos):
- '''Returns a mapping of pkgbase -> signoff objects for the given repos.'''
+ AND s.repo_id IN (%s)
+ """
cursor = connection.cursor()
# query pre-process- fill in table name and placeholders for IN
- sql = _SQL_SPEC_OR_SIGNOFF % ('packages_signoff',
- ','.join(['%s' for r in repos]))
- cursor.execute(sql, [r.pk for r in repos])
+ repo_sql = ','.join(['%s' for r in repos])
+ sql = sql % (model._meta.db_table, repo_sql, repo_sql)
+ repo_ids = [r.pk for r in repos]
+ # repo_ids are needed twice, so double the array
+ cursor.execute(sql, repo_ids * 2)
results = cursor.fetchall()
- # fetch all of the returned signoffs by ID
- to_fetch = [row[0] for row in results]
- signoffs = Signoff.objects.select_related('user').in_bulk(to_fetch)
- return signoffs.values()
+ return [row[0] for row in results]
-def get_current_specifications(repos):
- '''Returns a mapping of pkgbase -> signoff specification objects for the
- given repos.'''
- cursor = connection.cursor()
- sql = _SQL_SPEC_OR_SIGNOFF % ('packages_signoffspecification',
- ','.join(['%s' for r in repos]))
- cursor.execute(sql, [r.pk for r in repos])
+def get_current_signoffs(repos):
+ '''Returns a list of signoff objects for the given repos.'''
+ to_fetch = signoffs_id_query(Signoff, repos)
+ return Signoff.objects.select_related('user').in_bulk(to_fetch).values()
- results = cursor.fetchall()
- to_fetch = [row[0] for row in results]
+
+def get_current_specifications(repos):
+ '''Returns a list of signoff specification objects for the given repos.'''
+ to_fetch = signoffs_id_query(SignoffSpecification, repos)
return SignoffSpecification.objects.in_bulk(to_fetch).values()
--
cgit v1.2.3-54-g00ecf
From 2ee662c77cf559d6ea82c9096533abfcd38f4801 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 26 Oct 2012 17:05:39 -0500
Subject: Extract some common architecture grabbing logic
Signed-off-by: Dan McGee
---
packages/views/display.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
(limited to 'packages')
diff --git a/packages/views/display.py b/packages/views/display.py
index b5cd643a..efedf6ff 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -13,11 +13,16 @@
from ..utils import get_group_info, PackageJSONEncoder
+def arch_plus_agnostic(arch):
+ arches = [ arch ]
+ arches.extend(Arch.objects.filter(agnostic=True).order_by())
+ return arches
+
+
def split_package_details(request, name, repo, arch):
'''Check if we have a split package (e.g. pkgbase) value matching this
name. If so, we can show a listing page for the entire set of packages.'''
- arches = [ arch ]
- arches.extend(Arch.objects.filter(agnostic=True))
+ arches = arch_plus_agnostic(arch)
pkgs = Package.objects.normal().filter(pkgbase=name,
repo__testing=repo.testing, repo__staging=repo.staging,
arch__in=arches).order_by('pkgname')
@@ -42,8 +47,7 @@ def recently_removed_package(request, name, repo, arch, cutoff=CUTOFF):
'''Check our packages update table to see if this package has existed in
this repo before. If so, we can show a 410 Gone page and point the
requester in the right direction.'''
- arches = [ arch ]
- arches.extend(Arch.objects.filter(agnostic=True))
+ arches = arch_plus_agnostic(arch)
match = Update.objects.select_related('arch', 'repo').filter(
pkgname=name, repo=repo, arch__in=arches)
if cutoff is not None:
@@ -149,8 +153,7 @@ def groups(request, arch=None):
def group_details(request, arch, name):
arch = get_object_or_404(Arch, name=arch)
- arches = [ arch ]
- arches.extend(Arch.objects.filter(agnostic=True))
+ arches = arch_plus_agnostic(arch)
pkgs = Package.objects.normal().filter(
groups__name=name, arch__in=arches).order_by('pkgname')
if len(pkgs) == 0:
--
cgit v1.2.3-54-g00ecf
From bdee24b9d1279de67dd238e3644c2efff314bd7b Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 26 Oct 2012 17:11:11 -0500
Subject: Cleanup meta model attributes
Signed-off-by: Dan McGee
---
main/models.py | 5 ++---
news/models.py | 2 +-
packages/models.py | 7 +++++--
3 files changed, 8 insertions(+), 6 deletions(-)
(limited to 'packages')
diff --git a/main/models.py b/main/models.py
index 00549268..5700cdf1 100644
--- a/main/models.py
+++ b/main/models.py
@@ -62,7 +62,7 @@ def __lt__(self, other):
class Meta:
db_table = 'arches'
- ordering = ['name']
+ ordering = ('name',)
verbose_name_plural = 'arches'
@@ -87,8 +87,7 @@ def __lt__(self, other):
class Meta:
db_table = 'repos'
- ordering = ['name']
- verbose_name_plural = 'repos'
+ ordering = ('name',)
class Package(models.Model):
diff --git a/news/models.py b/news/models.py
index 2efea579..91232706 100644
--- a/news/models.py
+++ b/news/models.py
@@ -24,7 +24,7 @@ class Meta:
db_table = 'news'
verbose_name_plural = 'news'
get_latest_by = 'postdate'
- ordering = ['-postdate']
+ ordering = ('-postdate',)
def set_news_fields(sender, **kwargs):
news = kwargs['instance']
diff --git a/packages/models.py b/packages/models.py
index 0bea21b1..0d0fbdf2 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -329,6 +329,9 @@ class PackageGroup(models.Model):
def __unicode__(self):
return "%s: %s" % (self.name, self.pkg)
+ class Meta:
+ ordering = ('name',)
+
class License(models.Model):
pkg = models.ForeignKey(Package, related_name='licenses')
@@ -338,7 +341,7 @@ def __unicode__(self):
return self.name
class Meta:
- ordering = ['name']
+ ordering = ('name',)
class RelatedToBase(models.Model):
@@ -435,7 +438,7 @@ def __unicode__(self):
class Meta:
abstract = True
- ordering = ['name']
+ ordering = ('name',)
class Depend(RelatedToBase):
--
cgit v1.2.3-54-g00ecf
From 6dd4d54bb0adbbb0f8c2b1beaa92b7a58971cf88 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 16 Nov 2012 16:20:11 -0600
Subject: Use Python 2.7 dictionary comprehension syntax
Rather than the old idiom of dict((k, v) for <> in <>).
Signed-off-by: Dan McGee
---
devel/management/commands/reporead.py | 4 ++--
devel/management/commands/reporead_inotify.py | 2 +-
mirrors/views.py | 11 ++++-------
packages/templatetags/package_extras.py | 2 +-
packages/utils.py | 5 ++---
packages/views/signoff.py | 8 +++-----
public/views.py | 2 +-
visualize/views.py | 4 ++--
8 files changed, 16 insertions(+), 22 deletions(-)
(limited to 'packages')
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index a1e77b49..3d4e6375 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -377,7 +377,7 @@ def db_update(archname, reponame, pkgs, force=False):
# This makes our inner loop where we find packages by name *way* more
# efficient by not having to go to the database for each package to
# SELECT them by name.
- dbdict = dict((dbpkg.pkgname, dbpkg) for dbpkg in dbpkgs)
+ dbdict = {dbpkg.pkgname: dbpkg for dbpkg in dbpkgs}
dbset = set(dbdict.keys())
syncset = set([pkg.name for pkg in pkgs])
@@ -446,7 +446,7 @@ def filesonly_update(archname, reponame, pkgs, force=False):
"""
logger.info('Updating files for %s (%s)', reponame, archname)
dbpkgs = update_common(archname, reponame, pkgs, False)
- dbdict = dict((dbpkg.pkgname, dbpkg) for dbpkg in dbpkgs)
+ dbdict = {dbpkg.pkgname: dbpkg for dbpkg in dbpkgs}
dbset = set(dbdict.keys())
for pkg in (pkg for pkg in pkgs if pkg.name in dbset):
diff --git a/devel/management/commands/reporead_inotify.py b/devel/management/commands/reporead_inotify.py
index c74762eb..16b3869c 100644
--- a/devel/management/commands/reporead_inotify.py
+++ b/devel/management/commands/reporead_inotify.py
@@ -69,7 +69,7 @@ def setup_notifier(self):
finally builds and returns a notifier object.'''
arches = Arch.objects.filter(agnostic=False)
repos = Repo.objects.all()
- arch_path_map = dict((arch, None) for arch in arches)
+ arch_path_map = {arch: None for arch in arches}
all_paths = set()
total_paths = 0
for arch in arches:
diff --git a/mirrors/views.py b/mirrors/views.py
index 2e1e83b6..d0ce0a97 100644
--- a/mirrors/views.py
+++ b/mirrors/views.py
@@ -94,7 +94,7 @@ def default_protocol_filter(original_urls):
def status_filter(original_urls):
status_info = get_mirror_statuses()
- scores = dict((u.id, u.score) for u in status_info['urls'])
+ scores = {u.id: u.score for u in status_info['urls']}
urls = []
for u in original_urls:
u.score = scores.get(u.id, None)
@@ -165,7 +165,7 @@ def mirrors(request):
if not request.user.is_authenticated():
mirror_list = mirror_list.filter(public=True, active=True)
protos = protos.filter(mirror__public=True, mirror__active=True)
- protos = dict((k, list(v)) for k, v in groupby(protos, key=itemgetter(0)))
+ protos = {k: list(v) for k, v in groupby(protos, key=itemgetter(0))}
for mirror in mirror_list:
items = protos.get(mirror.id, [])
mirror.protocols = [item[1] for item in items]
@@ -253,8 +253,7 @@ def default(self, obj):
# mainly for queryset serialization
return list(obj)
if isinstance(obj, MirrorUrl):
- data = dict((attr, getattr(obj, attr))
- for attr in self.url_attributes)
+ data = {attr: getattr(obj, attr) for attr in self.url_attributes}
# get any override on the country attribute first
country = obj.real_country
data['country'] = unicode(country.name)
@@ -277,9 +276,7 @@ def default(self, obj):
check_time__gte=cutoff).order_by('check_time')
return data
if isinstance(obj, MirrorLog):
- data = dict((attr, getattr(obj, attr))
- for attr in self.log_attributes)
- return data
+ return {attr: getattr(obj, attr) for attr in self.log_attributes}
return super(ExtendedMirrorStatusJSONEncoder, self).default(obj)
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index 994265d8..f3613e69 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -13,7 +13,7 @@
def link_encode(url, query):
# massage the data into all utf-8 encoded strings first, so urlencode
# doesn't barf at the data we pass it
- query = dict((k, unicode(v).encode('utf-8')) for k, v in query.items())
+ query = {k: unicode(v).encode('utf-8') for k, v in query.items()}
data = urlencode(query).replace('&', '&')
return "%s?%s" % (url, data)
diff --git a/packages/utils.py b/packages/utils.py
index 051fed8e..199e141d 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -385,7 +385,7 @@ def signoffs_id_query(model, repos):
repo_sql = ','.join(['%s' for r in repos])
sql = sql % (model._meta.db_table, repo_sql, repo_sql)
repo_ids = [r.pk for r in repos]
- # repo_ids are needed twice, so double the array
+ # repo_ids are needed twice, so double the array
cursor.execute(sql, repo_ids * 2)
results = cursor.fetchall()
@@ -474,8 +474,7 @@ def default(self, obj):
# mainly for queryset serialization
return list(obj)
if isinstance(obj, Package):
- data = dict((attr, getattr(obj, attr))
- for attr in self.pkg_attributes)
+ data = {attr: getattr(obj, attr) for attr in self.pkg_attributes}
for attr in self.pkg_list_attributes:
data[attr] = getattr(obj, attr).all()
return data
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 56eb060c..824a9922 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -155,8 +155,8 @@ class SignoffJSONEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, PackageSignoffGroup):
- data = dict((attr, getattr(obj, attr))
- for attr in self.signoff_group_attrs)
+ data = {attr: getattr(obj, attr)
+ for attr in self.signoff_group_attrs}
data['pkgnames'] = [p.pkgname for p in obj.packages]
data['package_count'] = len(obj.packages)
data['approved'] = obj.approved()
@@ -164,9 +164,7 @@ def default(self, obj):
for attr in self.signoff_spec_attrs)
return data
elif isinstance(obj, Signoff):
- data = dict((attr, getattr(obj, attr))
- for attr in self.signoff_attrs)
- return data
+ return {attr: getattr(obj, attr) for attr in self.signoff_attrs}
elif isinstance(obj, Arch) or isinstance(obj, Repo):
return unicode(obj)
elif isinstance(obj, User):
diff --git a/public/views.py b/public/views.py
index 96120761..3e15f9df 100644
--- a/public/views.py
+++ b/public/views.py
@@ -118,7 +118,7 @@ def keys(request):
sig_counts = PGPSignature.objects.filter(not_expired, valid=True,
signee__in=user_key_ids).values_list('signer').annotate(
Count('signer'))
- sig_counts = dict((key_id[-16:], ct) for key_id, ct in sig_counts)
+ sig_counts = {key_id[-16:]: ct for key_id, ct in sig_counts}
for key in master_keys:
key.signature_count = sig_counts.get(key.pgp_key[-16:], 0)
diff --git a/visualize/views.py b/visualize/views.py
index 8d878937..48e8f86b 100644
--- a/visualize/views.py
+++ b/visualize/views.py
@@ -33,8 +33,8 @@ def build_map(name, arch, repo):
# now transform these results into two mappings: one ordered (repo, arch),
# and one ordered (arch, repo).
- arch_groups = dict((a, build_map(a, a, None)) for a in arches)
- repo_groups = dict((r, build_map(r, None, r)) for r in repos)
+ arch_groups = {a: build_map(a, a, None) for a in arches}
+ repo_groups = {r: build_map(r, None, r) for r in repos}
for row in qs:
arch = row['arch__name']
repo = row['repo__name']
--
cgit v1.2.3-54-g00ecf
From 9e9157d0a8cbf9ea076231e438fb30f58bff8e29 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 16 Nov 2012 16:37:31 -0600
Subject: Use python set comprehension syntax supported in 2.7
Signed-off-by: Dan McGee
---
devel/management/commands/import_signatures.py | 4 ++--
devel/management/commands/reporead.py | 2 +-
devel/management/commands/reporead_inotify.py | 2 +-
devel/views.py | 4 ++--
main/models.py | 2 +-
packages/models.py | 2 +-
packages/utils.py | 10 +++++-----
packages/views/signoff.py | 2 +-
todolists/views.py | 8 ++++----
9 files changed, 18 insertions(+), 18 deletions(-)
(limited to 'packages')
diff --git a/devel/management/commands/import_signatures.py b/devel/management/commands/import_signatures.py
index ce1aba90..da1397ca 100644
--- a/devel/management/commands/import_signatures.py
+++ b/devel/management/commands/import_signatures.py
@@ -98,8 +98,8 @@ def import_signatures(keyring):
# now prune the data down to what we actually want.
# prune edges not in nodes, remove duplicates, and self-sigs
- pruned_edges = set(edge for edge in edges
- if edge.signer in nodes and edge.signer != edge.signee)
+ pruned_edges = {edge for edge in edges
+ if edge.signer in nodes and edge.signer != edge.signee}
logger.info("creating or finding %d signatures", len(pruned_edges))
created_ct = updated_ct = 0
diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py
index 3d4e6375..981c4dce 100644
--- a/devel/management/commands/reporead.py
+++ b/devel/management/commands/reporead.py
@@ -380,7 +380,7 @@ def db_update(archname, reponame, pkgs, force=False):
dbdict = {dbpkg.pkgname: dbpkg for dbpkg in dbpkgs}
dbset = set(dbdict.keys())
- syncset = set([pkg.name for pkg in pkgs])
+ syncset = {pkg.name for pkg in pkgs}
in_sync_not_db = syncset - dbset
logger.info("%d packages in sync not db", len(in_sync_not_db))
diff --git a/devel/management/commands/reporead_inotify.py b/devel/management/commands/reporead_inotify.py
index 16b3869c..04f65764 100644
--- a/devel/management/commands/reporead_inotify.py
+++ b/devel/management/commands/reporead_inotify.py
@@ -77,7 +77,7 @@ def setup_notifier(self):
for repo in repos)
# take a python format string and generate all unique combinations
# of directories from it; using set() ensures we filter it down
- paths = set(self.path_template % values for values in combos)
+ paths = {self.path_template % values for values in combos}
total_paths += len(paths)
all_paths |= paths
arch_path_map[arch] = paths
diff --git a/devel/views.py b/devel/views.py
index 083665d9..7d5947d1 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -277,8 +277,8 @@ def report(request, report_name, username=None):
else:
raise Http404
- arches = set(pkg.arch for pkg in packages)
- repos = set(pkg.repo for pkg in packages)
+ arches = {pkg.arch for pkg in packages}
+ repos = {pkg.repo for pkg in packages}
context = {
'all_maintainers': maints,
'title': title,
diff --git a/main/models.py b/main/models.py
index 5700cdf1..cc81637c 100644
--- a/main/models.py
+++ b/main/models.py
@@ -197,7 +197,7 @@ def get_requiredby(self):
"""
from packages.models import Depend
provides = self.provides.all()
- provide_names = set(provide.name for provide in provides)
+ provide_names = {provide.name for provide in provides}
provide_names.add(self.pkgname)
requiredby = Depend.objects.select_related('pkg',
'pkg__arch', 'pkg__repo').filter(
diff --git a/packages/models.py b/packages/models.py
index 0d0fbdf2..ede8c275 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -33,7 +33,7 @@ def get_associated_packages(self):
def repositories(self):
packages = self.get_associated_packages()
- return sorted(set([p.repo for p in packages]))
+ return sorted({p.repo for p in packages})
def __unicode__(self):
return u'%s: %s (%s)' % (
diff --git a/packages/utils.py b/packages/utils.py
index 199e141d..5adc8637 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -79,8 +79,8 @@ def get_split_packages_info():
split_pkgs = Package.objects.exclude(pkgname=F('pkgbase')).exclude(
pkgbase__in=pkgnames).values('pkgbase', 'repo', 'arch').annotate(
last_update=Max('last_update'))
- all_arches = Arch.objects.in_bulk(set(s['arch'] for s in split_pkgs))
- all_repos = Repo.objects.in_bulk(set(s['repo'] for s in split_pkgs))
+ all_arches = Arch.objects.in_bulk({s['arch'] for s in split_pkgs})
+ all_repos = Repo.objects.in_bulk({s['repo'] for s in split_pkgs})
for split in split_pkgs:
split['arch'] = all_arches[split['arch']]
split['repo'] = all_repos[split['repo']]
@@ -143,7 +143,7 @@ def get_differences_info(arch_a, arch_b):
cursor.execute(sql, [arch_a.id, arch_b.id])
results = cursor.fetchall()
# column A will always have a value, column B might be NULL
- to_fetch = set(row[0] for row in results)
+ to_fetch = {row[0] for row in results}
# fetch all of the necessary packages
pkgs = Package.objects.normal().in_bulk(to_fetch)
# now build a list of tuples containing differences
@@ -249,13 +249,13 @@ def attach_maintainers(packages):
the maintainers and attach them to the packages to prevent N+1 query
cascading.'''
packages = list(packages)
- pkgbases = set(p.pkgbase for p in packages)
+ pkgbases = {p.pkgbase for p in packages}
rels = PackageRelation.objects.filter(type=PackageRelation.MAINTAINER,
pkgbase__in=pkgbases).values_list(
'pkgbase', 'user_id').order_by().distinct()
# get all the user objects we will need
- user_ids = set(rel[1] for rel in rels)
+ user_ids = {rel[1] for rel in rels}
users = User.objects.in_bulk(user_ids)
# now build a pkgbase -> [maintainers...] map
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 824a9922..340b2311 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -25,7 +25,7 @@ def signoffs(request):
context = {
'signoff_groups': signoff_groups,
'arches': Arch.objects.all(),
- 'repo_names': sorted(set(g.target_repo for g in signoff_groups)),
+ 'repo_names': sorted({g.target_repo for g in signoff_groups}),
}
return render(request, 'packages/signoffs.html', context)
diff --git a/todolists/views.py b/todolists/views.py
index b8d1dae1..9984ef9a 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -53,8 +53,8 @@ def view(request, list_id):
# we don't hold onto the result, but the objects are the same here,
# so accessing maintainers in the template is now cheap
attach_maintainers(tp.pkg for tp in todolist.packages)
- arches = set(tp.pkg.arch for tp in todolist.packages)
- repos = set(tp.pkg.repo for tp in todolist.packages)
+ arches = {tp.pkg.arch for tp in todolist.packages}
+ repos = {tp.pkg.repo for tp in todolist.packages}
return render(request, 'todolists/view.html', {
'list': todolist,
'svn_roots': svn_roots,
@@ -67,8 +67,8 @@ def list_pkgbases(request, list_id, svn_root):
'''Used to make bulk moves of packages a lot easier.'''
todolist = get_object_or_404(Todolist, id=list_id)
repos = get_list_or_404(Repo, svn_root=svn_root)
- pkgbases = set(tp.pkg.pkgbase for tp in todolist.packages
- if tp.pkg.repo in repos)
+ pkgbases = {tp.pkg.pkgbase for tp in todolist.packages
+ if tp.pkg.repo in repos}
return HttpResponse('\n'.join(sorted(pkgbases)),
mimetype='text/plain')
--
cgit v1.2.3-54-g00ecf
From e08096ee214b1fd60d093e2902c6acec9cf4ae5f Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 11 Dec 2012 21:19:16 -0600
Subject: Fix FS#32018, provides links always go to [testing] packages
Remove some of the smarts and do less, but be better about properly
sorting the items as one might expect.
Signed-off-by: Dan McGee
---
packages/models.py | 24 ++++++------------------
1 file changed, 6 insertions(+), 18 deletions(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index ede8c275..a4095f53 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -412,24 +412,12 @@ def get_providers(self):
new_pkgs.append(package)
pkgs = new_pkgs
- # Logic here is to filter out packages that are in multiple repos if
- # they are not requested. For example, if testing is False, only show a
- # testing package if it doesn't exist in a non-testing repo.
- filtered = {}
- for package in pkgs:
- if package.pkgname not in filtered or \
- package.repo.staging == self.pkg.repo.staging:
- filtered[package.pkgname] = package
- pkgs = filtered.values()
-
- filtered = {}
- for package in pkgs:
- if package.pkgname not in filtered or \
- package.repo.testing == self.pkg.repo.testing:
- filtered[package.pkgname] = package
- pkgs = filtered.values()
-
- return pkgs
+ # Sort providers by preference. We sort those in same staging/testing
+ # combination first, followed by others. We sort by a (staging,
+ # testing) match tuple that will be (True, True) in the best case.
+ key_func = lambda x: (x.repo.staging == self.pkg.repo.staging,
+ x.repo.testing == self.pkg.repo.testing)
+ return sorted(pkgs, key=key_func, reverse=True)
def __unicode__(self):
if self.version:
--
cgit v1.2.3-54-g00ecf
From a993295002ad36b40c49619e892de6e78aa00c06 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Thu, 27 Dec 2012 17:04:14 -0600
Subject: Add some more tests for ALPM API stuff
These are super-simple, but it is also trivial to test.
Signed-off-by: Dan McGee
---
packages/tests.py | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 packages/tests.py
(limited to 'packages')
diff --git a/packages/tests.py b/packages/tests.py
new file mode 100644
index 00000000..3a460865
--- /dev/null
+++ b/packages/tests.py
@@ -0,0 +1,27 @@
+import unittest
+
+from .alpm import AlpmAPI
+
+class AlpmTestCase(unittest.TestCase):
+ def test_version(self):
+ alpm = AlpmAPI()
+ version = alpm.version()
+ self.assertIsNotNone(version)
+ version = version.split('.')
+ # version is a 3-tuple, e.g., '7.0.2'
+ self.assertEqual(3, len(version))
+
+ def test_compare_versions(self):
+ alpm = AlpmAPI()
+ self.assertTrue(alpm.compare_versions("1.0", "<=", "2.0"))
+ self.assertTrue(alpm.compare_versions("1.0", "<", "2.0"))
+ self.assertFalse(alpm.compare_versions("1.0", ">=", "2.0"))
+ self.assertFalse(alpm.compare_versions("1.0", ">", "2.0"))
+ self.assertTrue(alpm.compare_versions("1:1.0", ">", "2.0"))
+ self.assertFalse(alpm.compare_versions("1.0.2", ">=", "2.1.0"))
+
+ self.assertTrue(alpm.compare_versions("1.0", "=", "1.0"))
+ self.assertTrue(alpm.compare_versions("1.0", "=", "1.0-1"))
+ self.assertFalse(alpm.compare_versions("1.0", "!=", "1.0"))
+
+# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From b801818eeed1068595cea863e9ae427f3931f925 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Thu, 27 Dec 2012 23:25:51 -0600
Subject: Make attach_maintainers null-safe
Signed-off-by: Dan McGee
---
packages/utils.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index 5adc8637..5f0c111e 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -249,7 +249,7 @@ def attach_maintainers(packages):
the maintainers and attach them to the packages to prevent N+1 query
cascading.'''
packages = list(packages)
- pkgbases = {p.pkgbase for p in packages}
+ pkgbases = {p.pkgbase for p in packages if p is not None}
rels = PackageRelation.objects.filter(type=PackageRelation.MAINTAINER,
pkgbase__in=pkgbases).values_list(
'pkgbase', 'user_id').order_by().distinct()
@@ -266,6 +266,8 @@ def attach_maintainers(packages):
annotated = []
# and finally, attach the maintainer lists on the original packages
for package in packages:
+ if package is None:
+ continue
package.maintainers = maintainers[package.pkgbase]
annotated.append(package)
--
cgit v1.2.3-54-g00ecf
From 6667b017669ffbae98ce70f5472f107b91da36d5 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Thu, 27 Dec 2012 23:56:34 -0600
Subject: Allow pkg_details_link to return link title if package is missing
For todolist packages that have had their package removed, this will
allow the package name to continue to appear even after the linked
package has been deleted.
Signed-off-by: Dan McGee
---
packages/templatetags/package_extras.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index f3613e69..f14fab1e 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -63,7 +63,7 @@ def do_buildsortqs(parser, token):
@register.simple_tag
def pkg_details_link(pkg, link_title=None):
if not pkg:
- return ''
+ return link_title or ''
if link_title is None:
link_title = pkg.pkgname
link = '%s '
--
cgit v1.2.3-54-g00ecf
From 20b64e42672d185821cc584dfa4b133ee259a144 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 28 Dec 2012 14:41:18 -0600
Subject: Reduce query count when retrieving satisfiers and providers
Django doesn't attach the parent object to the child objects, even when
queried through the related manager. This causes up to 3 extra queries:
one to retrieve the package again, and one each for arch and repo
retrieval.
For a package like archboot, this drops the number of necessary queries
for the package page from 805 to 222 (yes, this is still too high) and
cuts the time spent waiting on the database from 505ms to 262ms.
Signed-off-by: Dan McGee
---
main/models.py | 4 ++--
packages/models.py | 20 ++++++++++++--------
2 files changed, 14 insertions(+), 10 deletions(-)
(limited to 'packages')
diff --git a/main/models.py b/main/models.py
index b3cc97e1..ba246458 100644
--- a/main/models.py
+++ b/main/models.py
@@ -271,10 +271,10 @@ def get_depends(self):
arches = None
# TODO: we can use list comprehension and an 'in' query to make this more effective
for dep in self.depends.all():
- pkg = dep.get_best_satisfier()
+ pkg = dep.get_best_satisfier(self)
providers = None
if not pkg:
- providers = dep.get_providers()
+ providers = dep.get_providers(self)
deps.append({'dep': dep, 'pkg': pkg, 'providers': providers})
# sort the list; deptype sorting makes this tricker than expected
sort_order = {'D': 0, 'O': 1, 'M': 2, 'C': 3}
diff --git a/packages/models.py b/packages/models.py
index a4095f53..18d57c58 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -349,14 +349,16 @@ class RelatedToBase(models.Model):
name = models.CharField(max_length=255, db_index=True)
version = models.CharField(max_length=255, default='')
- def get_best_satisfier(self):
+ def get_best_satisfier(self, main_pkg=None):
'''Find a satisfier for this related package that best matches the
given criteria. It will not search provisions, but will find packages
named and matching repo characteristics if possible.'''
+ if main_pkg is None:
+ main_pkg = self.pkg
pkgs = Package.objects.normal().filter(pkgname=self.name)
- if not self.pkg.arch.agnostic:
+ if not main_pkg.arch.agnostic:
# make sure we match architectures if possible
- arches = self.pkg.applicable_arches()
+ arches = main_pkg.applicable_arches()
pkgs = pkgs.filter(arch__in=arches)
# if we have a comparison operation, make sure the packages we grab
# actually satisfy the requirements
@@ -376,25 +378,27 @@ def get_best_satisfier(self):
pkg = pkgs[0]
# prevents yet more DB queries, these lists should be short;
# after each grab the best available in case we remove all entries
- pkgs = [p for p in pkgs if p.repo.staging == self.pkg.repo.staging]
+ pkgs = [p for p in pkgs if p.repo.staging == main_pkg.repo.staging]
if len(pkgs) > 0:
pkg = pkgs[0]
- pkgs = [p for p in pkgs if p.repo.testing == self.pkg.repo.testing]
+ pkgs = [p for p in pkgs if p.repo.testing == main_pkg.repo.testing]
if len(pkgs) > 0:
pkg = pkgs[0]
return pkg
- def get_providers(self):
+ def get_providers(self, main_pkg=None):
'''Return providers of this related package. Does *not* include exact
matches as it checks the Provision names only, use get_best_satisfier()
instead for exact matches.'''
+ if main_pkg is None:
+ main_pkg = self.pkg
pkgs = Package.objects.normal().filter(
provides__name=self.name).order_by().distinct()
- if not self.pkg.arch.agnostic:
+ if not main_pkg.arch.agnostic:
# make sure we match architectures if possible
- arches = self.pkg.applicable_arches()
+ arches = main_pkg.applicable_arches()
pkgs = pkgs.filter(arch__in=arches)
# If we have a comparison operation, make sure the packages we grab
--
cgit v1.2.3-54-g00ecf
From 255a992c151b1c9386c3450083144447be2ce27a Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 9 Jan 2013 08:02:19 -0500
Subject: Add get_associated_packages method to FlagRequest
Signed-off-by: Dan McGee
---
packages/models.py | 7 +++++++
1 file changed, 7 insertions(+)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index 18d57c58..ef86d8e9 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -206,6 +206,13 @@ def full_version(self):
return u'%d:%s-%s' % (self.epoch, self.pkgver, self.pkgrel)
return u'%s-%s' % (self.pkgver, self.pkgrel)
+ def get_associated_packages(self):
+ return Package.objects.normal().filter(
+ pkgbase=self.pkgbase,
+ repo__testing=self.repo.testing,
+ repo__staging=self.repo.staging).order_by(
+ 'pkgname', 'repo__name', 'arch__name')
+
def __unicode__(self):
return u'%s from %s on %s' % (self.pkgbase, self.who(), self.created)
--
cgit v1.2.3-54-g00ecf
From 2bfdcec869ed4fceb11b9e0a2777fa53d46fb336 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 12 Jan 2013 16:47:20 -0600
Subject: Make packages JSON search more performant
We were peppering the database with a bunch of queries here; using
prefetch_related and attach_maintainers can cut down the count
significantly.
Signed-off-by: Dan McGee
---
packages/views/search.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index f7b8ed1d..f6e670df 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -12,7 +12,7 @@
from main.models import Package, Arch, Repo
from main.utils import make_choice
from ..models import PackageRelation
-from ..utils import PackageJSONEncoder
+from ..utils import attach_maintainers, PackageJSONEncoder
def coerce_limit_value(value):
@@ -185,10 +185,14 @@ def search_json(request):
form = PackageSearchForm(data=request.GET,
show_staging=request.user.is_authenticated())
if form.is_valid():
- packages = Package.objects.normal()
+ packages = Package.objects.select_related('arch', 'repo',
+ 'packager')
if not request.user.is_authenticated():
packages = packages.filter(repo__staging=False)
packages = parse_form(form, packages)[:limit]
+ packages = packages.prefetch_related('groups', 'licenses',
+ 'conflicts', 'provides', 'replaces', 'depends')
+ attach_maintainers(packages)
container['results'] = packages
container['valid'] = True
--
cgit v1.2.3-54-g00ecf
From 66850026ca934e5a09238e9033c541cdc5085a42 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 13 Jan 2013 22:34:33 -0600
Subject: Use content_type and not mimetype on HttpResponse()
Bug #16519 in Django deprecates mimetype, so update our code
accordingly.
Signed-off-by: Dan McGee
---
mirrors/views.py | 4 ++--
packages/views/__init__.py | 4 ++--
packages/views/display.py | 4 ++--
packages/views/search.py | 2 +-
packages/views/signoff.py | 4 ++--
public/views.py | 2 +-
todolists/views.py | 5 ++---
visualize/views.py | 4 ++--
8 files changed, 14 insertions(+), 15 deletions(-)
(limited to 'packages')
diff --git a/mirrors/views.py b/mirrors/views.py
index 22da631a..d3867802 100644
--- a/mirrors/views.py
+++ b/mirrors/views.py
@@ -199,7 +199,7 @@ def mirror_details_json(request, name):
data['version'] = 3
to_json = json.dumps(data, ensure_ascii=False,
cls=ExtendedMirrorStatusJSONEncoder)
- response = HttpResponse(to_json, mimetype='application/json')
+ response = HttpResponse(to_json, content_type='application/json')
return response
@@ -285,7 +285,7 @@ def status_json(request):
data = status_info.copy()
data['version'] = 3
to_json = json.dumps(data, ensure_ascii=False, cls=MirrorStatusJSONEncoder)
- response = HttpResponse(to_json, mimetype='application/json')
+ response = HttpResponse(to_json, content_type='application/json')
return response
# vim: set ts=4 sw=4 et:
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index f7952255..4c195385 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -42,7 +42,7 @@ def opensearch(request):
def opensearch_suggest(request):
search_term = request.GET.get('q', '')
if search_term == '':
- return HttpResponse('', mimetype='application/x-suggestions+json')
+ return HttpResponse('', content_type='application/x-suggestions+json')
cache_key = 'opensearch:packages:' + \
hashlib.md5(search_term.encode('utf-8')).hexdigest()
@@ -59,7 +59,7 @@ def opensearch_suggest(request):
results = [search_term, list(names)]
to_json = json.dumps(results, ensure_ascii=False)
cache.set(cache_key, to_json, 300)
- return HttpResponse(to_json, mimetype='application/x-suggestions+json')
+ return HttpResponse(to_json, content_type='application/x-suggestions+json')
@permission_required('main.change_package')
diff --git a/packages/views/display.py b/packages/views/display.py
index efedf6ff..445c1abe 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -188,7 +188,7 @@ def details_json(request, name, repo, arch):
pkg = get_object_or_404(Package,
pkgname=name, repo__name__iexact=repo, arch__name=arch)
to_json = json.dumps(pkg, ensure_ascii=False, cls=PackageJSONEncoder)
- return HttpResponse(to_json, mimetype='application/json')
+ return HttpResponse(to_json, content_type='application/json')
def files_json(request, name, repo, arch):
@@ -209,7 +209,7 @@ def files_json(request, name, repo, arch):
'files': fileslist,
}
to_json = json.dumps(data, ensure_ascii=False, cls=PackageJSONEncoder)
- return HttpResponse(to_json, mimetype='application/json')
+ return HttpResponse(to_json, content_type='application/json')
def download(request, name, repo, arch):
diff --git a/packages/views/search.py b/packages/views/search.py
index f6e670df..0f313ccb 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -197,6 +197,6 @@ def search_json(request):
container['valid'] = True
to_json = json.dumps(container, ensure_ascii=False, cls=PackageJSONEncoder)
- return HttpResponse(to_json, mimetype='application/json')
+ return HttpResponse(to_json, content_type='application/json')
# vim: set ts=4 sw=4 et:
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 340b2311..17f3095c 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -67,7 +67,7 @@ def signoff_package(request, name, repo, arch, revoke=False):
'user': str(request.user),
}
return HttpResponse(json.dumps(data, ensure_ascii=False),
- mimetype='application/json')
+ content_type='application/json')
return redirect('package-signoffs')
@@ -181,7 +181,7 @@ def signoffs_json(request):
'signoff_groups': signoff_groups,
}
to_json = json.dumps(data, ensure_ascii=False, cls=SignoffJSONEncoder)
- response = HttpResponse(to_json, mimetype='application/json')
+ response = HttpResponse(to_json, content_type='application/json')
return response
# vim: set ts=4 sw=4 et:
diff --git a/public/views.py b/public/views.py
index 44ceb45d..65b0c31f 100644
--- a/public/views.py
+++ b/public/views.py
@@ -185,6 +185,6 @@ def keys_json(request):
data = { 'nodes': node_list, 'edges': edge_list }
to_json = json.dumps(data, ensure_ascii=False)
- return HttpResponse(to_json, mimetype='application/json')
+ return HttpResponse(to_json, content_type='application/json')
# vim: set ts=4 sw=4 et:
diff --git a/todolists/views.py b/todolists/views.py
index f4f9ab21..fcf62e23 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -53,7 +53,7 @@ def flag(request, slug, pkg_id):
'status': tlpkg.get_status_display(),
'css_class': tlpkg.status_css_class(),
}
- return HttpResponse(json.dumps(data), mimetype='application/json')
+ return HttpResponse(json.dumps(data), content_type='application/json')
return redirect(todolist)
@@ -87,8 +87,7 @@ def list_pkgbases(request, slug, svn_root):
'pkgbase', flat=True).filter(
todolist=todolist, repo__in=repos, removed__isnull=True).order_by(
'pkgbase').distinct()
- return HttpResponse('\n'.join(pkgbases),
- mimetype='text/plain')
+ return HttpResponse('\n'.join(pkgbases), content_type='text/plain')
def todolist_list(request):
diff --git a/visualize/views.py b/visualize/views.py
index 48e8f86b..9c537c20 100644
--- a/visualize/views.py
+++ b/visualize/views.py
@@ -62,13 +62,13 @@ def build_map(name, arch, repo):
def by_arch(request):
data = arch_repo_data()
to_json = json.dumps(data['by_arch'], ensure_ascii=False)
- return HttpResponse(to_json, mimetype='application/json')
+ return HttpResponse(to_json, content_type='application/json')
@cache_page(1800)
def by_repo(request):
data = arch_repo_data()
to_json = json.dumps(data['by_repo'], ensure_ascii=False)
- return HttpResponse(to_json, mimetype='application/json')
+ return HttpResponse(to_json, content_type='application/json')
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From 375684ed91dd5499e7a4ea7787e45803e8467e16 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 18 Jan 2013 20:52:20 -0600
Subject: Use a set instead of list when gathering package differences
If we implement the __eq__ and __hash__ methods, we can use a set to
gather package difference objects and make deduplication of objects a
lot more efficient.
Signed-off-by: Dan McGee
---
packages/utils.py | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index 5f0c111e..a72404f4 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -1,6 +1,6 @@
from collections import defaultdict
from itertools import chain
-from operator import itemgetter
+from operator import attrgetter, itemgetter
import re
from django.core.serializers.json import DjangoJSONEncoder
@@ -108,10 +108,15 @@ def classes(self):
css_classes.append(self.pkg_b.arch.name)
return ' '.join(css_classes)
- def __cmp__(self, other):
- if isinstance(other, Difference):
- return cmp(self.__dict__, other.__dict__)
- return False
+ def __key(self):
+ return (self.pkgname, hash(self.repo),
+ hash(self.pkg_a), hash(self.pkg_b))
+
+ def __eq__(self, other):
+ return self.__key() == other.__key()
+
+ def __hash__(self):
+ return hash(self.__key())
@cache_function(127)
@@ -146,8 +151,8 @@ def get_differences_info(arch_a, arch_b):
to_fetch = {row[0] for row in results}
# fetch all of the necessary packages
pkgs = Package.objects.normal().in_bulk(to_fetch)
- # now build a list of tuples containing differences
- differences = []
+ # now build a set containing differences
+ differences = set()
for row in results:
pkg_a = pkgs.get(row[0])
pkg_b = pkgs.get(row[1])
@@ -160,11 +165,11 @@ def get_differences_info(arch_a, arch_b):
name = pkg_a.pkgname if pkg_a else pkg_b.pkgname
repo = pkg_a.repo if pkg_a else pkg_b.repo
item = Difference(name, repo, pkg_b, pkg_a)
- if item not in differences:
- differences.append(item)
+ differences.add(item)
# now sort our list by repository, package name
- differences.sort(key=lambda a: (a.repo.name, a.pkgname))
+ key_func = attrgetter('repo.name', 'pkgname')
+ differences = sorted(differences, key=key_func)
return differences
--
cgit v1.2.3-54-g00ecf
From 2c958511c41f53fb7de49ed4662eec966e0b76a5 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 22 Jan 2013 16:48:49 -0600
Subject: Use a subquery rather than two queries in attach_maintainers
Now that we are using a database that doesn't stink, it makes more sense
to do all of the stuff we need to do down at the database level. This
helps a lot when 500+ packages are in play at a given time, such as
some of our larger rebuild todo lists.
Signed-off-by: Dan McGee
---
packages/utils.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index a72404f4..49aeb8ce 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -6,6 +6,7 @@
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection
from django.db.models import Count, Max, F
+from django.db.models.query import QuerySet
from django.contrib.auth.models import User
from main.models import Package, PackageFile, Arch, Repo
@@ -253,8 +254,11 @@ def attach_maintainers(packages):
'''Given a queryset or something resembling it of package objects, find all
the maintainers and attach them to the packages to prevent N+1 query
cascading.'''
- packages = list(packages)
- pkgbases = {p.pkgbase for p in packages if p is not None}
+ if isinstance(packages, QuerySet):
+ pkgbases = packages.values('pkgbase')
+ else:
+ packages = list(packages)
+ pkgbases = {p.pkgbase for p in packages if p is not None}
rels = PackageRelation.objects.filter(type=PackageRelation.MAINTAINER,
pkgbase__in=pkgbases).values_list(
'pkgbase', 'user_id').order_by().distinct()
--
cgit v1.2.3-54-g00ecf
From 85dc9d6df9f9038be6049aefbf8eb9ea94f9f23c Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 22 Jan 2013 17:20:41 -0700
Subject: Override the default admin queryset for some models
Because some attributes are optional or otherwise not auto-magically
picked up by Django, we can help the performance of loading these pages
a lot by forcing a select_related() on the queryset used by the admin.
For something like signoff_specifications, this drops the query count
from ~107 to 9 queries.
Signed-off-by: Dan McGee
---
packages/admin.py | 10 ++++++++++
1 file changed, 10 insertions(+)
(limited to 'packages')
diff --git a/packages/admin.py b/packages/admin.py
index 5e32dbb4..820bbb29 100644
--- a/packages/admin.py
+++ b/packages/admin.py
@@ -3,6 +3,7 @@
from .models import (PackageRelation, FlagRequest,
Signoff, SignoffSpecification, Update)
+
class PackageRelationAdmin(admin.ModelAdmin):
list_display = ('pkgbase', 'user', 'type', 'created')
list_filter = ('type', 'user')
@@ -19,6 +20,10 @@ class FlagRequestAdmin(admin.ModelAdmin):
ordering = ('-created',)
date_hierarchy = 'created'
+ def queryset(self, request):
+ qs = super(FlagRequestAdmin, self).queryset(request)
+ return qs.select_related('repo', 'user')
+
class SignoffAdmin(admin.ModelAdmin):
list_display = ('pkgbase', 'full_version', 'arch', 'repo',
@@ -28,6 +33,7 @@ class SignoffAdmin(admin.ModelAdmin):
ordering = ('-created',)
date_hierarchy = 'created'
+
class SignoffSpecificationAdmin(admin.ModelAdmin):
list_display = ('pkgbase', 'full_version', 'arch', 'repo',
'user', 'created', 'comments')
@@ -36,6 +42,10 @@ class SignoffSpecificationAdmin(admin.ModelAdmin):
ordering = ('-created',)
date_hierarchy = 'created'
+ def queryset(self, request):
+ qs = super(SignoffSpecificationAdmin, self).queryset(request)
+ return qs.select_related('arch', 'repo', 'user')
+
class UpdateAdmin(admin.ModelAdmin):
list_display = ('pkgname', 'repo', 'arch', 'action_flag',
--
cgit v1.2.3-54-g00ecf
From be49f26a815cca589c625ff8dd99c85a80262281 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 22 Jan 2013 20:58:50 -0700
Subject: Slight optimization when searching for removed package
Signed-off-by: Dan McGee
---
packages/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index ef86d8e9..ff677883 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -318,7 +318,8 @@ def new_version(self):
return u'%s-%s' % (self.new_pkgver, self.new_pkgrel)
def elsewhere(self):
- return Package.objects.filter(pkgname=self.pkgname, arch=self.arch)
+ return Package.objects.normal().filter(
+ pkgname=self.pkgname, arch=self.arch)
def __unicode__(self):
return u'%s of %s on %s' % (self.get_action_flag_display(),
--
cgit v1.2.3-54-g00ecf
From a10798b756bbfc5d8dbad76546ca670efca75e56 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 23 Jan 2013 09:18:59 -0700
Subject: Use querysets for calls to get_object_or_404(Package)
This works better in most cases since we need the architecture and
repository objects at some point during the view process.
Signed-off-by: Dan McGee
---
packages/views/display.py | 8 ++++----
packages/views/flag.py | 6 +++---
2 files changed, 7 insertions(+), 7 deletions(-)
(limited to 'packages')
diff --git a/packages/views/display.py b/packages/views/display.py
index 445c1abe..c2369aba 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -168,7 +168,7 @@ def group_details(request, arch, name):
def files(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
# files are inserted in sorted order, so preserve that
fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
@@ -185,14 +185,14 @@ def files(request, name, repo, arch):
def details_json(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
to_json = json.dumps(pkg, ensure_ascii=False, cls=PackageJSONEncoder)
return HttpResponse(to_json, content_type='application/json')
def files_json(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
# files are inserted in sorted order, so preserve that
fileslist = PackageFile.objects.filter(pkg=pkg).order_by('id')
@@ -213,7 +213,7 @@ def files_json(request, name, repo, arch):
def download(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
url = get_mirror_url_for_download()
if not url:
diff --git a/packages/views/flag.py b/packages/views/flag.py
index dadadd19..edb3f092 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -49,7 +49,7 @@ def flaghelp(request):
@never_cache
def flag(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
if pkg.flag_date is not None:
# already flagged. do nothing.
@@ -158,7 +158,7 @@ def flag_confirmed(request, name, repo, arch):
@permission_required('main.change_package')
def unflag(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
pkg.flag_date = None
pkg.save()
@@ -166,7 +166,7 @@ def unflag(request, name, repo, arch):
@permission_required('main.change_package')
def unflag_all(request, name, repo, arch):
- pkg = get_object_or_404(Package,
+ pkg = get_object_or_404(Package.objects.normal(),
pkgname=name, repo__name__iexact=repo, arch__name=arch)
# find all packages from (hopefully) the same PKGBUILD
pkgs = Package.objects.filter(pkgbase=pkg.pkgbase,
--
cgit v1.2.3-54-g00ecf
From dc6cc49f6f876983f76f5f8c05a2285801f27ea0 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 23 Jan 2013 09:20:19 -0700
Subject: Use more modern verison of string template formatting
Signed-off-by: Dan McGee
---
packages/views/display.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
(limited to 'packages')
diff --git a/packages/views/display.py b/packages/views/display.py
index c2369aba..497c8d48 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -1,6 +1,5 @@
import datetime
import json
-from string import Template
from urllib import urlencode
from django.http import HttpResponse, Http404
@@ -223,12 +222,9 @@ def download(request, name, repo, arch):
# grab the first non-any arch to fake the download path
arch = Arch.objects.exclude(agnostic=True)[0].name
values = {
- 'host': url.url,
- 'arch': arch,
- 'repo': pkg.repo.name.lower(),
- 'file': pkg.filename,
}
- url = Template('${host}${repo}/os/${arch}/${file}').substitute(values)
+ url = '{host}{repo}/os/{arch}/{filename}'.format(host=url.url,
+ repo=pkg.repo.name.lower(), arch=arch, filename=pkg.filename)
return redirect(url)
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From e65c7805547484cad1be55dfa20355ef18b857be Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Fri, 8 Feb 2013 21:09:47 -0600
Subject: Remove package seach by 'Last Updated After'
It is a lot easier to just sort the list rather than mess with this
particular field, which didn't even allow you to specify a range or
direction to search in.
Signed-off-by: Dan McGee
---
packages/views/search.py | 9 ---------
templates/packages/search.html | 14 --------------
2 files changed, 23 deletions(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 0f313ccb..9cb5f38d 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -3,7 +3,6 @@
from pytz import utc
from django import forms
-from django.contrib.admin.widgets import AdminDateWidget
from django.contrib.auth.models import User
from django.db.models import Q
from django.http import HttpResponse
@@ -44,8 +43,6 @@ class PackageSearchForm(forms.Form):
sort = forms.CharField(required=False, widget=forms.HiddenInput())
maintainer = forms.ChoiceField(required=False)
packager = forms.ChoiceField(required=False)
- last_update = forms.DateField(required=False, widget=AdminDateWidget(),
- label='Last Updated After')
flagged = forms.ChoiceField(
choices=[('', 'All')] + make_choice(['Flagged', 'Not Flagged']),
required=False)
@@ -104,12 +101,6 @@ def parse_form(form, packages):
elif form.cleaned_data['flagged'] == 'Not Flagged':
packages = packages.filter(flag_date__isnull=True)
- if form.cleaned_data['last_update']:
- lu = form.cleaned_data['last_update']
- cutoff = datetime(lu.year, lu.month, lu.day, 0, 0)
- cutoff = cutoff.replace(tzinfo=utc)
- packages = packages.filter(last_update__gte=cutoff)
-
if form.cleaned_data['name']:
name = form.cleaned_data['name']
packages = packages.filter(pkgname=name)
diff --git a/templates/packages/search.html b/templates/packages/search.html
index ab9b6d32..a5d52d6c 100644
--- a/templates/packages/search.html
+++ b/templates/packages/search.html
@@ -1,13 +1,11 @@
{% extends "base.html" %}
{% load package_extras %}
-{% load admin_static %}
{% block title %}Arch Linux - Package Database{% endblock %}
{% block navbarclass %}anb-packages{% endblock %}
{% block head %}
{% if is_paginated and page_obj.number > 1 %} {% endif %}
-
{% endblock %}
@@ -35,9 +33,6 @@ Package Search
{{ search_form.maintainer.errors }}
Maintainer {{ search_form.maintainer}}
- {{ search_form.last_update.errors }}
-
- Last Updated After {{ search_form.last_update }}
{{ search_form.flagged.errors }}
Flagged {{ search_form.flagged }}
@@ -126,13 +121,4 @@ Package Search
For unsupported packages, browse the Arch User Repository (AUR).
-
-{% load cdn %}{% jquery %}
-
-
-
-{{search_form.media}}
{% endblock %}
--
cgit v1.2.3-54-g00ecf
From 271d1babbf8038e17d9dc5cfc3cd659463848400 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 20 Jan 2013 12:43:33 -0600
Subject: Revert "Reduce query count when retrieving satisfiers and providers"
This reverts commit 20b64e42672d185821cc584dfa4b133ee259a144.
Django 1.5 fixed this issue and now parent objects are automatically
attached to their children when queries go through the related manager.
See "Caching of related model instances" in the release notes.
Signed-off-by: Dan McGee
---
main/models.py | 4 ++--
packages/models.py | 20 ++++++++------------
2 files changed, 10 insertions(+), 14 deletions(-)
(limited to 'packages')
diff --git a/main/models.py b/main/models.py
index 46fd3a63..da9d8b6e 100644
--- a/main/models.py
+++ b/main/models.py
@@ -277,10 +277,10 @@ def get_depends(self):
# TODO: we can use list comprehension and an 'in' query to make this
# more effective
for dep in self.depends.all():
- pkg = dep.get_best_satisfier(self)
+ pkg = dep.get_best_satisfier()
providers = None
if not pkg:
- providers = dep.get_providers(self)
+ providers = dep.get_providers()
deps.append({'dep': dep, 'pkg': pkg, 'providers': providers})
# sort the list; deptype sorting makes this tricker than expected
sort_order = {'D': 0, 'O': 1, 'M': 2, 'C': 3}
diff --git a/packages/models.py b/packages/models.py
index ff677883..7bcdc000 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -357,16 +357,14 @@ class RelatedToBase(models.Model):
name = models.CharField(max_length=255, db_index=True)
version = models.CharField(max_length=255, default='')
- def get_best_satisfier(self, main_pkg=None):
+ def get_best_satisfier(self):
'''Find a satisfier for this related package that best matches the
given criteria. It will not search provisions, but will find packages
named and matching repo characteristics if possible.'''
- if main_pkg is None:
- main_pkg = self.pkg
pkgs = Package.objects.normal().filter(pkgname=self.name)
- if not main_pkg.arch.agnostic:
+ if not self.pkg.arch.agnostic:
# make sure we match architectures if possible
- arches = main_pkg.applicable_arches()
+ arches = self.pkg.applicable_arches()
pkgs = pkgs.filter(arch__in=arches)
# if we have a comparison operation, make sure the packages we grab
# actually satisfy the requirements
@@ -386,27 +384,25 @@ def get_best_satisfier(self, main_pkg=None):
pkg = pkgs[0]
# prevents yet more DB queries, these lists should be short;
# after each grab the best available in case we remove all entries
- pkgs = [p for p in pkgs if p.repo.staging == main_pkg.repo.staging]
+ pkgs = [p for p in pkgs if p.repo.staging == self.pkg.repo.staging]
if len(pkgs) > 0:
pkg = pkgs[0]
- pkgs = [p for p in pkgs if p.repo.testing == main_pkg.repo.testing]
+ pkgs = [p for p in pkgs if p.repo.testing == self.pkg.repo.testing]
if len(pkgs) > 0:
pkg = pkgs[0]
return pkg
- def get_providers(self, main_pkg=None):
+ def get_providers(self):
'''Return providers of this related package. Does *not* include exact
matches as it checks the Provision names only, use get_best_satisfier()
instead for exact matches.'''
- if main_pkg is None:
- main_pkg = self.pkg
pkgs = Package.objects.normal().filter(
provides__name=self.name).order_by().distinct()
- if not main_pkg.arch.agnostic:
+ if not self.pkg.arch.agnostic:
# make sure we match architectures if possible
- arches = main_pkg.applicable_arches()
+ arches = self.pkg.applicable_arches()
pkgs = pkgs.filter(arch__in=arches)
# If we have a comparison operation, make sure the packages we grab
--
cgit v1.2.3-54-g00ecf
From 5566d43a7734f6bb2f48d5d511351da12ddc5cc1 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 9 Feb 2013 16:43:40 -0600
Subject: Use 'update_fields' model.save() kwarg
This was added in Django 1.5 and allows saving only a subset of a
model's fields. It makes sense in a few cases to utilize it.
Signed-off-by: Dan McGee
---
mirrors/management/commands/mirrorresolv.py | 2 +-
packages/views/signoff.py | 2 +-
releng/management/commands/syncisos.py | 2 +-
todolists/views.py | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
(limited to 'packages')
diff --git a/mirrors/management/commands/mirrorresolv.py b/mirrors/management/commands/mirrorresolv.py
index 0370f8ed..a6c2523e 100644
--- a/mirrors/management/commands/mirrorresolv.py
+++ b/mirrors/management/commands/mirrorresolv.py
@@ -53,7 +53,7 @@ def resolve_mirrors():
newvals = (mirrorurl.has_ipv4, mirrorurl.has_ipv6)
if newvals != oldvals:
logger.debug("values changed for %s", mirrorurl)
- mirrorurl.save(force_update=True)
+ mirrorurl.save(update_fields=('has_ipv4', 'has_ipv6'))
except socket.error, e:
logger.warn("error resolving %s: %s", mirrorurl.hostname, e)
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 17f3095c..c37aa0fc 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -45,7 +45,7 @@ def signoff_package(request, name, repo, arch, revoke=False):
except Signoff.DoesNotExist:
raise Http404
signoff.revoked = now()
- signoff.save()
+ signoff.save(update_fields=('revoked',))
created = False
else:
# ensure we should even be accepting signoffs
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index 223c771b..c9f61964 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -53,7 +53,7 @@ def handle(self, *args, **options):
if not existing.active:
existing.active = True
existing.removed = None
- existing.save()
+ existing.save(update_fields=('active', 'removed'))
# and then mark all other names as no longer active
Iso.objects.filter(active=True).exclude(name__in=active_isos).update(
active=False, removed=now())
diff --git a/todolists/views.py b/todolists/views.py
index f333728a..9935987b 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -47,7 +47,7 @@ def flag(request, slug, pkg_id):
else:
tlpkg.status = TodolistPackage.INCOMPLETE
tlpkg.user = request.user
- tlpkg.save()
+ tlpkg.save(update_fields=('status', 'user', 'last_modified'))
if request.is_ajax():
data = {
'status': tlpkg.get_status_display(),
--
cgit v1.2.3-54-g00ecf
From 5bc85244281efc916132c86046018d0ebe70b5e9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 10 Feb 2013 12:45:24 -0600
Subject: Fix split packages sitemap
We had a ton of duplicate entries included due to the query implicitly
including a 'GROUP BY' clause on the default sorting by pkgname. Fix it
and cut the sitemap down to the correct size without duplicate entries.
Signed-off-by: Dan McGee
---
packages/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index 49aeb8ce..ef6311eb 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -79,7 +79,7 @@ def get_split_packages_info():
pkgnames = Package.objects.values('pkgname')
split_pkgs = Package.objects.exclude(pkgname=F('pkgbase')).exclude(
pkgbase__in=pkgnames).values('pkgbase', 'repo', 'arch').annotate(
- last_update=Max('last_update'))
+ last_update=Max('last_update')).order_by().distinct()
all_arches = Arch.objects.in_bulk({s['arch'] for s in split_pkgs})
all_repos = Repo.objects.in_bulk({s['repo'] for s in split_pkgs})
for split in split_pkgs:
--
cgit v1.2.3-54-g00ecf
From e3837b5a872b6203b7ae338bc8075b339e031627 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 16 Feb 2013 15:23:49 -0600
Subject: Remove configurable pagination for package search
Switch it to a hardcoded value of 100 for all searches instead. It
didn't make much sense having a page number be part of the URL and a
limit value being part of the query string.
Signed-off-by: Dan McGee
---
packages/views/search.py | 38 +-------------------------------------
templates/packages/search.html | 3 ---
2 files changed, 1 insertion(+), 40 deletions(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 9cb5f38d..9ca299f6 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -1,6 +1,4 @@
-from datetime import datetime
import json
-from pytz import utc
from django import forms
from django.contrib.auth.models import User
@@ -14,26 +12,6 @@
from ..utils import attach_maintainers, PackageJSONEncoder
-def coerce_limit_value(value):
- if not value:
- return None
- if value == 'all':
- # negative value indicates show all results
- return -1
- value = int(value)
- if value < 0:
- raise ValueError
- return value
-
-class LimitTypedChoiceField(forms.TypedChoiceField):
- def valid_value(self, value):
- try:
- coerce_limit_value(value)
- return True
- except (ValueError, TypeError):
- return False
-
-
class PackageSearchForm(forms.Form):
repo = forms.MultipleChoiceField(required=False)
arch = forms.MultipleChoiceField(required=False)
@@ -46,11 +24,6 @@ class PackageSearchForm(forms.Form):
flagged = forms.ChoiceField(
choices=[('', 'All')] + make_choice(['Flagged', 'Not Flagged']),
required=False)
- limit = LimitTypedChoiceField(
- choices=make_choice([50, 100, 250]) + [('all', 'All')],
- coerce=coerce_limit_value,
- required=False,
- initial=50)
def __init__(self, *args, **kwargs):
show_staging = kwargs.pop('show_staging', False)
@@ -119,6 +92,7 @@ def parse_form(form, packages):
class SearchListView(ListView):
template_name = "packages/search.html"
+ paginate_by = 100
sort_fields = ("arch", "repo", "pkgname", "pkgbase", "compressed_size",
"installed_size", "build_date", "last_update", "flag_date")
@@ -145,16 +119,6 @@ def get_queryset(self):
# Form had errors so don't return any results
return Package.objects.none()
- def get_paginate_by(self, queryset):
- limit = 50
- if self.form.is_valid():
- asked_limit = self.form.cleaned_data['limit']
- if asked_limit and asked_limit < 0:
- limit = None
- elif asked_limit:
- limit = asked_limit
- return limit
-
def get_context_data(self, **kwargs):
context = super(SearchListView, self).get_context_data(**kwargs)
context['current_query'] = self.request.GET.urlencode()
diff --git a/templates/packages/search.html b/templates/packages/search.html
index a5d52d6c..bf1eecd9 100644
--- a/templates/packages/search.html
+++ b/templates/packages/search.html
@@ -36,9 +36,6 @@ Package Search
{{ search_form.flagged.errors }}
Flagged {{ search_form.flagged }}
- {{ search_form.limit.errors }}
-
- Per Page {{ search_form.limit }}
--
cgit v1.2.3-54-g00ecf
From 746023d529489b68f1a2494ff7572734b3b368ce Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 16 Feb 2013 15:38:29 -0600
Subject: Make page a query string parameter on package search
This is a bit silly to encode in the URL, or at least makes it much
harder to screen out via robots.txt and other such things.
Signed-off-by: Dan McGee
---
packages/urls.py | 1 -
packages/views/display.py | 2 ++
templates/packages/search_paginator.html | 4 ++--
3 files changed, 4 insertions(+), 3 deletions(-)
(limited to 'packages')
diff --git a/packages/urls.py b/packages/urls.py
index 9a151b4b..4e2e2638 100644
--- a/packages/urls.py
+++ b/packages/urls.py
@@ -24,7 +24,6 @@
(r'^update/$', 'update'),
(r'^$', SearchListView.as_view(), {}, 'packages-search'),
- (r'^(?P\d+)/$', SearchListView.as_view()),
(r'^search/json/$', 'search_json'),
(r'^differences/$', 'arch_differences', {}, 'packages-differences'),
diff --git a/packages/views/display.py b/packages/views/display.py
index 497c8d48..fcf8fdea 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -104,6 +104,8 @@ def redirect_agnostic(request, name, repo, arch):
def redirect_to_search(request, name, repo, arch):
+ if request.GET.get('q'):
+ name = request.GET.get('q')
pkg_data = [
('arch', arch.lower()),
('repo', repo.lower()),
diff --git a/templates/packages/search_paginator.html b/templates/packages/search_paginator.html
index 758dca4c..3c368b82 100644
--- a/templates/packages/search_paginator.html
+++ b/templates/packages/search_paginator.html
@@ -6,7 +6,7 @@
{% if page_obj.has_previous %}
- < Prev
{% else %}
< Prev
@@ -14,7 +14,7 @@
{% if page_obj.has_next %}
- Next >
{% else %}
Next >
--
cgit v1.2.3-54-g00ecf
From 0491bdb2452e496b0a243e7abb3d15ef3fd71743 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 20 Feb 2013 00:19:12 -0600
Subject: Fix some fallout with moving page to query params
Signed-off-by: Dan McGee
---
packages/views/search.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/views/search.py b/packages/views/search.py
index 9ca299f6..0362602e 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -121,7 +121,9 @@ def get_queryset(self):
def get_context_data(self, **kwargs):
context = super(SearchListView, self).get_context_data(**kwargs)
- context['current_query'] = self.request.GET.urlencode()
+ query_params = self.request.GET.copy()
+ query_params.pop('page', None)
+ context['current_query'] = query_params.urlencode()
context['search_form'] = self.form
return context
--
cgit v1.2.3-54-g00ecf
From ddcba1117722095bfb5f4f1c98273c54e747eea9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 26 Feb 2013 19:06:27 -0600
Subject: Fix ALPM unittest to run when ALPM isn't present
This gets some proper unittest skip decorator action now in addition to
adding more testing around everything.
Signed-off-by: Dan McGee
---
packages/tests.py | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
(limited to 'packages')
diff --git a/packages/tests.py b/packages/tests.py
index 3a460865..bbe9f00e 100644
--- a/packages/tests.py
+++ b/packages/tests.py
@@ -2,17 +2,27 @@
from .alpm import AlpmAPI
+
+alpm = AlpmAPI()
+
+
class AlpmTestCase(unittest.TestCase):
+
+ @unittest.skipUnless(alpm.available, "ALPM is unavailable")
def test_version(self):
- alpm = AlpmAPI()
version = alpm.version()
self.assertIsNotNone(version)
version = version.split('.')
# version is a 3-tuple, e.g., '7.0.2'
self.assertEqual(3, len(version))
+ @unittest.skipUnless(alpm.available, "ALPM is unavailable")
+ def test_vercmp(self):
+ self.assertEqual(0, alpm.vercmp("1.0", "1.0"))
+ self.assertEqual(1, alpm.vercmp("1.1", "1.0"))
+
+ @unittest.skipUnless(alpm.available, "ALPM is unavailable")
def test_compare_versions(self):
- alpm = AlpmAPI()
self.assertTrue(alpm.compare_versions("1.0", "<=", "2.0"))
self.assertTrue(alpm.compare_versions("1.0", "<", "2.0"))
self.assertFalse(alpm.compare_versions("1.0", ">=", "2.0"))
@@ -24,4 +34,13 @@ def test_compare_versions(self):
self.assertTrue(alpm.compare_versions("1.0", "=", "1.0-1"))
self.assertFalse(alpm.compare_versions("1.0", "!=", "1.0"))
+ def test_behavior_when_unavailable(self):
+ mock_alpm = AlpmAPI()
+ mock_alpm.available = False
+
+ self.assertIsNone(mock_alpm.version())
+ self.assertIsNone(mock_alpm.vercmp("1.0", "1.0"))
+ self.assertIsNone(mock_alpm.compare_versions("1.0", "=", "1.0"))
+
+
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From dd0ecfaeaceb1e1b8a185800de35f0f6e741feac Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 26 Feb 2013 19:51:40 -0600
Subject: Use user.userprofile rather than user.get_profile()
The get_profile() function is deprecated as of Django 1.5.
Signed-off-by: Dan McGee
---
devel/views.py | 4 ++--
packages/views/flag.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
(limited to 'packages')
diff --git a/devel/views.py b/devel/views.py
index ff1dec12..61c1e568 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -152,7 +152,7 @@ def change_profile(request):
if request.POST:
form = ProfileForm(request.POST)
profile_form = UserProfileForm(request.POST, request.FILES,
- instance=request.user.get_profile())
+ instance=request.user.userprofile)
if form.is_valid() and profile_form.is_valid():
request.user.email = form.cleaned_data['email']
if form.cleaned_data['passwd1']:
@@ -163,7 +163,7 @@ def change_profile(request):
return HttpResponseRedirect('/devel/')
else:
form = ProfileForm(initial={'email': request.user.email})
- profile_form = UserProfileForm(instance=request.user.get_profile())
+ profile_form = UserProfileForm(instance=request.user.userprofile)
return render(request, 'devel/profile.html',
{'form': form, 'profile_form': profile_form})
diff --git a/packages/views/flag.py b/packages/views/flag.py
index edb3f092..5c76e1d5 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -110,7 +110,7 @@ def perform_updates():
subject = '%s package [%s] marked out-of-date' % \
(pkg.repo.name, pkg.pkgname)
for maint in maints:
- if maint.get_profile().notify == True:
+ if maint.userprofile.notify == True:
toemail.append(maint.email)
if toemail:
--
cgit v1.2.3-54-g00ecf
From 1f2a6384f332e75e9befc13b5a4b7b2906db6c50 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 11 Mar 2013 21:25:27 -0500
Subject: Remove function caching in packages/utils
We don't see these called enough to make caching the data worth it.
Signed-off-by: Dan McGee
---
packages/utils.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
(limited to 'packages')
diff --git a/packages/utils.py b/packages/utils.py
index ef6311eb..a4217fbd 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -10,7 +10,7 @@
from django.contrib.auth.models import User
from main.models import Package, PackageFile, Arch, Repo
-from main.utils import (cache_function, database_vendor,
+from main.utils import (database_vendor,
groupby_preserve_order, PackageStandin)
from .models import (PackageGroup, PackageRelation,
License, Depend, Conflict, Provision, Replacement,
@@ -33,7 +33,6 @@ def parse_version(version):
return ver, rel, epoch
-@cache_function(127)
def get_group_info(include_arches=None):
raw_groups = PackageGroup.objects.values_list(
'name', 'pkg__arch__name').order_by('name').annotate(
@@ -120,7 +119,6 @@ def __hash__(self):
return hash(self.__key())
-@cache_function(127)
def get_differences_info(arch_a, arch_b):
# This is a monster. Join packages against itself, looking for packages in
# our non-'any' architectures only, and not having a corresponding package
--
cgit v1.2.3-54-g00ecf
From f2a6316be0b025a9ee22f22d34df1c00f60a8bdf Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 13 Apr 2013 11:56:26 -0500
Subject: Add additional pg_trgm indexes for quicker searches
This allows our normal keyword-based search to be index-optimized rather
than always doing full table scans. It requires the pg_trgm extension
which is shipped out of the box with any sane install of PostgreSQL.
Signed-off-by: Dan McGee
---
packages/sql/search_indexes.postgresql_psycopg2.sql | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 packages/sql/search_indexes.postgresql_psycopg2.sql
(limited to 'packages')
diff --git a/packages/sql/search_indexes.postgresql_psycopg2.sql b/packages/sql/search_indexes.postgresql_psycopg2.sql
new file mode 100644
index 00000000..a7eaf998
--- /dev/null
+++ b/packages/sql/search_indexes.postgresql_psycopg2.sql
@@ -0,0 +1,3 @@
+CREATE EXTENSION IF NOT EXISTS pg_trgm;
+CREATE INDEX packages_pkgname_trgm_gist ON packages USING gist (UPPER(pkgname) gist_trgm_ops);
+CREATE INDEX packages_pkgdesc_trgm_gist ON packages USING gist (UPPER(pkgdesc) gist_trgm_ops);
--
cgit v1.2.3-54-g00ecf
From 7fc8da7d959556b1204b7864959e73e7f5f5ec59 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 13 Apr 2013 13:05:02 -0500
Subject: Show replacments for package if it has been removed
This covers the case where we can't find the package in any other
repositories, but it was removed recently enough that we have a found
package update object.
Signed-off-by: Dan McGee
---
packages/models.py | 9 +++++++++
packages/views/display.py | 2 ++
2 files changed, 11 insertions(+)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index 7bcdc000..92566a56 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -321,6 +321,15 @@ def elsewhere(self):
return Package.objects.normal().filter(
pkgname=self.pkgname, arch=self.arch)
+ def replacements(self):
+ pkgs = Package.objects.normal().filter(
+ replaces__name=self.pkgname)
+ if not self.arch.agnostic:
+ # make sure we match architectures if possible
+ arches = self.pkg.applicable_arches()
+ pkgs = pkgs.filter(arch__in=arches)
+ return pkgs
+
def __unicode__(self):
return u'%s of %s on %s' % (self.get_action_flag_display(),
self.pkgname, self.created)
diff --git a/packages/views/display.py b/packages/views/display.py
index fcf8fdea..50783835 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -55,6 +55,8 @@ def recently_removed_package(request, name, repo, arch, cutoff=CUTOFF):
try:
update = match.latest()
elsewhere = update.elsewhere()
+ if len(elsewhere) == 0:
+ elsewhere = update.replacements()
if len(elsewhere) == 1:
return redirect(elsewhere[0])
context = {
--
cgit v1.2.3-54-g00ecf
From 4d7d08f93de9e6af9e664a00e090158e738a890c Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sun, 14 Apr 2013 13:45:00 -0500
Subject: Fix missing attribute error in replacment find code
Whoops. Just introduced this when ensuring we look for both the packgae
in other repositories as well as any replacments.
Signed-off-by: Dan McGee
---
packages/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/packages/models.py b/packages/models.py
index 92566a56..f830aade 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -326,7 +326,8 @@ def replacements(self):
replaces__name=self.pkgname)
if not self.arch.agnostic:
# make sure we match architectures if possible
- arches = self.pkg.applicable_arches()
+ arches = set(Arch.objects.filter(agnostic=True))
+ arches.add(self.arch)
pkgs = pkgs.filter(arch__in=arches)
return pkgs
--
cgit v1.2.3-54-g00ecf
From 283cd944beefce8e364f238f25133e2d65b7702b Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 16 Apr 2013 20:16:06 -0500
Subject: Use require_safe decorator rather than require_GET
This was added in Django 1.4, and ensures both GET and HEAD requests,
but not POST requests, are allowed through.
Signed-off-by: Dan McGee
---
packages/views/__init__.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'packages')
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index 4c195385..c1f0f492 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -9,7 +9,7 @@
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.views.decorators.cache import cache_control
-from django.views.decorators.http import require_GET, require_POST
+from django.views.decorators.http import require_safe, require_POST
from main.models import Package, Arch
from ..models import PackageRelation
@@ -24,7 +24,7 @@
from .signoff import signoffs, signoff_package, signoff_options, signoffs_json
-@require_GET
+@require_safe
@cache_control(public=True, max_age=86400)
def opensearch(request):
if request.is_secure():
@@ -37,7 +37,7 @@ def opensearch(request):
content_type='application/opensearchdescription+xml')
-@require_GET
+@require_safe
@cache_control(public=True, max_age=300)
def opensearch_suggest(request):
search_term = request.GET.get('q', '')
--
cgit v1.2.3-54-g00ecf
From 31d39e75eea7fb6cdf3bb8bfd8b490d45de04ee9 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 16 Apr 2013 21:59:32 -0500
Subject: Add shortcut for HEAD requests on slower views
We sometimes see some web bots and crawlers make HEAD requests to verify
existence of certain pages in the application. However, they are less
than kind as 20-50 requests might arrive at the same time, and package
search and details pages are some of the slowest rendering pages we have
due to the Django template engine.
Rather than waste time generating the content only to throw it away,
response as soon as we can with either a 404 or 200 response as
appropriate, omitting the 'Content-Length' header completely, which
seems to be acceptable by the HTTP spec.
Signed-off-by: Dan McGee
---
main/utils.py | 9 +++++++++
packages/views/display.py | 3 +++
packages/views/search.py | 4 +++-
3 files changed, 15 insertions(+), 1 deletion(-)
(limited to 'packages')
diff --git a/main/utils.py b/main/utils.py
index cdd4ff71..8394e5cd 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -8,6 +8,7 @@
from django.core.cache import cache
from django.db import connections, router
+from django.http import HttpResponse
from django.utils.timezone import now
from django.template.defaultfilters import slugify
@@ -55,6 +56,14 @@ def clear_cache_function(func, args, kwargs):
cache.delete(key)
+def empty_response():
+ empty = HttpResponse('')
+ # designating response as 'streaming' forces ConditionalGetMiddleware to
+ # not add a 'Content-Length: 0' header
+ empty.streaming = True
+ return empty
+
+
def format_http_headers(request):
headers = sorted((k, v) for k, v in request.META.items()
if k.startswith('HTTP_'))
diff --git a/packages/views/display.py b/packages/views/display.py
index 50783835..87424483 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -7,6 +7,7 @@
from django.utils.timezone import now
from main.models import Package, PackageFile, Arch, Repo
+from main.utils import empty_response
from mirrors.utils import get_mirror_url_for_download
from ..models import Update
from ..utils import get_group_info, PackageJSONEncoder
@@ -126,6 +127,8 @@ def details(request, name='', repo='', arch=''):
pkg = Package.objects.select_related(
'arch', 'repo', 'packager').get(pkgname=name,
repo=repo_obj, arch=arch_obj)
+ if request.method == 'HEAD':
+ return empty_response()
return render(request, 'packages/details.html', {'pkg': pkg})
except Package.DoesNotExist:
# attempt a variety of fallback options before 404ing
diff --git a/packages/views/search.py b/packages/views/search.py
index 0362602e..b3778172 100644
--- a/packages/views/search.py
+++ b/packages/views/search.py
@@ -7,7 +7,7 @@
from django.views.generic import ListView
from main.models import Package, Arch, Repo
-from main.utils import make_choice
+from main.utils import empty_response, make_choice
from ..models import PackageRelation
from ..utils import attach_maintainers, PackageJSONEncoder
@@ -99,6 +99,8 @@ class SearchListView(ListView):
allowed_sort = list(sort_fields) + ["-" + s for s in sort_fields]
def get(self, request, *args, **kwargs):
+ if request.method == 'HEAD':
+ return empty_response()
self.form = PackageSearchForm(data=request.GET,
show_staging=self.request.user.is_authenticated())
return super(SearchListView, self).get(request, *args, **kwargs)
--
cgit v1.2.3-54-g00ecf
From b7b24740640e24883cd17fd683e1d465fbb343f8 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 16 Apr 2013 22:12:01 -0500
Subject: Various minor code cleanups and fixes
Most of these were suggested by PyCharm, and include everything from
little syntax issues and other bad smells to dead or bad code.
Signed-off-by: Dan McGee
---
devel/management/commands/pgp_import.py | 1 +
devel/models.py | 1 -
devel/utils.py | 2 +-
devel/views.py | 2 +-
main/log.py | 1 -
main/migrations/0029_fill_in_repo_data.py | 1 -
main/models.py | 12 +++++-------
main/utils.py | 1 -
mirrors/management/commands/mirrorcheck.py | 12 +++---------
mirrors/models.py | 2 +-
mirrors/utils.py | 6 +++---
packages/migrations/0002_populate_package_relation.py | 2 --
packages/templatetags/package_extras.py | 4 ++--
packages/utils.py | 2 +-
packages/views/display.py | 2 --
packages/views/flag.py | 3 +--
public/views.py | 1 -
releng/management/commands/syncisos.py | 2 +-
releng/models.py | 2 +-
releng/views.py | 2 +-
retro/templates/retro/index-20030330.html | 1 -
sitestatic/archweb.js | 1 -
todolists/utils.py | 1 -
todolists/views.py | 1 -
visualize/static/visualize.js | 2 +-
25 files changed, 23 insertions(+), 44 deletions(-)
(limited to 'packages')
diff --git a/devel/management/commands/pgp_import.py b/devel/management/commands/pgp_import.py
index 10e6cfcb..b1f29d77 100644
--- a/devel/management/commands/pgp_import.py
+++ b/devel/management/commands/pgp_import.py
@@ -95,6 +95,7 @@ def parse_keydata(data):
# parse all of the output from our successful GPG command
logger.info("parsing command output")
+ node = None
for line in data.split('\n'):
parts = line.split(':')
if parts[0] == 'pub':
diff --git a/devel/models.py b/devel/models.py
index 67de40a6..4354e0f2 100644
--- a/devel/models.py
+++ b/devel/models.py
@@ -4,7 +4,6 @@
from django.db import models
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
-from django.utils.timezone import now
from django_countries import CountryField
from .fields import PGPKeyField
diff --git a/devel/utils.py b/devel/utils.py
index e8e3a6c4..340841f5 100644
--- a/devel/utils.py
+++ b/devel/utils.py
@@ -131,7 +131,7 @@ def find(self, userstring):
self.username_email, self.user_name)
for matcher in find_methods:
user = matcher(name, email)
- if user != None:
+ if user is not None:
break
self.cache[userstring] = user
diff --git a/devel/views.py b/devel/views.py
index 61c1e568..4258ea7f 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -34,7 +34,7 @@
@login_required
def index(request):
'''the developer dashboard'''
- if(request.user.is_authenticated()):
+ if request.user.is_authenticated():
inner_q = PackageRelation.objects.filter(user=request.user)
else:
inner_q = PackageRelation.objects.none()
diff --git a/main/log.py b/main/log.py
index 63634874..5c745cc8 100644
--- a/main/log.py
+++ b/main/log.py
@@ -46,7 +46,6 @@ def filter(self, record):
trace = '\n'.join(traceback.format_exception(*record.exc_info))
key = md5(trace).hexdigest()
- duplicate = False
cache = self.cache_module.cache
# Test if the cache works
diff --git a/main/migrations/0029_fill_in_repo_data.py b/main/migrations/0029_fill_in_repo_data.py
index 0887b28c..7da6b1c4 100644
--- a/main/migrations/0029_fill_in_repo_data.py
+++ b/main/migrations/0029_fill_in_repo_data.py
@@ -7,7 +7,6 @@
class Migration(DataMigration):
def forwards(self, orm):
- "Write your forwards methods here."
orm.Repo.objects.filter(name__istartswith='community').update(bugs_project=5, svn_root='community')
orm.Repo.objects.filter(name__iexact='multilib').update(bugs_project=5, svn_root='community')
diff --git a/main/models.py b/main/models.py
index 89215f05..24aeed89 100644
--- a/main/models.py
+++ b/main/models.py
@@ -7,7 +7,6 @@
from django.db.models import Q
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
-from django.utils.timezone import now
from .fields import PositiveBigIntegerField
from .utils import set_created_field
@@ -140,7 +139,7 @@ def get_full_url(self, proto='https'):
@property
def signature(self):
try:
- data = b64decode(self.pgp_signature)
+ data = b64decode(self.pgp_signature.encode('utf-8'))
except TypeError:
return None
if not data:
@@ -274,7 +273,6 @@ def get_depends(self):
Packages will match the testing status of this package if possible.
"""
deps = []
- arches = None
# TODO: we can use list comprehension and an 'in' query to make this
# more effective
for dep in self.depends.all():
@@ -400,13 +398,13 @@ def elsewhere(self):
'''attempt to locate this package anywhere else, regardless of
architecture or repository. Excludes this package from the list.'''
names = [self.pkgname]
- if self.pkgname.startswith('lib32-'):
+ if self.pkgname.startswith(u'lib32-'):
names.append(self.pkgname[6:])
- elif self.pkgname.endswith('-multilib'):
+ elif self.pkgname.endswith(u'-multilib'):
names.append(self.pkgname[:-9])
else:
- names.append('lib32-' + self.pkgname)
- names.append(self.pkgname + '-multilib')
+ names.append(u'lib32-' + self.pkgname)
+ names.append(self.pkgname + u'-multilib')
return Package.objects.normal().filter(
pkgname__in=names).exclude(id=self.id).order_by(
'arch__name', 'repo__name')
diff --git a/main/utils.py b/main/utils.py
index 8394e5cd..9ee8db58 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -3,7 +3,6 @@
except ImportError:
import pickle
-from datetime import datetime
import hashlib
from django.core.cache import cache
diff --git a/mirrors/management/commands/mirrorcheck.py b/mirrors/management/commands/mirrorcheck.py
index d6de8f22..e7dd7b49 100644
--- a/mirrors/management/commands/mirrorcheck.py
+++ b/mirrors/management/commands/mirrorcheck.py
@@ -106,19 +106,13 @@ def parse_lastsync(log, data):
def check_mirror_url(mirror_url, location, timeout):
- if location:
- if location.family == socket.AF_INET6:
- ipopt = '--ipv6'
- elif location.family == socket.AF_INET:
- ipopt = '--ipv4'
-
url = mirror_url.url + 'lastsync'
logger.info("checking URL %s", url)
log = MirrorLog(url=mirror_url, check_time=now(), location=location)
headers = {'User-Agent': 'archweb/1.0'}
req = urllib2.Request(url, None, headers)
+ start = time.time()
try:
- start = time.time()
result = urllib2.urlopen(req, timeout=timeout)
data = result.read()
result.close()
@@ -147,12 +141,12 @@ def check_mirror_url(mirror_url, location, timeout):
elif isinstance(e.reason, socket.error):
log.error = e.reason.args[1]
logger.debug("failed: %s, %s", url, log.error)
- except HTTPException as e:
+ except HTTPException:
# e.g., BadStatusLine
log.is_success = False
log.error = "Exception in processing HTTP request."
logger.debug("failed: %s, %s", url, log.error)
- except socket.timeout as e:
+ except socket.timeout:
log.is_success = False
log.error = "Connection timed out."
logger.debug("failed: %s, %s", url, log.error)
diff --git a/mirrors/models.py b/mirrors/models.py
index 791b0078..d8ac7952 100644
--- a/mirrors/models.py
+++ b/mirrors/models.py
@@ -92,7 +92,7 @@ def clean(self):
families = self.address_families()
self.has_ipv4 = socket.AF_INET in families
self.has_ipv6 = socket.AF_INET6 in families
- except socket.error as e:
+ except socket.error:
# We don't fail in this case; we'll just set both to False
self.has_ipv4 = False
self.has_ipv6 = False
diff --git a/mirrors/utils.py b/mirrors/utils.py
index 5a8bbf5d..531cf005 100644
--- a/mirrors/utils.py
+++ b/mirrors/utils.py
@@ -1,13 +1,13 @@
from datetime import timedelta
from django.db import connection
-from django.db.models import Avg, Count, Max, Min, StdDev
+from django.db.models import Count, Max, Min
from django.utils.dateparse import parse_datetime
from django.utils.timezone import now
from django_countries.fields import Country
from main.utils import cache_function, database_vendor
-from .models import MirrorLog, MirrorProtocol, MirrorUrl
+from .models import MirrorLog, MirrorUrl
DEFAULT_CUTOFF = timedelta(hours=24)
@@ -165,7 +165,7 @@ def get_mirror_errors(cutoff=DEFAULT_CUTOFF, mirror_id=None):
).order_by('-last_occurred', '-error_count')
if mirror_id:
- urls = urls.filter(mirror_id=mirror_id)
+ errors = errors.filter(url__mirror_id=mirror_id)
errors = list(errors)
for err in errors:
diff --git a/packages/migrations/0002_populate_package_relation.py b/packages/migrations/0002_populate_package_relation.py
index 738e068f..b0d32c7a 100644
--- a/packages/migrations/0002_populate_package_relation.py
+++ b/packages/migrations/0002_populate_package_relation.py
@@ -11,7 +11,6 @@ class Migration(DataMigration):
)
def forwards(self, orm):
- "Write your forwards methods here."
# search by pkgbase first and insert those records
qs = orm['main.Package'].objects.exclude(maintainer=None).exclude(
pkgbase=None).distinct().values('pkgbase', 'maintainer_id')
@@ -29,7 +28,6 @@ def forwards(self, orm):
defaults={'user_id': row['maintainer_id']})
def backwards(self, orm):
- "Write your backwards methods here."
if not db.dry_run:
orm.PackageRelation.objects.all().delete()
pass
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index f14fab1e..ef0e1aea 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -53,10 +53,10 @@ def do_buildsortqs(parser, token):
tagname, sortfield = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
- "%r tag requires a single argument" % tagname)
+ "%r tag requires a single argument" % token)
if not (sortfield[0] == sortfield[-1] and sortfield[0] in ('"', "'")):
raise template.TemplateSyntaxError(
- "%r tag's argument should be in quotes" % tagname)
+ "%r tag's argument should be in quotes" % token)
return BuildQueryStringNode(sortfield[1:-1])
diff --git a/packages/utils.py b/packages/utils.py
index a4217fbd..4f3b8665 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -391,7 +391,7 @@ def signoffs_id_query(model, repos):
"""
cursor = connection.cursor()
# query pre-process- fill in table name and placeholders for IN
- repo_sql = ','.join(['%s' for r in repos])
+ repo_sql = ','.join(['%s' for _ in repos])
sql = sql % (model._meta.db_table, repo_sql, repo_sql)
repo_ids = [r.pk for r in repos]
# repo_ids are needed twice, so double the array
diff --git a/packages/views/display.py b/packages/views/display.py
index 87424483..021c7ed8 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -228,8 +228,6 @@ 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
- values = {
- }
url = '{host}{repo}/os/{arch}/{filename}'.format(host=url.url,
repo=pkg.repo.name.lower(), arch=arch, filename=pkg.filename)
return redirect(url)
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 5c76e1d5..39cdcef8 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -110,7 +110,7 @@ def perform_updates():
subject = '%s package [%s] marked out-of-date' % \
(pkg.repo.name, pkg.pkgname)
for maint in maints:
- if maint.userprofile.notify == True:
+ if maint.userprofile.notify is True:
toemail.append(maint.email)
if toemail:
@@ -133,7 +133,6 @@ def perform_updates():
return redirect('package-flag-confirmed', name=name, repo=repo,
arch=arch)
else:
- initial = {}
form = FlagForm(authenticated=authenticated)
context = {
diff --git a/public/views.py b/public/views.py
index 22cb8759..39273396 100644
--- a/public/views.py
+++ b/public/views.py
@@ -125,7 +125,6 @@ def keys(request):
master_keys = MasterKey.objects.select_related('owner', 'revoker',
'owner__userprofile', 'revoker__userprofile').filter(
revoked__isnull=True)
- master_key_ids = frozenset(key.pgp_key[-16:] for key in master_keys)
sig_counts = PGPSignature.objects.filter(not_expired, valid=True,
signee__in=user_key_ids).order_by().values_list('signer').annotate(
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index c9f61964..f182cc33 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -20,7 +20,7 @@ def handle_starttag(self, tag, attrs):
if tag == 'a':
for name, value in attrs:
if name == "href":
- if value != '../' and self.url_re.search(value) != None:
+ if value != '../' and self.url_re.search(value) is not None:
self.hyperlinks.append(value[:-1])
def parse(self, url):
diff --git a/releng/models.py b/releng/models.py
index b95f7d52..5ee2f325 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -160,7 +160,7 @@ def info_html(self):
def torrent(self):
try:
- data = b64decode(self.torrent_data)
+ data = b64decode(self.torrent_data.encode('utf-8'))
except TypeError:
return None
if not data:
diff --git a/releng/views.py b/releng/views.py
index ad4b07d1..b1c76a4a 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -231,7 +231,7 @@ def release_torrent(request, version):
release = get_object_or_404(Release, version=version)
if not release.torrent_data:
raise Http404
- data = b64decode(release.torrent_data)
+ data = b64decode(release.torrent_data.encode('utf-8'))
response = HttpResponse(data, content_type='application/x-bittorrent')
# TODO: this is duplicated from Release.iso_url()
filename = 'archlinux-%s-dual.iso.torrent' % release.version
diff --git a/retro/templates/retro/index-20030330.html b/retro/templates/retro/index-20030330.html
index 449731af..51cc8ba3 100644
--- a/retro/templates/retro/index-20030330.html
+++ b/retro/templates/retro/index-20030330.html
@@ -232,7 +232,6 @@
-
diff --git a/sitestatic/archweb.js b/sitestatic/archweb.js
index dda22d9e..aa225f5f 100644
--- a/sitestatic/archweb.js
+++ b/sitestatic/archweb.js
@@ -146,7 +146,6 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
(function($) {
$.fn.enableCheckboxRangeSelection = function() {
var lastCheckbox = null,
- lastElement = null,
spec = this;
spec.unbind("click.checkboxrange");
diff --git a/todolists/utils.py b/todolists/utils.py
index 51a75a3c..7b98c887 100644
--- a/todolists/utils.py
+++ b/todolists/utils.py
@@ -1,5 +1,4 @@
from django.db import connections, router
-from django.db.models import Count
from .models import Todolist, TodolistPackage
from packages.models import Package
diff --git a/todolists/views.py b/todolists/views.py
index 7636d38e..d5b39934 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -9,7 +9,6 @@
from django.views.decorators.cache import never_cache
from django.views.generic import DeleteView
from django.template import Context, loader
-from django.template.defaultfilters import slugify
from django.utils.timezone import now
from main.models import Package, Repo
diff --git a/visualize/static/visualize.js b/visualize/static/visualize.js
index 7e240d44..5004fe6c 100644
--- a/visualize/static/visualize.js
+++ b/visualize/static/visualize.js
@@ -55,7 +55,7 @@ function packages_treemap(chart_id, orderings, default_order) {
var nodes = d3_div.data([json]).selectAll("div")
.data(treemap.nodes, key_func);
/* start out new nodes in the center of the picture area */
- var w_center = jq_div.width() / 2;
+ var w_center = jq_div.width() / 2,
h_center = jq_div.height() / 2;
nodes.enter().append("div")
.attr("class", "treemap-cell")
--
cgit v1.2.3-54-g00ecf
From bb18fa3323a0494a2774ea61911572b089d04b6d Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 20 Apr 2013 13:32:09 -0500
Subject: Fix parsing issues when query string keys contain unicode
This is dirty, but it works. There is probably a better and cleaner way
to do all of this, but for now just fix it quickly.
Signed-off-by: Dan McGee
---
packages/templatetags/package_extras.py | 6 ++++++
1 file changed, 6 insertions(+)
(limited to 'packages')
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index ef0e1aea..f7392a96 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -37,6 +37,12 @@ def __init__(self, sortfield):
def render(self, context):
qs = parse_qs(context['current_query'])
+ # This is really dirty. The crazy round trips we do on our query string
+ # mean we get things like u'\xe2\x98\x83' in our views, when we should
+ # have simply u'\u2603' or a byte string of the UTF-8 value. Force the
+ # keys and list of values to be byte strings only.
+ qs = {k.encode('latin-1'): [v.encode('latin-1') for v in vals]
+ for k, vals in qs.items()}
if 'sort' in qs and self.sortfield in qs['sort']:
if self.sortfield.startswith('-'):
qs['sort'] = [self.sortfield[1:]]
--
cgit v1.2.3-54-g00ecf