From 49ac7efd683152e4936f8013bb7a001470260034 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 3 Nov 2011 17:18:55 -0500 Subject: Package signoff email report, initial revision Signed-off-by: Dan McGee --- packages/management/commands/signoff_report.py | 110 +++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 packages/management/commands/signoff_report.py (limited to 'packages/management/commands/signoff_report.py') diff --git a/packages/management/commands/signoff_report.py b/packages/management/commands/signoff_report.py new file mode 100644 index 00000000..17e58f39 --- /dev/null +++ b/packages/management/commands/signoff_report.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +""" +signoff_report command + +Send an email summarizing the state of outstanding signoffs for the given +repository. + +Usage: ./manage.py signoff_report +""" + +from django.core.urlresolvers import reverse +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.db.models import Count +from django.template import loader, Context + +from collections import namedtuple +from datetime import datetime, timedelta +import logging +from operator import attrgetter +import sys + +from main.models import Package, Repo +from packages.models import Signoff +from packages.utils import get_signoff_groups + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s -> %(levelname)s: %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + stream=sys.stderr) +logger = logging.getLogger() + +class Command(BaseCommand): + args = " " + help = "Send a signoff report for the given repository." + + def handle(self, *args, **options): + v = int(options.get('verbosity', None)) + if v == 0: + logger.level = logging.ERROR + elif v == 1: + logger.level = logging.INFO + elif v == 2: + logger.level = logging.DEBUG + + if len(args) != 2: + raise CommandError("email and repository must be provided") + + return generate_report(args[0], args[1]) + +def generate_report(email, repo_name): + repo = Repo.objects.get(name__iexact=repo_name) + # Collect all existing signoffs for these packages + signoff_groups = sorted(get_signoff_groups([repo]), + key=attrgetter('target_repo', 'arch', 'pkgbase')) + complete = [] + incomplete = [] + new = [] + old = [] + + new_hours = 24 + old_days = 14 + now = datetime.utcnow() + new_cutoff = now - timedelta(hours=new_hours) + old_cutoff = now - timedelta(days=old_days) + + for group in signoff_groups: + if group.approved(): + complete.append(group) + else: + incomplete.append(group) + if group.package.last_update > new_cutoff: + new.append(group) + if group.package.last_update < old_cutoff: + old.append(group) + + old.sort(key=attrgetter('last_update')) + + proto = 'https' + domain = Site.objects.get_current().domain + signoffs_url = '%s://%s%s' % (proto, domain, reverse('package-signoffs')) + + # and the fun bit + Leader = namedtuple('Leader', ['user', 'count']) + leaders = Signoff.objects.filter(created__gt=new_cutoff, + revoked__isnull=True).values_list('user').annotate( + signoff_count=Count('pk')).order_by('-signoff_count')[:5] + users = User.objects.in_bulk([l[0] for l in leaders]) + leaders = (Leader(users[l[0]], l[1]) for l in leaders) + + subject = 'Signoff report for [%s]' % repo.name.lower() + t = loader.get_template('packages/signoff_report.txt') + c = Context({ + 'repo': repo, + 'signoffs_url': signoffs_url, + 'incomplete': incomplete, + 'complete': complete, + 'new': new, + 'new_hours': new_hours, + 'old': old, + 'old_days': old_days, + 'leaders': leaders, + }) + from_addr = 'Arch Website Notification ' + #send_mail(subject, t.render(c), from_addr, email) + print t.render(c) + +# vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 9a5410ba4b622b68306de53abfa28b5a49e30107 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 3 Nov 2011 17:33:00 -0500 Subject: Make signoff_report command send email Signed-off-by: Dan McGee --- packages/management/commands/signoff_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/management/commands/signoff_report.py') diff --git a/packages/management/commands/signoff_report.py b/packages/management/commands/signoff_report.py index 17e58f39..02f3d985 100644 --- a/packages/management/commands/signoff_report.py +++ b/packages/management/commands/signoff_report.py @@ -8,6 +8,7 @@ repository. Usage: ./manage.py signoff_report """ +from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.core.management.base import BaseCommand, CommandError from django.contrib.auth.models import User @@ -104,7 +105,6 @@ def generate_report(email, repo_name): 'leaders': leaders, }) from_addr = 'Arch Website Notification ' - #send_mail(subject, t.render(c), from_addr, email) - print t.render(c) + send_mail(subject, t.render(c), from_addr, [email]) # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 5f2c3bf98baabf919681525e600639643aa2c119 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 3 Nov 2011 20:39:59 -0500 Subject: Signoffs changes and improvements * Better signoff report with more detail * Show signoff specification in signoffs view * Honor disabled/bad flags and display in approval column * Various other small bugfixes and tweaks Signed-off-by: Dan McGee --- packages/management/commands/signoff_report.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'packages/management/commands/signoff_report.py') diff --git a/packages/management/commands/signoff_report.py b/packages/management/commands/signoff_report.py index 02f3d985..3431dada 100644 --- a/packages/management/commands/signoff_report.py +++ b/packages/management/commands/signoff_report.py @@ -56,6 +56,8 @@ def generate_report(email, repo_name): # Collect all existing signoffs for these packages signoff_groups = sorted(get_signoff_groups([repo]), key=attrgetter('target_repo', 'arch', 'pkgbase')) + disabled = [] + bad = [] complete = [] incomplete = [] new = [] @@ -68,10 +70,16 @@ def generate_report(email, repo_name): old_cutoff = now - timedelta(days=old_days) for group in signoff_groups: - if group.approved(): + spec = group.specification + if spec.known_bad: + bad.append(group) + elif not spec.enabled: + disabled.append(group) + elif group.approved(): complete.append(group) else: incomplete.append(group) + if group.package.last_update > new_cutoff: new.append(group) if group.package.last_update < old_cutoff: @@ -96,6 +104,9 @@ def generate_report(email, repo_name): c = Context({ 'repo': repo, 'signoffs_url': signoffs_url, + 'disabled': disabled, + 'bad': bad, + 'all': signoff_groups, 'incomplete': incomplete, 'complete': complete, 'new': new, -- cgit v1.2.3-2-g168b From e565fde00f56c7a01ff55a204a0a56d3ce4bf8b4 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 4 Nov 2011 11:31:35 -0500 Subject: Signoff email: prune empty content Don't send the email at all if there are no packages even in the repository, and don't print empty sections. Signed-off-by: Dan McGee --- packages/management/commands/signoff_report.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'packages/management/commands/signoff_report.py') diff --git a/packages/management/commands/signoff_report.py b/packages/management/commands/signoff_report.py index 3431dada..3357bc1e 100644 --- a/packages/management/commands/signoff_report.py +++ b/packages/management/commands/signoff_report.py @@ -69,6 +69,10 @@ def generate_report(email, repo_name): new_cutoff = now - timedelta(hours=new_hours) old_cutoff = now - timedelta(days=old_days) + if len(signoff_groups) == 0: + # no need to send an email at all + return + for group in signoff_groups: spec = group.specification if spec.known_bad: -- cgit v1.2.3-2-g168b From 12408702eaf89ea338670ba808da9ef49e35c562 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 14 Nov 2011 12:19:17 -0600 Subject: Allow population of signoff specs with SVN commit messages This pulls them from the latest SVN commit on trunk. We don't have a failproof method of getting the exact right commit, but this should be close if it is run on a regular basis via cron (aka hourly). Note that running locally, I needed the development version of South to get the migration included here to apply because of information_schema changes in the current version of MySQL. Signed-off-by: Dan McGee --- packages/management/commands/signoff_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/management/commands/signoff_report.py') diff --git a/packages/management/commands/signoff_report.py b/packages/management/commands/signoff_report.py index 3357bc1e..3b67f518 100644 --- a/packages/management/commands/signoff_report.py +++ b/packages/management/commands/signoff_report.py @@ -22,7 +22,7 @@ import logging from operator import attrgetter import sys -from main.models import Package, Repo +from main.models import Repo from packages.models import Signoff from packages.utils import get_signoff_groups -- cgit v1.2.3-2-g168b