Example #1
0
def election_questions_validator(questions):
    '''
    Validates a list of questions checking the voting method used, etc
    '''
    error = django_forms.ValidationError(_('Invalid questions format'))

    # we need at least one question
    if not isinstance(questions, list) or len(questions) < 1:
        raise error

    for question in questions:
        # check type
        if not isinstance(question, dict):
            raise error

        # check it contains the valid elements
        if not list_contains_all(['a', 'answers', 'max', 'min', 'question',
            'randomize_answer_order', 'tally_type', 'layout'], question.keys()):
            raise error

        # let the voting system check the rest
        voting_system = get_voting_system_by_id(question['tally_type'])
        if not voting_system:
            raise error
        voting_system.validate_question(question)
Example #2
0
def election_questions_validator(questions):
    """
    Validates a list of questions checking the voting method used, etc
    """
    error = django_forms.ValidationError(_("Invalid questions format"))

    # we need at least one question
    if not isinstance(questions, list) or len(questions) < 1:
        raise error

    for question in questions:
        # check type
        if not isinstance(question, dict):
            raise error

        # check it contains the valid elements
        if not list_contains_all(
            ["a", "answers", "max", "min", "question", "randomize_answer_order", "tally_type"], question.keys()
        ):
            raise error

        # let the voting system check the rest
        voting_system = get_voting_system_by_id(question["tally_type"])
        if not voting_system:
            raise error
        voting_system.validate_question(question)
def election_questions_validator(questions):
    '''
    Validates a list of questions checking the voting method used, etc
    '''
    error = django_forms.ValidationError(_('Invalid questions format'))

    # we need at least one question
    if not isinstance(questions, list) or len(questions) < 1:
        raise error

    for question in questions:
        # check type
        if not isinstance(question, dict):
            raise error

        # check it contains the valid elements
        if not list_contains_all([
                'a', 'answers', 'max', 'min', 'question',
                'randomize_answer_order', 'tally_type'
        ], question.keys()):
            raise error

        # let the voting system check the rest
        voting_system = get_voting_system_by_id(question['tally_type'])
        if not voting_system:
            raise error
        voting_system.validate_question(question)
Example #4
0
    def __init__(self, request, election, *args, **kwargs):
        super(VoteForm, self).__init__(*args, **kwargs)
        self.election = election
        self.request = request

        i = 0
        for question in election.questions:
            voting_system = get_voting_system_by_id(question['tally_type'])
            field = voting_system.get_question_field(election, question)
            self.fields.insert(0, 'question%d' % i, field)
            i += 1
Example #5
0
    def __init__(self, request, election, *args, **kwargs):
        super(VoteForm, self).__init__(*args, **kwargs)
        self.election = election
        self.request = request

        i = 0
        for question in election.questions:
            voting_system = get_voting_system_by_id(question['tally_type'])
            field = voting_system.get_question_field(election, question)
            self.fields.insert(0, 'question%d' % i, field)
            i += 1
Example #6
0
    def get_winning_option(self):
        """
        Returns data of the winning option for the first question or throw an exception
        """
        if not self.result:
            raise Exception("Election not tallied yet")
        elif len(self.result["counts"]) == 0 or not get_voting_system_by_id(self.result["counts"][0]["tally_type"]):
            raise Exception("Unknown election result type: %s" % self.result["counts"][0]["tally_type"])

        winner = dict(value="", total_count=0.0, total_count_percentage=0.0)

        for answer in self.result["counts"][0]["answers"]:
            if answer["value"] == self.result["counts"][0]["winners"][0]:
                winner = answer

        return winner
Example #7
0
    def get_winning_option(self):
        '''
        Returns data of the winning option for the first question or throw an exception
        '''
        if not self.result:
            raise Exception('Election not tallied yet')
        elif len(self.result['counts']) == 0 or\
            not get_voting_system_by_id(self.result['counts'][0]['tally_type']):
            raise Exception('Unknown election result type: %s' % self.result['counts'][0]['tally_type'])

        winner = dict(value='', total_count=0.0, total_count_percentage=0.0)

        for answer in self.result['counts'][0]['answers']:
            if answer['value'] == self.result['counts'][0]['winners'][0]:
                winner = answer

        return winner
Example #8
0
    def get_winning_option(self):
        '''
        Returns data of the winning option for the first question or throw an exception
        '''
        if not self.result:
            raise Exception('Election not tallied yet')
        elif len(self.result['counts']) == 0 or\
            not get_voting_system_by_id(self.result['counts'][0]['tally_type']):
            raise Exception('Unknown election result type: %s' % self.result['counts'][0]['tally_type'])

        winner = dict(value='', total_count=0.0, total_count_percentage=0.0)

        for answer in self.result['counts'][0]['answers']:
            if answer['value'] == self.result['counts'][0]['winners'][0]:
                winner = answer

        return winner
Example #9
0
    def compute_result(self):
        '''
        Computes the result of the election
        '''
        from agora_site.agora_core.models import CastVote

        # Query with the direct votes in this election
        q=self.cast_votes.filter(
            is_counted=True,
            invalidated_at_date=None
        ).values('voter__id').query

        # Query with the delegated votes
        self.delegated_votes = CastVote.objects.filter(
            election=self.agora.delegation_election,
            is_direct=False,
            is_counted=True,
            invalidated_at_date=None
            # we exclude from this query the people who voted directly so that
            # you cannot vote twice
        ).exclude(
            is_direct=False,
            voter__id__in=q
        )

        # These are all the people that can vote in this election
        self.electorate = self.agora.members.all()

        # These are all the direct votes, even from those who are not elegible 
        # to vote in this election
        nodes = self.cast_votes.filter(is_direct=True,
            #is_counted=True, FIXME
            invalidated_at_date=None)

        # These are all the delegation votes, i.e. those that point to a delegate
        #edges = self.agora.delegation_election.cast_votes.filter(
            #is_direct=False, invalidated_at_date=None)
        edges = self.delegated_votes

        # list of saved paths. A path represent a list of users who delegate
        # into a given vote.
        # A path has the following format:
        #{
            #user_ids: [id1, id2, ...],
            #answers: [ question1_plaintext_answer, question2_plaintext_answer, ..],
            #is_broken_loop: True|False
        #}
        # note that the user_ids do NOT include the last user in the chain
        # also note that is_broken_loop is set to true if either the loop is
        # closed (infinite) or does not end in a leaf (=node)
        paths = []

        # A dictionary where the number of delegated voted per delegate is
        # stored. This dict is used only for recording the number of delegated
        # votes a delegate has.
        #
        # The keys are the user_ids of the delegates, and the values are
        # the number of delegated votes.
        # Note that because of chains of delegations, the same vote can be
        # counted multiple times.
        delegation_counts = dict()

        def update_delegation_counts(vote):
            '''
            function used to update the delegation counts, for each valid vote.
            it basically goes deep in the delegation chain, updating the count
            for each delegate.

            NOTE: Calling to this function assumes a valid path for the vote,
            which means for example that the delegation chain is public.
            '''
            # if there is no vote we have nothing to do
            if not vote:
                return

            def increment_delegate(delegate_id):
                '''
                Increments the delegate count or sets it to one if doesn't it
                exist
                '''
                if delegate_id in delegation_counts:
                    delegation_counts[delegate_id] += 1
                else:
                    delegation_counts[delegate_id] = 1

            i = 0
            while not vote.is_direct:
                i += 1
                next_delegate = vote.get_delegate()
                if nodes.filter(voter=next_delegate).count() == 1:
                    increment_delegate(next_delegate.id)
                    return
                elif edges.filter(voter=next_delegate).count() == 1:
                    increment_delegate(next_delegate.id)
                    vote = edges.filter(voter=next_delegate)[0]
                else:
                    raise Exception('Broken delegation chain')

        def get_path_for_user(user_id):
            '''
            Given an user id, checks if it's already in any known path, and 
            return it if that path is found. Returns None otherwise.
            '''
            for path in paths:
                if user_id in path["user_ids"]:
                    return path
            return None

        def get_vote_for_voter(voter_id):
            '''
            Given a voter (an User), returns the vote of the vote of this voter
            on the election. It will be either a proxy or a direct vote
            '''
            if nodes.filter(voter_id=voter_id).count() == 1:
                return nodes.filter(voter_id=voter_id)[0]
            elif edges.filter(voter_id=voter_id).count() == 1:
                return edges.filter(voter_id=voter_id)[0]
            else:
                return None

        if self.election_type not in dict(parse_voting_methods()):
            raise Exception('do not know how to count this type of voting')

        voting_systems = []
        tallies = []

        import copy
        # result is in the same format as get_result_pretty(). Initialized here
        result = copy.deepcopy(self.questions)

        # setup the initial data common to all voting system
        i = 0
        for question in result:
            tally_type = self.election_type
            if 'tally_type' in question:
                tally_type = question['tally_type']
            voting_system = get_voting_system_by_id(tally_type)
            tally = voting_system.create_tally(self, i)
            voting_systems.append(voting_system)
            tallies.append(tally)
            i += 1

            question['a'] = "question/result/" + voting_system.get_id()
            question['winners'] = []
            question['total_votes'] = 0

            for answer in question['answers']:
                answer['a'] = "answer/result/" + voting_system.get_id()
                answer['total_count'] = 0
                answer['total_count_percentage'] = 0

            # prepare the tally
            tally.pre_tally(result)

        num_delegated_votes = 0
        def add_vote(user_answers, is_delegated):
            '''
            Given the answers of a vote, update the result
            '''
            for tally in tallies:
                tally.add_vote(voter_answers=user_answers, result=result,
                    is_delegated=is_delegated)

        # Here we go! for each voter, we try to find it in the paths, or in
        # the proxy vote chain, or in the direct votes pool
        for voter in self.electorate.all():
            path_for_user = get_path_for_user(voter.id)

            # Found the user in a known path
            if path_for_user and not path_for_user['is_broken_loop']:
                # found a path to which the user belongs

                # update delegation counts
                num_delegated_votes += 1
                add_vote(path_for_user['answers'], is_delegated=True)
                update_delegation_counts(get_vote_for_voter(voter.id))

            # found the user in a direct vote
            elif nodes.filter(voter=voter).count() == 1:
                vote = nodes.filter(voter=voter)[0]
                add_vote(vote.data["answers"], is_delegated=False)

            # found the user in an edge (delegated vote), but not yet in a path
            elif edges.filter(voter=voter).count() == 1:
                path = dict(
                    user_ids=[voter.id],
                    answers=[],
                    is_broken_loop=False
                )

                current_edge = edges.filter(voter=voter)[0]
                loop = True
                i = 0
                while loop:
                    i += 1
                    delegate = current_edge.get_delegate()
                    path_for_user = get_path_for_user(delegate.id)

                    if delegate.id in path['user_ids']:
                        # wrong path! loop found, vote won't be counted
                        path['is_broken_loop'] = True
                        paths += [path]
                        loop = False
                    elif path_for_user and not path_for_user['is_broken_loop']:
                        # extend the found path and count a new vote
                        path_for_user['user_ids'] += path['user_ids']

                        # Count the vote
                        num_delegated_votes += 1
                        add_vote(path_for_user['answers'], is_delegated=True)
                        update_delegation_counts(get_vote_for_voter(voter.id))
                        loop = False
                    elif nodes.filter(voter=delegate).count() == 1:
                        # The delegate voted directly
                        vote = nodes.filter(voter=delegate)[0]

                        # if the vote of the delegate is not public, then
                        # it doesn't count, we have finished
                        if not vote.is_public:
                            # wrong path! loop found, vote won't be counted
                            path['is_broken_loop'] = True
                            paths += [path]
                            loop = False
                            break

                        # add the path and count the vote
                        path["answers"] = vote.data['answers']
                        paths += [path]
                        num_delegated_votes += 1
                        add_vote(vote.data['answers'], is_delegated=True)
                        update_delegation_counts(get_vote_for_voter(voter.id))
                        loop = False

                    elif edges.filter(voter=delegate).count() == 1:
                        # the delegate also delegated
                        vote = edges.filter(voter=delegate)[0]

                        # if the vote of the delegate is not public, then
                        # it doesn't count, we have finished
                        if not vote.is_public:
                            # wrong path! loop found, vote won't be counted
                            path['is_broken_loop'] = True
                            paths += [path]
                            loop = False
                            break

                        # vote is public, so continue looping
                        path['user_ids'] += [delegate.id]
                        current_edge = vote
                    else:
                        # broken path! we cannot continue
                        path['is_broken_loop'] = True
                        paths += [path]
                        loop = False

        if not self.extra_data:
            self.extra_data = dict()

        # post process the tally
        for tally in tallies:
            tally.post_tally(result)

        self.result = dict(
            a= "result",
            counts = result,
            total_votes = result[0]['total_votes'] + result[0]['dirty_votes'],
            electorate_count = self.electorate.count(),
            total_delegated_votes = num_delegated_votes
        )

        tally_log = []
        for tally in tallies:
            tally_log.append(tally.get_log())
        self.extra_data['tally_log'] = tally_log

        def rank_delegate(delegate_count, delegation_counts):
            if delegate_count == 0:
                return None
            count = 0
            for key, value in delegation_counts.iteritems():
                if delegate_count <= value:
                    count += 1
            return count

        # refresh DelegateElectionCount items
        from agora_site.agora_core.models.delegateelectioncount import DelegateElectionCount
        DelegateElectionCount.objects.filter(election=self).delete()
        for key, value in delegation_counts.iteritems():
            dec = DelegateElectionCount(election=self, count=value)
            dec.rank = rank_delegate(value, delegation_counts)
            dec.count_percentage = value * 100.0 / self.result['total_votes']
            dec.delegate_vote = get_vote_for_voter(int(key))
            dec.delegate_id = int(key)
            dec.save()

        self.delegated_votes_frozen_at_date = self.voters_frozen_at_date =\
            self.result_tallied_at_date = timezone.now()

        # TODO: update result_hash
        self.save()
Example #10
0
    def compute_result(self):
        '''
        Computes the result of the election
        '''
        from agora_site.agora_core.models import CastVote

        # Query with the direct votes in this election
        q = self.cast_votes.filter(
            is_counted=True,
            invalidated_at_date=None).values('voter__id').query

        # Query with the delegated votes
        self.delegated_votes = CastVote.objects.filter(
            election=self.agora.delegation_election,
            is_direct=False,
            is_counted=True,
            invalidated_at_date=None
            # we exclude from this query the people who voted directly so that
            # you cannot vote twice
        ).exclude(is_direct=False, voter__id__in=q)

        # These are all the people that can vote in this election
        self.electorate = self.agora.members.all()

        # These are all the direct votes, even from those who are not elegible
        # to vote in this election
        nodes = self.cast_votes.filter(
            is_direct=True,
            #is_counted=True, FIXME
            invalidated_at_date=None)

        # These are all the delegation votes, i.e. those that point to a delegate
        #edges = self.agora.delegation_election.cast_votes.filter(
        #is_direct=False, invalidated_at_date=None)
        edges = self.delegated_votes

        # list of saved paths. A path represent a list of users who delegate
        # into a given vote.
        # A path has the following format:
        #{
        #user_ids: [id1, id2, ...],
        #answers: [ question1_plaintext_answer, question2_plaintext_answer, ..],
        #is_broken_loop: True|False
        #}
        # note that the user_ids do NOT include the last user in the chain
        # also note that is_broken_loop is set to true if either the loop is
        # closed (infinite) or does not end in a leaf (=node)
        paths = []

        # A dictionary where the number of delegated voted per delegate is
        # stored. This dict is used only for recording the number of delegated
        # votes a delegate has.
        #
        # The keys are the user_ids of the delegates, and the values are
        # the number of delegated votes.
        # Note that because of chains of delegations, the same vote can be
        # counted multiple times.
        delegation_counts = dict()

        def update_delegation_counts(vote):
            '''
            function used to update the delegation counts, for each valid vote.
            it basically goes deep in the delegation chain, updating the count
            for each delegate.

            NOTE: Calling to this function assumes a valid path for the vote,
            which means for example that the delegation chain is public.
            '''
            # if there is no vote we have nothing to do
            if not vote:
                return

            def increment_delegate(delegate_id):
                '''
                Increments the delegate count or sets it to one if doesn't it
                exist
                '''
                if delegate_id in delegation_counts:
                    delegation_counts[delegate_id] += 1
                else:
                    delegation_counts[delegate_id] = 1

            i = 0
            while not vote.is_direct:
                i += 1
                next_delegate = vote.get_delegate()
                if nodes.filter(voter=next_delegate).count() == 1:
                    increment_delegate(next_delegate.id)
                    return
                elif edges.filter(voter=next_delegate).count() == 1:
                    increment_delegate(next_delegate.id)
                    vote = edges.filter(voter=next_delegate)[0]
                else:
                    raise Exception('Broken delegation chain')

        def get_path_for_user(user_id):
            '''
            Given an user id, checks if it's already in any known path, and 
            return it if that path is found. Returns None otherwise.
            '''
            for path in paths:
                if user_id in path["user_ids"]:
                    return path
            return None

        def get_vote_for_voter(voter_id):
            '''
            Given a voter (an User), returns the vote of the vote of this voter
            on the election. It will be either a proxy or a direct vote
            '''
            if nodes.filter(voter_id=voter_id).count() == 1:
                return nodes.filter(voter_id=voter_id)[0]
            elif edges.filter(voter_id=voter_id).count() == 1:
                return edges.filter(voter_id=voter_id)[0]
            else:
                return None

        if self.election_type not in dict(parse_voting_methods()):
            raise Exception('do not know how to count this type of voting')

        voting_systems = []
        tallies = []

        import copy
        # result is in the same format as get_result_pretty(). Initialized here
        result = copy.deepcopy(self.questions)

        # setup the initial data common to all voting system
        i = 0
        for question in result:
            tally_type = self.election_type
            if 'tally_type' in question:
                tally_type = question['tally_type']
            voting_system = get_voting_system_by_id(tally_type)
            tally = voting_system.create_tally(self, i)
            voting_systems.append(voting_system)
            tallies.append(tally)
            i += 1

            question['a'] = "question/result/" + voting_system.get_id()
            question['winners'] = []
            question['total_votes'] = 0

            for answer in question['answers']:
                answer['a'] = "answer/result/" + voting_system.get_id()
                answer['total_count'] = 0
                answer['total_count_percentage'] = 0

            # prepare the tally
            tally.pre_tally(result)

        num_delegated_votes = 0

        def add_vote(user_answers, is_delegated):
            '''
            Given the answers of a vote, update the result
            '''
            for tally in tallies:
                tally.add_vote(voter_answers=user_answers,
                               result=result,
                               is_delegated=is_delegated)

        # Here we go! for each voter, we try to find it in the paths, or in
        # the proxy vote chain, or in the direct votes pool
        for voter in self.electorate.all():
            path_for_user = get_path_for_user(voter.id)

            # Found the user in a known path
            if path_for_user and not path_for_user['is_broken_loop']:
                # found a path to which the user belongs

                # update delegation counts
                num_delegated_votes += 1
                add_vote(path_for_user['answers'], is_delegated=True)
                update_delegation_counts(get_vote_for_voter(voter.id))

            # found the user in a direct vote
            elif nodes.filter(voter=voter).count() == 1:
                vote = nodes.filter(voter=voter)[0]
                add_vote(vote.data["answers"], is_delegated=False)

            # found the user in an edge (delegated vote), but not yet in a path
            elif edges.filter(voter=voter).count() == 1:
                path = dict(user_ids=[voter.id],
                            answers=[],
                            is_broken_loop=False)

                current_edge = edges.filter(voter=voter)[0]
                loop = True
                i = 0
                while loop:
                    i += 1
                    delegate = current_edge.get_delegate()
                    path_for_user = get_path_for_user(delegate.id)

                    if delegate.id in path['user_ids']:
                        # wrong path! loop found, vote won't be counted
                        path['is_broken_loop'] = True
                        paths += [path]
                        loop = False
                    elif path_for_user and not path_for_user['is_broken_loop']:
                        # extend the found path and count a new vote
                        path_for_user['user_ids'] += path['user_ids']

                        # Count the vote
                        num_delegated_votes += 1
                        add_vote(path_for_user['answers'], is_delegated=True)
                        update_delegation_counts(get_vote_for_voter(voter.id))
                        loop = False
                    elif nodes.filter(voter=delegate).count() == 1:
                        # The delegate voted directly
                        vote = nodes.filter(voter=delegate)[0]

                        # if the vote of the delegate is not public, then
                        # it doesn't count, we have finished
                        if not vote.is_public:
                            # wrong path! loop found, vote won't be counted
                            path['is_broken_loop'] = True
                            paths += [path]
                            loop = False
                            break

                        # add the path and count the vote
                        path["answers"] = vote.data['answers']
                        paths += [path]
                        num_delegated_votes += 1
                        add_vote(vote.data['answers'], is_delegated=True)
                        update_delegation_counts(get_vote_for_voter(voter.id))
                        loop = False

                    elif edges.filter(voter=delegate).count() == 1:
                        # the delegate also delegated
                        vote = edges.filter(voter=delegate)[0]

                        # if the vote of the delegate is not public, then
                        # it doesn't count, we have finished
                        if not vote.is_public:
                            # wrong path! loop found, vote won't be counted
                            path['is_broken_loop'] = True
                            paths += [path]
                            loop = False
                            break

                        # vote is public, so continue looping
                        path['user_ids'] += [delegate.id]
                        current_edge = vote
                    else:
                        # broken path! we cannot continue
                        path['is_broken_loop'] = True
                        paths += [path]
                        loop = False

        if not self.extra_data:
            self.extra_data = dict()

        # post process the tally
        for tally in tallies:
            tally.post_tally(result)

        self.result = dict(a="result",
                           counts=result,
                           total_votes=result[0]['total_votes'] +
                           result[0]['dirty_votes'],
                           electorate_count=self.electorate.count(),
                           total_delegated_votes=num_delegated_votes)

        tally_log = []
        for tally in tallies:
            tally_log.append(tally.get_log())
        self.extra_data['tally_log'] = tally_log

        def rank_delegate(delegate_count, delegation_counts):
            if delegate_count == 0:
                return None
            count = 0
            for key, value in delegation_counts.iteritems():
                if delegate_count <= value:
                    count += 1
            return count

        # refresh DelegateElectionCount items
        from agora_site.agora_core.models.delegateelectioncount import DelegateElectionCount
        DelegateElectionCount.objects.filter(election=self).delete()
        for key, value in delegation_counts.iteritems():
            dec = DelegateElectionCount(election=self, count=value)
            dec.rank = rank_delegate(value, delegation_counts)
            dec.count_percentage = value * 100.0 / self.result['total_votes']
            dec.delegate_vote = get_vote_for_voter(int(key))
            dec.delegate_id = int(key)
            dec.save()

        self.delegated_votes_frozen_at_date = self.voters_frozen_at_date =\
            self.result_tallied_at_date = timezone.now()

        # TODO: update result_hash
        self.save()
Example #11
0
    def compute_result(self):
        """
        Computes the result of the election
        """
        from agora_site.agora_core.models import CastVote

        # maximum delegation depth, so that we don't enter in near-infinite loops
        MAX_DELEGATION_DEPTH = 20

        # Query with the direct votes in this election
        q = self.cast_votes.filter(is_counted=True, invalidated_at_date=None).values("voter__id").query

        # Query with the delegated votes
        self.delegated_votes = CastVote.objects.filter(
            election=self.agora.delegation_election,
            is_direct=False,
            is_counted=True,
            invalidated_at_date=None
            # we exclude from this query the people who voted directly so that
            # you cannot vote twice
        ).exclude(is_direct=False, voter__id__in=q)

        # These are all the people that can vote in this election
        self.electorate = self.agora.members.all()

        # These are all the direct votes, even from those who are not elegible
        # to vote in this election
        nodes = self.cast_votes.filter(
            is_direct=True,
            # is_counted=True, FIXME
            invalidated_at_date=None,
        )

        # These are all the delegation votes, i.e. those that point to a delegate
        # edges = self.agora.delegation_election.cast_votes.filter(
        # is_direct=False, invalidated_at_date=None)
        edges = self.delegated_votes

        # list of saved paths. A path represent a list of users who delegate
        # into a given vote.
        # A path has the following format:
        # {
        # user_ids: [id1, id2, ...],
        # answers: [ question1_plaintext_answer, question2_plaintext_answer, ..],
        # is_broken_loop: True|False
        # }
        # note that the user_ids do NOT include the last user in the chain
        # also note that is_broken_loop is set to true if either the loop is
        # closed (infinite) or does not end in a leaf (=node)
        paths = []

        # A dictionary where the number of delegated voted per delegate is
        # stored. This dict is used only for recording the number of delegated
        # votes a delegate has.
        #
        # The keys are the user_ids of the delegates, and the values are
        # the number of delegated votes.
        # Note that because of chains of delegations, the same vote can be
        # counted multiple times.
        delegation_counts = dict()

        def update_delegation_counts(vote):
            """
            function used to update the delegation counts, for each valid vote.
            it basically goes deep in the delegation chain, updating the count
            for each delegate
            """
            # if there is no vote we have nothing to do
            if not vote:
                return

            def increment_delegate(delegate_id):
                """
                Increments the delegate count or sets it to one if doesn't it
                exist
                """
                if delegate_id in delegation_counts:
                    delegation_counts[delegate_id] += 1
                else:
                    delegation_counts[delegate_id] = 1

            i = 0
            while not vote.is_direct and i < MAX_DELEGATION_DEPTH:
                i += 1
                next_delegate = vote.get_delegate()
                if nodes.filter(voter=next_delegate).count() == 1:
                    increment_delegate(next_delegate.id)
                    return
                elif edges.filter(voter=next_delegate).count() == 1:
                    increment_delegate(next_delegate.id)
                    vote = edges.filter(voter=next_delegate)[0]
                else:
                    raise Exception("Broken delegation chain")

        def get_path_for_user(user_id):
            """
            Given an user id, checks if it's already in any known path, and 
            return it if that path is found. Returns None otherwise.
            """
            for path in paths:
                if user_id in path["user_ids"]:
                    return path
            return None

        def get_vote_for_voter(voter):
            """
            Given a voter (an User), returns the vote of the vote of this voter
            on the election. It will be either a proxy or a direct vote
            """
            if nodes.filter(voter=voter).count() == 1:
                return nodes.filter(voter=voter)[0]
            elif edges.filter(voter=voter) == 1:
                return edges.filter(voter=voter)[0]
            else:
                return None

        if self.election_type not in dict(parse_voting_methods()):
            raise Exception("do not know how to count this type of voting")

        voting_systems = []
        tallies = []

        import copy

        # result is in the same format as get_result_pretty(). Initialized here
        result = copy.deepcopy(self.questions)

        # setup the initial data common to all voting system
        i = 0
        for question in result:
            voting_system = get_voting_system_by_id(self.election_type)
            tally = voting_system.create_tally(self, i)
            voting_systems.append(voting_system)
            tallies.append(tally)
            i += 1

            question["a"] = "question/result/" + voting_system.get_id()
            question["winners"] = []
            question["total_votes"] = 0

            for answer in question["answers"]:
                answer["a"] = "answer/result/" + voting_system.get_id()
                answer["total_count"] = 0
                answer["total_count_percentage"] = 0

            # prepare the tally
            tally.pre_tally(result)

        def add_vote(user_answers, is_delegated):
            """
            Given the answers of a vote, update the result
            """
            for tally in tallies:
                tally.add_vote(voter_answers=user_answers, result=result, is_delegated=is_delegated)

        # Here we go! for each voter, we try to find it in the paths, or in
        # the proxy vote chain, or in the direct votes pool
        for voter in self.electorate.all():
            path_for_user = get_path_for_user(voter.id)

            # Found the user in a known path
            if path_for_user and not path_for_user["is_broken_loop"]:
                # found a path to which the user belongs

                # update delegation counts
                update_delegation_counts(get_vote_for_voter(voter))
                add_vote(path_for_user["answers"], is_delegated=True)
            # found the user in a direct vote
            elif nodes.filter(voter=voter).count() == 1:
                vote = nodes.filter(voter=voter)[0]
                add_vote(vote.data["answers"], is_delegated=False)
            # found the user in an edge (delegated vote), but not yet in a path
            elif edges.filter(voter=voter).count() == 1:
                path = dict(user_ids=[voter.id], answers=[], is_broken_loop=False)

                current_edge = edges.filter(voter=voter)[0]
                loop = True
                i = 0
                while loop and i < MAX_DELEGATION_DEPTH:
                    i += 1
                    delegate = current_edge.get_delegate()
                    path_for_user = get_path_for_user(delegate.id)
                    check_depth = i < MAX_DELEGATION_DEPTH

                    if check_depth and delegate in path["user_ids"]:
                        # wrong path! loop found, vote won't be counted
                        path["is_broken_loop"] = True
                        paths += [path]
                        loop = False
                    elif check_depth and path_for_user and not path_for_user["is_broken_loop"]:
                        # extend the found path and count a new vote
                        path_for_user["user_ids"] += path["user_ids"]

                        # Count the vote
                        add_vote(path_for_user["answers"], is_delegated=True)
                        update_delegation_counts(current_edge)
                        loop = False
                    elif check_depth and nodes.filter(voter=delegate).count() == 1:
                        # The delegate voted directly, add the path and count
                        # the vote
                        vote = nodes.filter(voter=delegate)[0]
                        path["answers"] = vote.data["answers"]
                        paths += [path]
                        add_vote(vote.data["answers"], is_delegated=True)
                        update_delegation_counts(current_edge)
                        loop = False

                    elif check_depth and edges.filter(voter=delegate).count() == 1:
                        # the delegate also delegated, so continue looping
                        path["user_ids"] += [delegate.id]
                        current_edge = edges.filter(voter=delegate)[0]
                    else:
                        # broken path! we cannot continue
                        path["is_broken_loop"] = True
                        paths += [path]
                        loop = False

        # post process the tally
        for tally in tallies:
            tally.post_tally(result)

        self.result = dict(a="result", counts=result, delegation_counts=delegation_counts)

        tally_log = []
        for tally in tallies:
            tally_log.append(tally.get_log())
        self.extra_data["tally_log"] = tally_log

        self.delegated_votes_frozen_at_date = (
            self.voters_frozen_at_date
        ) = self.result_tallied_at_date = datetime.datetime.now()

        # TODO: update result_hash
        self.save()
Example #12
0
    def do_tally(tally_path, election):
        import copy
        # result is in the same format as get_result_pretty(). Initialized here
        result = copy.deepcopy(election.questions)
        base_vote =[dict(choices=[]) for q in result]

        # setup the initial data common to all voting system
        i = 0
        tallies = []
        for question in result:
            tally_type = election.election_type
            if 'tally_type' in question:
                tally_type = question['tally_type']
            voting_system = get_voting_system_by_id(tally_type)
            tally = voting_system.create_tally(election, i)
            tallies.append(tally)

            question['a'] = "question/result/" + voting_system.get_id()
            question['winners'] = []
            question['total_votes'] = 0

            for answer in question['answers']:
                answer['a'] = "answer/result/" + voting_system.get_id()
                answer['total_count'] = 0
                answer['total_count_percentage'] = 0

            tally.pre_tally(result)

            plaintexts_path = os.path.join(settings.PRIVATE_DATA_ROOT, 'elections',
                str(election.id), "%d_plaintexts_json" % i)
            with codecs.open(plaintexts_path, encoding='utf-8', mode='r') as plaintexts_file:
                for line in plaintexts_file.readlines():
                    voter_answers = base_vote
                    choices = []
                    try:
                        # Note line starts with " (1 character) and ends with
                        # "\n (2 characters). It contains the index of the
                        # option selected by the user but starting with 1
                        # because number 0 cannot be encrypted with elgammal
                        # so we trim beginning and end, parse the int and
                        # substract one
                        number = int(line[1:-2]) - 1
                        choices = tally.parse_vote(number, question)

                        # craft the voter_answers in the format admitted by
                        # tally.add_vote
                        voter_answers[i]['choices'] = choices
                    except:
                        print "invalid/blank vote: " + line

                    tally.add_vote(voter_answers=voter_answers,
                        result=result, is_delegated=False)

            i += 1


        if not election.extra_data:
            election.extra_data = dict()

        # post process the tally
        for tally in tallies:
            tally.post_tally(result)

        election.electorate = election.agora.members.all()
        election.result = dict(
            a= "result",
            counts = result,
            total_votes = result[0]['total_votes'] + result[0]['dirty_votes'],
            electorate_count = election.electorate.count(),
            total_delegated_votes = 0
        )

        tally_log = []
        for tally in tallies:
            tally_log.append(tally.get_log())
        election.extra_data['tally_log'] = tally_log

        election.delegated_votes_frozen_at_date = election.voters_frozen_at_date =\
            election.result_tallied_at_date = timezone.now()