def populate_demo(): """Populates a database with demo content.""" config = get_config() engine = engine_from_config(config, "sqlalchemy.") initialize_sql(engine) engine.echo = False session = DBSession() districts = session.query(District).order_by(District.code) provider = urlparse(config["nuorisovaalit.openid_provider"]) provider_tmpl = u"{0}://{{0}}.{1}".format(provider.scheme, provider.netloc) # Create voters for district in districts: username = u"test.user{0:02}".format(district.code) school = School(u"Koulu vaalipiirissä {0}".format(district.name), district) session.add(school) session.flush() session.add( Voter( provider_tmpl.format(username), u"Testi", u"Käyttäjä #{0:02}".format(district.code), date(1990, 4, 5), u"040 123 4567", u"*****@*****.**", u"Nowhere street 5, 00000 Helsinki", school, ) ) transaction.commit() print ("Created demo structure.")
def _add_votes(self, candidate, votes): """Adds votes to the given candidate.""" from nuorisovaalit.models import School from nuorisovaalit.models import Vote kind = Vote.ELECTRONIC if votes == 1 else Vote.PAPER session = DBSession() session.add(Vote(candidate, session.query(School).first(), kind, votes)) session.flush()
def _add_coalition(self, name, district, *parties): """Creates a new coalition containing the given parties.""" from nuorisovaalit.models import Coalition session = DBSession() coalition = Coalition(name, district) for party in parties: coalition.parties.append(party) session.add(coalition) session.flush()
def create_districts(): """Populates the database with information about the voting districts. """ session = DBSession() districts = parse_districts( seats=datafile("../data/paikkamaarat.txt"), municipalities=datafile("../data/luokitusavain_kunta_teksti.txt") ) # Create voting districts and municipalities for (distname, code, seats), municipalities in districts.iteritems(): session.add(District(u"{0} vaalipiiri".format(distname), code, seats)) session.flush()
def test_has_voted__negative_with_other_user_voted(self): from nuorisovaalit.models import Voter from nuorisovaalit.models import VotingLog session = DBSession() populate_testing_db() voter = session.query(Voter).first() session = DBSession() # Add a voting log entry for a different voter id. session.add(VotingLog(voter.id + 1)) self.assertEquals(1, session.query(VotingLog).count()) # Assert that the voter has not voted. self.failIf(voter.has_voted())
def test_has_voted__positive(self): from nuorisovaalit.models import Voter from nuorisovaalit.models import VotingLog session = DBSession() populate_testing_db() voter = session.query(Voter).first() session = DBSession() # Create a voting log entry for the voter id. session.add(VotingLog(voter.id)) self.assertEquals(1, session.query(VotingLog).count()) # Assert self.failUnless(voter.has_voted())
def test_coalition__single_coalition(self): from nuorisovaalit.models import Coalition from nuorisovaalit.models import District from nuorisovaalit.models import Party district = District(u'District X', 1) party = Party(u'Foobar') session = DBSession() session.add_all([district, party]) session.flush() coalition = Coalition(u'Reds', district) session.add(coalition) coalition.parties.append(party) session.flush() self.assertEquals(coalition, party.coalition(district))
def test_select__already_voted(self): from nuorisovaalit.models import Voter from nuorisovaalit.models import VotingLog from nuorisovaalit.views.voting import select from pyramid.url import route_url from webob.exc import HTTPFound session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('vote-finish', '/valmis') session = DBSession() session.add(VotingLog(voter.id)) request = DummyRequest() response = select(request) self.assertTrue(isinstance(response, HTTPFound)) self.assertEquals(route_url('vote-finish', request), response.location)
def test_openid_response__success(self, make_consumer, openid_failure): from datetime import datetime from nuorisovaalit.models import District from nuorisovaalit.models import School from nuorisovaalit.models import Voter from nuorisovaalit.views.login import openid_response from openid.consumer.consumer import SUCCESS from pyramid.url import route_url from webob.exc import HTTPFound self.config.add_route('select', '/valitse') session = DBSession() # Add the needed records. district = District(u'district', 1) school = School(u'Mynämäen koulu', district) school.id = 1 district.schools.append(school) session.add(district) openid_url = u'http://matti.meikalainen.oid.fi' # Add a voter to the session. session.add(Voter(openid_url, u'Matti', u'Meikäläinen', datetime.now(), None, None, None, school)) response = mock.Mock() response.status = SUCCESS response.identity_url = openid_url consumer = mock.Mock() consumer.complete.return_value = response make_consumer.return_value = consumer request = DummyRequest({'openid.mode': 'id_res'}) response = openid_response(request) self.assertTrue(isinstance(response, HTTPFound)) self.assertEquals(route_url('select', request), response.location)
def vote_finish(request): """Renders the OpenID preference page after a successful voting process. The voter will be redirected to this page either after casting a vote or after any subsequent login to the system. The voter is allowed to select her OpenID preference once. If the voter chooses to keep the OpenID, he must provide valid contact information if it is not already known. Email, GSM, and address are required, but can be edited on the page. :param request: The currently active request. :type request: :py:class:`pyramid.request.Request` :rtype: dict """ log = logging.getLogger('nuorisovaalit') request.add_response_callback(disable_caching) voter = authenticated_user(request) if voter is None: raise Forbidden() options = { 'action_url': route_url('vote-finish', request), 'csrf_token': request.session.get_csrf_token(), 'accept_openid': voter.accept_openid, 'message': u'Äänesi on tallennettu', 'has_preference': voter.has_preference(), 'pref_selected': False, 'errors': [], 'voter': voter, 'gsm': request.POST.get('gsm', voter.gsm), 'email': request.POST.get('email', voter.email), 'street': request.POST.get('street', u''), 'zipcode': request.POST.get('zipcode', u''), 'city': request.POST.get('city', u''), } street = request.POST.get('street') zipcode = request.POST.get('zipcode') city = request.POST.get('city') # Parse the voter's address to the options if street, zipcode, # city were not provided. if (street, zipcode, city) == (None, None, None): match = RE_ADDRESS.match(voter.address or u'') if match: options['street'], options['zipcode'], options['city'] \ = (s.strip().strip(',').strip() for s in match.groups()) elif voter.address is not None: # Put the whole address to the street field if it could # not be parsed. log.info('The address for user "{0}" in the database is partial.'.format(voter.openid)) options['street'] = voter.address if request.session.get('vote_registered', 'no') == 'yes': log.info('User "{0}" returned after having voted already.'.format(voter.openid)) options['message'] = u'Olet jo äänestänyt' if 'form.submitted' in request.POST: # CSRF protection. if request.session.get_csrf_token() != request.POST.get('csrf_token'): log.warn('CSRF attempt at: {0}.'.format(request.url)) raise Forbidden() session = DBSession() # Voter has already selected his/her preference. if voter.has_preference(): log.info('User "{0}" has already selected an OpenID preference, skipping.'.format(voter.openid)) return exit_voting(request) # OpenID declined. if request.POST.get('use_open_identity') == 'no': voter.accept_openid = False session.add(voter) log.info('User "{0}" declined the OpenID account.'.format(voter.openid)) return exit_voting(request) def normalize_gsm(gsm): """Remove whitespace and hyphens from the GSM number.""" return u''.join(gsm.strip().replace('-', '').split()) # GSM number is required. gsm = normalize_gsm(request.POST.get('gsm', u'')) if RE_GSM.match(gsm) is None: options['errors'].append( u'GSM-numero on virheellinen, ' u'esimerkki oikeasta muodosta "0501234567".') # Email is required. email = request.POST.get('email', u'').strip() if RE_EMAIL.match(email) is None: options['errors'].append( u'Sähköpostiosoite on virheellinen, ' u'esimerkki oikeasta muodosta "*****@*****.**".') # Street is required. street = request.POST.get('street', u'').strip() if not street: options['errors'].append(u'Katuosoite puuttuu.') # Zipcode is required. zipcode = request.POST.get('zipcode', u'').strip() if len(zipcode) != 5 or not zipcode.isdigit(): options['errors'].append(u'Postinumero on virheellinen, ' u'esimerkki oikeasta muodosta "12345".') # City is required. city = request.POST.get('city', u'').strip() if not city: options['errors'].append(u'Postitoimipaikka puuttuu.') # OpenID preference must be selected. if request.POST.get('use_open_identity', None) is None: options['errors'].append(u'Valitse haluatko verkkovaikuttajaidentiteetin.') # Update voter info if no errors were found. if not options['errors']: voter.accept_openid = True voter.gsm = gsm voter.email = email voter.address = u'{0}, {1}, {2}'.format(street, zipcode, city) session.add(voter) log.info('User "{0}" accepted the OpenID account.'.format(voter.openid)) return exit_voting(request) elif request.POST.get('use_open_identity', None) is not None: options['accept_openid'] = True options['pref_selected'] = True if options['errors']: # pragma: no cover log.warn('User "{0}" failed validation for the OpenID preference.'.format(voter.openid)) for error_msg in options['errors']: log.warn(u'Validation error: {0}'.format(error_msg)) return options
def vote(request): """Renders the voting form for the selected candidate and processes the vote. A valid vote must meet all of the following criteria: * The voter must be authenticated. * The voter must not have voted previously. * The candidate must be the one chosen in the previous step (See :py:func:`select`). * The candidate must belong to the same voting district as the voter. * The CSRF token included in the form must be valid. :param request: The currently active request. :type request: :py:class:`pyramid.request.Request` :rtype: dict """ error = False session = DBSession() voter = authenticated_user(request) log = logging.getLogger('nuorisovaalit') request.add_response_callback(disable_caching) # The user must be authenticated at this time if voter is None: log.warn('Unauthenticated attempt to vote.') raise Forbidden() # The user may vote only once if voter.has_voted(): log.warn('User "{0}" attempted to vote a second time.'.format(voter.openid)) return HTTPFound(location=route_url('vote-finish', request)) # The selected candidate must either be the special empty candidate or a # valid candidate in the voter's voting district. candidate = session.query(Candidate)\ .filter(Candidate.number == int(request.matchdict['number']))\ .filter(or_(Candidate.district_id == voter.school.district.id, Candidate.number == Candidate.EMPTY_CANDIDATE))\ .first() if candidate is None: log.warn('User "{0}" attempted to vote for a non-existing candidate "{1}".'.format( voter.openid, request.matchdict['number'])) raise NotFound # Handle voting if 'vote' in request.POST: if request.session.get_csrf_token() != request.POST.get('csrf_token'): log.warn('CSRF attempt at: {0}.'.format(request.url)) error = True elif request.POST['vote'].strip() == request.matchdict['number']: session.add(Vote(candidate, voter.school, Vote.ELECTRONIC, 1)) session.add(VotingLog(voter)) log.info('Stored vote cast by "{0}".'.format(voter.openid)) return HTTPFound(location=route_url('vote-finish', request)) else: error = True options = { 'action_url': request.path_url, 'select_url': route_url('select', request), 'candidate': { 'number': candidate.number, 'name': candidate.fullname(), }, 'profile': { 'fullname': voter.fullname(), 'district': voter.school.district.name, }, 'error': error, 'csrf_token': request.session.get_csrf_token(), } return options
def populate_testing_db(): """Populates the session with testing data.""" from nuorisovaalit.models import Candidate from nuorisovaalit.models import District from nuorisovaalit.models import Party from nuorisovaalit.models import School from nuorisovaalit.models import Voter from itertools import count DISTRICTS = { (u'Ahvenanmaan maakunnan vaalipiiri', 1): { u'schools': (u'Brändö', u'Eckerö', u'Föglö'), u'candidates': (u'xxxx xxxx', u'xxxx xxxx', u'xxxx xxxx'), }, (u'Etelä-Savon vaalipiiri', 2): { u'schools': (u'Heinävesi', u'Hirvensalmi'), u'candidates': (u'xxxx xxxx', u'xxxx xxxx', u'xxxx xxxx'), }, } session = DBSession() # Check that there are no records in the db. assert session.query(Candidate).count() == 0 assert session.query(District).count() == 0 assert session.query(Party).count() == 0 assert session.query(School).count() == 0 assert session.query(Voter).count() == 0 # Create parties parties = [ Party(u'Köyhien asialla'), Party(u'Piraattipuolue'), Party(u'Suomen työväenpuolue'), ] for p in parties: session.add(p) session.flush() assert session.query(Party).count() == 3 # Create the districts and schools. for (distname, code), rest in sorted(DISTRICTS.items()): district = District(distname, code, 5) for mname in rest['schools']: district.schools.append(School(u'{0} koulu'.format(mname))) session.add(district) session.flush() cnumber = count(1) for index, cname in enumerate(rest['candidates']): party = parties[index % len(parties)] candidate = Candidate(cnumber.next(), cname.split()[0], cname.split()[1], date(xxxx, xx, xx), u'xxxx', u'xxxx', party, district) session.add(candidate) session.add(district) session.flush() districts = session.query(District).order_by(District.name).all() session.add(Voter( u'http://example.com/id/matti.meikalainen', u'Matti', u'Meikäläinen', date(1970, 1, 1), u'123 456789', u'*****@*****.**', u'Mynämäkitie 1, Mynämäki', districts[0].schools[0])) session.add(Voter( u'http://example.com/id/maija.meikalainen', u'Maija', u'Meikäläinen', date(1979, 1, 1), u'987 654321', u'*****@*****.**', u'Vääksyntie1, Sysmä', districts[1].schools[0])) # Create the empty candidate. We need to create a district and party to # satisfy the database constraints but these are not used for anything. empty_district = District(u'Tyhjä', 3) empty_party = Party(u'Tyhjä') session.add(empty_district) session.add(empty_party) session.flush() empty_candidate = Candidate(Candidate.EMPTY_CANDIDATE, u'Tyhjä', u'Tyhjä', date(1999, 1, 1), u'Tyhjä', u'Tyhjä', empty_party, empty_district) session.add(empty_candidate) session.flush()
def test_select_party_grouping_with_coalitions(self): from datetime import datetime from itertools import cycle from nuorisovaalit.models import Candidate from nuorisovaalit.models import Coalition from nuorisovaalit.models import District from nuorisovaalit.models import Party from nuorisovaalit.models import School from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import select session = DBSession() # Populate the db. district = District(u'Distrïct', 1) session.add(district) session.flush() school = School(u'Schööl', district) session.add(school) session.flush() voter = Voter(u'http://[email protected]', u'test', u'user', datetime(2011, 1, 1), gsm=None, email=None, address=None, school_or_id=school) session.add(voter) session.flush() party1 = Party(u'Först party') party2 = Party(u'Secönd party') session.add(party1) session.add(party2) session.flush() coalition = Coalition(u'Red coalition', district) coalition.parties.append(party1) coalition.parties.append(party2) session.add(coalition) party1.candidates.append(Candidate(2, u'Mätti2', u'Meikäläinen', datetime(2011, 1, 1), u'', u'', party1, district)) party1.candidates.append(Candidate(3, u'Mätti3', u'Meikäläinen', datetime(2011, 1, 1), u'', u'', party1, district)) party1.candidates.append(Candidate(4, u'Mätti4', u'Meikäläinen', datetime(2011, 1, 1), u'', u'', party1, district)) party1.candidates.append(Candidate(5, u'Mätti5', u'Meikäläinen', datetime(2011, 1, 1), u'', u'', party1, district)) party2.candidates.append(Candidate(2, u'Mäijä2', u'Meikäläinen', datetime(2011, 1, 1), u'', u'', party2, district)) session.flush() self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('vote', '/aanesta/{number}') options = select(DummyRequest()) # Remove the cycle generators and serialize the candidate generators # for easiear comparison. for party in options['parties']: self.assertTrue(isinstance(party.pop('positions'), cycle)) party['candidates'] = list(party['candidates']) self.assertEquals(options, { 'empty_vote_url': 'http://example.com/aanesta/0', 'district': u'Distrïct', 'coalitions': [u'Först party, Secönd party'], 'columns': 3, 'parties': [ {'candidates': [ [{'url': 'http://example.com/aanesta/2', 'number': 2, 'name': u'Meikäläinen, Mätti2'}, {'url': 'http://example.com/aanesta/3', 'number': 3, 'name': u'Meikäläinen, Mätti3'}], [{'url': 'http://example.com/aanesta/4', 'number': 4, 'name': u'Meikäläinen, Mätti4'}], [{'url': 'http://example.com/aanesta/5', 'number': 5, 'name': u'Meikäläinen, Mätti5'}]], 'title': u'Först party'}, {'candidates': [ [{'url': 'http://example.com/aanesta/2', 'number': 2, 'name': u'Meikäläinen, Mäijä2'}]], 'title': u'Secönd party'}]})
def populate_candidates(): """Populates the database with candidate information. The information is read from two files. The first file contains the list of candidates and parties and the second file contains the information about coalitions. Based on the information we create the following types of objects in the database: * Candidate * Party * Coalition It is also assumed that the database has already been populated with the voting districts because both candidates and coalitions are always directly related to a given voting district. Additionally, this script creates the necessary objects to facilitate casting an empty vote. """ config = get_config() engine = engine_from_config(config, "sqlalchemy.") initialize_sql(engine) engine.echo = False if len(sys.argv) < 4: print ("Usage: {0} <config> <candidate> <coalitions>".format(sys.argv[0])) transaction.abort() sys.exit(1) def fail_unless(condition, message): """Assert the given condition and upon failure prints out the message and aborts the current transaction. """ if not condition: print message print "Aborting transaction" transaction.abort() # Raise an error instead of calling sys.exit(1) to better facilitate testing raise ValueError(message) # Mapping for party abbreviations to full titles. party_names = { u"AFÅ": u"Gemensam lista Alliansen för Åland - samarbete för självstyrelse och utveckling", u"ÅS": u"Åländsk samling gemensam lista", u"E109": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E119": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E133": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E157": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E159": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E266": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E267": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E268": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E404": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E405": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E406": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"E407": u"Yhteislistoihin kuulumattomien valitsijayhdistysten ehdokkaat", # u'xxxx xxxx', u"ITSP": u"Itsenäisyyspuolue", u"KD": u"Suomen Kristillisdemokraatit (KD)", u"KESK": u"Suomen Keskusta", u"KOK": u"Kansallinen Kokoomus", u"KÖY": u"Köyhien Asialla", u"KTP": u"Kommunistinen Työväenpuolue - Rauhan ja Sosialismin puolesta", u"M11": u"Muutos 2011", u"PIR": u"Piraattipuolue", u"PS": u"Perussuomalaiset", u"RKP": u"Suomen ruotsalainen kansanpuolue", u"SDP": u"Suomen Sosialidemokraattinen Puolue", u"SKP": u"Suomen Kommunistinen Puolue", u"SSP": u"Suomen Senioripuolue", u"STP": u"Suomen Työväenpuolue STP", u"VAS": u"Vasemmistoliitto", u"VIHR": u"Vihreä liitto", u"VP": u"Vapauspuolue (VP) - Suomen tulevaisuus", u"YS": u"Yhteislista sitoutumattomat", } session = DBSession() # Create the objects to support casting an empty vote. print "Setting up support for empty votes." empty_district = District(u"Tyhjä", 0) empty_party = Party(u"Tyhjä") session.add_all([empty_district, empty_party]) session.flush() session.add( Candidate( Candidate.EMPTY_CANDIDATE, u"Tyhjä", u"", date(1999, 1, 1), u"Tyhjä", u"Tyhjä", empty_party, empty_district ) ) # Create Party objects. parties = {} print "Creating parties" for abbr, title in party_names.iteritems(): party = Party(title) session.add(party) parties[abbr] = party print " - ", party.name.encode("utf-8") print session.flush() party_abbrevs = set(parties.keys()) districts = dict((d.code, d) for d in session.query(District).all()) # Read in the coalition information. # The excel sheet contains the district code, district name and a comma # separated list of party abbreviations. wb = xlrd.open_workbook(filename=os.path.join(os.getcwd(), sys.argv[3])) ws = wb.sheet_by_index(0) coalition_count = 0 print "Creating coalitions" for row in xrange(ws.nrows): code = int(ws.cell_value(row, 0)) abbrevs = set(p.strip() for p in ws.cell_value(row, 2).strip().split(u",") if p.strip()) # Assert that the party abbreviations are all known. fail_unless(abbrevs.issubset(party_abbrevs), "Unknown parties in: {0}".format(abbrevs)) # Assert the voting district code refers to a valid district. fail_unless(code in districts, "Unknown voting district code: {0}".format(code)) if len(abbrevs): coalition = Coalition(u", ".join(sorted(abbrevs)), districts[code]) for party_id in abbrevs: coalition.parties.append(parties[party_id]) session.add(coalition) coalition_count += 1 print " - '{0}' in {1}.".format(coalition.name.encode("utf-8"), districts[code].name.encode("utf-8")) session.flush() # Read in the candidate information wb = xlrd.open_workbook(filename=os.path.join(os.getcwd(), sys.argv[2])) ws = wb.sheet_by_index(0) candidate_count = 0 print print "Creating candidates" for row in xrange(1, ws.nrows): code = int(ws.cell_value(row, 0)) number = int(ws.cell_value(row, 2)) firstname = unicode(ws.cell_value(row, 5).strip()) lastname = unicode(ws.cell_value(row, 4).strip()) municipality = unicode(ws.cell_value(row, 6).strip()) occupation = unicode(ws.cell_value(row, 7).strip()) party_id = unicode(ws.cell_value(row, 8).strip()) # Assert the party abbreviation refers to a known party. fail_unless(party_id in parties, "Unknown party {0}".format(party_id.encode("utf-8"))) # Assert the voting district code refers to a valid district. fail_unless(code in districts, "Unknown voting district code: {0}".format(code)) dob_match = RE_DOB.match(ws.cell_value(row, 3).strip()) fail_unless(dob_match is not None, "Invalid dob: {0}".format(ws.cell_value(row, 3))) dob = date(int(dob_match.group(3)) + 1900, int(dob_match.group(2)), int(dob_match.group(1))) fail_unless(dob.strftime("%d%m%y") == ws.cell_value(row, 3).strip(), "Failed dob parsing.") # Create the Candidate object candidate = Candidate( number, firstname, lastname, dob, municipality, occupation, parties[party_id], districts[code] ) session.add(candidate) print " - ", candidate.fullname().encode("utf-8"), candidate.number candidate_count += 1 session.flush() # Assert that we created the right number of candidates, which is # ws.nrows - 1 + 1 (all rows, minus header row, plus the empty candidate) fail_unless(ws.nrows == session.query(Candidate).count(), "Failed to create correct number of candidates.") print print "Created", len(parties), "parties." print "Created", coalition_count, "coalitions." print "Created", candidate_count, "candidates." transaction.commit()
def _populate(self, quota=3): """Populates the database with data.""" from nuorisovaalit.models import Candidate from nuorisovaalit.models import Coalition from nuorisovaalit.models import District from nuorisovaalit.models import DBSession from nuorisovaalit.models import Party from nuorisovaalit.models import School from nuorisovaalit.models import Vote session = DBSession() # Pre-conditions self.assertEquals(0, session.query(Candidate).count()) self.assertEquals(0, session.query(Coalition).count()) self.assertEquals(0, session.query(District).count()) self.assertEquals(0, session.query(Party).count()) self.assertEquals(0, session.query(School).count()) self.assertEquals(0, session.query(Vote).count()) # Create parties parties = [ Party(u'Köyhien asialla'), Party(u'Piraattipuolue'), Party(u'Suomen työväenpuolue'), Party(u'Valitsijalista'), ] for p in parties: session.add(p) session.flush() self.assertEquals(4, session.query(Party).count()) # Create district and school district = District(u'Tëst District', 1, quota) district.schools.append(School(u'Tëst Schööl')) session.add(district) session.flush() dob = date(1945, 12, 13) # Create candidates session.add(Candidate(1, u'Candidate', u'0', dob, u'', u'', parties[0], district)) session.add(Candidate(2, u'Candidate', u'1', dob, u'', u'', parties[0], district)) session.add(Candidate(3, u'Candidate', u'2', dob, u'', u'', parties[1], district)) session.add(Candidate(4, u'Candidate', u'3', dob, u'', u'', parties[1], district)) session.add(Candidate(5, u'Candidate', u'4', dob, u'', u'', parties[2], district)) session.add(Candidate(6, u'Candidate', u'5', dob, u'', u'', parties[2], district)) session.add(Candidate(7, u'Candidate', u'6', dob, u'', u'', parties[2], district)) session.add(Candidate(8, u'Candidate', u'7', dob, u'', u'', parties[3], district)) session.add(Candidate(Candidate.EMPTY_CANDIDATE, u'Empty', u'candidate', dob, u'', u'', parties[3], district)) session.flush() self.assertEquals(9, len(district.candidates)) return district