def test_irv_01(self): candidates = ' A B C' ballots = ( (15, ' A B C'), (10, ' B C A'), (8, ' C B A'), ) tie_breaker = ' A B C' options = {} exp_elected = ('B', ) exp_status = _test_aids.build_expected_status(( ('A', 'defeated', 2, 15), ('B', 'elected', 2, 18), ('C', 'defeated', 1, 8), )) exp_tally = { 'A': [15, 15], 'B': [10, 18], 'C': [8], ':Overvotes': [0, 0], ':Abstentions': [0, 0], ':Other exhausted': [0, 0], } elected, status, tally = rcv.Tabulation(1, candidates, ballots, 3, tie_breaker, options).tabulate() status_dict = { candidate: status.as_dict() for candidate, status in status.items() } self.assertEqual(set(elected), set(exp_elected)) self.assertEqual(status_dict, exp_status) self.assertEqual(tally, exp_tally)
def run_test_spec(test_case, input_json): """ Run a test case using test specs from a JSON text file """ tabulate_args, test_spec = with_json.build_tabulate_args( input_json, 'all-tests-spec.json') if 'maxDiff' in test_spec: maxDiff = test_spec['maxDiff'] if maxDiff is None: test_case.maxDiff = None if type(maxDiff) == int and maxDiff >= 0: test_case.maxDiff = maxDiff try: print_description = test_spec['print_description'] except KeyError: print_description = False if print_description: print('\n """' + test_spec['description'] + '"""') if 'exception' in test_spec: exception_type, exception_message = test_spec['exception'] _test_aids.assertRaises_with_message(test_case, u2s(exception_type), u2s(exception_message), rcv.tabulate, tabulate_args) else: expected_elected = validate.str_tuple(test_spec['elected']) expected_status = _test_aids.build_expected_status( test_spec['status_codes']) expected_tally = { _test_aids.u2s(candidate): [ K.Decimal(vote_total) if test_spec['nbr_seats_to_fill'] > 1 else vote_total for vote_total in votes ] for candidate, votes in test_spec['tally'].items() } elected, status, tally = rcv.Tabulation(*tabulate_args).tabulate() if 'print_results' in test_spec and test_spec['print_results']: print_elected(elected) print_status(status) print_tally(tally) try: description = test_spec['description'] except KeyError: description = None jason_str = with_json.results_to_json(elected, status, tally, description) print(jason_str) status_dict = { candidate: status.as_dict() for candidate, status in status.items() } test_case.assertEqual(tally, expected_tally) test_case.assertEqual(status_dict, expected_status) test_case.assertEqual(set(elected), set(expected_elected))
def make_irv_01(self): candidates = ' A B C' ballots = ( (15, ' A B C'), (10, ' B C A'), (8, ' C B A'), ) tie_breaker = ' A B C' options = {} return rcv.Tabulation(1, candidates, ballots, 3, tie_breaker, options)
def make_stv_01(self): candidates = ' A B C D' ballots = ( (15, ' A B C'), (8, ' B C D'), (1, ' B'), (1, ' B #'), (8, ' C B A'), (5, ' D C B'), ) tie_breaker = ' A B C D' options = {} return rcv.Tabulation(3, candidates, ballots, 3, tie_breaker, options)
def test_stv_01(self): candidates = ' A B C D E' ballots = ( (15, ' A B C D E'), (6, ' B C A E D'), (3, ' C B A D E'), (7, ' D E A B C'), (9, ' E D C B A'), ) tie_breaker = ' A B C D E' options = {} exp_elected = ('A', 'B', 'D') exp_status = _test_aids.build_expected_status( (('A', 'elected', 1, 15.0), ('B', 'elected', 2, 10.99995), ('C', 'defeated', 3, 3.99975), ('D', 'elected', 4, 10.45435), ('E', 'defeated', 4, 9.54540))) exp_tally = _test_aids.build_stv_tally({ 'A': [15.0, 10.0, 10.0, 10.0], 'B': [6.0, 10.99995, 10.0, 10.0], 'C': [3.0, 3.0, 3.99975], 'D': [7.0, 7.0, 7.0, 10.45435], 'E': [9.0, 9.0, 9.0, 9.54540], ':Overvotes': [0.0, 0.0, 0.0, 0.0], ':Abstentions': [0.0, 0.0, 0.0, 0.0], ':Other exhausted': [0.0, 0.0, 0.0, 0.0], ':Residual surplus': [0.0, 0.00005, 0.00025, 0.00025], }) elected, status, tally = rcv.Tabulation(3, candidates, ballots, 5, tie_breaker, options).tabulate() status_dict = { candidate: status.as_dict() for candidate, status in status.items() } self.assertEqual(set(elected), set(exp_elected)) self.assertEqual(status_dict, exp_status) self.assertEqual(tally, exp_tally)
def tabulate(input_json='', output_json='', default_json=None): """ Tabulate an RCV contest using JSON files for input and output Arguments --------- input_json A str name of a file or an opened file that is read to get a JSON specification of the tabulation to be performed. If the value is an empty str, standard input is read. If the value is None, nothing is read. The JSON specification should be a JSON object with names that correspond to the parameters of rcv.Tabulation() initialization. Additional names may be specified. Some that are recognized include: description A description of the contest being tabulated. include An array of additional input JSON file names that are read. The name / value pairs in included files are subject to being overridden by subsequent file names in the array of included file names and also, ultimately, contents of the input_json file. Any include value from an included file is ignored. output_json A str name of a file or an opened file that is written to with a JSON specification of the tabulation results. If the value is an empty str, results are written to standard output. If the value is None, nothing is written. The JSON specification of the tabulation result is a JSON object with the following names: elected An array of winners, corresponding to the first value returned by rcv.Tabulation().tabulate(). status An array of status values, each expressed as an array, corresponding to the values of the second value returned by rcv.Tabulation().tabulate(). The status values are listed in the following order: candidate, status, nbr_round, votes. For STV, votes are expressed as a real number. tally An object of tally values, corresponding to the third value returned by rcv.Tabulation().tabulate(). description A string value of the input description value, if a non-empty description value string was provided. Otherwise, this name is not included in the JSON output. default_json A str name of a file or an opened file that is read to provide default values for input_json specification before that file is read. If the value is an empty string, standard input is read. If the value is None, no attempt to read defaults is made. If both this value and the input_json value are empty strings, this value is treated as if it were None. The include name is recognized from this file, but its value may be overridden by an include value specified in the input_json file. Returns ------- A four-tuple consisting of the three values returned by the rcv.Tabulation().tabulate() function, and a dict of the input values built from input_json and default_json. Raises ------ The same as the rcv.Tabulation().tabulate() method, plus other exceptions that might be related to accessing files to build the tabulation specification or to store its results. """ tabulate_args, tabulation_spec = build_tabulate_args( input_json, default_json) try: description = tabulation_spec['description'] except KeyError: description = None elected, status, tally = rcv.Tabulation(*tabulate_args).tabulate() json_str = results_to_json(elected, status, tally, description) write_file(output_json, s2u(json_str)) return elected, status, tally, tabulation_spec