def post(self, request): logging_dict = { 'client_ip': get_client_ip(request), 'user': request.user, 'body': request.body[:1024], 'election': '-1', } serialized_data = AddVoteSerializer(data=request.body) if serialized_data.is_valid(): election = self._get_next_election() if not election: logger.error('User has no valid election left', extra=logging_dict) messages.add_message( request, messages.ERROR, 'You\'ve no active election. This incident is logged', AlertTags.DANGER) return self.get(request) logging_dict['election'] = election.id votes_for_this_ip = None if election.votes_per_ip > 0: votes_for_this_ip = election.vote_ips.filter( ip=logging_dict['client_ip']).first() if votes_for_this_ip: if votes_for_this_ip.votes >= election.votes_per_ip: logger.error('User is voting for extra votes', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Only {} vote(s) are allowed per IP'.format( election.votes_per_ip), AlertTags.DANGER) request.method = 'POST' return VoterLogoutView.as_view()(request) # Validate is valid voter voters = election.voter voter = voters[0] if len(voters) != 1: logger.error('User is not a valid voter', extra=logging_dict) messages.add_message(request, messages.ERROR, 'You\'re not a voter.', AlertTags.DANGER) return self.get(request) # Validate key if election.is_key_required: key = serialized_data.validated_data['key'] if not key or key.upper() != voter.key.upper(): logger.error('User has entered invalid key', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Invalid Key. This incident is logged', AlertTags.DANGER) return self.get(request) # Validate votes votes = serialized_data.validated_data['votes'] keys = dict() for key, value in votes.items(): keys[key] = (False, value) # Checks that there is a vote for all candidate in election that are visible to voter # Like it checks for posts for which voter should be allowed to vote (UG/PG) # It also checks if number of non-neutral votes for a post are less then total posts. for post in election.posts.all(): non_neutral_nota_votes = 0 candidate_count = len(post.human_candidates) post_processed = False for candidate in post.auto_candidates: if candidate.id in keys: if post_processed: logger.error( 'Multiple entries for non auto candidates', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Found invalid data. Attempt is logged', AlertTags.DANGER) return self.get(request) keys[candidate.id] = (True, keys[candidate.id][1]) # Checking if NO is voted for neutral and NOTA if keys[candidate.id][1] == VoteTypes.NO: logger.log( 'User voted for NO for auto accounts. User is stupid', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Found invalid data. Attempt is logged', AlertTags.DANGER) return self.get(request) post_processed = True for candidate in post.human_candidates: if candidate.id in keys: if post_processed: logger.error( 'Entries for normal candidates is present when with auto candidates', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Found invalid data. Attempt is logged', AlertTags.DANGER) return self.get(request) keys[candidate.id] = (True, keys[candidate.id][1]) if keys[candidate.id][1] == VoteTypes.NO: """ Checking if NO vote is casted for a post where candidates > post """ if candidate_count > post.number: logger.error( 'Voted NO for a post where candidates > post', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Found invalid data. Attempt is logged', AlertTags.DANGER) return self.get(request) non_neutral_nota_votes += 1 if non_neutral_nota_votes > post.number: logger.error( 'Number of non-neutral votes are greater than number of posts', extra=logging_dict) messages.add_message( request, messages.ERROR, 'Found invalid data. Attempt is logged', AlertTags.DANGER) return self.get(request) # Check if there is an entry present which are not pk for candidates in election available # To person for key, value in keys.items(): if not value[0]: logger.error( 'User has entered a candidate id %s which is not a valid value', key, extra=logging_dict) messages.add_message( request, messages.ERROR, 'Found corrupted data. Incident will be reported', AlertTags.DANGER) return self.get(request) # create votes with transaction.atomic(): # Create Session. Each vote will be associated with a session vote_session = VoteSession.objects.create(election=election) if not votes_for_this_ip: votes_for_this_ip = VoteIPMap( election=election, votes=1, ip=logging_dict['client_ip'], ) else: votes_for_this_ip.votes += 1 votes_for_this_ip.save() vote_list = [] for key, value in keys.items(): vote = Vote(candidate_id=key, session=vote_session) vote.vote = value[1] vote_list.append(vote) # Add votes to server Vote.objects.bulk_create(vote_list) voter.voted = True voter.save() messages.add_message(request, messages.INFO, 'Your vote has been recorded', AlertTags.SUCCESS) return self.get(request, new_session=True) else: logger.error('Request data is invalidated by serializer', extra=logging_dict) messages.add_message( request, messages.INFO, 'Received corrupted data. Incident is recorded', AlertTags.DANGER) return self.get(request)