def form_valid(self, form): post_id = form.cleaned_data["post_id"] with transaction.atomic(): post = get_object_or_404(Post, slug=post_id) raise_if_locked(self.request, post, self.election_data) change_metadata = get_change_metadata(self.request, form.cleaned_data["source"]) person = get_object_or_404(Person, id=form.cleaned_data["person_id"]) LoggedAction.objects.create( user=self.request.user, action_type="candidacy-delete", ip_address=get_client_ip(self.request), popit_person_new_version=change_metadata["version_id"], person=person, source=change_metadata["information_source"], ) memberships_to_delete = person.memberships.filter( post=post, role=self.election_data.candidate_membership_role, post_election__election=self.election_data, ) for m in memberships_to_delete: raise_if_unsafe_to_delete(m) m.delete() check_no_candidancy_for_election(person, self.election_data) person.not_standing.add(self.election_data) person.record_version(change_metadata) person.save() if self.request.is_ajax(): return JsonResponse({"success": True}) return get_redirect_to_post(self.election, post)
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 clean(self): if "extra_election_id" in self.data: # We're adding a new election election = Election.objects.get( slug=self.data["extra_election_id"]) # Add this new election to elections_with_fields if election not in self.elections_with_fields: self.elections_with_fields.append(election) # Then re-create the form with the new election in self.add_elections_fields(self.elections_with_fields) # Now we need to re-clean the data, with the new election in it self._clean_fields() for field in self.changed_data: if field.startswith("constituency_"): # We're changing a constituency, so we need to make sure # that's allowed if self.initial.get(field): membership = self.initial["person"].memberships.get( ballot__post__slug=self.initial[field], ballot__election__slug=field.replace( "constituency_", ""), ) try: raise_if_unsafe_to_delete(membership) except UnsafeToDelete as e: self.add_error(field, e) cleaned_data = super().clean() return self.check_party_and_constituency_are_selected(cleaned_data)
def mark_as_unsure_if_standing(person, election_data, post): # Remove any existing candidacy: for membership in Membership.objects.filter(ballot__election=election_data, person=person): raise_if_unsafe_to_delete(membership) membership.delete() # Now remove any entry that indicates that they're standing in # this election: person.not_standing.remove(election_data)
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 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 mark_as_not_standing(person, election_data, post): # Remove any existing candidacy: for membership in Membership.objects.filter( ballot__election=election_data, person=person, # n.b. we are planning to make "not standing" post # specific in the future, in which case we would also want # this line: # post__slug=post_slug, ): raise_if_unsafe_to_delete(membership) membership.delete() from candidates.models.constraints import check_no_candidancy_for_election check_no_candidancy_for_election(person, election_data) person.not_standing.add(election_data)
def mark_as_standing(person, election_data, post, party, party_list_position): # First, if the person is marked as "not standing" in that # election, remove that record: person.not_standing.remove(election_data) membership_ids_to_remove = set() membership = None # This person might already be marked as standing for this # post. In that case, we need to make sure we preserve that # existing membership, since there may be metadata attached to it # (such as the extra.elected property or CandidateResult records) # which would be lost if we deleted and recreated the membership. # Go through the person's existing candidacies for this election: for existing_membership in Membership.objects.filter( post_election__election=election_data, role=election_data.candidate_membership_role, person=person, ): if existing_membership.post == post: membership = existing_membership else: membership_ids_to_remove.add(existing_membership.id) # If there was no existing membership, we need to create one: if not membership: membership = Membership.objects.create( post=post, person=person, role=election_data.candidate_membership_role, post_election=election_data.postextraelection_set.get(post=post), ) # Update the party list position in case it's changed: membership.party_list_position = party_list_position membership.save() # Update the party, in case it's changed: membership.party = party membership.save() # Now remove any memberships that shouldn't now be there: for membership_to_remove in Membership.objects.filter( pk__in=membership_ids_to_remove ): raise_if_unsafe_to_delete(membership_to_remove) membership_to_remove.delete()
def revert_person_from_version_data(person, person_extra, version_data, part_of_merge=False): from popolo.models import Membership, Organization, Post from candidates.models import raise_if_unsafe_to_delete from elections.models import Election for field in settings.SIMPLE_POPOLO_FIELDS: new_value = version_data.get(field.name) if new_value: setattr(person, field.name, new_value) else: setattr(person, field.name, '') # Remove any old values in complex fields: for field in ComplexPopoloField.objects.all(): related_manager = getattr(person, field.popolo_array) type_kwargs = {field.info_type_key: field.info_type} related_manager.filter(**type_kwargs).delete() # Then recreate any that should be there: for field in ComplexPopoloField.objects.all(): new_value = version_data.get(field.name, '') if new_value: person_extra.update_complex_field(field, version_data[field.name]) # Remove any extra field data and create them from the JSON: person.extra_field_values.all().delete() extra_fields_from_version = version_data.get('extra_fields', {}) for extra_field in ExtraField.objects.all(): value = extra_fields_from_version.get(extra_field.key) if value is not None: person.extra_field_values.create( field=extra_field, value=value, ) # Other fields to preserve: person.image = version_data.get('image') # Remove all other names, and recreate: person.other_names.all().delete() for on in version_data.get('other_names', []): person.other_names.create( name=on['name'], note=on.get('note', ''), start_date=on.get('start_date'), end_date=on.get('end_date'), ) # Remove all identifiers, and recreate: person.identifiers.all().delete() for i in version_data.get('identifiers', []): person.identifiers.create( scheme=i['scheme'], identifier=i['identifier'], ) # Remove all candidacies, and recreate: for membership in Membership.objects.filter( person=person_extra.base, role=F('post_election__election__candidate_membership_role')): # At the moment the merge code has its own way of preserving # the uk_results CandidateResult data (see # additional_merge_actions), so they will be # recreated. (FIXME: omitting this check when merging does # mean that we're not checking for other models in the future # that may have a foreign key to Membership when doing # merges.) if not part_of_merge: raise_if_unsafe_to_delete(membership) membership.delete() # Also remove the indications of elections that this person is # known not to be standing in: person_extra.not_standing.clear() for election_slug, standing_in in version_data['standing_in'].items(): election = Election.objects.get(slug=election_slug) # If the value for that election slug is None, then that means # the person is known not to be standing: if standing_in is None: person_extra.not_standing.add(election) else: # Get the corresponding party membership data: party = Organization.objects.get( extra__slug=version_data['party_memberships'][election_slug] ['id']) post = Post.objects.get(extra__slug=standing_in['post_id']) Membership.objects.create( on_behalf_of=party, person=person, post=post, role=election.candidate_membership_role, elected=standing_in.get('elected'), party_list_position=standing_in.get('party_list_position'), post_election=election.postextraelection_set.get( postextra=post.extra)) person.save() person_extra.save() try: update_twitter_user_id(person) except TwitterAPITokenMissing: pass
def revert_person_from_version_data(person, version_data): from popolo.models import Membership from candidates.models import raise_if_unsafe_to_delete from elections.models import Election for field in settings.SIMPLE_POPOLO_FIELDS: new_value = version_data.get(field.name) if new_value: setattr(person, field.name, new_value) else: setattr(person, field.name, "") person.favourite_biscuit = version_data.get("extra_fields", {}).get("favourite_biscuits") # Remove old PersonIdentifier objects from people.models import PersonIdentifier PersonIdentifier.objects.filter( person=person).editable_value_types().delete() # Add PersonIdentifier objects we want back again # TODO: https://github.com/DemocracyClub/yournextrepresentative/issues/697 for field in PersonIdentifierFields: new_value = version_data.get(field.name, "") if new_value: PersonIdentifier.objects.update_or_create(person=person, value=new_value, value_type=field.name) # Remove all other names, and recreate: person.other_names.all().delete() for on in version_data.get("other_names", []): person.other_names.create( name=on["name"], note=on.get("note", ""), start_date=on.get("start_date"), end_date=on.get("end_date"), ) # Remove all candidacies, and recreate: qs = (Membership.objects.filter(person=person).filter(result=None).filter( ballot__candidates_locked=False)) for membership in qs: raise_if_unsafe_to_delete(membership) membership.delete() # Also remove the indications of elections that this person is # known not to be standing in: person.not_standing.clear() for ballot_paper_id, candidacy in version_data["candidacies"].items(): ballot = Ballot.objects.get(ballot_paper_id=ballot_paper_id) # Get the corresponding party membership data: party = Party.objects.get(ec_id=candidacy["party"]) Membership.objects.update_or_create( person=person, ballot=ballot, defaults={ "party": party, "post": ballot.post, # TODO: Remove this "elected": candidacy.get("elected"), "party_list_position": candidacy.get("party_list_position"), }, ) for election_slug in version_data.get("not_standing", []): election = Election.objects.get(slug=election_slug) person.not_standing.add(election) person.save()
def revert_person_from_version_data(person, version_data): from popolo.models import Membership, Organization, Post from candidates.models import raise_if_unsafe_to_delete from elections.models import Election for field in settings.SIMPLE_POPOLO_FIELDS: new_value = version_data.get(field.name) if new_value: setattr(person, field.name, new_value) else: setattr(person, field.name, "") # Remove old PersonIdentifier objects from people.models import PersonIdentifier PersonIdentifier.objects.filter( person=person).editable_value_types().delete() # Add PersonIdentifier objects we want back again # TODO: https://github.com/DemocracyClub/yournextrepresentative/issues/697 for field in PersonIdentifierFields: new_value = version_data.get(field.name, "") if new_value: PersonIdentifier.objects.update_or_create(person=person, value=new_value, value_type=field.name) # Remove all other names, and recreate: person.other_names.all().delete() for on in version_data.get("other_names", []): person.other_names.create( name=on["name"], note=on.get("note", ""), start_date=on.get("start_date"), end_date=on.get("end_date"), ) # Remove all candidacies, and recreate: for membership in Membership.objects.filter(person=person): raise_if_unsafe_to_delete(membership) membership.delete() # Also remove the indications of elections that this person is # known not to be standing in: person.not_standing.clear() for election_slug, standing_in in version_data["standing_in"].items(): election = Election.objects.get(slug=election_slug) # If the value for that election slug is None, then that means # the person is known not to be standing: if standing_in is None: person.not_standing.add(election) else: # Get the corresponding party membership data: party = Party.objects.get( legacy_slug=version_data["party_memberships"][election_slug] ["id"]) post = Post.objects.get(slug=standing_in["post_id"]) Membership.objects.create( party=party, person=person, post=post, role=election.candidate_membership_role, elected=standing_in.get("elected"), party_list_position=standing_in.get("party_list_position"), post_election=election.postextraelection_set.get(post=post), ) person.save()