Ejemplo n.º 1
0
def summarize(name, results, field):
    print
    print "== Results from %s" % name
    print
    print "Votes from %s." % (", ".join(sorted(r[field] for r in results)))
    print

    rankings = collections.Counter(r[RANK] for r in results).items()
    rankings.sort(key=lambda x: x[1], reverse=True)
    ballots = []
    for order, count in rankings:
        print "%3d  %s" % (count, order)
        ballots.append({
            "count": count,
            "ballot": [[x] for x in order.split(",")]
        })

    from pyvotecore.schulze_method import SchulzeMethod
    from pyvotecore.condorcet import CondorcetHelper
    schulze = SchulzeMethod(
        ballots, ballot_notation=CondorcetHelper.BALLOT_NOTATION_GROUPING)

    print
    if hasattr(schulze, "tied_winners"):
        print "*Schulze winners: tie between %s*" % (", ".join(
            schulze.tied_winners))
    else:
        print "*Schulze winner: %s*" % schulze.winner

    print
    print "Algorithm details:"
    print "```"
    pprint.pprint(schulze.as_dict())
    print "```"
Ejemplo n.º 2
0
 def condorcet_results(self):
     result = Condorcet(
             list(self.hashes_with_counts(self.ballots_as_rankings())),
             ballot_notation=Schulze.BALLOT_NOTATION_RANKING,
         ).as_dict()
     if not result.get('tied_winners'):
         return [result['winner']]
     else:
         return []
Ejemplo n.º 3
0
 def condorcet_results(self):
     result = Condorcet(
         list(self.hashes_with_counts(self.ballots_as_rankings())),
         ballot_notation=Schulze.BALLOT_NOTATION_RANKING,
     ).as_dict()
     if not result.get('tied_winners'):
         return [result['winner']]
     else:
         return []
Ejemplo n.º 4
0
    def test_ranking_format(self):

        # Generate data
        input = [{
            "count": 12,
            "ballot": {
                "Andrea": 1,
                "Brad": 2,
                "Carter": 3
            }
        }, {
            "count": 26,
            "ballot": {
                "Andrea": 1,
                "Carter": 2,
                "Brad": 3
            }
        }, {
            "count": 12,
            "ballot": {
                "Andrea": 1,
                "Carter": 2,
                "Brad": 3
            }
        }, {
            "count": 13,
            "ballot": {
                "Carter": 1,
                "Andrea": 2,
                "Brad": 3
            }
        }, {
            "count": 27,
            "ballot": {
                "Brad": 1
            }
        }]
        output = SchulzeMethod(
            input,
            ballot_notation=SchulzeMethod.BALLOT_NOTATION_RANKING).as_dict()

        # Run tests
        self.assertEqual(
            output, {
                "candidates": set(['Carter', 'Brad', 'Andrea']),
                "pairs": {
                    ('Andrea', 'Brad'): 63,
                    ('Brad', 'Carter'): 39,
                    ('Carter', 'Andrea'): 13,
                    ('Andrea', 'Carter'): 50,
                    ('Brad', 'Andrea'): 27,
                    ('Carter', 'Brad'): 51
                },
                "strong_pairs": {
                    ('Andrea', 'Brad'): 63,
                    ('Carter', 'Brad'): 51,
                    ('Andrea', 'Carter'): 50
                },
                "winner": 'Andrea'
            })
    def test_tiebreaker_bug(self):

        # Generate data
        input = [
            {
                "count": 1,
                "ballot": [["A"], ["B", "C"]]
            },
            {
                "count": 1,
                "ballot": [["B"], ["A"], ["C"]]
            },
        ]
        output = SchulzeMethod(input, ballot_notation="grouping").as_dict()

        # Run tests
        self.assertEqual(output['candidates'], set(['A', 'B', 'C']))
        self.assertEqual(
            output['pairs'], {
                ('A', 'B'): 1,
                ('A', 'C'): 2,
                ('B', 'A'): 1,
                ('B', 'C'): 1,
                ('C', 'A'): 0,
                ('C', 'B'): 0,
            })
        self.assertEqual(output['strong_pairs'], {
            ('A', 'C'): 2,
            ('B', 'C'): 1,
        })
        self.assertEqual(output['tied_winners'], set(['A', 'B']))
Ejemplo n.º 6
0
 def handle_close(self):
     # IMPORTANT! Use deepcopy, we don't want the SchulzePollPlugin to modify our ballots,
     # just calculate a result
     ballots = deepcopy(self.context.ballots)
     if ballots:
         schulze_ballots = self.schulze_format_ballots(ballots)
         self.context.poll_result = SchulzeMethod(
             schulze_ballots, ballot_notation="ranking"
         ).as_dict()
     else:
         raise HTTPForbidden(_("No votes, cancel the poll instead."))
Ejemplo n.º 7
0
def calculateVote(db, event, break_ties=False):
    '''Calculate the winner for this event based on current votes'''

    #return a dict of all votes for all users for a single event
    votes = {}
    event_votes = db.query(User.name, Vote.rank, Restaurant.name).join(Vote).join(Restaurant).filter(Vote.event==event.id)

    if event_votes.count() > 0:
        for user, rank, rest in event_votes.all():
            entry = votes.get(user, {})
            entry[rest] = rank
            votes[user] = entry
    
        #Format the list into the input for Schulze
        input = []
        for v in votes:
            input.append({"count":1, "ballot":copy(votes[v])})

        output = SchulzeMethod(input, ballot_notation="rating").as_dict()
        tb_user = None

        if "tied_winners" in output and break_ties:            
            #Randomly select a tie-breaker user among those voters tied for the lowest tb_count            
            users = db.query(User).join(Vote).filter(Vote.event==event.id).order_by(User.tb_count).all()
            users = [U for U in users if U.tb_count == users[0].tb_count]
            tb_user = random.choice(users)

            tb_votes = event_votes.filter(Vote.user == tb_user.id).order_by(Vote.rank.desc()).all()
            tb_list = [x[2] for x in tb_votes]        

            #Update the user's tb_count
            tb_user.tb_count += 1
            db.commit()

            output = SchulzeMethod(input, tie_breaker=tb_list, ballot_notation="rating").as_dict()

        return output, tb_user

    else:
        #No votes?!
        return None, ""    
Ejemplo n.º 8
0
 def add_ballots(self,
                 ballots,
                 tie_breaker=None,
                 ballot_notation=None,
                 reverse=False):
     """
     Add ballots of ordered issues.
     Ballots are translated to their complementary graph using PyVoteCoreAssistance and the graph's 
     edges values are added/deducted from the IssuesGraph instance.
     
     Example:
       input = [{'ballot': [[1], [2], [3], [4], [5]], 'count': 3},
        {'ballot': [[5], [2], [3], [4], [1]], 'count': 9},
        {'ballot': [[5], [1], [3], [4], [1]], 'count': 8},
        {'ballot': [[3], [2], [4], [1], [5]], 'count': 5},
        {'ballot': [[1], [2], [3], [4], [5]], 'count': 5}]
     
       g.add_ballots(input)
     
     use reverse=True to deduct the ballot from the IssuesGraph, reversing it's effect.
     """
     output = SchulzeMethod(ballots, ballot_notation="grouping").as_dict(
     )  #TODO: remove this line when iteritems bug is solved
     assistance = PyVoteCoreAssistance()
     assistance.add_ballot(ballots, tie_breaker, ballot_notation)
     for edge_key in assistance.pairs.keys():
         try:
             from_node = IssueNode.objects.get(issue_id=edge_key[0])
         except IssueNode.DoesNotExist:
             try:
                 new_issue = Issue.objects.get(id=edge_key[0])
                 from_node = self.add_node(new_issue)
             except Issue.DoesNotExist:
                 raise  # TODO: decide on proper exception
         try:
             to_node = IssueNode.objects.get(issue_id=edge_key[1])
         except IssueNode.DoesNotExist:
             try:
                 new_issue = Issue.objects.get(id=edge_key[1])
                 to_node = self.add_node(new_issue)
             except Issue.DoesNotExist:
                 raise  # TODO: decide on proper exception
         edge = IssueEdge.objects.get(graph=self,
                                      from_node=from_node,
                                      to_node=to_node)
         if reverse:
             edge.weight -= assistance.pairs[edge_key]
         else:
             edge.weight += assistance.pairs[edge_key]
         edge.save()
Ejemplo n.º 9
0
    def test_grouping_format(self):

        # Generate data
        input = [
            {
                "count": 12,
                "ballot": [["Andrea"], ["Brad"], ["Carter"]]
            },
            {
                "count": 26,
                "ballot": [["Andrea"], ["Carter"], ["Brad"]]
            },
            {
                "count": 12,
                "ballot": [["Andrea"], ["Carter"], ["Brad"]]
            },
            {
                "count": 13,
                "ballot": [["Carter"], ["Andrea"], ["Brad"]]
            },
            {
                "count": 27,
                "ballot": [["Brad"]]
            },
        ]
        output = SchulzeMethod(input, ballot_notation="grouping").as_dict()

        # Run tests
        self.assertEqual(
            output, {
                "candidates": set(['Carter', 'Brad', 'Andrea']),
                "pairs": {
                    ('Andrea', 'Brad'): 63,
                    ('Brad', 'Carter'): 39,
                    ('Carter', 'Andrea'): 13,
                    ('Andrea', 'Carter'): 50,
                    ('Brad', 'Andrea'): 27,
                    ('Carter', 'Brad'): 51
                },
                "strong_pairs": {
                    ('Andrea', 'Brad'): 63,
                    ('Carter', 'Brad'): 51,
                    ('Andrea', 'Carter'): 50
                },
                "winner": 'Andrea'
            })
	def do_POST(self):
		try:
			# Parse the incoming data
			request_raw = self.rfile.read(int(self.headers["content-length"]))
			request_raw = re.sub("\n", "", request_raw)
			request = json.loads(request_raw)
			
			# Send the data to the requested voting system
			if request["voting_system"] == "plurality":
				system = Plurality(request["ballots"])
			elif request["voting_system"] == "plurality_at_large":
				system = PluralityAtLarge(request["ballots"], required_winners = request["winners"])
			elif request["voting_system"] == "irv":
				system = IRV(request["ballots"])
			elif request["voting_system"] == "stv":
				system = STV(request["ballots"], required_winners = request["winners"])
			elif request["voting_system"] == "schulze_method":
				system = SchulzeMethod(request["ballots"], ballot_notation = request["notation"])
			elif request["voting_system"] == "schulze_stv":
				system = SchulzeSTV(request["ballots"], required_winners = request["winners"], ballot_notation = request["notation"])
			elif request["voting_system"] == "schulze_pr":
				system = SchulzePR(request["ballots"], winner_threshold = request["winners"], ballot_notation = request["notation"])
			else:
				raise Exception("No voting system specified")
			response = system.as_dict()
			
			# Ensure a response came back from the voting system
			if response == None:
				raise
		
		except:
			fp = StringIO.StringIO()
			traceback.print_exc(10,fp)
			response = fp.getvalue()
			self.send_response(500)
		
		else:
			self.send_response(200)
		
		finally:
			response = json.dumps(self.__simplify_object__(response))
			self.send_header("Content-type", "application/json")
			self.send_header("Content-length", str(len(response)))
			self.end_headers()
			self.wfile.write(response)
Ejemplo n.º 11
0
    def handle_close(self):
        """
        Calculate results per round instead. Each round has exactly 1 winner.
        (It could be a randomized tie though)
        """
        # IMPORTANT! Use deepcopy, we don't want the SortedSchulzePollPlugin to modify our ballots,
        # just calculate a result
        ballots = deepcopy(self.context.ballots)
        wcount = self.context.poll_settings.get("winners", 0)

        if ballots:
            candidates_left = set(self.context.proposals)
            if wcount and len(self.context.proposals) > wcount:
                rounds = wcount
            else:
                rounds = len(self.context.proposals)
            schulze_ballots = self.schulze_format_ballots(ballots)
            round_data = []
            for i in range(rounds):
                if len(candidates_left) > 1:
                    # SchulzeMethod changed the ballots, so we need another copy here!
                    res = SchulzeMethod(
                        deepcopy(schulze_ballots), ballot_notation="ranking"
                    ).as_dict()
                    round_data.append(res)
                    schulze_ballots = self.eliminate_candidate(
                        res["winner"], schulze_ballots
                    )
                    candidates_left.remove(res["winner"])
                else:
                    # Only 1 candidate left
                    round_data.append({"winner": list(candidates_left)[0]})
            self.context.poll_result = {
                "rounds": round_data,
                "candidates": set(self.context.proposals),
                "winners": [x["winner"] for x in round_data],
            }
        else:
            raise HTTPForbidden(_("No votes, cancel the poll instead."))
Ejemplo n.º 12
0
def _calculate_results(unresolved):
    """
    Given a QuerySet of unresolved topics (with expired deadlines),
    resolve them into results, possibly indicating a tie of some sort.

    TODO: Handle the case of abstaning being a "winning" option.
    """

    extra = ''
    for topic in unresolved:
        # if topic.agenda.quorum:
        # raise Exception, '%d / %d' % (topic.agenda.quorum, topic.num_voters())
        if topic.agenda.quorum and topic.num_voters() < topic.agenda.quorum:
            # Mark invalid with no results.  Somewhat counter-intuitively,
            # we consider the results to have been "calculated" in this case
            # (specifically, we calculated that there are no valid results).
            topic.invalid = True
            topic.result_calculated = True
            topic.save()
            continue

        # Currently, proceed with results even if there are fewer results
        # than expected.  It is conceivable that this is a valid outcome,
        # for instance, if only three candidates receive any votes in a Board
        # election, then there should be only three winners even though there
        # are four or five positions.

        options = topic.counted_options().filter(num_votes__gt=0)
        num_options = options.count()

        if topic.vote_type.name == TYPE_CHARTER:
            # Charter amendments require a 2/3 majority.
            for_option = options.get(name='For')
            against_option = options.get(name='Against')
            total_votes = for_option.num_votes + against_option.num_votes
            if for_option.num_votes >= (total_votes * (2.0 / 3.0)):
                for_option.result = True
                for_option.save()
            else:
                against_option.result = True
                against_option.save()

            # It's not possible for this sort of measure to tie- it either reaches
            # the two-thirds threshold or it does not.
            topic.invalid = False
            topic.result_calculated = True
            topic.save()
            extra = ('\n\nPlease note that Charter Amendments require a 2/3 '
                     'majority to pass.')

        elif not topic.vote_type.max_votes:
            # evaluate Schulze method. First collect votes, than use library.
            voters = User.objects.filter(votes__option__topic=topic).distinct()
            options = topic.options.all()
            ballots = []
            for voter in voters:
                votes = Vote.objects.filter(option__in=options, voter=voter)\
                                    .order_by('rank')
                ordered_votes = []
                last_rank = votes[0].rank - 1
                current_rank = 0
                for vote in votes:
                    if vote.rank == last_rank:
                        ordered_votes[-1].append(vote.option_id)
                    else:
                        ordered_votes.append([vote.option_id])
                        last_rank = vote.rank
                        current_rank += 1
                    vote.rank = current_rank
                    vote.save()
                ballot = {'ballot': ordered_votes}
                ballots.append(ballot)
            result = SchulzeMethod(ballots, ballot_notation="grouping")
            if hasattr(result, 'tied_winners'):
                # Schulze method can be tied as well, treat as other ties
                options = Option.objects.filter(id__in=result.tied_winners)
                for option in options:
                    option.result = True
                    option.save()
                topic.invalid = True
                topic.result_calculated = True
                topic.save()
            else:
                option = Option.objects.get(id=result.winner)
                option.result = True
                option.save()
                options = options.exclude(id=result.winner)
                for option in options:
                    option.result = False
                    option.save()
                topic.result_calculated = True
                topic.save()

        elif topic.vote_type.max_votes <= topic.vote_type.max_winners:
            # Flag ties that affect the validity of the results,
            # and set any tied options after the valid winning range
            # to a "winning" result as well, indicating that they are all
            # equally plausible as winners despite producing more winners
            # than are allowed.
            i = topic.vote_type.max_winners
            while (i > 0 and i < num_options
                   and options[i - 1].num_votes == options[i].num_votes):
                topic.invalid = True

                # Note that setting options[i].result = True and then
                # saving options[i] does not work, probably as a side effect
                # of evaluationg the options queryset (it's not actualy an array)
                option = options[i]
                option.result = True
                option.save()
                i += 1

            # Set all non-tied winning results.
            for option in options[0:topic.vote_type.max_winners]:
                option.result = True
                option.save()

            topic.result_calculated = True
            topic.save()

        else:
            return

        _send_result_email(topic, extra)
    def test_wiki_example2(self):

        # Generate data
        input = [{
            "count": 5,
            "ballot": [["A"], ["C"], ["B"], ["E"], ["D"]]
        }, {
            "count": 5,
            "ballot": [["A"], ["D"], ["E"], ["C"], ["B"]]
        }, {
            "count": 8,
            "ballot": [["B"], ["E"], ["D"], ["A"], ["C"]]
        }, {
            "count": 3,
            "ballot": [["C"], ["A"], ["B"], ["E"], ["D"]]
        }, {
            "count": 7,
            "ballot": [["C"], ["A"], ["E"], ["B"], ["D"]]
        }, {
            "count": 2,
            "ballot": [["C"], ["B"], ["A"], ["D"], ["E"]]
        }, {
            "count": 7,
            "ballot": [["D"], ["C"], ["E"], ["B"], ["A"]]
        }, {
            "count": 8,
            "ballot": [["E"], ["B"], ["A"], ["D"], ["C"]]
        }]
        output = SchulzeMethod(input, ballot_notation="grouping").as_dict()

        # Run tests
        self.assertEqual(
            output, {
                'candidates':
                set(['A', 'C', 'B', 'E', 'D']),
                'pairs': {
                    ('A', 'B'): 20,
                    ('A', 'C'): 26,
                    ('A', 'D'): 30,
                    ('A', 'E'): 22,
                    ('B', 'A'): 25,
                    ('B', 'C'): 16,
                    ('B', 'D'): 33,
                    ('B', 'E'): 18,
                    ('C', 'A'): 19,
                    ('C', 'B'): 29,
                    ('C', 'D'): 17,
                    ('C', 'E'): 24,
                    ('D', 'A'): 15,
                    ('D', 'B'): 12,
                    ('D', 'C'): 28,
                    ('D', 'E'): 14,
                    ('E', 'A'): 23,
                    ('E', 'B'): 27,
                    ('E', 'C'): 21,
                    ('E', 'D'): 31
                },
                'strong_pairs': {
                    ('B', 'D'): 33,
                    ('E', 'D'): 31,
                    ('A', 'D'): 30,
                    ('C', 'B'): 29,
                    ('D', 'C'): 28,
                    ('E', 'B'): 27,
                    ('A', 'C'): 26,
                    ('B', 'A'): 25,
                    ('C', 'E'): 24,
                    ('E', 'A'): 23
                },
                'actions': [{
                    'edges': set([('E', 'A')])
                }, {
                    'edges': set([('C', 'E')])
                }, {
                    'nodes': set(['A', 'C', 'B', 'D'])
                }],
                'winner':
                'E'
            })
Ejemplo n.º 14
0
#!/usr/bin/env python

from pyvotecore.schulze_method import SchulzeMethod
from pyvotecore.condorcet import CondorcetHelper
from allvotes import ballots
from pprint import pprint

pprint(SchulzeMethod(ballots, ballot_notation = CondorcetHelper.BALLOT_NOTATION_GROUPING).as_dict())