X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=apps%2Fpatchwork%2Futils.py;h=5cb45e8eeaa41191955ead4a9fea3ca434dbb81d;hb=f1e5f6a2c9d737f12290f5bd5a934b74c362616f;hp=05e66e26eb0c72bd9b7e0db0378fc9e5d012a1cb;hpb=f6113ee4e29241c03560ba0854a885c67ea1903a;p=patchwork diff --git a/apps/patchwork/utils.py b/apps/patchwork/utils.py index 05e66e2..5cb45e8 100644 --- a/apps/patchwork/utils.py +++ b/apps/patchwork/utils.py @@ -18,10 +18,17 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -from patchwork.forms import MultiplePatchForm -from patchwork.models import Bundle, Project, State, UserProfile +import itertools +import datetime +from django.shortcuts import get_object_or_404 +from django.template.loader import render_to_string +from django.contrib.sites.models import Site from django.conf import settings -from django.shortcuts import render_to_response, get_object_or_404 +from django.core.mail import EmailMessage +from django.db.models import Max +from patchwork.forms import MultiplePatchForm +from patchwork.models import Bundle, Project, BundlePatch, UserProfile, \ + PatchChangeNotification, EmailOptout def get_patch_ids(d, prefix = 'patch_id'): ids = [] @@ -46,13 +53,17 @@ class Order(object): 'submitter': 'submitter__name', 'delegate': 'delegate__username', } - default_order = 'date' + default_order = ('date', True) - def __init__(self, str = None): + def __init__(self, str = None, editable = False): self.reversed = False + self.editable = editable + + if self.editable: + return if str is None or str == '': - self.order = self.default_order + (self.order, self.reversed) = self.default_order return reversed = False @@ -61,7 +72,7 @@ class Order(object): reversed = True if str not in self.order_map.keys(): - self.order = self.default_order + (self.order, self.reversed) = self.default_order return self.order = str @@ -93,105 +104,102 @@ def set_bundle(user, project, action, data, patches, context): # set up the bundle bundle = None if action == 'create': + bundle_name = data['bundle_name'].strip() + if not bundle_name: + return ['No bundle name was specified'] + bundle = Bundle(owner = user, project = project, - name = data['bundle_name']) + name = bundle_name) bundle.save() - str = 'added to new bundle "%s"' % bundle.name - auth_required = False + context.add_message("Bundle %s created" % bundle.name) elif action =='add': bundle = get_object_or_404(Bundle, id = data['bundle_id']) - str = 'added to bundle "%s"' % bundle.name - auth_required = False elif action =='remove': bundle = get_object_or_404(Bundle, id = data['removed_bundle_id']) - str = 'removed from bundle "%s"' % bundle.name - auth_required = False if not bundle: return ['no such bundle'] for patch in patches: if action == 'create' or action == 'add': - bundle.patches.add(patch) + bundlepatch_count = BundlePatch.objects.filter(bundle = bundle, + patch = patch).count() + if bundlepatch_count == 0: + bundle.append_patch(patch) + context.add_message("Patch '%s' added to bundle %s" % \ + (patch.name, bundle.name)) + else: + context.add_message("Patch '%s' already in bundle %s" % \ + (patch.name, bundle.name)) elif action == 'remove': - bundle.patches.remove(patch) - - if len(patches) > 0: - if len(patches) == 1: - str = 'patch ' + str - else: - str = 'patches ' + str - context.add_message(str) + try: + bp = BundlePatch.objects.get(bundle = bundle, patch = patch) + bp.delete() + context.add_message("Patch '%s' removed from bundle %s\n" % \ + (patch.name, bundle.name)) + except Exception: + pass bundle.save() return [] +def send_notifications(): + date_limit = datetime.datetime.now() - \ + datetime.timedelta(minutes = + settings.NOTIFICATION_DELAY_MINUTES) -def set_patches(user, project, action, data, patches, context): - errors = [] - form = MultiplePatchForm(project = project, data = data) + # This gets funky: we want to filter out any notifications that should + # be grouped with other notifications that aren't ready to go out yet. To + # do that, we join back onto PatchChangeNotification (PCN -> Patch -> + # Person -> Patch -> max(PCN.last_modified)), filtering out any maxima + # that are with the date_limit. + qs = PatchChangeNotification.objects \ + .annotate(m = Max('patch__submitter__patch__patchchangenotification' + '__last_modified')) \ + .filter(m__lt = date_limit) - try: - project = Project.objects.get(id = data['project']) - except: - errors = ['No such project'] - return (errors, form) + groups = itertools.groupby(qs.order_by('patch__submitter'), + lambda n: n.patch.submitter) - str = '' + errors = [] - # this may be a bundle action, which doesn't modify a patch. in this - # case, don't require a valid form, or patch editing permissions - if action in bundle_actions: - errors = set_bundle(user, project, action, data, patches, context) - return (errors, form) + for (recipient, notifications) in groups: + notifications = list(notifications) - if not form.is_valid(): - errors = ['The submitted form data was invalid'] - return (errors, form) + def delete_notifications(): + PatchChangeNotification.objects.filter( + pk__in = notifications).delete() - for patch in patches: - if not patch.is_editable(user): - errors.append('You don\'t have permissions to edit the ' + \ - 'patch "%s"' \ - % patch.name) + if EmailOptout.is_optout(recipient.email): + delete_notifications() continue - if action == 'update': - form.save(patch) - str = 'updated' - - elif action == 'ack': - pass - - elif action == 'archive': - patch.archived = True - patch.save() - str = 'archived' - - elif action == 'unarchive': - patch.archived = True - patch.save() - str = 'un-archived' - - elif action == 'delete': - patch.delete() - str = 'un-archived' - - - if len(patches) > 0: - if len(patches) == 1: - str = 'patch ' + str - else: - str = 'patches ' + str - context.add_message(str) - - return (errors, form) + context = { + 'site': Site.objects.get_current(), + 'person': recipient, + 'notifications': notifications, + } + subject = render_to_string( + 'patchwork/patch-change-notification-subject.text', + context).strip() + content = render_to_string('patchwork/patch-change-notification.mail', + context) + + message = EmailMessage(subject = subject, body = content, + from_email = settings.NOTIFICATION_FROM_EMAIL, + to = [recipient.email], + headers = {'Precedence': 'bulk'}) + + try: + message.send() + except ex: + errors.append((recipient, ex)) + continue -def userprofile_register_callback(user): - profile = UserProfile(user = user) - profile.save() + delete_notifications() + return errors