From 6a429a8d898d5f0789f5b5a3f2858c6578fa5227 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 7 Mar 2011 21:50:13 -0600 Subject: Clean up current_query and preserve multiple args When implementing search for multiple architectures or repositories, I didn't update this method to accomidate the new query parameters. Clean it up a bit by not appending/stripping the leading '?' anywhere but in the template itself, and ensure we can handle multiple of any parameter passed in. Fixes FS#23180. Signed-off-by: Dan McGee --- packages/views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 59779fe4..3cef8226 100644 --- a/packages/views.py +++ b/packages/views.py @@ -168,12 +168,10 @@ class PackageSearchForm(forms.Form): [(m.username, m.get_full_name()) for m in maints] def search(request, page=None): - current_query = '?' limit = 50 packages = Package.objects.select_related('arch', 'repo') if request.GET: - current_query += request.GET.urlencode() form = PackageSearchForm(data=request.GET) if form.is_valid(): if form.cleaned_data['repo']: @@ -208,12 +206,14 @@ def search(request, page=None): else: form = PackageSearchForm() - page_dict = {'search_form': form, - 'current_query': current_query - } if packages.count() == 1: return redirect(packages[0]) + current_query = request.GET.urlencode() + page_dict = { + 'search_form': form, + 'current_query': current_query + } allowed_sort = ["arch", "repo", "pkgname", "last_update", "flag_date"] allowed_sort += ["-" + s for s in allowed_sort] sort = request.GET.get('sort', None) -- cgit v1.2.3-2-g168b From a0ef88770f5fe318f38eaa7dc794727a507c797b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 15 Mar 2011 09:02:22 -0500 Subject: Ensure package search form correctly handles errors We were silently eating errors and just showing a normal package list if the form didn't validate. Rather than do that, make sure we return no packages at all and display the form errors back to the user in a sane fashion. Adjust the validation methods on the 'limit' parameter so any integer is acceptable. Signed-off-by: Dan McGee --- packages/views.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 3cef8226..6239f01a 100644 --- a/packages/views.py +++ b/packages/views.py @@ -126,6 +126,23 @@ def getmaintainer(request, name, repo, arch): return HttpResponse(str('\n'.join(names)), mimetype='text/plain') +def coerce_limit_value(value): + if not value: + return 50 + if value == 'all': + return None + value = int(value) + if value < 0: + raise ValueError + return value + +class LimitTypedChoiceField(forms.TypedChoiceField): + def valid_value(self, value): + try: + return coerce_limit_value(value) + except ValueError, TypeError: + return False + class PackageSearchForm(forms.Form): repo = forms.MultipleChoiceField(required=False) arch = forms.MultipleChoiceField(required=False) @@ -136,25 +153,12 @@ class PackageSearchForm(forms.Form): flagged = forms.ChoiceField( choices=[('', 'All')] + make_choice(['Flagged', 'Not Flagged']), required=False) - limit = forms.ChoiceField( + limit = LimitTypedChoiceField( choices=make_choice([50, 100, 250]) + [('all', 'All')], + coerce=coerce_limit_value, required=False, initial=50) - def clean_limit(self): - limit = self.cleaned_data['limit'] - if limit == 'all': - limit = None - elif limit: - try: - limit = int(limit) - except: - raise forms.ValidationError("Should be an integer") - else: - limit = 50 - return limit - - def __init__(self, *args, **kwargs): super(PackageSearchForm, self).__init__(*args, **kwargs) self.fields['repo'].choices = make_choice( @@ -186,7 +190,8 @@ def search(request, page=None): inner_q = PackageRelation.objects.all().values('pkgbase') packages = packages.exclude(pkgbase__in=inner_q) elif form.cleaned_data['maintainer']: - inner_q = PackageRelation.objects.filter(user__username=form.cleaned_data['maintainer']).values('pkgbase') + inner_q = PackageRelation.objects.filter( + user__username=form.cleaned_data['maintainer']).values('pkgbase') packages = packages.filter(pkgbase__in=inner_q) if form.cleaned_data['flagged'] == 'Flagged': @@ -203,6 +208,9 @@ def search(request, page=None): packages = packages.filter(last_update__gte= datetime(lu.year, lu.month, lu.day, 0, 0)) limit = form.cleaned_data['limit'] + else: + # Form had errors, don't return any results, just the busted form + packages = Package.objects.none() else: form = PackageSearchForm() -- cgit v1.2.3-2-g168b From 2360e2c4bcb0fc873a60599165e7f24f20465786 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 15 Mar 2011 11:06:34 -0500 Subject: Only set limit if we have one, else default to 50 This was the cause of some pretty awesome performance headaches this morning. Signed-off-by: Dan McGee --- packages/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 6239f01a..70b3a84b 100644 --- a/packages/views.py +++ b/packages/views.py @@ -207,7 +207,9 @@ def search(request, page=None): lu = form.cleaned_data['last_update'] packages = packages.filter(last_update__gte= datetime(lu.year, lu.month, lu.day, 0, 0)) - limit = form.cleaned_data['limit'] + + if form.cleaned_data['limit']: + limit = form.cleaned_data['limit'] else: # Form had errors, don't return any results, just the busted form packages = Package.objects.none() -- cgit v1.2.3-2-g168b From aca7700dd7519d45e677e18b1a0199f3712ce140 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 16 Mar 2011 09:03:38 +0100 Subject: Fix valid_value() in "LimitTypedChoiceField" class. We just returned the coerced value in valid_value() which may become None if the valid value "all" was passed, resulting in valid_value() evaluating to False. Explicitly returning True if the value can be coerced without an error fixes this. Signed-off-by: Lukas Fleischer Signed-off-by: Dan McGee --- packages/views.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 70b3a84b..4101c538 100644 --- a/packages/views.py +++ b/packages/views.py @@ -128,9 +128,10 @@ def getmaintainer(request, name, repo, arch): def coerce_limit_value(value): if not value: - return 50 - if value == 'all': return None + if value == 'all': + # negative value indicates show all results + return -1 value = int(value) if value < 0: raise ValueError @@ -139,7 +140,8 @@ def coerce_limit_value(value): class LimitTypedChoiceField(forms.TypedChoiceField): def valid_value(self, value): try: - return coerce_limit_value(value) + coerce_limit_value(value) + return True except ValueError, TypeError: return False @@ -208,8 +210,11 @@ def search(request, page=None): packages = packages.filter(last_update__gte= datetime(lu.year, lu.month, lu.day, 0, 0)) - if form.cleaned_data['limit']: - limit = form.cleaned_data['limit'] + asked_limit = form.cleaned_data['limit'] + if asked_limit and asked_limit < 0: + limit = None + elif asked_limit: + limit = asked_limit else: # Form had errors, don't return any results, just the busted form packages = Package.objects.none() -- cgit v1.2.3-2-g168b From f583d05d84d48d1fd78f2472adb6106383af6f45 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 12:29:22 -0500 Subject: Use select_related() on group details page Signed-off-by: Dan McGee --- packages/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 4101c538..d393e694 100644 --- a/packages/views.py +++ b/packages/views.py @@ -107,7 +107,7 @@ def group_details(request, arch, name): arches = [ arch ] arches.extend(Arch.objects.filter(agnostic=True)) pkgs = Package.objects.filter(groups__name=name, arch__in=arches) - pkgs = pkgs.order_by('pkgname') + pkgs = pkgs.select_related('arch', 'repo').order_by('pkgname') if len(pkgs) == 0: raise Http404 context = { -- cgit v1.2.3-2-g168b From f46e5b1a94c845ea2125b4f9d6777dff56c9ad29 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 12:44:12 -0500 Subject: Generalize group details page We will be able to use this same table-based package listing elsewhere. Signed-off-by: Dan McGee --- packages/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index d393e694..374a1a20 100644 --- a/packages/views.py +++ b/packages/views.py @@ -111,11 +111,12 @@ def group_details(request, arch, name): if len(pkgs) == 0: raise Http404 context = { - 'groupname': name, + 'list_title': 'Group Details', + 'name': name, 'arch': arch, 'packages': pkgs, } - return direct_to_template(request, 'packages/group_details.html', context) + return direct_to_template(request, 'packages/packages_list.html', context) def getmaintainer(request, name, repo, arch): "Returns the maintainers as plaintext." -- cgit v1.2.3-2-g168b From a52ddb5c48dd2cb7856779f64611679aca7d660d Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 12:47:26 -0500 Subject: Allow virtual base packages to display in web interface Repurpose the old group details page to show a listing of all packages built from a particular pkgbase value, even if this value is not an actual package. Signed-off-by: Dan McGee --- packages/views.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 374a1a20..263165fd 100644 --- a/packages/views.py +++ b/packages/views.py @@ -83,9 +83,29 @@ def update(request): def details(request, name='', repo='', arch=''): if all([name, repo, arch]): - pkg = get_object_or_404(Package, - pkgname=name, repo__name__iexact=repo, arch__name=arch) - return direct_to_template(request, 'packages/details.html', {'pkg': pkg, }) + try: + pkg = Package.objects.get(pkgname=name, + repo__name__iexact=repo, arch__name=arch) + return direct_to_template(request, 'packages/details.html', + {'pkg': pkg, }) + except Package.DoesNotExist: + 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.filter(pkgbase=name, + repo__testing=repo.testing, arch__in=arches) + pkgs = pkgs.select_related('arch', 'repo').order_by('pkgname') + if len(pkgs) == 0: + raise Http404 + context = { + 'list_title': 'Split Package Details', + 'name': name, + 'arch': arch, + 'packages': pkgs, + } + return direct_to_template(request, 'packages/packages_list.html', + context) else: return redirect("/packages/?arch=%s&repo=%s&q=%s" % ( arch.lower(), repo.title(), name)) -- cgit v1.2.3-2-g168b From ecfcdfda941ef0c991300c6eb25fe336c3fd9cbe Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 7 Apr 2011 15:13:37 -0500 Subject: Select related needed objects when pulling package details Signed-off-by: Dan McGee --- packages/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 263165fd..2f614e34 100644 --- a/packages/views.py +++ b/packages/views.py @@ -84,7 +84,8 @@ def update(request): def details(request, name='', repo='', arch=''): if all([name, repo, arch]): try: - pkg = Package.objects.get(pkgname=name, + pkg = Package.objects.select_related( + 'arch', 'repo', 'packager').get(pkgname=name, repo__name__iexact=repo, arch__name=arch) return direct_to_template(request, 'packages/details.html', {'pkg': pkg, }) -- cgit v1.2.3-2-g168b From 01db07bad844e17e084f650b6732647f77a91c5c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 7 Apr 2011 15:39:53 -0500 Subject: Use UTC datetime objects everywhere Rather than the twisted mix of local times and UTC times we currently have. Signed-off-by: Dan McGee --- packages/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 2f614e34..1587563b 100644 --- a/packages/views.py +++ b/packages/views.py @@ -371,7 +371,7 @@ def flag(request, name, repo, arch): # find all packages from (hopefully) the same PKGBUILD pkgs = Package.objects.filter( pkgbase=pkg.pkgbase, repo__testing=pkg.repo.testing) - pkgs.update(flag_date=datetime.now()) + pkgs.update(flag_date=datetime.utcnow()) maints = pkg.maintainers if not maints: -- cgit v1.2.3-2-g168b From 9fd0995aa5567bc3b2df939cebb02cc6efeaa3b6 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 16 Apr 2011 09:34:31 -0500 Subject: Only include known values in generated search query Signed-off-by: Dan McGee --- packages/views.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 1587563b..73692d37 100644 --- a/packages/views.py +++ b/packages/views.py @@ -18,6 +18,7 @@ from django.views.generic.simple import direct_to_template from datetime import datetime import string +from urllib import urlencode from main.models import Package, PackageFile from main.models import Arch, Repo, Signoff @@ -108,8 +109,14 @@ def details(request, name='', repo='', arch=''): return direct_to_template(request, 'packages/packages_list.html', context) else: - return redirect("/packages/?arch=%s&repo=%s&q=%s" % ( - arch.lower(), repo.title(), name)) + pkg_data = [ + ('arch', arch.lower()), + ('repo', repo.lower()), + ('q', name), + ] + # only include non-blank values in the query we generate + pkg_data = [(x, y) for x, y in pkg_data if y] + return redirect("/packages/?%s" % urlencode(pkg_data)) def groups(request, arch=None): arches = [] -- cgit v1.2.3-2-g168b From 43964627a31bbf7e583a3aeb0ab6bc5a5a108396 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 18 Apr 2011 13:48:44 -0500 Subject: Update out of date flag screen and email Now that multiple packages get marked out of date whenever this form is processed, have the page and email itself reflect this fact. Signed-off-by: Dan McGee --- packages/views.py | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 73692d37..bcb66413 100644 --- a/packages/views.py +++ b/packages/views.py @@ -367,17 +367,20 @@ class FlagForm(forms.Form): def flag(request, name, repo, arch): pkg = get_object_or_404(Package, pkgname=name, repo__name__iexact=repo, arch__name=arch) - context = {'pkg': pkg} if pkg.flag_date is not None: # already flagged. do nothing. return direct_to_template(request, 'packages/flagged.html', context) + # find all packages from (hopefully) the same PKGBUILD + pkgs = Package.objects.select_related('arch', 'repo').filter( + pkgbase=pkg.pkgbase, flag_date__isnull=True, + repo__testing=pkg.repo.testing).order_by( + 'pkgname', 'repo__name', 'arch__name') if request.POST: form = FlagForm(request.POST) if form.is_valid() and form.cleaned_data['website'] == '': - # find all packages from (hopefully) the same PKGBUILD - pkgs = Package.objects.filter( - pkgbase=pkg.pkgbase, repo__testing=pkg.repo.testing) + # save the package list for later use + flagged_pkgs = list(pkgs) pkgs.update(flag_date=datetime.utcnow()) maints = pkg.maintainers @@ -394,13 +397,13 @@ def flag(request, name, repo, arch): toemail.append(maint.email) if toemail: - # send notification email to the maintainer + # send notification email to the maintainers t = loader.get_template('packages/outofdate.txt') c = Context({ 'email': form.cleaned_data['email'], 'message': form.cleaned_data['usermessage'], 'pkg': pkg, - 'weburl': pkg.get_full_url(), + 'packages': flagged_pkgs, }) send_mail(subject, t.render(c), @@ -408,14 +411,30 @@ def flag(request, name, repo, arch): toemail, fail_silently=True) - context['confirmed'] = True + return redirect('package-flag-confirmed', name=name, repo=repo, + arch=arch) else: form = FlagForm() - context['form'] = form - + context = { + 'package': pkg, + 'packages': pkgs, + 'form': form + } return direct_to_template(request, 'packages/flag.html', context) +def flag_confirmed(request, name, repo, arch): + pkg = get_object_or_404(Package, + pkgname=name, repo__name__iexact=repo, arch__name=arch) + pkgs = Package.objects.select_related('arch', 'repo').filter( + pkgbase=pkg.pkgbase, flag_date=pkg.flag_date, + repo__testing=pkg.repo.testing).order_by( + 'pkgname', 'repo__name', 'arch__name') + + context = {'package': pkg, 'packages': pkgs} + + return direct_to_template(request, 'packages/flag_confirmed.html', context) + def download(request, name, repo, arch): pkg = get_object_or_404(Package, pkgname=name, repo__name__iexact=repo, arch__name=arch) -- cgit v1.2.3-2-g168b From 08ce9c5cd9a5d2dc0c15ee8c88ce7b78748339e5 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 18 Apr 2011 15:19:24 -0500 Subject: packages: pylint suggested cleanups Signed-off-by: Dan McGee --- packages/views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index bcb66413..59c685bd 100644 --- a/packages/views.py +++ b/packages/views.py @@ -171,7 +171,7 @@ class LimitTypedChoiceField(forms.TypedChoiceField): try: coerce_limit_value(value) return True - except ValueError, TypeError: + except (ValueError, TypeError): return False class PackageSearchForm(forms.Form): @@ -226,7 +226,7 @@ def search(request, page=None): packages = packages.filter(pkgbase__in=inner_q) if form.cleaned_data['flagged'] == 'Flagged': - packages=packages.filter(flag_date__isnull=False) + packages = packages.filter(flag_date__isnull=False) elif form.cleaned_data['flagged'] == 'Not Flagged': packages = packages.filter(flag_date__isnull=True) @@ -369,7 +369,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', context) + return direct_to_template(request, 'packages/flagged.html', {'pkg': pkg}) # find all packages from (hopefully) the same PKGBUILD pkgs = Package.objects.select_related('arch', 'repo').filter( pkgbase=pkg.pkgbase, flag_date__isnull=True, @@ -445,13 +445,13 @@ def download(request, name, repo, arch): if pkg.arch.agnostic: # grab the first non-any arch to fake the download path arch = Arch.objects.exclude(agnostic=True)[0].name - details = { + values = { 'host': mirrorurl.url, 'arch': arch, 'repo': pkg.repo.name.lower(), 'file': pkg.filename, } - url = string.Template('${host}${repo}/os/${arch}/${file}').substitute(details) + url = string.Template('${host}${repo}/os/${arch}/${file}').substitute(values) return redirect(url) def arch_differences(request): -- cgit v1.2.3-2-g168b From 7cab8ae9f807deff494a9efad1a1747cda2ba0f0 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 3 May 2011 13:45:43 -0500 Subject: Never cache the stale relations page Signed-off-by: Dan McGee --- packages/views.py | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 59c685bd..23797f71 100644 --- a/packages/views.py +++ b/packages/views.py @@ -467,6 +467,7 @@ def arch_differences(request): return direct_to_template(request, 'packages/differences.html', context) @permission_required('main.change_package') +@never_cache def stale_relations(request): relations = PackageRelation.objects.select_related('user') pkgbases = Package.objects.all().values('pkgbase') -- cgit v1.2.3-2-g168b From d8f82d9d72eec6042536797f75e06a9296f4cc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Reynolds?= Date: Thu, 12 May 2011 15:52:47 -0300 Subject: More rebranding --- packages/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/views.py') diff --git a/packages/views.py b/packages/views.py index 263165fd..73594507 100644 --- a/packages/views.py +++ b/packages/views.py @@ -396,7 +396,7 @@ def flag(request, name, repo, arch): }) send_mail(subject, t.render(c), - 'Arch Website Notification ', + 'Parabola Packages ', toemail, fail_silently=True) -- cgit v1.2.3-2-g168b