Пример #1
0
 def crop_and_upload_image_to_popit(self, image_filename, crop_bounds,
                                    moderator_why_allowed, make_primary):
     original = Image.open(image_filename)
     # Some uploaded images are CYMK, which gives you an error when
     # you try to write them as PNG, so convert to RGB:
     original = original.convert('RGB')
     cropped = original.crop(crop_bounds)
     ntf = NamedTemporaryFile(delete=False)
     cropped.save(ntf.name, 'PNG')
     # Upload the image to PopIt...
     person_id = self.queued_image.popit_person_id
     person = PopItPerson.create_from_popit(self.api, person_id)
     image_upload_url = '{base}persons/{person_id}/image'.format(
         base=get_base_url(), person_id=person_id)
     data = {
         'md5sum': get_file_md5sum(ntf.name),
         'user_why_allowed': self.queued_image.why_allowed,
         'user_justification_for_use':
         self.queued_image.justification_for_use,
         'moderator_why_allowed': moderator_why_allowed,
         'mime_type': 'image/png',
         'notes': _('Approved from photo moderation queue'),
         'uploaded_by_user': self.queued_image.user.username,
         'created': None,
     }
     if make_primary:
         data['index'] = 'first'
     with open(ntf.name) as f:
         requests.post(image_upload_url,
                       data=data,
                       files={'image': f.read()},
                       headers={'APIKey': self.api.api_key})
     person.invalidate_cache_entries()
     # Remove the cropped temporary image file:
     os.remove(ntf.name)
    def handle(self, *args, **options):
        self.verbosity = int(options.get('verbosity', 1))
        api = create_popit_api_object()
        if len(args) != 2:
            raise CommandError("You must provide all two arguments")

        person_id, other_name = args

        person = PopItPerson.create_from_popit(api, person_id)

        person.other_names.append(
            {
                'name': other_name,
                'note': options['note'],
                'start_date': options['start_date'],
                'end_date': options['end_date']
            }
        )

        person.save_to_popit(api)
        person.invalidate_cache_entries()

        # FIXME: this should create a new version in the versions
        # array too, otherwise you manually have to edit on YourNextMP
        # too to create a new version with a change message.

        print "Successfully updated {0}".format(person_id)
Пример #3
0
def upload_photo(request, popit_person_id):
    if request.method == 'POST':
        form = UploadPersonPhotoForm(request.POST, request.FILES)
        if form.is_valid():
            # Make sure that we save the user that made the upload
            queued_image = form.save(commit=False)
            queued_image.user = request.user
            queued_image.save()
            # Record that action:
            LoggedAction.objects.create(
                user=request.user,
                action_type='photo-upload',
                ip_address=get_client_ip(request),
                popit_person_new_version='',
                popit_person_id=popit_person_id,
                source=form.cleaned_data['justification_for_use'],
            )
            return HttpResponseRedirect(
                reverse('photo-upload-success',
                        kwargs={
                            'popit_person_id':
                            form.cleaned_data['popit_person_id']
                        }))
    else:
        form = UploadPersonPhotoForm(
            initial={'popit_person_id': popit_person_id})
    api = create_popit_api_object()
    return render(
        request, 'moderation_queue/photo-upload-new.html', {
            'form': form,
            'person': PopItPerson.create_from_popit(api, popit_person_id)
        })
    def handle(self, *args, **options):
        self.verbosity = int(options.get('verbosity', 1))
        api = create_popit_api_object()
        if len(args) != 2:
            raise CommandError("You must provide all two arguments")

        person_id, other_name = args

        person = PopItPerson.create_from_popit(api, person_id)

        person.other_names.append(
            {
                'name': other_name,
                'note': options['note'],
                'start_date': options['start_date'],
                'end_date': options['end_date']
            }
        )

        person.save_to_popit(api)
        person.invalidate_cache_entries()

        # FIXME: this should create a new version in the versions
        # array too, otherwise you manually have to edit on the
        # YourNextRepresentative site too to create a new version with
        # a change message.

        print "Successfully updated {0}".format(person_id)
Пример #5
0
 def get_context_data(self, **kwargs):
     context = super(PhotoUploadSuccess, self).get_context_data(**kwargs)
     context['person'] = PopItPerson.create_from_popit(
         self.api,
         kwargs['popit_person_id']
     )
     return context
    def handle(self, *args, **options):
        self.verbosity = int(options.get('verbosity', 1))
        api = create_popit_api_object()
        if len(args) != 3:
            raise CommandError("You must provide all three arguments")

        person_id, scheme, identifier = args

        person = PopItPerson.create_from_popit(api, person_id)

        person.identifiers.append(
            {
                'scheme': scheme,
                'identifier': identifier,
            }
        )

        person.save_to_popit(api)
        person.invalidate_cache_entries()

        # FIXME: this should create a new version in the versions
        # array too, otherwise you manually have to edit on YourNextMP
        # too to create a new version with a change message.

        print "Successfully updated {0}".format(person_id)
Пример #7
0
 def get_context_data(self, **kwargs):
     context = super(PhotoReview, self).get_context_data(**kwargs)
     self.queued_image = get_object_or_404(
         QueuedImage,
         pk=kwargs['queued_image_id']
     )
     context['queued_image'] = self.queued_image
     person = PopItPerson.create_from_popit(
         self.api,
         self.queued_image.popit_person_id,
     )
     context['has_crop_bounds'] = int(self.queued_image.has_crop_bounds)
     max_x = self.queued_image.image.width - 1
     max_y = self.queued_image.image.height - 1
     guessed_crop_bounds = [
         value_if_none(self.queued_image.crop_min_x, 0),
         value_if_none(self.queued_image.crop_min_y, 0),
         value_if_none(self.queued_image.crop_max_x, max_x),
         value_if_none(self.queued_image.crop_max_y, max_y),
     ]
     context['form'] = PhotoReviewForm(
         initial = {
             'queued_image_id': self.queued_image.id,
             'decision': self.queued_image.decision,
             'x_min': guessed_crop_bounds[0],
             'y_min': guessed_crop_bounds[1],
             'x_max': guessed_crop_bounds[2],
             'y_max': guessed_crop_bounds[3],
             'moderator_why_allowed': self.queued_image.why_allowed,
             'make_primary': True,
         }
     )
     context['guessed_crop_bounds'] = guessed_crop_bounds
     context['why_allowed'] = self.queued_image.why_allowed
     context['moderator_why_allowed'] = self.queued_image.why_allowed
     # There are often source links supplied in the justification,
     # and it's convenient to be able to follow them. However, make
     # sure that any maliciously added HTML tags have been stripped
     # before linkifying any URLs:
     context['justification_for_use'] = \
         bleach.linkify(
             bleach.clean(
                 self.queued_image.justification_for_use,
                 tags=[],
                 strip=True
             )
         )
     context['google_image_search_url'] = self.get_google_image_search_url(
         person
     )
     context['google_reverse_image_search_url'] = \
         self.get_google_reverse_image_search_url(
             self.queued_image.image.url
     )
     context['person'] = person
     return context
Пример #8
0
 def handle(self, *args, **kwargs):
     if len(args) < 1:
         raise CommandError("You must provide one or more PopIt person ID")
     for person_id in args:
         person = PopItPerson.create_from_popit(
             create_popit_api_object(), person_id
         )
         person.delete_memberships()
         self.create_party_memberships(person_id, person.popit_data)
         self.create_candidate_list_memberships(person_id, person.popit_data)
Пример #9
0
 def get_context_data(self, **kwargs):
     context = super(PhotoReview, self).get_context_data(**kwargs)
     self.queued_image = get_object_or_404(QueuedImage,
                                           pk=kwargs['queued_image_id'])
     context['queued_image'] = self.queued_image
     person = PopItPerson.create_from_popit(
         self.api,
         self.queued_image.popit_person_id,
     )
     context['has_crop_bounds'] = int(self.queued_image.has_crop_bounds)
     max_x = self.queued_image.image.width - 1
     max_y = self.queued_image.image.height - 1
     guessed_crop_bounds = [
         value_if_none(self.queued_image.crop_min_x, 0),
         value_if_none(self.queued_image.crop_min_y, 0),
         value_if_none(self.queued_image.crop_max_x, max_x),
         value_if_none(self.queued_image.crop_max_y, max_y),
     ]
     context['form'] = PhotoReviewForm(
         initial={
             'queued_image_id': self.queued_image.id,
             'decision': self.queued_image.decision,
             'x_min': guessed_crop_bounds[0],
             'y_min': guessed_crop_bounds[1],
             'x_max': guessed_crop_bounds[2],
             'y_max': guessed_crop_bounds[3],
             'moderator_why_allowed': self.queued_image.why_allowed,
             'make_primary': True,
         })
     context['guessed_crop_bounds'] = guessed_crop_bounds
     context['why_allowed'] = self.queued_image.why_allowed
     context['moderator_why_allowed'] = self.queued_image.why_allowed
     # There are often source links supplied in the justification,
     # and it's convenient to be able to follow them. However, make
     # sure that any maliciously added HTML tags have been stripped
     # before linkifying any URLs:
     context['justification_for_use'] = \
         bleach.linkify(
             bleach.clean(
                 self.queued_image.justification_for_use,
                 tags=[],
                 strip=True
             )
         )
     context['google_image_search_url'] = self.get_google_image_search_url(
         person)
     context['google_reverse_image_search_url'] = \
         self.get_google_reverse_image_search_url(
             self.queued_image.image.url
     )
     context['person'] = person
     return context
    def handle(self, *args, **options):
        from candidates.models import PopItPerson
        from candidates.popit import create_popit_api_object

        self.verbosity = int(options.get('verbosity', 1))
        api = create_popit_api_object()
        if len(args) != 1:
            raise CommandError("You must provide a person.js URL")
        person_js_url = args[0]
        people_data = requests.get(person_js_url).json()
        for person_data in people_data['persons']:
            twfy_person = PopItPerson.create_from_dict(person_data)
            ynmp_id = twfy_person.get_identifier('yournextmp')
            if not ynmp_id:
                continue
            parlparse_id = twfy_person.id
            ynmp_person = PopItPerson.create_from_popit(api, ynmp_id)
            existing_parlparse_id = ynmp_person.get_identifier('uk.org.publicwhip')
            if existing_parlparse_id:
                if existing_parlparse_id == parlparse_id:
                    # That's fine, there's already the right parlparse ID
                    pass
                else:
                    # Otherwise there's a mismatch, which needs investigation
                    msg = "Warning: parlparse ID mismatch between YNMP {0} "
                    msg += "and TWFY {1} for YNMP person {2}\n"
                    self.stderr.write(
                        msg.format(
                            existing_parlparse_id,
                            parlparse_id,
                            ynmp_id,
                        )
                    )
                continue
            msg = "Updating the YourNextMP person {0} with parlparse_id {1}\n"
            self.stdout.write(msg.format(ynmp_id, parlparse_id))
            ynmp_person.set_identifier(
                'uk.org.publicwhip',
                parlparse_id,
            )
            change_metadata = get_change_metadata(
                None, "Fetched a new parlparse ID"
            )
            ynmp_person.record_version(change_metadata)
            ynmp_person.save_to_popit(api)
            ynmp_person.invalidate_cache_entries()
    def handle(self, *args, **options):
        from candidates.models import PopItPerson
        from candidates.popit import create_popit_api_object

        self.verbosity = int(options.get('verbosity', 1))
        api = create_popit_api_object()
        if len(args) != 1:
            raise CommandError("You must provide a person.js URL")
        person_js_url = args[0]
        people_data = requests.get(person_js_url).json()
        for person_data in people_data['persons']:
            twfy_person = PopItPerson.create_from_dict(person_data)
            ynmp_id = twfy_person.get_identifier('yournextmp')
            if not ynmp_id:
                continue
            parlparse_id = twfy_person.id
            ynmp_person = PopItPerson.create_from_popit(api, ynmp_id)
            existing_parlparse_id = ynmp_person.get_identifier(
                'uk.org.publicwhip')
            if existing_parlparse_id:
                if existing_parlparse_id == parlparse_id:
                    # That's fine, there's already the right parlparse ID
                    pass
                else:
                    # Otherwise there's a mismatch, which needs investigation
                    msg = "Warning: parlparse ID mismatch between YNMP {0} "
                    msg += "and TWFY {1} for YNMP person {2}\n"
                    self.stderr.write(
                        msg.format(
                            existing_parlparse_id,
                            parlparse_id,
                            ynmp_id,
                        ))
                continue
            msg = "Updating the YourNextMP person {0} with parlparse_id {1}\n"
            self.stdout.write(msg.format(ynmp_id, parlparse_id))
            ynmp_person.set_identifier(
                'uk.org.publicwhip',
                parlparse_id,
            )
            change_metadata = get_change_metadata(
                None, "Fetched a new parlparse ID")
            ynmp_person.record_version(change_metadata)
            ynmp_person.save_to_popit(api)
            ynmp_person.invalidate_cache_entries()
 def handle(self, *args, **kwargs):
     api = create_popit_api_object()
     if len(args) < 1:
         raise CommandError("You must provide one or more PopIt person ID")
     for person_id in args:
         invalidate_person(person_id)
         person = PopItPerson.create_from_popit(api, person_id)
         posts_to_invalidate = person.get_associated_posts()
         person.delete_memberships(api)
         # The memberships are recreated when you assign to
         # standing_in and party_memberships; this script assumes
         # these are correct and so re-setting these should
         # recreate the memberships correctly.
         person.standing_in = person.standing_in
         person.party_memberships = person.party_memberships
         person.save_to_popit(api)
         invalidate_posts(posts_to_invalidate)
         invalidate_person(person_id)
Пример #13
0
 def handle(self, *args, **kwargs):
     api = create_popit_api_object()
     if len(args) < 1:
         raise CommandError("You must provide one or more PopIt person ID")
     for person_id in args:
         invalidate_person(person_id)
         person = PopItPerson.create_from_popit(api, person_id)
         posts_to_invalidate = person.get_associated_posts()
         person.delete_memberships(api)
         # The memberships are recreated when you assign to
         # standing_in and party_memberships; this script assumes
         # these are correct and so re-setting these should
         # recreate the memberships correctly.
         person.standing_in = person.standing_in
         person.party_memberships = person.party_memberships
         person.save_to_popit(api)
         invalidate_posts(posts_to_invalidate)
         invalidate_person(person_id)
Пример #14
0
def upload_photo(request, popit_person_id):
    if request.method == 'POST':
        form = UploadPersonPhotoForm(request.POST, request.FILES)
        if form.is_valid():
            # Make sure that we save the user that made the upload
            queued_image = form.save(commit=False)
            queued_image.user = request.user
            queued_image.save()
            # Record that action:
            LoggedAction.objects.create(
                user=request.user,
                action_type='photo-upload',
                ip_address=get_client_ip(request),
                popit_person_new_version='',
                popit_person_id=popit_person_id,
                source=form.cleaned_data['justification_for_use'],
            )
            return HttpResponseRedirect(reverse(
                'photo-upload-success',
                kwargs={
                    'popit_person_id': form.cleaned_data['popit_person_id']
                }
            ))
    else:
        form = UploadPersonPhotoForm(
            initial={
                'popit_person_id': popit_person_id
            }
        )
    api = create_popit_api_object()
    return render(
        request,
        'moderation_queue/photo-upload-new.html',
        {'form': form,
         'queued_images': QueuedImage.objects.filter(
             popit_person_id=popit_person_id,
             decision='undecided',
         ).order_by('created'),
         'person': PopItPerson.create_from_popit(api, popit_person_id)}
    )
Пример #15
0
 def crop_and_upload_image_to_popit(self, image_filename, crop_bounds, moderator_why_allowed, make_primary):
     original = Image.open(image_filename)
     # Some uploaded images are CYMK, which gives you an error when
     # you try to write them as PNG, so convert to RGBA (this is
     # RGBA rather than RGB so that any alpha channel (transparency)
     # is preserved).
     original = original.convert('RGBA')
     cropped = original.crop(crop_bounds)
     ntf = NamedTemporaryFile(delete=False)
     cropped.save(ntf.name, 'PNG')
     # Upload the image to PopIt...
     person_id = self.queued_image.popit_person_id
     person = PopItPerson.create_from_popit(self.api, person_id)
     image_upload_url = '{base}persons/{person_id}/image'.format(
         base=get_base_url(),
         person_id=person_id
     )
     data = {
         'md5sum': get_file_md5sum(ntf.name),
         'user_why_allowed': self.queued_image.why_allowed,
         'user_justification_for_use': self.queued_image.justification_for_use,
         'moderator_why_allowed': moderator_why_allowed,
         'mime_type': 'image/png',
         'notes': _('Approved from photo moderation queue'),
         'uploaded_by_user': self.queued_image.user.username,
         'created': None,
     }
     if make_primary:
         data['index'] = 'first'
     with open(ntf.name) as f:
         requests.post(
             image_upload_url,
             data=data,
             files={'image': f.read()},
             headers={'APIKey': self.api.api_key}
         )
     person.invalidate_cache_entries()
     # Remove the cropped temporary image file:
     os.remove(ntf.name)
Пример #16
0
 def form_valid(self, form):
     decision = form.cleaned_data['decision']
     person = PopItPerson.create_from_popit(
         self.api,
         self.queued_image.popit_person_id
     )
     candidate_path = person.get_absolute_url()
     candidate_name = person.name
     candidate_link = u'<a href="{url}">{name}</a>'.format(
         url=candidate_path,
         name=candidate_name,
     )
     photo_review_url = self.request.build_absolute_uri(
         self.queued_image.get_absolute_url()
     )
     def flash(level, message):
         messages.add_message(
             self.request,
             level,
             message,
             extra_tags='safe photo-review'
         )
     if decision == 'approved':
         # Crop the image...
         crop_fields = ('x_min', 'y_min', 'x_max', 'y_max')
         self.crop_and_upload_image_to_popit(
             self.queued_image.image.path,
             [form.cleaned_data[e] for e in crop_fields],
             form.cleaned_data['moderator_why_allowed'],
             form.cleaned_data['make_primary'],
         )
         self.queued_image.decision = 'approved'
         for i, field in enumerate(crop_fields):
             setattr(
                 self.queued_image,
                 'crop_' + field,
                 form.cleaned_data[field]
             )
         self.queued_image.save()
         update_message = _(u'Approved a photo upload from '
             u'{uploading_user} who provided the message: '
             u'"{message}"').format(
             uploading_user=self.queued_image.user.username,
             message=self.queued_image.justification_for_use,
         )
         change_metadata = get_change_metadata(
             self.request,
             update_message
         )
         # We have to refetch the person from PopIt, otherwise
         # saving the new version will write back the images array
         # from before we uploaded the image:
         person = PopItPerson.create_from_popit(self.api, person.id)
         person.record_version(change_metadata)
         person.save_to_popit(self.api, self.request.user)
         LoggedAction.objects.create(
             user=self.request.user,
             action_type='photo-approve',
             ip_address=get_client_ip(self.request),
             popit_person_new_version=change_metadata['version_id'],
             popit_person_id=self.queued_image.popit_person_id,
             source=update_message,
         )
         self.send_mail(
             _('YourNextMP image upload approved'),
             render_to_string(
                 'moderation_queue/photo_approved_email.txt',
                 {'candidate_page_url':
                  person.get_absolute_url(self.request)}
             ),
         )
         flash(
             messages.SUCCESS,
             _(u'You approved a photo upload for %s') % candidate_link
         )
     elif decision == 'rejected':
         self.queued_image.decision = 'rejected'
         self.queued_image.save()
         update_message = _(u'Rejected a photo upload from '
             u'{uploading_user}').format(
             uploading_user=self.queued_image.user.username,
         )
         LoggedAction.objects.create(
             user=self.request.user,
             action_type='photo-reject',
             ip_address=get_client_ip(self.request),
             popit_person_new_version='',
             popit_person_id=self.queued_image.popit_person_id,
             source=update_message,
         )
         retry_upload_link = self.request.build_absolute_uri(
             reverse(
                 'photo-upload',
                 kwargs={'popit_person_id': self.queued_image.popit_person_id}
             )
         )
         self.send_mail(
             _('YourNextMP image moderation results'),
             render_to_string(
                 'moderation_queue/photo_rejected_email.txt',
                 {'reason': form.cleaned_data['rejection_reason'],
                  'candidate_name': candidate_name,
                  'retry_upload_link': retry_upload_link,
                  'photo_review_url': photo_review_url},
             ),
             email_support_too=True,
         )
         flash(
             messages.INFO,
             _(u'You rejected a photo upload for %s') % candidate_link
         )
     elif decision == 'undecided':
         # If it's left as undecided, just redirect back to the
         # photo review queue...
         flash(
             messages.INFO,
             _(u'You left a photo upload for {0} in the queue').format(
                 candidate_link
             )
         )
     elif decision == 'ignore':
         self.queued_image.decision = 'ignore'
         self.queued_image.save()
         update_message = _(u'Ignored a photo upload from '
             u'{uploading_user} (This usually means it was a duplicate)').format(
             uploading_user=self.queued_image.user.username)
         LoggedAction.objects.create(
             user=self.request.user,
             action_type='photo-ignore',
             ip_address=get_client_ip(self.request),
             popit_person_new_version='',
             popit_person_id=self.queued_image.popit_person_id,
             source=update_message,
         )
         flash(
             messages.INFO,
             _(u'You indicated a photo upload for {0} should be ignored').format(
                 candidate_link
             )
         )
     else:
         raise Exception("BUG: unexpected decision {0}".format(decision))
     return HttpResponseRedirect(reverse('photo-review-list'))
Пример #17
0
 def get_context_data(self, **kwargs):
     context = super(PhotoUploadSuccess, self).get_context_data(**kwargs)
     context['person'] = PopItPerson.create_from_popit(
         self.api, kwargs['popit_person_id'])
     return context
Пример #18
0
    def form_valid(self, form):
        decision = form.cleaned_data['decision']
        person = PopItPerson.create_from_popit(
            self.api, self.queued_image.popit_person_id)
        candidate_path = person.get_absolute_url()
        candidate_name = person.name
        candidate_link = u'<a href="{url}">{name}</a>'.format(
            url=candidate_path,
            name=candidate_name,
        )
        photo_review_url = self.request.build_absolute_uri(
            self.queued_image.get_absolute_url())

        def flash(level, message):
            messages.add_message(self.request,
                                 level,
                                 message,
                                 extra_tags='safe photo-review')

        if decision == 'approved':
            # Crop the image...
            crop_fields = ('x_min', 'y_min', 'x_max', 'y_max')
            self.crop_and_upload_image_to_popit(
                self.queued_image.image.path,
                [form.cleaned_data[e] for e in crop_fields],
                form.cleaned_data['moderator_why_allowed'],
                form.cleaned_data['make_primary'],
            )
            self.queued_image.decision = 'approved'
            for i, field in enumerate(crop_fields):
                setattr(self.queued_image, 'crop_' + field,
                        form.cleaned_data[field])
            self.queued_image.save()
            update_message = _(
                u'Approved a photo upload from '
                u'{uploading_user} who provided the message: '
                u'"{message}"').format(
                    uploading_user=self.queued_image.user.username,
                    message=self.queued_image.justification_for_use,
                )
            change_metadata = get_change_metadata(self.request, update_message)
            # We have to refetch the person from PopIt, otherwise
            # saving the new version will write back the images array
            # from before we uploaded the image:
            person = PopItPerson.create_from_popit(self.api, person.id)
            person.record_version(change_metadata)
            person.save_to_popit(self.api, self.request.user)
            LoggedAction.objects.create(
                user=self.request.user,
                action_type='photo-approve',
                ip_address=get_client_ip(self.request),
                popit_person_new_version=change_metadata['version_id'],
                popit_person_id=self.queued_image.popit_person_id,
                source=update_message,
            )
            self.send_mail(
                _('YourNextMP image upload approved'),
                render_to_string('moderation_queue/photo_approved_email.txt', {
                    'candidate_page_url':
                    person.get_absolute_url(self.request)
                }),
            )
            flash(messages.SUCCESS,
                  _(u'You approved a photo upload for %s') % candidate_link)
        elif decision == 'rejected':
            self.queued_image.decision = 'rejected'
            self.queued_image.save()
            update_message = _(
                u'Rejected a photo upload from '
                u'{uploading_user}').format(
                    uploading_user=self.queued_image.user.username, )
            LoggedAction.objects.create(
                user=self.request.user,
                action_type='photo-reject',
                ip_address=get_client_ip(self.request),
                popit_person_new_version='',
                popit_person_id=self.queued_image.popit_person_id,
                source=update_message,
            )
            retry_upload_link = self.request.build_absolute_uri(
                reverse('photo-upload',
                        kwargs={
                            'popit_person_id':
                            self.queued_image.popit_person_id
                        }))
            self.send_mail(
                _('YourNextMP image moderation results'),
                render_to_string(
                    'moderation_queue/photo_rejected_email.txt',
                    {
                        'reason': form.cleaned_data['rejection_reason'],
                        'candidate_name': candidate_name,
                        'retry_upload_link': retry_upload_link,
                        'photo_review_url': photo_review_url
                    },
                ),
                email_support_too=True,
            )
            flash(messages.INFO,
                  _(u'You rejected a photo upload for %s') % candidate_link)
        elif decision == 'undecided':
            # If it's left as undecided, just redirect back to the
            # photo review queue...
            flash(
                messages.INFO,
                _(u'You left a photo upload for {0} in the queue').format(
                    candidate_link))
        elif decision == 'ignore':
            self.queued_image.decision = 'ignore'
            self.queued_image.save()
            update_message = _(
                u'Ignored a photo upload from '
                u'{uploading_user} (This usually means it was a duplicate)'
            ).format(uploading_user=self.queued_image.user.username)
            LoggedAction.objects.create(
                user=self.request.user,
                action_type='photo-ignore',
                ip_address=get_client_ip(self.request),
                popit_person_new_version='',
                popit_person_id=self.queued_image.popit_person_id,
                source=update_message,
            )
            flash(
                messages.INFO,
                _(u'You indicated a photo upload for {0} should be ignored').
                format(candidate_link))
        else:
            raise Exception("BUG: unexpected decision {0}".format(decision))
        return HttpResponseRedirect(reverse('photo-review-list'))