def test_valid_voting_process__decline_openid(self): from nuorisovaalit.models import Vote from nuorisovaalit.models import Voter from nuorisovaalit.models import VotingLog # Fake the OpenID authentication. self.authenticate(u"http://example.com/id/matti.meikalainen") # Select a candidate response = self.testapp.get("/valitse") self.assertEquals("200 OK", response.status) # Select a candidate response = response.click(href="/aanesta/1$") self.assertEquals("200 OK", response.status) # Assert we are showing the correct voter information self.assertEquals( [tag.text for tag in response.html.find("table", id="voter").findAll("td")], [u"Matti Meikäläinen", u"Ahvenanmaan maakunnan vaalipiiri"], ) # Assert we got the candidate we selected self.assertEquals(response.html.find("p", "candidate-name").text, u"1 Turhapuro, Uuno") # Vote for the candidate response.form[u"vote"] = u"1" response = response.form.submit() # The form submission uses a redirect to avoid accidental resubmissions. self.assertEquals("302 Found", response.status) response = response.follow() self.assertEquals("200 OK", response.status) # Assert the vote was registered self.assertEquals(response.html.find("h1").text, u"Äänesi on tallennettu") session = DBSession() self.assertEquals( 1, session.query(VotingLog).join(Voter).filter_by(openid=u"http://example.com/id/matti.meikalainen").count() ) self.assertEquals(1, session.query(Vote).count()) # Submit the OpenID preference form with default values which will # decline the offer to use OpenID in the future. response = response.form.submit() self.assertEquals("302 Found", response.status) response = response.follow() self.assertEquals("200 OK", response.status) # Assert that the accept_openid is False for the voter. session = DBSession() voter = session.query(Voter).filter_by(openid=u"http://example.com/id/matti.meikalainen").one() self.assertFalse(voter.accept_openid) # Assert we got logged out. self.assertEquals(self.testapp.cookies["auth_tkt"], "") # Assert we are suggested to close the browser window. self.assertEquals(response.html.h1.text, u"Sulje ikkuna")
def test_has_voted__negative(self): from nuorisovaalit.models import Voter from nuorisovaalit.models import VotingLog session = DBSession() populate_testing_db() # Make sure no-one has voted self.assertEquals(0, session.query(VotingLog).count()) # Make sure that a given user has not voted. voter = session.query(Voter).first() self.failIf(voter.has_voted())
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_valid_voting_process__accept_openid(self): from nuorisovaalit.models import Voter # Fake the OpenID authentication. self.authenticate(u"http://example.com/id/matti.meikalainen") # Select a candidate. response = self.testapp.get("/valitse") response = response.click(href="/aanesta/1$") # Vote for the candidate. response.form[u"vote"] = u"1" response = response.form.submit() # Follow the redirect to the OpenID preference page. response = response.follow() # Fill in valid information and choose to keep the OpenID. response.form[u"gsm"] = u"0401234567" response.form[u"email"] = u"*****@*****.**" response.form[u"street"] = u"Söme street 16" response.form[u"zipcode"] = u"01234" response.form[u"city"] = u"Söme city, Länd" response.form[u"use_open_identity"] = u"yes" response = response.form.submit() self.assertEquals("302 Found", response.status) response = response.follow() self.assertEquals("200 OK", response.status) # Assert that the voter information is correct. session = DBSession() voter = session.query(Voter).filter_by(openid=u"http://example.com/id/matti.meikalainen").one() self.assertTrue(voter.accept_openid) self.assertEquals(u"0401234567", voter.gsm) self.assertEquals(u"*****@*****.**", voter.email) self.assertEquals(u"Söme street 16, 01234, Söme city, Länd", voter.address)
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 test_vote_finish__declined(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig from webob.exc import HTTPFound session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.assertEquals(False, voter.has_preference()) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('close-window', '/close-window') self.config.add_route('vote-finish', '/valmis') request = DummyRequest() csrf_token = request.session.new_csrf_token() request.POST = { 'form.submitted': u'1', 'csrf_token': csrf_token, 'use_open_identity': u'no', } response = vote_finish(request) self.assertTrue(isinstance(response, HTTPFound)) self.assertEquals('http://example.com/close-window', response.location) self.assertEquals(False, voter.accept_openid) self.assertEquals(True, voter.has_preference())
def test_vote__returned_options(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote from pyramid.session import UnencryptedCookieSessionFactoryConfig from pyramid.url import route_url session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.add_route('select', '/valitse') self.config.testing_securitypolicy(userid=voter.openid) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) request = DummyRequest() csrf_token = request.session.new_csrf_token() request.matchdict['number'] = '1' options = vote(request) self.assertEquals({ 'action_url': request.path_url, 'select_url': route_url('select', request), 'candidate': { 'number': 1, 'name': u'Turhapuro, Uuno', }, 'profile': { 'fullname': u'Matti Meikäläinen', 'district': u'Ahvenanmaan maakunnan vaalipiiri', }, 'error': False, 'csrf_token': csrf_token }, options)
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 openid_response(request): """Processes the response of the OpenID provider and upon successful response authenticates the user. In order for a user to be authenticated the following criteria must be met: * the OpenID provider has responded with a positive assertation. * the verified OpenID identity must match an existing :py:attr:`nuorisovaalit.models.Voter.openid` record. In case the above criteria are not met the user is presented with an error page. :param request: The currently active request. :type request: :py:class:`pyramid.request.Request` """ log = logging.getLogger('nuorisovaalit') mode = request.params.get('openid.mode', None) if mode == 'id_res': # We received a positive authentication response. consumer = make_consumer(request) response = consumer.complete(request.params, request.url) if response.status == SUCCESS: identity = response.identity_url.rstrip('/') session = DBSession() user = session.query(Voter).filter(Voter.openid == identity).first() if user is None: log.warn('Failed to authenticate "{0}": unknown user.'.format(identity)) return openid_failure(request) log.info('Authenticated "{0}".'.format(identity)) if user.has_voted(): url = route_url('vote-finish', request) request.session['vote_registered'] = 'yes' log.info('User "{0}" has already voted.'.format(identity)) else: url = route_url('select', request) headers = remember(request, identity) return HTTPFound(location=url, headers=headers) else: log.warn('Failed to authenticate "{0}".'.format(response.identity_url)) return openid_failure(request) elif mode == 'cancel': # We received an explicit negative authentication response. log.info('OpenID authentication canceled.') return openid_canceled(request) else: log.warn('Unknown OpenID response mode: {0}.'.format(mode)) return openid_failure(request)
def test_vote_finish__message_vote_saved(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('vote-finish', '/valmis') self.assertEquals(u'Äänesi on tallennettu', vote_finish(DummyRequest())['message'])
def test_vote_finish__accepted_empty_fields(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.assertEquals(False, voter.has_preference()) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('close-window', '/close-window') self.config.add_route('vote-finish', '/valmis') request = DummyRequest() csrf_token = request.session.new_csrf_token() request.POST = { 'csrf_token': csrf_token, 'use_open_identity': 'yes', 'gsm': u'', 'email': u'', 'street': u'', 'zipcode': u'', 'city': u'', 'form.submitted': u'1', } self.assertEquals({ 'action_url': 'http://example.com/valmis', 'csrf_token': csrf_token, 'accept_openid': True, 'message': u'Äänesi on tallennettu', 'has_preference': False, 'pref_selected': True, 'errors': [ u'GSM-numero on virheellinen, esimerkki oikeasta muodosta "0501234567".', u'Sähköpostiosoite on virheellinen, esimerkki oikeasta muodosta "*****@*****.**".', u'Katuosoite puuttuu.', u'Postinumero on virheellinen, esimerkki oikeasta muodosta "12345".', u'Postitoimipaikka puuttuu.', ], 'voter': voter, 'gsm': u'', 'email': u'', 'street': u'', 'zipcode': u'', 'city': u'', }, vote_finish(request)) self.assertEquals(False, voter.has_preference())
def test_vote__invalid_candidate_number(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote from pyramid.exceptions import NotFound session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.testing_securitypolicy(userid=voter.openid) request = DummyRequest() request.matchdict['number'] = 666 self.assertRaises(NotFound, lambda: vote(request))
def test_vote_finish__message_already_voted(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('vote-finish', '/valmis') request = DummyRequest() request.session['vote_registered'] = 'yes' self.assertEquals(u'Olet jo äänestänyt', vote_finish(request)['message'])
def test_vote_finish__preference_selection_missing(self): """Tests a case where the OpenID selection (yes or no) is missing.""" from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.assertEquals(False, voter.has_preference()) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('close-window', '/close-window') self.config.add_route('vote-finish', '/valmis') request = DummyRequest() csrf_token = request.session.new_csrf_token() request.POST = { 'form.submitted': u'1', 'csrf_token': csrf_token, 'gsm': u' 00358- 40 1234 567', 'email': u'[email protected] \t', 'street': u' söme stréét', 'zipcode': u' 09123', 'city': u' citÜ', } self.assertEquals({ 'action_url': 'http://example.com/valmis', 'csrf_token': csrf_token, 'accept_openid': None, 'message': u'Äänesi on tallennettu', 'has_preference': False, 'pref_selected': False, 'errors': [ u'Valitse haluatko verkkovaikuttajaidentiteetin.', ], 'voter': voter, 'gsm': u' 00358- 40 1234 567', 'email': u'[email protected] \t', 'street': u' söme stréét', 'zipcode': u' 09123', 'city': u' citÜ', }, vote_finish(request)) self.failIf(voter.has_preference())
def test_exit_voting__declined(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import exit_voting from pyramid.session import UnencryptedCookieSessionFactoryConfig from pyramid.url import route_url from webob.exc import HTTPFound session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('close-window', '/close-window') request = DummyRequest(post=dict(use_open_identity='no')) response = exit_voting(request) self.assertTrue(isinstance(response, HTTPFound)) self.assertEquals(route_url('close-window', request), response.location)
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_vote__candidate_number_mismatch(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.add_route('select', '/valitse') self.config.testing_securitypolicy(userid=voter.openid) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) request = DummyRequest(post=dict(vote='1')) csrf_token = request.session.new_csrf_token() request.POST['csrf_token'] = csrf_token request.matchdict['number'] = '2' options = vote(request) self.assertTrue(options['error']) self.assertEquals(csrf_token, options['csrf_token'])
def test_select__with_candidates(self): from itertools import cycle from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import select session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('vote', '/aanesta/{number}') options = select(DummyRequest()) for party in options['parties']: self.assertTrue(isinstance(party.pop('positions'), cycle)) party['candidates'] = list(party['candidates']) self.assertEquals({ 'coalitions': [], 'columns': 3, 'district': u'Ahvenanmaan maakunnan vaalipiiri', 'empty_vote_url': 'http://example.com/aanesta/0', 'parties': [{ 'candidates': [[{'name': u'Turhapuro, Uuno', 'number': 1, 'url': 'http://example.com/aanesta/1'}]], 'title': u'Köyhien asialla' }, { 'candidates': [[{'name': u'Hartikainen, Härski', 'number': 2, 'url': 'http://example.com/aanesta/2'}]], 'title': u'Piraattipuolue' }, { 'candidates': [[{'name': u'Sörsselssön, Sami', 'number': 3, 'url': 'http://example.com/aanesta/3'}]], 'title': u'Suomen työväenpuolue'} ] }, options)
def test_vote_finish__invalid_csrf_token(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('exit-voting', '/exit') self.config.add_route('vote-finish', '/valmis') request = DummyRequest() csrf_token = request.session.new_csrf_token() request.POST = { 'form.submitted': u'1', 'csrf_token': 'invalid {0}'.format(csrf_token), } self.assertRaises(Forbidden, lambda: vote_finish(request))
def test_vote_finish__accepted_valid_submission(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig from webob.exc import HTTPFound session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.assertEquals(False, voter.has_preference()) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('close-window', '/close-window') self.config.add_route('vote-finish', '/valmis') request = DummyRequest() csrf_token = request.session.new_csrf_token() request.POST = { 'form.submitted': u'1', 'csrf_token': csrf_token, 'use_open_identity': 'yes', 'gsm': u' 00358- 40 1234 567', 'email': u'[email protected] \t', 'street': u' söme stréét', 'zipcode': u' 09123', 'city': u' citÜ', } response = vote_finish(request) self.assertTrue(isinstance(response, HTTPFound)) self.assertEquals('http://example.com/close-window', response.location) self.assertEquals(True, voter.accept_openid) self.assertTrue(voter.has_preference()) self.assertEquals(u'00358401234567', voter.gsm) self.assertEquals(u'*****@*****.**', voter.email) self.assertEquals(u'söme stréét, 09123, citÜ', voter.address)
def test_vote__successful_voting_response(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote from pyramid.session import UnencryptedCookieSessionFactoryConfig from pyramid.url import route_url from webob.exc import HTTPFound session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.add_route('select', '/valitse') self.config.add_route('vote-finish', '/valmis') self.config.testing_securitypolicy(userid=voter.openid) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) request = DummyRequest(post=dict(vote='1')) request.POST['csrf_token'] = request.session.new_csrf_token() request.matchdict['number'] = '1' response = vote(request) self.assertTrue(isinstance(response, HTTPFound)) self.assertEquals(route_url('vote-finish', request), response.location)
def test_vote_finish__no_pref_selected_valid_address(self): from nuorisovaalit.models import Voter from nuorisovaalit.views.voting import vote_finish from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.assertEquals(False, voter.has_preference()) voter.address = u'Äyrävänpolku 666, 09123 Mikä-mikämaa, Pohjoisnapa' self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) self.config.testing_securitypolicy(userid=voter.openid) self.config.add_route('exit-voting', '/exit') self.config.add_route('vote-finish', '/valmis') request = DummyRequest() csrf_token = request.session.new_csrf_token() request.POST['csrf_token'] = csrf_token self.assertEquals({ 'action_url': 'http://example.com/valmis', 'csrf_token': csrf_token, 'accept_openid': None, 'message': u'Äänesi on tallennettu', 'has_preference': False, 'pref_selected': False, 'errors': [], 'voter': voter, 'gsm': voter.gsm, 'email': voter.email, 'street': u'Äyrävänpolku 666', 'zipcode': u'09123', 'city': u'Mikä-mikämaa, Pohjoisnapa', }, vote_finish(request)) self.assertEquals(False, voter.has_preference())
def test_vote__successful_vote_log(self): from nuorisovaalit.models import Vote from nuorisovaalit.models import Voter from nuorisovaalit.models import VotingLog from nuorisovaalit.views.voting import vote from pyramid.session import UnencryptedCookieSessionFactoryConfig session = DBSession() populate_testing_db() voter = session.query(Voter).first() self.config.add_route('select', '/valitse') self.config.add_route('vote-finish', '/valmis') self.config.testing_securitypolicy(userid=voter.openid) self.config.set_session_factory(UnencryptedCookieSessionFactoryConfig) session = DBSession() request = DummyRequest(post=dict(vote='1')) request.POST['csrf_token'] = request.session.new_csrf_token() request.matchdict['number'] = '1' # Check the initial conditions. self.assertEquals(0, session.query(VotingLog).count()) self.assertEquals(0, session.query(Vote).count()) vote(request) # Check that the vote was recorded. self.assertEquals(1, session.query(VotingLog).count()) self.assertEquals(1, session.query(Vote).count()) # Check that the vote record info is correct. vote_record = session.query(Vote).first() self.assertEquals(u'Uuno', vote_record.candidate.firstname) self.assertEquals(u'Turhapuro', vote_record.candidate.lastname) self.assertEquals(u'xxxx xxxx', vote_record.school.name)
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
def select(request): """Renders the candidate selection list. The candidate list is generated based on the :py:class:`nuorisovaalit.models.District` the authenticated user is associated with. The user is only allowed to vote candidates in her own voting district. :param request: The currently active request. :type request: :py:class:`pyramid.request.Request` :rtype: dict """ # Deco Grid positions for the candidate columns. positions = ['0', '1:3', '2:3'] columns = len(positions) session = DBSession() log = logging.getLogger('nuorisovaalit') # Disable caching request.add_response_callback(disable_caching) # Require authentication. voter = authenticated_user(request) if voter is None: log.warn('Unauthenticated attempt to select candidates.') raise Forbidden() # User should vote only once. if voter.has_voted(): log.warn('User "{0}" attempted to select candidates after voting.'.format(voter.openid)) return HTTPFound(location=route_url('vote-finish', request)) # Query the candidates in the voter's election district. query = session.query(Party, Candidate)\ .filter(Candidate.party_id == Party.id)\ .filter(Candidate.district_id == voter.school.district.id)\ .order_by(Party.name, Candidate.number) candidates_by_party = {} for party, candidate in query.all(): candidates_by_party.setdefault(party.name, []).append(candidate) parties = [] for party_name, candidates in sorted(candidates_by_party.items()): parties.append({ 'title': party_name, 'candidates': split_candidates([ dict(name=c.fullname(), number=c.number, url=route_url('vote', request, number=c.number)) for c in candidates], columns), 'positions': cycle(positions), }) coalitions = [] for coalition in session.query(Coalition).filter_by(district_id=voter.school.district.id).all(): coalitions.append(u', '.join(sorted(party.name for party in coalition.parties))) options = { 'parties': parties, 'columns': columns, 'coalitions': coalitions, 'district': voter.school.district.name, 'empty_vote_url': route_url('vote', request, number=Candidate.EMPTY_CANDIDATE), } 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_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_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_mixed_coalitions(self): """Tests when some parties are part of coalitions""" from nuorisovaalit.models import Coalition from nuorisovaalit.results import dhont_selection session = DBSession() district = self._populate(quota=3) # Create a coalition for Köyhien asialla and Piraattipuolue self._add_coalition(u'Red coalition', district, district.candidates[0].party, district.candidates[2].party) self.assertEquals(1, session.query(Coalition).count()) # Create votes for red coalition self._add_votes(district.candidates[0], 2) self._add_votes(district.candidates[1], 3) self._add_votes(district.candidates[2], 2) self._add_votes(district.candidates[3], 4) # Create votes for Suomen työväenpuolue self._add_votes(district.candidates[4], 1) self._add_votes(district.candidates[5], 2) self._add_votes(district.candidates[6], 4) # Create votes for valitsijalista self._add_votes(district.candidates[7], 8) winners = dhont_selection(district) # Assert the secondary sorting key ordering. The expected ordering # was determined manually by evaluating the hashes in an interpreter. self.assertOrdering( district.candidates[2], district.candidates[6], district.candidates[5], district.candidates[7], district.candidates[0], district.candidates[4], district.candidates[3], district.candidates[1], ) # First selected candidate self.assertCandidate(winners[0], { 'name': u'3, Candidate', 'proportional_votes': Decimal('11'), 'absolute_votes': 4, }) # Second selected candidate self.assertCandidate(winners[1], { 'name': u'7, Candidate', 'proportional_votes': Decimal('8'), 'absolute_votes': 8, }) # Third selected candidate self.assertCandidate(winners[2], { 'name': u'6, Candidate', 'proportional_votes': Decimal('7'), 'absolute_votes': 4, }) self.assertCandidate(winners[3], { 'name': u'1, Candidate', 'proportional_votes': Decimal('5.5'), 'absolute_votes': 3, }) self.assertCandidate(winners[4], { 'name': u'2, Candidate', 'proportional_votes': Decimal('11') / Decimal('3'), 'absolute_votes': 2, }) self.assertCandidate(winners[5], { 'name': u'5, Candidate', 'proportional_votes': Decimal('3.5'), 'absolute_votes': 2, }) self.assertCandidate(winners[6], { 'name': u'0, Candidate', 'proportional_votes': Decimal('2.75'), 'absolute_votes': 2, }) self.assertCandidate(winners[7], { 'name': u'4, Candidate', 'proportional_votes': Decimal('7') / Decimal('3'), 'absolute_votes': 1, })