def test_no_duplicate_versions(self): self.person.record_version(get_change_metadata(None, "First update")) self.assertEqual(len(json.loads(self.person.versions)), 1) self.person.record_version(get_change_metadata(None, "Nothing changed")) self.assertEqual(len(json.loads(self.person.versions)), 1) self.person.name = "New Name" self.person.record_version(get_change_metadata(None, "Nothing changed")) self.assertEqual(len(json.loads(self.person.versions)), 2)
def update_popit_person(self, popit_person_id, ppc_data, image_filename): from candidates.models import PopItPerson from ..images import image_uploaded_already # Get the existing data first: person_data, _ = self.get_person(popit_person_id) previous_versions = person_data.pop("versions") new_person_data = self.get_person_data_from_ppc(ppc_data) # Remove any empty keys, we don't want to overwrite exiting # data with nothing: keys = new_person_data.keys() warnings = [] for key in keys: if not new_person_data[key]: del new_person_data[key] # Also make sure that we don't overwrite any existing # fields that are filled in with different values: if key not in ("standing_in", "party_memberships"): new_person_data_value = new_person_data.get(key) person_data_value = person_data.get(key) if (person_data_value and new_person_data_value and new_person_data_value != person_data_value): if key_value_appeared_in_previous_version( key, new_person_data_value, previous_versions): warning_message = "[{0}] it looks as if a previous " warning_message += "version had {1}, so not " warning_message += "overwriting the current value {2}" warnings.append( warning_message.format(key, new_person_data_value, person_data_value)) del new_person_data[key] else: warnings.append("[{}] replacing {}".format( key, person_data_value)) warnings.append("[{}] with new value {}".format( key, new_person_data_value)) if warnings: print("Warnings for person/{} {}".format( popit_person_id, person_data["name"]).encode("utf-8")) for warning in warnings: print(" ...", warning.encode("utf-8")) merged_person_data = merge_person_data(person_data, new_person_data) change_metadata = get_change_metadata( None, "Updated candidate from official PPC data ({})".format( ppc_data["party_slug"]), ) person = PopItPerson.create_from_reduced_json(merged_person_data) person.record_version(change_metadata) person_id = person.save_to_popit(self.api) if image_filename: if image_uploaded_already(self.api.persons, person_id, image_filename): print("That image has already been uploaded!") else: print("Uploading image...") self.upload_person_image(person_id, image_filename, ppc_data["image_url"]) person.invalidate_cache_entries() return person_id
def get_change_metadata_for_bot(self, source): """ Wraps get_change_metadata without requiring a request object """ metadata = get_change_metadata(None, source) metadata["username"] = self.user.username return metadata
def add_person(self, person_data): # TODO Move this out of the view layer person = Person.objects.create(name=person_data['name']) person_extra = PersonExtra.objects.create(base=person) check_creation_allowed( self.request.user, person_extra.current_candidacies ) change_metadata = get_change_metadata( self.request, person_data['source'] ) person_extra.record_version(change_metadata) person_extra.save() LoggedAction.objects.create( user=self.request.user, person=person, action_type='person-create', ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], ) # Add a message to be displayed after redirect: messages.add_message( self.request, messages.SUCCESS, get_call_to_action_flash_message(person, new_person=True), extra_tags='safe do-something-else' ) return person_extra
def add_person(self, person_data): # TODO Move this out of the view layer person = Person.objects.create(name=person_data['name']) person_extra = PersonExtra.objects.create(base=person) check_creation_allowed(self.request.user, person_extra.current_candidacies) change_metadata = get_change_metadata(self.request, person_data['source']) person_extra.record_version(change_metadata) person_extra.save() LoggedAction.objects.create( user=self.request.user, person=person, action_type='person-create', ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], ) # Add a message to be displayed after redirect: messages.add_message(self.request, messages.SUCCESS, get_call_to_action_flash_message(person, new_person=True), extra_tags='safe do-something-else') return person_extra
def update_person( request=None, person=None, party=None, post_election=None, source=None ): election = post_election.election person.not_standing.remove(election) check_creation_allowed(request.user, person.current_candidacies) membership, _ = Membership.objects.update_or_create( post=post_election.post, person=person, post_election=post_election, defaults={ "party": party, "party_list_position": None, "elected": None, "role": election.candidate_membership_role, }, ) # Now remove other memberships in this election for that # person, although we raise an exception if there is any # object that has a # ForeignKey to the membership, since that would result in # losing data. old_memberships = ( Membership.objects.exclude(pk=membership.pk) .exclude(post_election__candidates_locked=True) .filter(person=person, post_election__election=post_election.election) ) for old_membership in old_memberships: raise_if_unsafe_to_delete(old_membership) old_membership.delete() memberships_for_election = Membership.objects.filter( person=person, post_election__election=post_election.election ) if ( not memberships_for_election.exists() or memberships_for_election.count() > 1 ): raise ValueError( "Attempt to create invalid memberships for {}".format(person) ) change_metadata = get_change_metadata(request, source) person.record_version(change_metadata) person.save() LoggedAction.objects.create( user=request.user, person=person, action_type="person-update", ip_address=get_client_ip(request), popit_person_new_version=change_metadata["version_id"], source=change_metadata["information_source"], )
def update_popit_person(self, popit_person_id, ppc_data, image_filename): from candidates.models import PopItPerson from ..images import image_uploaded_already # Get the existing data first: person_data, _ = self.get_person(popit_person_id) previous_versions = person_data.pop('versions') new_person_data = self.get_person_data_from_ppc(ppc_data) # Remove any empty keys, we don't want to overwrite exiting # data with nothing: keys = new_person_data.keys() warnings = [] for key in keys: if not new_person_data[key]: del new_person_data[key] # Also make sure that we don't overwrite any existing # fields that are filled in with different values: if key not in ('standing_in', 'party_memberships'): new_person_data_value = new_person_data.get(key) person_data_value = person_data.get(key) if person_data_value and new_person_data_value and new_person_data_value != person_data_value: if key_value_appeared_in_previous_version( key, new_person_data_value, previous_versions ): warning_message = "[{0}] it looks as if a previous " warning_message += "version had {1}, so not " warning_message += "overwriting the current value {2}" warnings.append(warning_message.format( key, new_person_data_value, person_data_value )) del new_person_data[key] else: warnings.append("[{0}] replacing {1}".format(key, person_data_value)) warnings.append("[{0}] with new value {1}".format(key, new_person_data_value)) if warnings: print("Warnings for person/{0} {1}".format( popit_person_id, person_data['name'] ).encode('utf-8')) for warning in warnings: print(" ...", warning.encode('utf-8')) merged_person_data = merge_person_data(person_data, new_person_data) change_metadata = get_change_metadata( None, 'Updated candidate from official PPC data ({0})'.format(ppc_data['party_slug']), ) person = PopItPerson.create_from_reduced_json(merged_person_data) person.record_version(change_metadata) person_id = person.save_to_popit(self.api) if image_filename: if image_uploaded_already(self.api.persons, person_id, image_filename): print("That image has already been uploaded!") else: print("Uploading image...") self.upload_person_image(person_id, image_filename, ppc_data['image_url']) person.invalidate_cache_entries() return person_id
def update_person(request=None, person_extra=None, party=None, post_election=None, source=None): election = post_election.election person_extra.not_standing.remove(election) check_creation_allowed(request.user, person_extra.current_candidacies) membership, _ = Membership.objects.update_or_create( post=post_election.postextra.base, person=person_extra.base, extra__election=election, role=election.candidate_membership_role, defaults={ 'on_behalf_of': party, }) MembershipExtra.objects.get_or_create(base=membership, defaults={ 'party_list_position': None, 'election': election, 'elected': None, 'post_election': post_election, }) # Now remove other memberships in this election for that # person, although we raise an exception if there is any # object (other than its MembershipExtra) that has a # ForeignKey to the membership, since that would result in # losing data. old_memberships = Membership.objects \ .exclude(pk=membership.pk) \ .filter( person=person_extra.base, extra__election=election, role=election.candidate_membership_role, ) for old_membership in old_memberships: raise_if_unsafe_to_delete(old_membership) old_membership.delete() change_metadata = get_change_metadata(request, source) person_extra.record_version(change_metadata) person_extra.save() LoggedAction.objects.create( user=request.user, person=person_extra.base, action_type='person-update', ip_address=get_client_ip(request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], )
def handle(self, **options): for person_popit_data in popit_unwrap_pagination( self.api.persons, per_page=100 ): # Exclude anyone who doesn't have a parlparse ID; this is # more or less the incumbents (by-elections causing the # "more or less" bit.) if not get_parlparse_id(person_popit_data): continue if '2015' in person_popit_data['standing_in']: print "We already have 2015 information for", person_popit_data['name'] continue person = PopItPerson.create_from_dict(person_popit_data) print "Considering person:", person.name cons_id = person.standing_in['2010']['post_id'] party_id = person.party_memberships['2010']['id'] party_name = person.party_memberships['2010']['name'] cons_url = 'https://yournextmp.com/constituency/{0}'.format(cons_id) # We're now considering marking the candidate as standing # again in the same consituency as in 2010. Check that # there's not another candidate from their party already # marked as standing in that consituency. if self.existing_candidate_same_party(cons_id, party_id): msg = u"There was already a candidate for {0} in {1} - skipping" print msg.format(party_name, cons_url).format('utf-8') continue # Now it should be safe to update the candidate and set # them as standing in 2015. person.standing_in['2015'] = person.standing_in['2010'] person.party_memberships['2015'] = person.party_memberships['2010'] person.record_version( get_change_metadata( None, "Assuming that incumbents we don't have definite information on yet are standing again", ) ) person.save_to_popit(self.api) person.invalidate_cache_entries() message = u"Marked an incumbent ({name} - {party}) as standing again in {cons_url}" print message.format( name=person.name, party=party_name, cons_url=cons_url, ).encode('utf-8')
def handle(self, **options): for person_popit_data in popit_unwrap_pagination(self.api.persons, per_page=100): # Exclude anyone who doesn't have a parlparse ID; this is # more or less the incumbents (by-elections causing the # "more or less" bit.) if not get_parlparse_id(person_popit_data): continue if '2015' in person_popit_data['standing_in']: print "We already have 2015 information for", person_popit_data[ 'name'] continue person = PopItPerson.create_from_dict(person_popit_data) print "Considering person:", person.name cons_id = person.standing_in['2010']['post_id'] party_id = person.party_memberships['2010']['id'] party_name = person.party_memberships['2010']['name'] cons_url = 'https://yournextmp.com/constituency/{0}'.format( cons_id) # We're now considering marking the candidate as standing # again in the same consituency as in 2010. Check that # there's not another candidate from their party already # marked as standing in that consituency. if self.existing_candidate_same_party(cons_id, party_id): msg = u"There was already a candidate for {0} in {1} - skipping" print msg.format(party_name, cons_url).format('utf-8') continue # Now it should be safe to update the candidate and set # them as standing in 2015. person.standing_in['2015'] = person.standing_in['2010'] person.party_memberships['2015'] = person.party_memberships['2010'] person.record_version( get_change_metadata( None, "Assuming that incumbents we don't have definite information on yet are standing again", )) person.save_to_popit(self.api) person.invalidate_cache_entries() message = u"Marked an incumbent ({name} - {party}) as standing again in {cons_url}" print message.format( name=person.name, party=party_name, cons_url=cons_url, ).encode('utf-8')
def add_popit_person(self, ppc_data, image_filename): change_metadata = get_change_metadata( None, 'Created new candidate from official PPC data ({0})'.format(ppc_data['party_slug']), ) person_data = self.get_person_data_from_ppc(ppc_data) person = PopItPerson.create_from_reduced_json(person_data) person.record_version(change_metadata) person_id = person.save_to_popit(self.api) if image_filename: self.upload_person_image(person_id, image_filename, ppc_data['image_url']) person.invalidate_cache_entries() return person_id
def update_person(self, context, data, person_extra): party = Organization.objects.get(pk=data['party'].split('__')[0]) post = context['post_extra'].base election = Election.objects.get(slug=context['election']) person_extra.not_standing.remove(election) membership, _ = Membership.objects.update_or_create( post=post, person=person_extra.base, extra__election=election, role=election.candidate_membership_role, defaults={ 'on_behalf_of': party, }) MembershipExtra.objects.get_or_create(base=membership, defaults={ 'party_list_position': None, 'election': election, 'elected': None, }) # Now remove other memberships in this election for that # person, although we raise an exception if there is any # object (other than its MembershipExtra) that has a # ForeignKey to the membership, since that would result in # losing data. for old_membership in Membership.objects \ .exclude(pk=membership.pk) \ .filter( person=person_extra.base, extra__election=election, role=election.candidate_membership_role, ): raise_if_unsafe_to_delete(old_membership) old_membership.delete() change_metadata = get_change_metadata(self.request, data['source']) person_extra.record_version(change_metadata) person_extra.save() LoggedAction.objects.create( user=self.request.user, person=person_extra.base, action_type='person-update', ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], )
def handle(self, *args, **options): person = Person.objects.get(pk=options["PERSON-ID"]) person.identifiers.create(scheme=options["SCHEME"], identifier=options["IDENTIFIER"]) if options["source"]: source = options["source"] else: source = "Added from the command-line with no source supplied" change_metadata = get_change_metadata(None, source) person.record_version(change_metadata) person.save() print("Successfully updated {}".format(person.name))
def update_person(self, context, data, person_extra): party = Organization.objects.get(pk=data['party']) post = context['post_extra'].base election = Election.objects.get(slug=context['election']) previous_memberships_in_this_election = Membership.objects.filter( person=person_extra.base, extra__election=election, role=election.candidate_membership_role, ) previous_memberships_in_this_election.delete() person_extra.not_standing.remove(election) membership, _ = Membership.objects.get_or_create( post=post, person=person_extra.base, extra__election=election, role=election.candidate_membership_role, defaults={ 'on_behalf_of': party, } ) MembershipExtra.objects.get_or_create( base=membership, defaults={ 'party_list_position': None, 'election': election, 'elected': False, } ) change_metadata = get_change_metadata( self.request, data['source'] ) person_extra.record_version(change_metadata) person_extra.save() LoggedAction.objects.create( user=self.request.user, person=person_extra.base, action_type='person-update', ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], )
def add_parlparse_id_to_member(self, member): # Try and find the corresponding person in the popit instance name = u'{} {}'.format(member['firstname'], member['lastname']) query = u'"{}"'.format(name) result = self.popit.api.search.persons.get(q=query)['result'] # Have we not got any results? if len(result) == 0: if self.verbosity > 0: msg = u'No results for {}, skipping' self.stderr.write(msg.format(name)) return # Find the person with a matching constituency person = self.person_match(member=member, people=result) if not person: if self.verbosity > 0: msg = u'{} result but no matches found for {}' self.stderr.write(msg.format(len(result), name)) return parlparse_id = self.id_mapping[name] person_data, _ = self.get_person(person['id']) previous_versions = person_data.pop('versions') identifiers = person_data.get('identifiers', []) existing_identifiers = [i['identifier'] for i in identifiers] # Does that person already have this identifier? if parlparse_id in existing_identifiers: if self.verbosity > 1: msg = u'Found existing identifier for {} ({}), skipping' self.stderr.write(msg.format(name, parlparse_id)) return identifier = { 'identifier': parlparse_id, 'scheme': 'uk.org.publicwhip' } identifiers.append(identifier) person_data['identifiers'] = identifiers change_metadata = get_change_metadata( None, 'Updated candidate with parlparse person id', ) self.update_person( person_data, change_metadata, previous_versions, ) if self.verbosity > 0: msg = u"Successfully updated {} with {}" self.stdout.write(msg.format(name, parlparse_id))
def mark_candidates_as_winner(self, request, instance): for candidate_result in instance.candidate_results.all(): membership = candidate_result.membership post = instance.post_result.post election = membership.extra.election source = instance.review_source if not source: source = instance.source change_metadata = get_change_metadata( request, source ) if candidate_result.is_winner: membership.extra.elected = True membership.extra.save() ResultEvent.objects.create( election=election, winner=membership.person, winner_person_name=membership.person.name, post_id=post.extra.slug, post_name=post.label, winner_party_id=membership.on_behalf_of.extra.slug, source=source, user=instance.reviewed_by, ) membership.person.extra.record_version(change_metadata) membership.person.save() LoggedAction.objects.create( user=instance.reviewed_by, action_type='set-candidate-elected', popit_person_new_version=change_metadata['version_id'], person=membership.person, source=source, ) else: change_metadata['information_source'] = \ 'Setting as "not elected" by implication' membership.person.extra.record_version(change_metadata) membership.extra.elected = False membership.extra.save()
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 merge(self, delete=True): """ Do everything we need in order to merge two people. At the end of this, `self.source_person` should have no related objects left, and is deleted safely using `self.safe_delete` """ with transaction.atomic(): # Merge all the things self.merge_person_attrs() self.merge_versions_json() self.merge_person_identifiers() self.merge_images() self.merge_logged_actions() self.merge_memberships() self.merge_queued_images() self.merge_not_standing() self.merge_result_events() # Post merging tasks # Save the dest person (don't assume the methods above do this) change_metadata = get_change_metadata( self.request, "After merging person {}".format(self.source_person.pk), ) # Log that the merge has taken place, and will be shown in # the recent changes, leaderboards, etc. if self.request: LoggedAction.objects.create( user=self.request.user, action_type="person-merge", ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata["version_id"], person=self.dest_person, source=change_metadata["information_source"], ) self.dest_person.record_version(change_metadata) self.dest_person.save() self.setup_redirect() if delete: # Delete the old person self.safe_delete(self.source_person)
def handle(self, *args, **options): person = Person.objects.get(pk=options['PERSON-ID']) person.identifiers.create( scheme=options['SCHEME'], identifier=options['IDENTIFIER'], ) if options['source']: source = options['source'] else: source = "Added from the command-line with no source supplied" change_metadata = get_change_metadata(None, source) person.extra.record_version(change_metadata) person.extra.save() print "Successfully updated {0}".format(person.name)
def add_person(request, person_data): person = Person.objects.create(name=person_data["name"]) change_metadata = get_change_metadata(request, person_data["source"]) person.record_version(change_metadata, new_person=True) person.save() LoggedAction.objects.create( user=request.user, person=person, action_type="person-create", ip_address=get_client_ip(request), popit_person_new_version=change_metadata["version_id"], source=change_metadata["information_source"], ) return person
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 add_person(request, person_data): person = Person.objects.create(name=person_data['name']) person_extra = PersonExtra.objects.create(base=person) change_metadata = get_change_metadata(request, person_data['source']) person_extra.record_version(change_metadata, new_person=True) person_extra.save() LoggedAction.objects.create( user=request.user, person=person, action_type='person-create', ip_address=get_client_ip(request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], ) return person_extra
def handle(self, *args, **options): person = Person.objects.get(pk=options["PERSON-ID"]) kwargs = {"name": options["OTHER-NAME"]} for k in ("note", "start_date", "end_date"): if options[k]: kwargs[k] = options[k] person.other_names.create(**kwargs) if options["source"]: source = options["source"] else: source = "Added from the command-line with no source supplied" change_metadata = get_change_metadata(None, source) person.extra.record_version(change_metadata) person.extra.save() print "Successfully updated {0}".format(person.name)
def handle(self, *args, **options): person = Person.objects.get(pk=options["PERSON-ID"]) kwargs = {"name": options["OTHER-NAME"]} for k in ("note", "start_date", "end_date"): if options[k]: kwargs[k] = options[k] person.other_names.create(**kwargs) if options["source"]: source = options["source"] else: source = "Added from the command-line with no source supplied" change_metadata = get_change_metadata(None, source) person.record_version(change_metadata) person.save() print("Successfully updated {}".format(person.name))
def mark_candidates_as_winner(self, request, instance): for candidate_result in instance.candidate_results.all(): membership = candidate_result.membership post = instance.post_result.post election = membership.extra.election source = instance.review_source if not source: source = instance.source change_metadata = get_change_metadata(request, source) if candidate_result.is_winner: membership.extra.elected = True membership.extra.save() ResultEvent.objects.create( election=election, winner=membership.person, winner_person_name=membership.person.name, post_id=post.extra.slug, post_name=post.label, winner_party_id=membership.on_behalf_of.extra.slug, source=source, user=instance.reviewed_by, ) membership.person.extra.record_version(change_metadata) membership.person.save() LoggedAction.objects.create( user=instance.reviewed_by, action_type='set-candidate-elected', popit_person_new_version=change_metadata['version_id'], person=membership.person, source=source, ) else: change_metadata['information_source'] = \ 'Setting as "not elected" by implication' membership.person.extra.record_version(change_metadata) membership.extra.elected = False membership.extra.save()
def merge(self, delete=True): """ Do everything we need in order to merge two people. At the end of this, `self.source_person` should have no related objects left, and is deleted safely using `self.safe_delete` """ with transaction.atomic(): # Merge all the things for method_name in set(self.SUPPORTED_FIELDS.values()): method = getattr(self, method_name) method() # Post merging tasks # Save the dest person (don't assume the methods above do this) change_metadata = get_change_metadata( self.request, "After merging person {}".format(self.source_person.pk), ) # Save the dest person before creating a LoggedAction # See https://github.com/DemocracyClub/yournextrepresentative/issues/1037 # for more self.dest_person.record_version(change_metadata) self.dest_person.save() # Log that the merge has taken place, and will be shown in # the recent changes, leaderboards, etc. if self.request: LoggedAction.objects.create( user=self.request.user, action_type="person-merge", ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata["version_id"], person=self.dest_person, source=change_metadata["information_source"], ) self.setup_redirect() if delete: # Delete the old person self.safe_delete(self.source_person) return self.dest_person
def handle(self, *args, **options): person = Person.objects.get(pk=options['PERSON-ID']) kwargs = { 'name': options['OTHER-NAME'], } for k in ('note', 'start_date', 'end_date'): if options[k]: kwargs[k] = options[k] person.other_names.create(**kwargs) if options['source']: source = options['source'] else: source = "Added from the command-line with no source supplied" change_metadata = get_change_metadata(None, source) person.extra.record_version(change_metadata) person.extra.save() print("Successfully updated {0}".format(person.name))
def mark_candidates_as_winner(request, instance): for candidate_result in instance.candidate_results.all(): membership = candidate_result.membership post_election = instance.post_election election = post_election.election source = instance.source change_metadata = get_change_metadata(request, source) if candidate_result.is_winner: membership.elected = True membership.save() ResultEvent.objects.create( election=election, winner=membership.person, post=post_election.post, old_post_id=post_election.post.slug, old_post_name=post_election.post.label, winner_party=membership.party, source=source, user=request.user, ) membership.person.record_version(change_metadata) membership.person.save() LoggedAction.objects.create( user=instance.user, action_type="set-candidate-elected", popit_person_new_version=change_metadata["version_id"], person=membership.person, source=source, ) else: change_metadata[ "information_source"] = 'Setting as "not elected" by implication' membership.person.record_version(change_metadata) membership.elected = False membership.save()
def add_person(self, person_data): # TODO Move this out of the view layer person = Person.objects.create(name=person_data['name']) person_extra = PersonExtra.objects.create(base=person) check_creation_allowed(self.request.user, person_extra.current_candidacies) change_metadata = get_change_metadata(self.request, person_data['source']) person_extra.record_version(change_metadata) person_extra.save() LoggedAction.objects.create( user=self.request.user, person=person, action_type='person-create', ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata['version_id'], source=change_metadata['information_source'], ) return person_extra
def form_valid(self, form): decision = form.cleaned_data['decision'] person = Person.objects.get( id=self.queued_image.person.id ) person_extra = person.extra candidate_path = person_extra.get_absolute_url() candidate_name = person.name candidate_link = '<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() ) site_name = Site.objects.get_current().name def flash(level, message): messages.add_message( self.request, level, message, extra_tags='safe photo-review' ) if self.queued_image.user: uploaded_by = self.queued_image.user.username else: uploaded_by = _("a script") 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 = _('Approved a photo upload from ' '{uploading_user} who provided the message: ' '"{message}"').format( uploading_user=uploaded_by, message=self.queued_image.justification_for_use, ) change_metadata = get_change_metadata( self.request, update_message ) person_extra.record_version(change_metadata) person_extra.save() person.save() 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'], person=person, source=update_message, ) candidate_full_url = person_extra.get_absolute_url(self.request) self.send_mail( _('{site_name} image upload approved').format( site_name=site_name ), render_to_string( 'moderation_queue/photo_approved_email.txt', { 'site_name': site_name, 'candidate_page_url': candidate_full_url, 'intro': _( "Thank-you for submitting a photo to " "{site_name}; that's been uploaded now for " "the candidate page here:" ).format(site_name=site_name), 'signoff': _( "Many thanks from the {site_name} volunteers" ).format(site_name=site_name), } ), ) flash( messages.SUCCESS, _('You approved a photo upload for %s') % candidate_link ) elif decision == 'rejected': self.queued_image.decision = 'rejected' self.queued_image.save() update_message = _('Rejected a photo upload from ' '{uploading_user}').format( uploading_user=uploaded_by, ) LoggedAction.objects.create( user=self.request.user, action_type='photo-reject', ip_address=get_client_ip(self.request), popit_person_new_version='', person=person, source=update_message, ) retry_upload_link = self.request.build_absolute_uri( reverse( 'photo-upload', kwargs={'person_id': self.queued_image.person.id} ) ) self.send_mail( _('{site_name} image moderation results').format( site_name=Site.objects.get_current().name ), render_to_string( 'moderation_queue/photo_rejected_email.txt', { 'reason': form.cleaned_data['rejection_reason'], 'retry_upload_link': retry_upload_link, 'photo_review_url': photo_review_url, 'intro': _( "Thank-you for uploading a photo of " "{candidate_name} to {site_name}, but " "unfortunately we can't use that image because:" ).format( candidate_name=candidate_name, site_name=site_name ), 'possible_actions': _( 'You can just reply to this email if you want to ' 'discuss that further, or you can try uploading a ' 'photo with a different reason or justification ' 'for its use using this link:' ), 'signoff': _( "Many thanks from the {site_name} volunteers" ).format(site_name=site_name), }, ), email_support_too=True, ) flash( messages.INFO, _('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, _('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 = _('Ignored a photo upload from ' '{uploading_user} (This usually means it was a duplicate)').format( uploading_user=uploaded_by) LoggedAction.objects.create( user=self.request.user, action_type='photo-ignore', ip_address=get_client_ip(self.request), popit_person_new_version='', person=person, source=update_message, ) flash( messages.INFO, _('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'))
def form_valid(self, form): decision = form.cleaned_data['decision'] if decision == 'approved': # Crop the image... self.crop_and_upload_image_to_popit( self.queued_image.image.path, [form.cleaned_data[e] for e in ('x_min', 'y_min', 'x_max', 'y_max')], form.cleaned_data['moderator_why_allowed'] ) self.queued_image.decision = 'approved' self.queued_image.save() # Now create a new version in PopIt: person_data, _ = self.get_person( self.queued_image.popit_person_id ) previous_versions = person_data.pop('versions') 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 ) self.update_person( person_data, change_metadata, previous_versions, ) 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, ) candidate_path = reverse( 'person-view', kwargs={'person_id': self.queued_image.popit_person_id} ) self.send_mail( 'YourNextMP image upload approved', render_to_string( 'moderation_queue/photo_approved_email.txt', {'candidate_page_url': self.request.build_absolute_uri(candidate_path)} ), ) 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, ) self.send_mail( 'YourNextMP image moderation results', render_to_string( 'moderation_queue/photo_rejected_email.txt', {'reason': form.cleaned_data['rejection_reason']} ), ) elif decision == 'undecided': # If it's left as undecided, just redirect back to the # photo review queue... pass else: raise Exception("BUG: unexpected decision {0}".format(decision)) return HttpResponseRedirect(reverse('photo-review-list'))
def handle(self, username=None, **options): from slumber.exceptions import HttpClientError from candidates.popit import create_popit_api_object from candidates.election_specific import PARTY_DATA, shorten_post_label from candidates.models import PopItPerson if username is None: message = "You must supply the name of a user to be associated with the image uploads." raise CommandError(message) try: user = User.objects.get(username=username) except User.DoesNotExist: message = "No user with the username '{0}' could be found" raise CommandError(message.format(username)) api = create_popit_api_object() json_filename = join( dirname(__file__), '..', '..','data', 'candidates.json' ) with open(json_filename) as f: all_data = json.load(f) # This map is needed to get getting YNR election data from # the election ID used in the JSON file. json_election_id_to_name = { e['pk']: e['fields']['name'] for e in all_data if e['model'] == 'elections.election' } person_dict = { e['pk']: e['fields'] for e in all_data if e['model'] == 'popolo.person' } candidate_list = [ dict(person_id=e['pk'], election_id=e['fields']['election']) for e in all_data if e['model'] == 'elections.candidate' ] for candidate in candidate_list: vi_person_id = candidate['person_id'] person_data = person_dict[vi_person_id] election_data, post_data = get_post_data( api, candidate['election_id'], json_election_id_to_name ) birth_date = None if person_data['birth_date']: birth_date = str(dateutil.parser.parse( person_data['birth_date'], dayfirst=True ).date()) name = person_data['name'] gender = person_data['gender'] image_url = person_data['image'] person = get_existing_popit_person(vi_person_id) if person: print("Found an existing person:", person.get_absolute_url()) else: print("No existing person, creating a new one:", name) person = PopItPerson() # Now update fields from the imported data: person.name = name person.gender = gender if birth_date: person.birth_date = str(birth_date) else: person.birth_date = None standing_in_election = { 'post_id': post_data['id'], 'name': shorten_post_label(post_data['label']), } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area']['identifier'] person.standing_in = { election_data.slug: standing_in_election } person.party_memberships = { election_data.slug: { 'id': UNKNOWN_PARTY_ID, 'name': PARTY_DATA.party_id_to_name[UNKNOWN_PARTY_ID], } } person.set_identifier('import-id', vi_person_id) change_metadata = get_change_metadata( None, 'Imported candidate from JSON', ) person.record_version(change_metadata) try: person.save_to_popit(api) if image_url: enqueue_image(person, user, image_url) except HttpClientError as hce: print("Got an HttpClientError:", hce.content) raise
def form_valid(self, form): decision = form.cleaned_data['decision'] if decision == 'approved': # Crop the image... self.crop_and_upload_image_to_popit( self.queued_image.image.path, [ form.cleaned_data[e] for e in ('x_min', 'y_min', 'x_max', 'y_max') ], form.cleaned_data['moderator_why_allowed']) self.queued_image.decision = 'approved' self.queued_image.save() # Now create a new version in PopIt: person_data, _ = self.get_person(self.queued_image.popit_person_id) previous_versions = person_data.pop('versions') 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) self.update_person( person_data, change_metadata, previous_versions, ) 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, ) candidate_path = reverse( 'person-view', kwargs={'person_id': self.queued_image.popit_person_id}) self.send_mail( 'YourNextMP image upload approved', render_to_string( 'moderation_queue/photo_approved_email.txt', { 'candidate_page_url': self.request.build_absolute_uri(candidate_path) }), ) 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, ) self.send_mail( 'YourNextMP image moderation results', render_to_string( 'moderation_queue/photo_rejected_email.txt', {'reason': form.cleaned_data['rejection_reason']}), ) elif decision == 'undecided': # If it's left as undecided, just redirect back to the # photo review queue... pass elif decision == 'ignore': self.queued_image.decision = 'ignore' self.queued_image.save() else: raise Exception("BUG: unexpected decision {0}".format(decision)) return HttpResponseRedirect(reverse('photo-review-list'))
def handle(self, **options): from slumber.exceptions import HttpClientError from candidates.cache import get_post_cached, UnknownPostException from candidates.election_specific import PARTY_DATA, shorten_post_label from candidates.models import PopItPerson from candidates.popit import create_popit_api_object spreadsheet_url = 'https://docs.google.com/spreadsheets/d/{0}/pub?output=csv'\ .format(GOOGLE_DOC_ID) candidate_list = requests.get(spreadsheet_url) content = StringIO(unicode(candidate_list.content)) reader = csv.DictReader(content) api = create_popit_api_object() for row in reader: try: election_data = Election.objects.get_by_slug('council-member-2015') ocd_division = election_data.post_id_format.format(area_id=row['Ward']) post_data = get_post_cached(api, ocd_division)['result'] except (UnknownPostException, memcache.Client.MemcachedKeyCharacterError): election_data = Election.objects.get_by_slug('school-board-2015') post_data = get_post_cached(api, election_data.post_id_format)['result'] person_id = slugify(row['Name']) person = get_existing_popit_person(person_id) if person: print("Found an existing person:", row['Name']) else: print("No existing person, creating a new one:", row['Name']) person = PopItPerson() person.name = row['Name'] # TODO: Get these attributes in the spreadsheet # person.gender = gender # if birth_date: # person.birth_date = str(birth_date) # else: # person.birth_date = None person.email = row['Campaign Email'] person.facebook_personal_url = row["Candidate's Personal Facebook Profile"] person.facebook_page_url = row['Campaign Facebook Page'] person.twitter_username = row['Campaign Twitter']\ .replace('N', '')\ .replace('N/A', '')\ .replace('http://twitter.com/', '')\ .replace('https://twitter.com/', '') person.linkedin_url = row['LinkedIn'] person.homepage_url = row['Campaign Website\n'] standing_in_election = { 'post_id': post_data['id'], 'name': shorten_post_label(post_data['label']), } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area']['identifier'] person.standing_in = { election_data.slug: standing_in_election } if 'dfl' in row['Party'].lower(): party_id = 'party:101' elif 'green' in row['Party'].lower(): party_id = 'party:201' elif 'independence' in row['Party'].lower(): party_id = 'party:301' else: party_id = 'party:401' party_name = PARTY_DATA.party_id_to_name[party_id] person.party_memberships = { election_data.slug: { 'id': party_id, 'name': party_name, } } person.set_identifier('import-id', person_id) change_metadata = get_change_metadata( None, 'Imported candidate from Google Spreadsheet', ) person.record_version(change_metadata) try: person.save_to_popit(api) # TODO: Get candidate Images # if image_url: # enqueue_image(person, user, image_url) except HttpClientError as hce: print "Got an HttpClientError:", hce.content raise
def form_valid(self, form): decision = form.cleaned_data["decision"] person = Person.objects.get(id=self.queued_image.person.id) candidate_path = person.get_absolute_url() candidate_name = person.name candidate_link = '<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()) site_name = Site.objects.get_current().name def flash(level, message): messages.add_message(self.request, level, message, extra_tags="safe photo-review") if self.queued_image.user: uploaded_by = self.queued_image.user.username else: uploaded_by = _("a script") 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.file, [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() sentence = "Approved a photo upload from {uploading_user}" ' who provided the message: "{message}"' update_message = _(sentence).format( uploading_user=uploaded_by, message=self.queued_image.justification_for_use, ) change_metadata = get_change_metadata(self.request, update_message) person.record_version(change_metadata) person.save() person.save() 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"], person=person, source=update_message, ) candidate_full_url = self.request.build_absolute_uri( person.get_absolute_url(self.request)) self.send_mail( _("{site_name} image upload approved").format( site_name=site_name), render_to_string( "moderation_queue/photo_approved_email.txt", { "site_name": site_name, "candidate_page_url": candidate_full_url, "intro": _("Thank you for submitting a photo to " "{site_name}. It has been uploaded to " "the candidate page here:").format( site_name=site_name), "signoff": _("Many thanks from the {site_name} volunteers"). format(site_name=site_name), }, ), ) flash( messages.SUCCESS, _("You approved a photo upload for %s") % candidate_link, ) elif decision == "rejected": self.queued_image.decision = "rejected" self.queued_image.save() sentence = "Rejected a photo upload from {uploading_user}" update_message = _(sentence).format(uploading_user=uploaded_by) LoggedAction.objects.create( user=self.request.user, action_type="photo-reject", ip_address=get_client_ip(self.request), popit_person_new_version="", person=person, source=update_message, ) retry_upload_link = self.request.build_absolute_uri( reverse( "photo-upload", kwargs={"person_id": self.queued_image.person.id}, )) self.send_mail( _("{site_name} image moderation results").format( site_name=Site.objects.get_current().name), render_to_string( "moderation_queue/photo_rejected_email.txt", { "reason": form.cleaned_data["rejection_reason"], "retry_upload_link": retry_upload_link, "photo_review_url": photo_review_url, "intro": _("Thank-you for uploading a photo of " "{candidate_name} to {site_name}, " "but unfortunately we can't use that image because:" ).format(candidate_name=candidate_name, site_name=site_name), "possible_actions": _("You can just reply to this email if you want to " "discuss that further, or you can try uploading a " "photo with a different reason or justification " "for its use using this link:"), "signoff": _("Many thanks from the {site_name} volunteers"). format(site_name=site_name), }, ), email_support_too=True, ) flash( messages.INFO, _("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, _("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() sentence = "Ignored a photo upload from {uploading_user}" " (This usually means it was a duplicate)" update_message = _(sentence).format(uploading_user=uploaded_by) LoggedAction.objects.create( user=self.request.user, action_type="photo-ignore", ip_address=get_client_ip(self.request), popit_person_new_version="", person=person, source=update_message, ) flash( messages.INFO, _("You indicated a photo upload for {0} should be ignored"). format(candidate_link), ) else: raise Exception("BUG: unexpected decision {}".format(decision)) return HttpResponseRedirect(reverse("photo-review-list"))
def handle(self, **options): from slumber.exceptions import HttpClientError, HttpServerError from candidates.election_specific import PARTY_DATA, shorten_post_label from candidates.models import PopItPerson from candidates.popit import create_popit_api_object api = create_popit_api_object() csv_filename = join(dirname(__file__), '..', '..', 'data', 'candidates.csv') with open(csv_filename) as f: all_data = csv.DictReader(f) for candidate in all_data: vi_person_id = candidate['Distrito'] + candidate[ 'Numero Lista'] + candidate['Posicion'] + candidate[ 'Cargo'] + candidate['Nombre Lista'] election_data, post_data = get_post_data( api, candidate['Cargo'], candidate['Distrito']) if (election_data == False): print("Skipping: " + candidate['Cargo'] + ", " + candidate['Distrito'] + ", " + candidate['Nombre']) continue name = candidate['Nombre'] birth_date = None gender = None image_url = None person = get_existing_popit_person(vi_person_id) if person: print("Found an existing person:", person.get_absolute_url()) else: print("No existing person, creating a new one:", name) person = PopItPerson() # Now update fields from the imported data: person.name = name.split(",")[1] + " " + name.split(",")[0] person.gender = gender if birth_date: person.birth_date = str(birth_date) else: person.birth_date = None standing_in_election = { 'post_id': post_data['id'], 'name': shorten_post_label(post_data['label']), 'party_list_position': candidate['Posicion'], } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area'][ 'identifier'] person.standing_in = {election_data.slug: standing_in_election} party_id = get_party_id(candidate["Partido"]) person.party_memberships = { election_data.slug: { 'id': party_id, 'name': PARTY_DATA.party_id_to_name[party_id], } } person.set_identifier('import-id', vi_person_id) change_metadata = get_change_metadata( None, 'Imported candidate from CSV', ) person.record_version(change_metadata) try: person.save_to_popit(api) except HttpClientError as hce: print("Got an HttpClientError:", hce.content) raise except HttpServerError as hse: print("The server error content was:", hse.content) raise
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'))
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'))
def handle(self, username=None, **options): from slumber.exceptions import HttpClientError from candidates.popit import create_popit_api_object from candidates.election_specific import PARTY_DATA, shorten_post_label from candidates.models import PopItPerson if username is None: message = "You must supply the name of a user to be associated with the image uploads." raise CommandError(message) try: user = User.objects.get(username=username) except User.DoesNotExist: message = "No user with the username '{0}' could be found" raise CommandError(message.format(username)) api = create_popit_api_object() json_filename = join(dirname(__file__), '..', '..', 'data', 'candidates.json') with open(json_filename) as f: all_data = json.load(f) # This map is needed to get getting YNR election data from # the election ID used in the JSON file. json_election_id_to_name = { e['pk']: e['fields']['name'] for e in all_data if e['model'] == 'elections.election' } person_dict = { e['pk']: e['fields'] for e in all_data if e['model'] == 'popolo.person' } candidate_list = [ dict(person_id=e['pk'], election_id=e['fields']['election']) for e in all_data if e['model'] == 'elections.candidate' ] for candidate in candidate_list: vi_person_id = candidate['person_id'] person_data = person_dict[vi_person_id] election_data, post_data = get_post_data(api, candidate['election_id'], json_election_id_to_name) birth_date = None if person_data['birth_date']: birth_date = str( dateutil.parser.parse(person_data['birth_date'], dayfirst=True).date()) name = person_data['name'] gender = person_data['gender'] image_url = person_data['image'] person = get_existing_popit_person(vi_person_id) if person: print("Found an existing person:", person.get_absolute_url()) else: print("No existing person, creating a new one:", name) person = PopItPerson() # Now update fields from the imported data: person.name = name person.gender = gender if birth_date: person.birth_date = str(birth_date) else: person.birth_date = None standing_in_election = { 'post_id': post_data['id'], 'name': shorten_post_label(post_data['label']), } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area'][ 'identifier'] person.standing_in = {election_data.slug: standing_in_election} person.party_memberships = { election_data.slug: { 'id': UNKNOWN_PARTY_ID, 'name': PARTY_DATA.party_id_to_name[UNKNOWN_PARTY_ID], } } person.set_identifier('import-id', vi_person_id) change_metadata = get_change_metadata( None, 'Imported candidate from JSON', ) person.record_version(change_metadata) try: person.save_to_popit(api) if image_url: enqueue_image(person, user, image_url) except HttpClientError as hce: print("Got an HttpClientError:", hce.content) raise
def handle(self, username=None, **options): from slumber.exceptions import HttpClientError from candidates.election_specific import PARTY_DATA, shorten_post_label from candidates.models import PopItPerson from candidates.popit import create_popit_api_object election_data = { 'prv-2015': 'listedescandidatsauxelectionslegislativeslisteprovincialeanptic.csv', 'nat-2015': 'listedescandidatsauxelectionslegislativesanptic.csv' } field_map = { 'prv-2015': { 'region': 1, 'party': 4, 'list_order': 5, 'first_name': 7, 'last_name': 6, 'gender': 8, 'birth_date': 9, 'party_short': 3 }, 'nat-2015': { 'region': 0, 'party': 2, 'list_order': 3, 'first_name': 5, 'last_name': 4, 'gender': 6, 'birth_date': 7, 'party_short': 2 } } api = create_popit_api_object() party_id_missing = {} party_name_to_id = {} for party_id, party_name in PARTY_DATA.party_id_to_name.items(): party_name_to_id[party_name] = party_id for election_id, filename in election_data.items(): csv_filename = join(dirname(__file__), '..', '..', 'data', filename) fields = field_map[election_id] with codecs.open(csv_filename, 'r', encoding='windows-1252') as f: initial = True for candidate in unicode_csv_reader(f): # skip header line if initial: initial = False continue region = candidate[fields['region']] party = candidate[fields['party']] party_list_order = candidate[fields['list_order']] first_name = string.capwords( candidate[fields['first_name']]) last_name = string.capwords(candidate[fields['last_name']]) gender = candidate[fields['gender']] birth_date = None if candidate[fields['birth_date']] is not None: birth_date = str( dateutil.parser.parse( candidate[fields['birth_date']], dayfirst=True).date()) name = first_name + ' ' + last_name id = '-'.join([ re.sub('[^\w]*', '', re.sub(r' ', '-', strip_accents(name.lower()))), re.sub('[^\w]*', '', candidate[fields['party_short']].lower()), birth_date ]) # national candidate if region == 'PAYS': region = 'Burkina Faso' election_data, post_data = get_post_data( api, election_id, region) # debug # tmp = '%s %s %s (%s) - %s (%s)' % ( id, first_name, last_name, party, region, post_data['label'] ) # print(tmp) person = get_existing_popit_person(id) if person: # print("Found an existing person:", person.get_absolute_url()) pass else: print("No existing person, creating a new one:", name) person = PopItPerson() person.set_identifier('import-id', id) person.family_name = last_name person.given_name = first_name person.name = name person.gender = gender if birth_date: person.birth_date = str(birth_date) else: person.birth_date = None standing_in_election = { 'post_id': post_data['id'], 'name': shorten_post_label(post_data['label']), 'party_list_position': party_list_order, } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area'][ 'identifier'] person.standing_in = { election_data.slug: standing_in_election } change_metadata = get_change_metadata( None, 'Imported candidate from CSV', ) party_comp = re.sub(' +', ' ', party) party_id = UNKNOWN_PARTY_ID if party_comp in party_name_to_id.keys(): party_id = party_name_to_id[party_comp] party = party_comp else: party_id = party_name_to_id['Unknown Party'] party = 'Unknown Party' if party_id == UNKNOWN_PARTY_ID and party_comp not in party_id_missing.keys( ): party_id_missing[party_comp] = 1 person.party_memberships = { election_data.slug: { 'id': party_id, 'name': party, 'imported_name': party_comp } } person.record_version(change_metadata) try: person.save_to_popit(api) except HttpClientError as hce: print("Got an HttpClientError:", hce.content) raise if len(party_id_missing) > 0: print("Unmatched party names:") for name in party_id_missing.keys(): print(name)
def handle(self, **options): from slumber.exceptions import HttpClientError, HttpServerError from candidates.election_specific import PARTY_DATA, shorten_post_label from candidates.models import PopItPerson from candidates.popit import create_popit_api_object api = create_popit_api_object() csv_filename = join( dirname(__file__), '..', '..','data', 'candidates.csv' ) with open(csv_filename) as f: all_data = csv.DictReader(f) for candidate in all_data: vi_person_id = candidate['Distrito']+candidate['Numero Lista']+candidate['Posicion']+candidate['Cargo']+candidate['Nombre Lista'] election_data, post_data = get_post_data( api, candidate['Cargo'], candidate['Distrito'] ) if (election_data == False): print("Skipping: "+ candidate['Cargo'] +", " + candidate['Distrito']+", " + candidate['Nombre']) continue; name = candidate['Nombre'] birth_date = None gender = None image_url = None person = get_existing_popit_person(vi_person_id) if person: print("Found an existing person:", person.get_absolute_url()) else: print("No existing person, creating a new one:", name) person = PopItPerson() # Now update fields from the imported data: person.name = name.split(",")[1] + " " + name.split(",")[0] person.gender = gender if birth_date: person.birth_date = str(birth_date) else: person.birth_date = None standing_in_election = { 'post_id': post_data['id'], 'name': shorten_post_label(post_data['label']), 'party_list_position': candidate['Posicion'], } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area']['identifier'] person.standing_in = { election_data.slug: standing_in_election } party_id = get_party_id(candidate["Partido"]); person.party_memberships = { election_data.slug: { 'id': party_id, 'name': PARTY_DATA.party_id_to_name[party_id], } } person.set_identifier('import-id', vi_person_id) change_metadata = get_change_metadata( None, 'Imported candidate from CSV', ) person.record_version(change_metadata) try: person.save_to_popit(api) except HttpClientError as hce: print("Got an HttpClientError:", hce.content) raise except HttpServerError as hse: print("The server error content was:", hse.content) raise
def form_valid(self, form): decision = form.cleaned_data['decision'] person = Person.objects.get( id=self.queued_image.person.id ) person_extra = person.extra candidate_path = person_extra.get_absolute_url() candidate_name = person.name candidate_link = '<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() ) site_name = Site.objects.get_current().name 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 = _('Approved a photo upload from ' '{uploading_user} who provided the message: ' '"{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 ) person_extra.record_version(change_metadata) person_extra.save() person.save() 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'], person=person, source=update_message, ) candidate_full_url = person_extra.get_absolute_url(self.request) self.send_mail( _('{site_name} image upload approved').format( site_name=site_name ), render_to_string( 'moderation_queue/photo_approved_email.txt', { 'site_name': site_name, 'candidate_page_url': candidate_full_url, 'intro': _( "Thank-you for submitting a photo to " "{site_name}; that's been uploaded now for " "the candidate page here:" ).format(site_name=site_name), 'signoff': _( "Many thanks from the {site_name} volunteers" ).format(site_name=site_name), } ), ) flash( messages.SUCCESS, _('You approved a photo upload for %s') % candidate_link ) elif decision == 'rejected': self.queued_image.decision = 'rejected' self.queued_image.save() update_message = _('Rejected a photo upload from ' '{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='', person=person, source=update_message, ) retry_upload_link = self.request.build_absolute_uri( reverse( 'photo-upload', kwargs={'person_id': self.queued_image.person.id} ) ) self.send_mail( _('{site_name} image moderation results').format( site_name=Site.objects.get_current().name ), render_to_string( 'moderation_queue/photo_rejected_email.txt', { 'reason': form.cleaned_data['rejection_reason'], 'retry_upload_link': retry_upload_link, 'photo_review_url': photo_review_url, 'intro': _( "Thank-you for uploading a photo of " "{candidate_name} to {site_name}, but " "unfortunately we can't use that image because:" ).format( candidate_name=candidate_name, site_name=site_name ), 'possible_actions': _( 'You can just reply to this email if you want to ' 'discuss that further, or you can try uploading a ' 'photo with a different reason or justification ' 'for its use using this link:' ), 'signoff': _( "Many thanks from the {site_name} volunteers" ).format(site_name=site_name), }, ), email_support_too=True, ) flash( messages.INFO, _('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, _('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 = _('Ignored a photo upload from ' '{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='', person=person, source=update_message, ) flash( messages.INFO, _('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'))
def handle(self, username=None, **options): election_data = { 'prv-2015': 'listedescandidatsauxelectionslegislativeslisteprovincialeanptic.csv', 'nat-2015': 'listedescandidatsauxelectionslegislativesanptic.csv' } field_map = { 'prv-2015': { 'region': 1, 'party': 4, 'list_order': 5, 'first_name': 7, 'last_name': 6, 'gender': 8, 'birth_date': 9, 'party_short': 3 }, 'nat-2015': { 'region': 0, 'party': 2, 'list_order': 3, 'first_name': 5, 'last_name': 4, 'gender': 6, 'birth_date': 7, 'party_short': 2 } } api = create_popit_api_object() party_id_missing = {} party_name_to_id = {} for party_id, party_name in PARTY_DATA.party_id_to_name.items(): party_name_to_id[party_name] = party_id for election_id, filename in election_data.items(): csv_filename = join( dirname(__file__), '..', '..', 'data', filename ) fields = field_map[election_id] with codecs.open(csv_filename, 'r', encoding='windows-1252') as f: initial = True for candidate in unicode_csv_reader(f): # skip header line if initial: initial = False continue region = candidate[fields['region']] party = candidate[fields['party']] party_list_order = candidate[fields['list_order']] first_name = string.capwords(candidate[fields['first_name']]) last_name = string.capwords(candidate[fields['last_name']]) gender = candidate[fields['gender']] birth_date = None if candidate[fields['birth_date']] is not None: birth_date = str(dateutil.parser.parse( candidate[fields['birth_date']], dayfirst=True ).date()) name = first_name + ' ' + last_name id = '-'.join([ re.sub('[^\w]*', '', re.sub(r' ', '-', strip_accents(name.lower()))), re.sub('[^\w]*', '', candidate[fields['party_short']].lower()), birth_date ]) # national candidate if region == 'PAYS': region = 'Burkina Faso' election_data, post_data = get_post_data( api, election_id, region ) # debug # tmp = '%s %s %s (%s) - %s (%s)' % ( id, first_name, last_name, party, region, post_data['label'] ) # print tmp person = get_existing_popit_person(id) if person: # print "Found an existing person:", person.get_absolute_url() pass else: print "No existing person, creating a new one:", name person = PopItPerson() person.set_identifier('import-id', id) person.family_name = last_name person.given_name = first_name person.name = name person.gender = gender if birth_date: person.birth_date = str(birth_date) else: person.birth_date = None standing_in_election = { 'post_id': post_data['id'], 'name': AREA_POST_DATA.shorten_post_label( election_data.slug, post_data['label'], ), 'party_list_position': party_list_order, } if 'area' in post_data: standing_in_election['mapit_url'] = post_data['area']['identifier'] person.standing_in = { election_data.slug: standing_in_election } change_metadata = get_change_metadata( None, 'Imported candidate from CSV', ) party_comp = re.sub(' +', ' ', party) party_id = UNKNOWN_PARTY_ID if party_comp in party_name_to_id.keys(): party_id = party_name_to_id[party_comp] party = party_comp else: party_id = party_name_to_id['Unknown Party'] party = 'Unknown Party' if party_id == UNKNOWN_PARTY_ID and party_comp not in party_id_missing.keys(): party_id_missing[party_comp] = 1 person.party_memberships = { election_data.slug: { 'id': party_id, 'name': party, 'imported_name': party_comp } } person.record_version(change_metadata) try: person.save_to_popit(api) except HttpClientError as hce: print "Got an HttpClientError:", hce.content raise if len(party_id_missing) > 0: print "Unmatched party names:" for name in party_id_missing.keys(): print name