diff options
Diffstat (limited to 'devel/views.py')
-rw-r--r-- | devel/views.py | 295 |
1 files changed, 213 insertions, 82 deletions
diff --git a/devel/views.py b/devel/views.py index 577a00c4..66f6a965 100644 --- a/devel/views.py +++ b/devel/views.py @@ -1,114 +1,230 @@ -from django import forms +from datetime import timedelta +import operator +import time + from django.http import HttpResponseRedirect -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import \ + login_required, permission_required, user_passes_test +from django.contrib import admin +from django.contrib.admin.models import LogEntry, ADDITION from django.contrib.auth.models import User -from django.contrib.sites.models import Site -from django.core.mail import send_mail +from django.contrib.contenttypes.models import ContentType +from django.db import transaction +from django.db.models import Count, Max +from django.http import Http404 +from django.shortcuts import get_object_or_404, render 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 main.models import Package, Todolist, TodolistPkg +from .forms import ProfileForm, UserProfileForm, NewUserForm +from .models import UserProfile +from .reports import available_reports +from main.models import Package from main.models import Arch, Repo -from main.models import UserProfile -from packages.models import PackageRelation +from news.models import News +from packages.models import PackageRelation, Signoff, FlagRequest +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 -import random -from string import ascii_letters, digits @login_required -@never_cache def index(request): - '''the Developer dashboard''' - inner_q = PackageRelation.objects.filter(user=request.user).values('pkgbase') - flagged = Package.objects.select_related('arch', 'repo').filter(flag_date__isnull=False) - flagged = flagged.filter(pkgbase__in=inner_q).order_by('pkgname') + """The developer dashboard.""" + if request.user.is_authenticated(): + inner_q = PackageRelation.objects.filter(user=request.user) + else: + inner_q = PackageRelation.objects.none() + inner_q = inner_q.values('pkgbase') + + flagged = Package.objects.normal().filter( + flag_date__isnull=False, pkgbase__in=inner_q).order_by('pkgname') + + todopkgs = TodolistPackage.objects.select_related( + 'todolist', 'pkg', 'arch', 'repo').exclude( + status=TodolistPackage.COMPLETE).filter(removed__isnull=True) + todopkgs = todopkgs.filter(pkgbase__in=inner_q).order_by( + 'todolist__name', 'pkgname') + + todolists = get_annotated_todolists(incomplete_only=True) + + signoffs = sorted(get_signoff_groups(user=request.user), + key=operator.attrgetter('pkgbase')) + + page_dict = { + 'todos': todolists, + 'flagged': flagged, + 'todopkgs': todopkgs, + 'signoffs': signoffs, + 'reports': available_reports(), + } - 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') + return render(request, 'devel/index.html', page_dict) + + +@login_required +def stats(request): + """The second half of the dev dashboard.""" + 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')) + # the join is huge unless we do this separately, so merge the result here + 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) maintainers = get_annotated_maintainers() + maintained = PackageRelation.objects.filter( + type=PackageRelation.MAINTAINER).values('pkgbase') + total_orphans = Package.objects.exclude(pkgbase__in=maintained).count() + total_flagged_orphans = Package.objects.filter( + flag_date__isnull=False).exclude(pkgbase__in=maintained).count() + total_updated = Package.objects.filter(packager__isnull=True).count() + orphan = { + 'package_count': total_orphans, + 'flagged_count': total_flagged_orphans, + 'updated_count': total_updated, + } + page_dict = { - 'todos': Todolist.objects.incomplete().order_by('-date_added'), - 'repos': Repo.objects.all(), - 'arches': Arch.objects.all(), + 'arches': arches, + 'repos': repos, 'maintainers': maintainers, - 'flagged' : flagged, - 'todopkgs' : todopkgs, - } + 'orphan': orphan, + } + + return render(request, 'devel/stats.html', page_dict) - return direct_to_template(request, 'devel/index.html', page_dict) @login_required -def change_notify(request): - maint = User.objects.get(username=request.user.username) - notify = request.POST.get('notify', 'no') - prof = maint.get_profile() - prof.notify = (notify == 'yes') - prof.save() - return HttpResponseRedirect('/devel/') - -class ProfileForm(forms.Form): - email = forms.EmailField(label='E-mail Address') - 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 +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'))) + # 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'))) + + for dev in devs: + dates = [ + 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, + ] + dates = [d for d in dates if d is not None] + if dates: + dev.last_action = max(dates) + else: + dev.last_action = None + + current_time = now() + page_dict = { + 'developers': devs, + 'utc_now': current_time, + } + + 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) + expire_time = time.mktime(expire_time.timetuple()) + response['Expires'] = http_date(expire_time) + return response + @login_required @never_cache def change_profile(request): + profile, _ = UserProfile.objects.get_or_create(user=request.user) if request.POST: form = ProfileForm(request.POST) - if form.is_valid(): + profile_form = UserProfileForm(request.POST, request.FILES, + instance=profile) + if form.is_valid() and profile_form.is_valid(): request.user.email = form.cleaned_data['email'] if form.cleaned_data['passwd1']: request.user.set_password(form.cleaned_data['passwd1']) - request.user.save() + with transaction.atomic(): + request.user.save() + profile_form.save() return HttpResponseRedirect('/devel/') else: form = ProfileForm(initial={'email': request.user.email}) - return direct_to_template(request, 'devel/profile.html', {'form': form}) - -class NewUserForm(forms.ModelForm): - class Meta: - model = UserProfile - exclude = ('picture', 'user') - username = forms.CharField(max_length=30) - email = forms.EmailField() - first_name = forms.CharField(required=False) - last_name = forms.CharField(required=False) - - def save(self): - profile = forms.ModelForm.save(self, False) - pwletters = ascii_letters + digits - pw = ''.join([random.choice(pwletters) for i in xrange(8)]) - user = User.objects.create_user(username=self.cleaned_data['username'], - email=self.cleaned_data['email'], password=pw) - user.first_name = self.cleaned_data['first_name'] - user.last_name = self.cleaned_data['last_name'] - user.save() - profile.user = user - profile.save() - domain = Site.objects.get_current().domain - - send_mail("Your new archweb account", - """You can now log into: -https://%s/login/ -with these login details: -Username: %s -Password: %s""" % (domain, user.username, pw), - 'Arch Website Notification <nobody@archlinux.org>', - [user.email], - fail_silently=False) + profile_form = UserProfileForm(instance=profile) + return render(request, 'devel/profile.html', + {'form': form, 'profile_form': profile_form}) + + +@login_required +def report(request, report_name, username=None): + available = {report.slug: report for report in available_reports()} + report = available.get(report_name, None) + if report is None: + raise Http404 + + packages = Package.objects.normal() + user = None + if username: + user = get_object_or_404(User, username=username, is_active=True) + maintained = PackageRelation.objects.filter(user=user, + type=PackageRelation.MAINTAINER).values('pkgbase') + packages = packages.filter(pkgbase__in=maintained) + + maints = User.objects.filter(id__in=PackageRelation.objects.filter( + type=PackageRelation.MAINTAINER).values('user')) + + packages = report.packages(packages, username) + arches = {pkg.arch for pkg in packages} + repos = {pkg.repo for pkg in packages} + context = { + 'all_maintainers': maints, + 'title': report.description, + 'report': report, + 'maintainer': user, + 'packages': packages, + 'arches': sorted(arches), + 'repos': sorted(repos), + 'column_names': report.names, + 'column_attrs': report.attrs, + } + return render(request, 'devel/packages.html', context) + + +def log_addition(request, obj): + """Cribbed from ModelAdmin.log_addition.""" + LogEntry.objects.log_action( + user_id = request.user.pk, + content_type_id = ContentType.objects.get_for_model(obj).pk, + object_id = obj.pk, + object_repr = force_unicode(obj), + action_flag = ADDITION, + change_message = "Added via Create New User form." + ) + @permission_required('auth.add_user') @never_cache @@ -116,7 +232,9 @@ def new_user_form(request): if request.POST: form = NewUserForm(request.POST) if form.is_valid(): - form.save() + with transaction.atomic(): + form.save() + log_addition(request, form.instance.user) return HttpResponseRedirect('/admin/auth/user/%d/' % \ form.instance.user.id) else: @@ -131,6 +249,19 @@ 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): + user = None + if username: + user = get_object_or_404(User, username=username) + context = { + 'title': "Admin Action Log", + 'log_user': user, + } + context.update(admin.site.each_context()) + return render(request, 'devel/admin_log.html', context) # vim: set ts=4 sw=4 et: |