def test_basic_parsing(self): """Parsing a straightforward line should succeed.""" cn, results = io.parse_result_line('Littleton, 10, C, 11, L') self.assertEqual(cn, 'Littleton') self.assertEqual(len(results), 2) self.assertEqual(results[0], (10, 'C')) self.assertEqual(results[1], (11, 'L'))
def test_odd_whitespace(self): """Odd whitespace characters do not affect the parsing.""" cn, results = io.parse_result_line( ' Littleton, 10, C, \t11, L\n') self.assertEqual(cn, 'Littleton') self.assertEqual(len(results), 2) self.assertEqual(results[0], (10, 'C')) self.assertEqual(results[1], (11, 'L'))
def test_constituency_with_number(self): """A constituency with a number is handled gracefully.""" cn, results = io.parse_result_line(''' Direction, The band, 1, 10, C, 11, L, 12, LD''') self.assertEqual(cn, 'Direction, The band, 1') self.assertEqual(len(results), 3) self.assertEqual(results[0], (10, 'C')) self.assertEqual(results[1], (11, 'L')) self.assertEqual(results[2], (12, 'LD'))
def test_constituency_with_comma(self): """A constituency with a comma is handled gracefully.""" cn, results = io.parse_result_line(''' Birmingham, City of, Edgbaston, 10, C, 11, L, 12, LD''') self.assertEqual(cn, 'Birmingham, City of, Edgbaston') self.assertEqual(len(results), 3) self.assertEqual(results[0], (10, 'C')) self.assertEqual(results[1], (11, 'L')) self.assertEqual(results[2], (12, 'LD'))
def test_multiple_results(self): """Multiple results for one party are handled gracefully.""" cn, results = io.parse_result_line( 'Littleton, 10, C, 9, L, 11, C, 12, C') self.assertEqual(cn, 'Littleton') self.assertEqual(len(results), 4) self.assertEqual(results[0], (10, 'C')) self.assertEqual(results[1], (9, 'L')) self.assertEqual(results[2], (11, 'C')) self.assertEqual(results[3], (12, 'C'))
def add_constituency_result_line(line, valid_codes=None, session=None): """Add in a result from a constituency. Any previous result is removed. If there is an error, ValueError is raised with an informative message. Session is the database session to use. If None, the global db.session is used. If valid_codes is non-None, it is a set containing the party codes which are allowed in this database. If None, this set is queried from the database. The session is not commit()-ed. """ session = session if session is not None else db.session valid_codes = ( valid_codes if valid_codes is not None else _query_valid_party_codes(session) ) cn, results = parse_result_line(line) # Check constituency name is non-empty if cn == '': raise ValueError('Constituency name cannot be empty') # Get the constituency or create one if necessary constituency = Constituency.query.filter(Constituency.name==cn).first() if constituency is None: constituency = Constituency(name=cn) session.add(constituency) # Delete any prior voting records for this constituency Voting.query.filter(Voting.constituency_id==constituency.id).delete() # Is there one result per party? if len(results) != len(set(p for _, p in results)): raise ValueError('Multiple results for one party') # Now add a voting record for each result for count, party_id in results: if party_id not in valid_codes: raise ValueError('Party code "{}" is unknown'.format(party_id)) session.add(Voting( count=count, party_id=party_id, constituency=constituency))
def test_no_results(self): """No results for a constituency is still parseable.""" cn, results = io.parse_result_line('Whiskey on Rye') self.assertEqual(cn, 'Whiskey on Rye') self.assertEqual(len(results), 0)
def test_empty_string(self): """An empty result line gives an empty parse.""" cn, results = io.parse_result_line('') self.assertEqual(cn, '') self.assertEqual(len(results), 0)