summaryrefslogtreecommitdiff
path: root/devel/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'devel/views.py')
-rw-r--r--devel/views.py295
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: