268317dd33
Signed-off-by: Dan McGee <dan@archlinux.org>
265 lines
9.4 KiB
Python
265 lines
9.4 KiB
Python
from datetime import timedelta
|
|
import operator
|
|
import time
|
|
|
|
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
|
|
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.utils.encoding import force_unicode
|
|
from django.utils.http import http_date
|
|
from django.utils.timezone import now
|
|
|
|
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 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
|
|
|
|
|
|
@login_required
|
|
def index(request):
|
|
"""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(),
|
|
}
|
|
|
|
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 = {
|
|
'arches': arches,
|
|
'repos': repos,
|
|
'maintainers': maintainers,
|
|
'orphan': orphan,
|
|
}
|
|
|
|
return render(request, 'devel/stats.html', page_dict)
|
|
|
|
|
|
@login_required
|
|
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)
|
|
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'])
|
|
with transaction.atomic():
|
|
request.user.save()
|
|
profile_form.save()
|
|
return HttpResponseRedirect('/devel/')
|
|
else:
|
|
form = ProfileForm(initial={'email': request.user.email})
|
|
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'))
|
|
|
|
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': report.packages(packages, username),
|
|
'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
|
|
def new_user_form(request):
|
|
if request.POST:
|
|
form = NewUserForm(request.POST)
|
|
if form.is_valid():
|
|
with transaction.atomic():
|
|
form.save()
|
|
log_addition(request, form.instance.user)
|
|
return HttpResponseRedirect('/admin/auth/user/%d/' % \
|
|
form.instance.user.id)
|
|
else:
|
|
form = NewUserForm()
|
|
|
|
context = {
|
|
'description': '''A new user will be created with the
|
|
following properties in their profile. A random password will be
|
|
generated and the user will be e-mailed with their account details
|
|
n plaintext.''',
|
|
'form': form,
|
|
'title': 'Create User',
|
|
'submit_text': 'Create User'
|
|
}
|
|
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,
|
|
}
|
|
return render(request, 'devel/admin_log.html', context)
|
|
|
|
# vim: set ts=4 sw=4 et:
|