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