From dc94eade03022ce3a5286f5e781576321a5f1653 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 27 Apr 2012 08:59:00 -0500 Subject: Incomplete-only todolists optimization We can push this down to the database if we know in advance we only need the incomplete lists. This helps our call on the developer dashboard quite a bit; the time of the single query in question drops from >1300ms to around 40ms. Signed-off-by: Dan McGee --- devel/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index d2ce65db..39f28a65 100644 --- a/devel/views.py +++ b/devel/views.py @@ -49,8 +49,7 @@ def index(request): todopkgs = todopkgs.filter(pkg__pkgbase__in=inner_q).order_by( 'list__name', 'pkg__pkgname') - todolists = get_annotated_todolists() - todolists = [todolist for todolist in todolists if todolist.incomplete_count > 0] + todolists = get_annotated_todolists(incomplete_only=True) signoffs = sorted(get_signoff_groups(user=request.user), key=operator.attrgetter('pkgbase')) -- cgit v1.2.3-2-g168b From 80e7d19726a95b40727b7f35b9ad80b436b14b93 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 27 Apr 2012 09:24:34 -0500 Subject: Dev dashboard performance improvement Rather than one query per cell in the arches and repos statistics tables, we can group these together up front using Django annotations. This means we only need one query per table. In my local instance with all of the staging repos imported, this reduces the total query count on this page from 56 to 26, a rather marked improvement. Signed-off-by: Dan McGee --- devel/views.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 39f28a65..cf0d8ad2 100644 --- a/devel/views.py +++ b/devel/views.py @@ -13,7 +13,7 @@ from django.contrib.auth.models import User, Group from django.contrib.sites.models import Site from django.core.mail import send_mail from django.db import transaction -from django.db.models import F +from django.db.models import F, Count from django.http import Http404 from django.shortcuts import get_object_or_404 from django.template import loader, Context @@ -54,6 +54,11 @@ def index(request): signoffs = sorted(get_signoff_groups(user=request.user), key=operator.attrgetter('pkgbase')) + arches = Arch.objects.all().annotate( + total_ct=Count('packages'), flagged_ct=Count('packages__flag_date')) + repos = Repo.objects.all().annotate( + total_ct=Count('packages'), flagged_ct=Count('packages__flag_date')) + maintainers = get_annotated_maintainers() maintained = PackageRelation.objects.filter( @@ -70,12 +75,12 @@ def index(request): page_dict = { 'todos': todolists, - 'repos': Repo.objects.all(), - 'arches': Arch.objects.all(), + 'arches': arches, + 'repos': repos, 'maintainers': maintainers, 'orphan': orphan, - 'flagged' : flagged, - 'todopkgs' : todopkgs, + 'flagged': flagged, + 'todopkgs': todopkgs, 'signoffs': signoffs } -- cgit v1.2.3-2-g168b From 25a2fbc7c1cb50fa80ed4de50830721507765a91 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 29 Apr 2012 19:15:19 -0500 Subject: Add a "last action" column to developer clocks page This allows people to easily see if a developer has done anything recently that we can easily grab a date for. Obviously this doesn't include all sources of activity, so the list of things checked is clearly stated at the top. Signed-off-by: Dan McGee --- devel/views.py | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index cf0d8ad2..85acda74 100644 --- a/devel/views.py +++ b/devel/views.py @@ -1,4 +1,4 @@ -from datetime import date, datetime, timedelta +from datetime import timedelta import operator import pytz import random @@ -9,24 +9,28 @@ from django import forms from django.http import HttpResponseRedirect from django.contrib.auth.decorators import \ login_required, permission_required, user_passes_test +from django.contrib.admin.models import LogEntry, ADDITION from django.contrib.auth.models import User, Group +from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site from django.core.mail import send_mail from django.db import transaction -from django.db.models import F, Count +from django.db.models import F, Count, Max from django.http import Http404 from django.shortcuts import get_object_or_404 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 .models import UserProfile from main.models import Package, PackageDepend, PackageFile, TodolistPkg from main.models import Arch, Repo from main.utils import utc_now -from packages.models import PackageRelation +from news.models import News +from packages.models import PackageRelation, Signoff from packages.utils import get_signoff_groups from todolists.utils import get_annotated_todolists from .utils import get_annotated_maintainers, UserFinder @@ -91,6 +95,33 @@ def clock(request): devs = User.objects.filter(is_active=True).order_by( 'first_name', 'last_name').select_related('userprofile') + latest_news = dict(News.objects.filter( + author__is_active=True).values_list('author').order_by( + ).annotate(last_post=Max('postdate'))) + latest_package = dict(Package.objects.filter( + packager__is_active=True).values_list('packager').order_by( + ).annotate(last_build=Max('build_date'))) + latest_signoff = dict(Signoff.objects.filter( + user__is_active=True).values_list('user').order_by( + ).annotate(last_signoff=Max('created'))) + latest_log = dict(LogEntry.objects.filter( + user__is_active=True).values_list('user').order_by( + ).annotate(last_log=Max('action_time'))) + + for dev in devs: + dates = [ + latest_news.get(dev.id, None), + latest_package.get(dev.id, None), + latest_signoff.get(dev.id, None), + latest_log.get(dev.id, None), + dev.last_login, + ] + dates = [d for d in dates if d is not None] + if dates: + dev.last_action = max(dates) + else: + dev.last_action = None + now = utc_now() page_dict = { 'developers': devs, @@ -135,7 +166,8 @@ class UserProfileForm(forms.ModelForm): def change_profile(request): if request.POST: form = ProfileForm(request.POST) - profile_form = UserProfileForm(request.POST, request.FILES, instance=request.user.get_profile()) + profile_form = UserProfileForm(request.POST, request.FILES, + instance=request.user.get_profile()) if form.is_valid() and profile_form.is_valid(): request.user.email = form.cleaned_data['email'] if form.cleaned_data['passwd1']: @@ -331,9 +363,6 @@ class NewUserForm(forms.ModelForm): def log_addition(request, obj): """Cribbed from ModelAdmin.log_addition.""" - from django.contrib.admin.models import LogEntry, ADDITION - from django.contrib.contenttypes.models import ContentType - from django.utils.encoding import force_unicode LogEntry.objects.log_action( user_id = request.user.pk, content_type_id = ContentType.objects.get_for_model(obj).pk, -- cgit v1.2.3-2-g168b 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 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'devel/views.py') 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' -- cgit v1.2.3-2-g168b From f36d876aca5571f09032d0d2a67c8b1f1a3258c8 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 12 May 2012 09:22:52 -0500 Subject: Change to new time access methods in pgpdump code Signed-off-by: Dan McGee --- devel/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 7ef33362..0f1c8d15 100644 --- a/devel/views.py +++ b/devel/views.py @@ -280,7 +280,7 @@ def report(request, report_name, username=None): filtered = [] packages = packages.filter(pgp_signature__isnull=False) for package in packages: - sig_date = package.signature.datetime.replace(tzinfo=pytz.utc) + sig_date = package.signature.creation_time.replace(tzinfo=pytz.utc) package.sig_date = sig_date.date() key_id = package.signature.key_id signer = finder.find_by_pgp_key(key_id) -- cgit v1.2.3-2-g168b 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/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'devel/views.py') 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.encoding import force_unicode 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) -- cgit v1.2.3-2-g168b From 1b03069f0d9e0397a7ff07404343c9400bbcfa1c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 7 Jun 2012 20:53:52 -0500 Subject: Use 3 decimal places for showing compression ratio Otherwise there are too many grouped under each value. Signed-off-by: Dan McGee --- devel/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 16b6acc6..78ed26f2 100644 --- a/devel/views.py +++ b/devel/views.py @@ -234,7 +234,7 @@ def report(request, report_name, username=None): package.installed_size_pretty = filesizeformat( package.installed_size) ratio = package.compressed_size / float(package.installed_size) - package.ratio = '%.2f' % ratio + package.ratio = '%.3f' % ratio package.compress_type = package.filename.split('.')[-1] elif report_name == 'uncompressed-man': title = 'Packages with uncompressed manpages' -- cgit v1.2.3-2-g168b 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/views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'devel/views.py') 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.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 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': -- cgit v1.2.3-2-g168b 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 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'devel/views.py') 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.core.mail import send_mail 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 new_user_form(request): '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: -- cgit v1.2.3-2-g168b From 978a5c61a5412eeed054307d3e2979324ffcb64a Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 8 Aug 2012 19:34:37 -0500 Subject: Add flag requests to developer last action calculation Signed-off-by: Dan McGee --- devel/views.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index f877bc84..ad1f4deb 100644 --- a/devel/views.py +++ b/devel/views.py @@ -29,7 +29,7 @@ from .models import UserProfile from main.models import Package, PackageFile, TodolistPkg from main.models import Arch, Repo from news.models import News -from packages.models import PackageRelation, Signoff, Depend +from packages.models import PackageRelation, Signoff, FlagRequest, Depend from packages.utils import get_signoff_groups from todolists.utils import get_annotated_todolists from .utils import get_annotated_maintainers, UserFinder @@ -103,6 +103,11 @@ def clock(request): latest_signoff = dict(Signoff.objects.filter( user__is_active=True).values_list('user').order_by( ).annotate(last_signoff=Max('created'))) + # The extra() bit ensures we can use our 'user_id IS NOT NULL' index + latest_flagreq = dict(FlagRequest.objects.filter( + user__is_active=True).extra( + where=['user_id IS NOT NULL']).values_list('user_id').order_by( + ).annotate(last_flagrequest=Max('created'))) latest_log = dict(LogEntry.objects.filter( user__is_active=True).values_list('user').order_by( ).annotate(last_log=Max('action_time'))) @@ -112,6 +117,7 @@ def clock(request): latest_news.get(dev.id, None), latest_package.get(dev.id, None), latest_signoff.get(dev.id, None), + latest_flagreq.get(dev.id, None), latest_log.get(dev.id, None), dev.last_login, ] -- cgit v1.2.3-2-g168b From e7e9b151643772f2bf9564d215ec8b90cd9b45c6 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 5 Sep 2012 08:44:33 -0500 Subject: Split devel forms out of devel/views.py Signed-off-by: Dan McGee --- devel/views.py | 106 ++++++--------------------------------------------------- 1 file changed, 10 insertions(+), 96 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index ad1f4deb..5406974e 100644 --- a/devel/views.py +++ b/devel/views.py @@ -1,31 +1,25 @@ from datetime import timedelta import operator import pytz -import random -from string import ascii_letters, digits import time -from django import forms from django.http import HttpResponseRedirect from django.contrib.auth.decorators import \ login_required, permission_required, user_passes_test from django.contrib.admin.models import LogEntry, ADDITION -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType -from django.contrib.sites.models import Site -from django.core.mail import send_mail 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, render -from django.template import loader, Context from django.template.defaultfilters import filesizeformat from django.views.decorators.cache import never_cache 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 .forms import ProfileForm, UserProfileForm, NewUserForm from main.models import Package, PackageFile, TodolistPkg from main.models import Arch, Repo from news.models import News @@ -89,6 +83,7 @@ def index(request): return render(request, 'devel/index.html', page_dict) + @login_required def clock(request): devs = User.objects.filter(is_active=True).order_by( @@ -141,30 +136,6 @@ def clock(request): response['Expires'] = http_date(expire_time) return response -class ProfileForm(forms.Form): - email = forms.EmailField(label='Private email (not shown publicly):', - help_text="Used for out-of-date notifications, etc.") - passwd1 = forms.CharField(label='New Password', required=False, - widget=forms.PasswordInput) - passwd2 = forms.CharField(label='Confirm Password', required=False, - widget=forms.PasswordInput) - - def clean(self): - if self.cleaned_data['passwd1'] != self.cleaned_data['passwd2']: - raise forms.ValidationError('Passwords do not match.') - return self.cleaned_data - -class UserProfileForm(forms.ModelForm): - def clean_pgp_key(self): - data = self.cleaned_data['pgp_key'] - # strip 0x prefix if provided; store uppercase - if data.startswith('0x'): - data = data[2:] - return data.upper() - - class Meta: - model = UserProfile - exclude = ('allowed_repos', 'user', 'latin_name') @login_required @never_cache @@ -177,8 +148,9 @@ def change_profile(request): request.user.email = form.cleaned_data['email'] if form.cleaned_data['passwd1']: request.user.set_password(form.cleaned_data['passwd1']) - request.user.save() - profile_form.save() + with transaction.commit_on_success(): + request.user.save() + profile_form.save() return HttpResponseRedirect('/devel/') else: form = ProfileForm(initial={'email': request.user.email}) @@ -186,6 +158,7 @@ def change_profile(request): return render(request, 'devel/profile.html', {'form': form, 'profile_form': profile_form}) + @login_required def report(request, report_name, username=None): title = 'Developer Report' @@ -309,65 +282,6 @@ def report(request, report_name, username=None): return render(request, 'devel/packages.html', context) -class NewUserForm(forms.ModelForm): - username = forms.CharField(max_length=30) - private_email = forms.EmailField() - first_name = forms.CharField(required=False) - last_name = forms.CharField(required=False) - groups = forms.ModelMultipleChoiceField(required=False, - queryset=Group.objects.all()) - - class Meta: - model = UserProfile - exclude = ('picture', 'user') - - def __init__(self, *args, **kwargs): - super(NewUserForm, self).__init__(*args, **kwargs) - # Hack ourself so certain fields appear first. self.fields is a - # SortedDict object where we can manipulate the keyOrder list. - order = self.fields.keyOrder - keys = ('username', 'private_email', 'first_name', 'last_name') - for key in reversed(keys): - order.remove(key) - order.insert(0, key) - - def clean_username(self): - username = self.cleaned_data['username'] - if User.objects.filter(username=username).exists(): - raise forms.ValidationError( - "A user with that username already exists.") - return username - - def save(self, commit=True): - profile = super(NewUserForm, self).save(False) - pwletters = ascii_letters + digits - password = ''.join([random.choice(pwletters) for _ in xrange(8)]) - user = User.objects.create_user(username=self.cleaned_data['username'], - email=self.cleaned_data['private_email'], password=password) - user.first_name = self.cleaned_data['first_name'] - user.last_name = self.cleaned_data['last_name'] - user.save() - # sucks that the MRM.add() method can't take a list directly... we have - # to resort to dirty * magic. - user.groups.add(*self.cleaned_data['groups']) - profile.user = user - if commit: - profile.save() - self.save_m2m() - - template = loader.get_template('devel/new_account.txt') - ctx = Context({ - 'site': Site.objects.get_current(), - 'user': user, - 'password': password, - }) - - send_mail("Your new archweb account", - template.render(ctx), - 'Arch Website Notification ', - [user.email], - fail_silently=False) - def log_addition(request, obj): """Cribbed from ModelAdmin.log_addition.""" LogEntry.objects.log_action( @@ -379,17 +293,16 @@ def log_addition(request, obj): change_message = "Added via Create New User form." ) + @permission_required('auth.add_user') @never_cache def new_user_form(request): if request.POST: form = NewUserForm(request.POST) if form.is_valid(): - @transaction.commit_on_success - def inner_save(): + with transaction.commit_on_success(): form.save() log_addition(request, form.instance.user) - inner_save() return HttpResponseRedirect('/admin/auth/user/%d/' % \ form.instance.user.id) else: @@ -406,6 +319,7 @@ def new_user_form(request): } return render(request, 'general_form.html', context) + @user_passes_test(lambda u: u.is_superuser) def admin_log(request, username=None): user = None -- cgit v1.2.3-2-g168b From a2034fc80d4e73816502537f8dfe864ab4ef8db3 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 15 Sep 2012 09:14:36 -0500 Subject: Add JS-based filtering to the developer reports This can use the todolist filtering functions we made more generic in a previous commit. Signed-off-by: Dan McGee --- devel/views.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 5406974e..23ff9f74 100644 --- a/devel/views.py +++ b/devel/views.py @@ -271,11 +271,15 @@ def report(request, report_name, username=None): else: raise Http404 + arches = set(pkg.arch for pkg in packages) + repos = set(pkg.repo for pkg in packages) context = { 'all_maintainers': maints, 'title': title, 'maintainer': user, 'packages': packages, + 'arches': sorted(arches), + 'repos': sorted(repos), 'column_names': names, 'column_attrs': attrs, } -- cgit v1.2.3-2-g168b From 05f309d7e57a66d9309abbf19b4328bad514b978 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 24 Sep 2012 21:13:02 -0500 Subject: Add a new column to developer repo stats Signed-off-by: Dan McGee --- devel/views.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 23ff9f74..ea85a901 100644 --- a/devel/views.py +++ b/devel/views.py @@ -55,6 +55,11 @@ def index(request): total_ct=Count('packages'), flagged_ct=Count('packages__flag_date')) repos = Repo.objects.all().annotate( total_ct=Count('packages'), flagged_ct=Count('packages__flag_date')) + # the join is huge unless we do this separately, so merge the result here + repo_maintainers = dict(Repo.objects.all().values_list('id').annotate( + Count('userprofile'))) + for repo in repos: + repo.maintainer_ct = repo_maintainers.get(repo.id, 0) maintainers = get_annotated_maintainers() -- cgit v1.2.3-2-g168b From a1c4d831c92cbb32cb8c34f95b8cd5eb541cdf00 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 24 Sep 2012 21:23:18 -0500 Subject: Exclude inactive developers in maintainer count Signed-off-by: Dan McGee --- devel/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index ea85a901..083665d9 100644 --- a/devel/views.py +++ b/devel/views.py @@ -56,7 +56,8 @@ def index(request): repos = Repo.objects.all().annotate( total_ct=Count('packages'), flagged_ct=Count('packages__flag_date')) # the join is huge unless we do this separately, so merge the result here - repo_maintainers = dict(Repo.objects.all().values_list('id').annotate( + repo_maintainers = dict(Repo.objects.order_by().filter( + userprofile__user__is_active=True).values_list('id').annotate( Count('userprofile'))) for repo in repos: repo.maintainer_ct = repo_maintainers.get(repo.id, 0) -- cgit v1.2.3-2-g168b From 9e9157d0a8cbf9ea076231e438fb30f58bff8e29 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 16 Nov 2012 16:37:31 -0600 Subject: Use python set comprehension syntax supported in 2.7 Signed-off-by: Dan McGee --- devel/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 083665d9..7d5947d1 100644 --- a/devel/views.py +++ b/devel/views.py @@ -277,8 +277,8 @@ def report(request, report_name, username=None): else: raise Http404 - arches = set(pkg.arch for pkg in packages) - repos = set(pkg.repo for pkg in packages) + arches = {pkg.arch for pkg in packages} + repos = {pkg.repo for pkg in packages} context = { 'all_maintainers': maints, 'title': title, -- cgit v1.2.3-2-g168b From c8ece67cec9c421ac0c711554edd34f022623b45 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 28 Dec 2012 00:27:20 -0600 Subject: Convert to using new todolist models everywhere This is a rather widespread set of changes converting usage to the new todo list and todo list package model recently introduced. The data migration is not included in this commit. After this commit, the old model should no longer be referenced anywhere. Signed-off-by: Dan McGee --- devel/views.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 7d5947d1..e01590a0 100644 --- a/devel/views.py +++ b/devel/views.py @@ -20,11 +20,12 @@ from django.utils.http import http_date from django.utils.timezone import now from .forms import ProfileForm, UserProfileForm, NewUserForm -from main.models import Package, PackageFile, TodolistPkg +from main.models import Package, PackageFile from main.models import Arch, Repo from news.models import News from packages.models import PackageRelation, Signoff, FlagRequest, Depend from packages.utils import get_signoff_groups +from todolists.models import TodolistPackage from todolists.utils import get_annotated_todolists from .utils import get_annotated_maintainers, UserFinder @@ -41,10 +42,11 @@ def index(request): flagged = Package.objects.normal().filter( flag_date__isnull=False, pkgbase__in=inner_q).order_by('pkgname') - todopkgs = TodolistPkg.objects.select_related( - 'pkg', 'pkg__arch', 'pkg__repo').filter(complete=False) - todopkgs = todopkgs.filter(pkg__pkgbase__in=inner_q).order_by( - 'list__name', 'pkg__pkgname') + todopkgs = TodolistPackage.objects.select_related( + 'todolist', 'pkg', 'arch', 'repo').exclude( + status=TodolistPackage.COMPLETE) + todopkgs = todopkgs.filter(pkgbase__in=inner_q).order_by( + 'todolist__name', 'pkgname') todolists = get_annotated_todolists(incomplete_only=True) -- cgit v1.2.3-2-g168b From 7952fe0ede3a5a68a64f05eccb180194394652f3 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 31 Dec 2012 11:31:35 -0600 Subject: Mark todolist packages as removed rather than deleting them This makes it easier to see the progression of a todolist and its contents easier since we are no longer losing the data. Signed-off-by: Dan McGee --- devel/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index e01590a0..90839847 100644 --- a/devel/views.py +++ b/devel/views.py @@ -44,7 +44,7 @@ def index(request): todopkgs = TodolistPackage.objects.select_related( 'todolist', 'pkg', 'arch', 'repo').exclude( - status=TodolistPackage.COMPLETE) + status=TodolistPackage.COMPLETE).filter(removed__isnull=True) todopkgs = todopkgs.filter(pkgbase__in=inner_q).order_by( 'todolist__name', 'pkgname') -- cgit v1.2.3-2-g168b From 508d06af810c787b2644331444279407ccfa27af Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 3 Feb 2013 15:08:21 -0600 Subject: Use DeveloperKey model on package page and reports This introduces the new model to the package page so subkey signings show up as attributed to the original developer. We also teach the mismatched signatures report to recognize all keys and subkeys of a given developer, cutting down on some of the bogus results. Signed-off-by: Dan McGee --- devel/views.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 90839847..ff1dec12 100644 --- a/devel/views.py +++ b/devel/views.py @@ -20,6 +20,7 @@ from django.utils.http import http_date from django.utils.timezone import now from .forms import ProfileForm, UserProfileForm, NewUserForm +from .models import DeveloperKey from main.models import Package, PackageFile from main.models import Arch, Repo from news.models import News @@ -27,7 +28,7 @@ from packages.models import PackageRelation, Signoff, FlagRequest, Depend from packages.utils import get_signoff_groups from todolists.models import TodolistPackage from todolists.utils import get_annotated_todolists -from .utils import get_annotated_maintainers, UserFinder +from .utils import get_annotated_maintainers @login_required @@ -262,18 +263,30 @@ def report(request, report_name, username=None): names = [ 'Signature Date', 'Signed By', 'Packager' ] attrs = [ 'sig_date', 'sig_by', 'packager' ] cutoff = timedelta(hours=24) - finder = UserFinder() filtered = [] - packages = packages.filter(pgp_signature__isnull=False) + packages = packages.select_related( + 'arch', 'repo', 'packager').filter(pgp_signature__isnull=False) + known_keys = DeveloperKey.objects.select_related( + 'owner').filter(owner__isnull=False) + known_keys = {dk.key: dk for dk in known_keys} for package in packages: - sig_date = package.signature.creation_time.replace(tzinfo=pytz.utc) + bad = False + sig = package.signature + sig_date = sig.creation_time.replace(tzinfo=pytz.utc) package.sig_date = sig_date.date() - key_id = package.signature.key_id - signer = finder.find_by_pgp_key(key_id) - package.sig_by = signer or key_id - if signer is None or signer.id != package.packager_id: - filtered.append(package) - elif sig_date > package.build_date + cutoff: + dev_key = known_keys.get(sig.key_id, None) + if dev_key: + package.sig_by = dev_key.owner + if dev_key.owner_id != package.packager_id: + bad = True + else: + package.sig_by = sig.key_id + bad = True + + if sig_date > package.build_date + cutoff: + bad = True + + if bad: filtered.append(package) packages = filtered else: -- cgit v1.2.3-2-g168b From dd0ecfaeaceb1e1b8a185800de35f0f6e741feac Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 26 Feb 2013 19:51:40 -0600 Subject: Use user.userprofile rather than user.get_profile() The get_profile() function is deprecated as of Django 1.5. Signed-off-by: Dan McGee --- devel/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index ff1dec12..61c1e568 100644 --- a/devel/views.py +++ b/devel/views.py @@ -152,7 +152,7 @@ def change_profile(request): if request.POST: form = ProfileForm(request.POST) profile_form = UserProfileForm(request.POST, request.FILES, - instance=request.user.get_profile()) + instance=request.user.userprofile) if form.is_valid() and profile_form.is_valid(): request.user.email = form.cleaned_data['email'] if form.cleaned_data['passwd1']: @@ -163,7 +163,7 @@ def change_profile(request): return HttpResponseRedirect('/devel/') else: form = ProfileForm(initial={'email': request.user.email}) - profile_form = UserProfileForm(instance=request.user.get_profile()) + profile_form = UserProfileForm(instance=request.user.userprofile) return render(request, 'devel/profile.html', {'form': form, 'profile_form': profile_form}) -- cgit v1.2.3-2-g168b From b7b24740640e24883cd17fd683e1d465fbb343f8 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 16 Apr 2013 22:12:01 -0500 Subject: Various minor code cleanups and fixes Most of these were suggested by PyCharm, and include everything from little syntax issues and other bad smells to dead or bad code. Signed-off-by: Dan McGee --- devel/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel/views.py') diff --git a/devel/views.py b/devel/views.py index 61c1e568..4258ea7f 100644 --- a/devel/views.py +++ b/devel/views.py @@ -34,7 +34,7 @@ from .utils import get_annotated_maintainers @login_required def index(request): '''the developer dashboard''' - if(request.user.is_authenticated()): + if request.user.is_authenticated(): inner_q = PackageRelation.objects.filter(user=request.user) else: inner_q = PackageRelation.objects.none() -- cgit v1.2.3-2-g168b