Beispiel #1
0
def browse_candidates(request):
    """Renders a listing of candidates prior to the election.

    This is a read-only page which does not allow voting. The main purpose is
    to allow voters to familiarize themselves with the candidates prior to the
    election.

    :param request: The currently active request.
    :type request: :py:class:`pyramid.request.Request`

    :rtype: dict
    """
    session = DBSession()
    query = session.query(Candidate)\
                .filter(Candidate.number != Candidate.EMPTY_CANDIDATE)\
                .order_by(Candidate.number)
    positions = '0 1:3 2:3'.split()

    candidates = []
    for candidate in query.all():
        candidates.append({
            'name': candidate.fullname(),
            'number': candidate.number,
            'slogan': candidate.slogan,
            'image_url': request.static_url('nuvavaalit:views/templates/static/images/candidates/{}.jpg'.format(candidate.number)),
            })

    return {
        'candidates': split_candidates(candidates, len(positions)),
        'positions': cycle(positions),
        'columns': len(positions),
        'page_name': 'browse',
    }
Beispiel #2
0
    def test_voting_forbidden_authorized_after_election_period(self):
        from nuvavaalit.models import Voter

        session = DBSession()
        session.add(Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers'))
        session.flush()

        self.authenticate(u'buck.rogers')
        response = self.testapp.get('/aanesta/abcdefg', expect_errors=True)
        self.assertEquals(response.status, '403 Forbidden')
Beispiel #3
0
    def test_candidates(self):
        from nuvavaalit.models import Candidate
        from nuvavaalit.views.public import browse_candidates

        session = DBSession()
        session.add(Candidate(2, u'Steven', u'Seagal', u'No pain, no game', u''))
        session.add(Candidate(7, u'Jet', u'Li', u'Yin, yang', u''))
        session.add(Candidate(11, u'Jean-Claude', u'van Damme', u'No engrish', u''))
        session.flush()

        options = browse_candidates(testing.DummyRequest(statsd=False))
        positions = options.pop('positions')
        self.assertEquals('0 1:3 2:3 0 1:3 2:3'.split(), [positions.next() for i in xrange(6)])
        self.assertEquals(list(options.pop('candidates')), [
            [{'image_url': 'http://example.com/static/images/candidates/2.jpg',
              'name': u'Seagal, Steven',
              'number': 2,
              'slogan': u'No pain, no game'},
             {'image_url': 'http://example.com/static/images/candidates/7.jpg',
              'name': u'Li, Jet',
              'number': 7,
              'slogan': u'Yin, yang'},
             {'image_url': 'http://example.com/static/images/candidates/11.jpg',
              'name': u'van Damme, Jean-Claude',
              'number': 11,
              'slogan': u'No engrish'}]])
        self.assertEquals(options, {
            'columns': 3,
            'page_name': 'browse'})
Beispiel #4
0
    def test_authenticated_user__authenticated(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.views.login import authenticated_user

        self.config.testing_securitypolicy(userid=u'buck.rogers')
        session = DBSession()

        user = Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers')
        session.add(user)

        self.assertEquals(user, authenticated_user(testing.DummyRequest(statsd=False)))
Beispiel #5
0
def select(request):
    """Renders the candidate selection list.

    The link to the voting page for each candidate contains an identifier
    which is the result of encrypting the candidate number with a random
    session key. The main benefit from this is that the chosen candidate can
    not be identified from the used URL. This allows us to use GET requests
    instead of POST requests without having to worry about leaking information
    in server logs and browser history.

    :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'.split()
    session = DBSession()
    log = logging.getLogger('nuvavaalit')
    # 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 HTTPForbidden()

    # User should vote only once.
    if voter.has_voted():
        log.warn('User "{}" attempted to select candidates after voting.'.format(voter.username))
        return exit_voting(request)

    query = session.query(Candidate)\
                .filter(Candidate.number != Candidate.EMPTY_CANDIDATE)\
                .order_by(Candidate.number)

    candidates = []
    for candidate in query.all():
        candidates.append({
            'name': candidate.fullname(),
            'number': candidate.number,
            'vote_url': route_url('vote', request, id=encrypt(str(candidate.number), request.session['encryption_key'])),
            'image_url': request.static_url('nuvavaalit:views/templates/static/images/candidates/{}.jpg'.format(candidate.number)),
        })

    return {
        'candidates': split_candidates(candidates, len(positions)),
        'positions': cycle(positions),
        'columns': len(positions),
        'empty_vote_url': route_url('vote', request, id=encrypt(str(Candidate.EMPTY_CANDIDATE), request.session['encryption_key'])),
        'empty_vote_number': Candidate.EMPTY_CANDIDATE,
    }
Beispiel #6
0
    def test_genusername__invalid_ascii_character(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.scripts.populate import CreateVoters

        session = DBSession()

        cs = CreateVoters(None, None)

        # Make sure we don't have any existing voters.
        self.assertEquals(0, session.query(Voter).count())
        self.assertEquals(0, len(cs.usernames))

        self.assertEquals(u"kai.lauta-portti", cs.genusername(u"Käi".split(), u"Läutä-pör'tti"))
Beispiel #7
0
    def test_genusername__no_duplicate(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.scripts.populate import CreateVoters

        session = DBSession()

        cs = CreateVoters(None, None)

        # Make sure we don't have any existing voters.
        self.assertEquals(0, session.query(Voter).count())
        self.assertEquals(0, len(cs.usernames))

        self.assertEquals(u"kai.lautaportti", cs.genusername(u"Kai".split(), u"Lautaportti"))
        self.assertEquals(u"pertti.pasanen", cs.genusername(u"Pertti Sakari".split(), u"Pasanen"))
        self.assertEquals(u"fuu.bar.boo", cs.genusername(u"Füü".split(), u"Bär bÖÖ"))
Beispiel #8
0
def login(request):
    """Renders a login form and logs in a user if given the correct
    credentials.

    :param request: The currently active request.
    :type request: :py:class:`pyramid.request.Request`
    """
    session = DBSession()
    log = logging.getLogger('nuvavaalit')
    request.add_response_callback(disable_caching)
    error = None

    if 'form.submitted' in request.POST:
        username = request.POST['username']

        if request.session.get_csrf_token() != request.POST.get('csrf_token'):
            log.warn('CSRF attempt at {}.'.format(request.url))
            raise HTTPForbidden(u'CSRF attempt detected.')
        else:
            user = session.query(Voter).filter_by(username=username).first()
            password = request.POST['password']

            if user is not None and user.check_password(password):
                if user.has_voted():
                    log.warn('User {} attempted to log in after already voting.'.format(user.username))
                    if request.statsd:
                        statsd.increment('login.voted')
                    return exit_voting(request)
                else:
                    headers = remember(request, user.username)
                    # Generate an encryption key for the duration of the session.
                    request.session['encryption_key'] = session_key()
                    log.info('Successful login for "{}".'.format(user.username))
                    if request.statsd:
                        statsd.increment('login.success')
                    return HTTPFound(location=route_url('select', request), headers=headers)

            error = u'Tunnistautuminen epäonnistui. Kokeile tunnistautua uudelleen!'
            if request.statsd:
                statsd.increment('login.failure')
            log.warn('Failed login attempt for {}'.format(request.POST.get('username').encode('utf-8')))

    return {
        'action_url': route_url('login', request),
        'csrf_token': request.session.get_csrf_token(),
        'error': error,
    }
Beispiel #9
0
    def test_genusername__duplicates_wo_middlename_fallback(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.scripts.populate import CreateVoters

        session = DBSession()

        cs = CreateVoters(None, None)

        # Make sure we don't have any existing voters.
        self.assertEquals(0, session.query(Voter).count())
        self.assertEquals(0, len(cs.usernames))

        # First username without duplicate.
        self.assertEquals(u"foo.boo", cs.genusername(u"Föö".split(), u"Böö"))
        # Without middle name we start using suffixes right away.
        self.assertEquals(u"foo.boo2", cs.genusername(u"Föö".split(), u"Böö"))
        self.assertEquals(u"foo.boo3", cs.genusername(u"Föö".split(), u"Böö"))
Beispiel #10
0
def populate_test_users():
    """Populates the database with test users."""
    parser = argparse.ArgumentParser(description='Generates test users in the database.')
    parser.add_argument('config', help='Path to a Paste configuration file, e.g. development.ini.')
    parser.add_argument('quantity', nargs='?', default=10, type=int, help='Number of test users to generate.')
    args = parser.parse_args()

    env = bootstrap(relpath(args.config))
    session = DBSession()
    session.bind.echo = False

    for i in xrange(1, args.quantity + 1):
        session.add(Voter(u'user{}'.format(i), u'testi', u'User #{}'.format(i), u'Fööbär'))

    session.flush()
    transaction.commit()

    print "Created {} test users".format(args.quantity)
    env['closer']()
Beispiel #11
0
def verify_voters():
    """Checks the username/password combinations in the generated Excel sheet
    against the values stored in the database to ensure their consistency.
    """
    parser = argparse.ArgumentParser(description='Verify voter passwords.')
    parser.add_argument('config', help='Path to a Paste configuration file, e.g. development.ini.')
    parser.add_argument('voters', help='Excel sheet containing the generated voter information.')
    args = parser.parse_args()

    env = bootstrap(relpath(args.config))
    session = DBSession()
    session.bind.echo = False
    voters = dict(session.query(Voter.username, Voter).all())

    print "Checking {} for username/password consistency with the database.".format(args.voters)
    failed = 0
    wb = xlrd.open_workbook(filename=relpath(args.voters))
    ws = wb.sheet_by_index(0)

    if len(voters) != ws.nrows - 1:
        print 'Mismatch in the number of voters in the database and Excel sheet'
        sys.exit(1)

    pbar = progressbar.ProgressBar(widgets=[progressbar.Percentage(), progressbar.Bar()], maxval=len(voters)).start()

    for row in xrange(1, ws.nrows):
        username = ws.cell_value(row, 0)
        password = ws.cell_value(row, 1)
        if username not in voters:
            print "Unknown username:"******"Password mismatch for username:", username
            failed += 1

        pbar.update(row)

    pbar.finish()

    print 'Checked {} users. Failures: {}'.format(len(voters), failed)

    env['closer']()
Beispiel #12
0
    def test_genusername__duplicates(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.scripts.populate import CreateVoters

        session = DBSession()

        cs = CreateVoters(None, None)

        # Make sure we don't have any existing voters.
        self.assertEquals(0, session.query(Voter).count())
        self.assertEquals(0, len(cs.usernames))

        # First username without duplicate.
        self.assertEquals(u"foo.boo", cs.genusername(u"Föö Büü".split(), u"Böö"))
        # First duplicate uses the middle initial.
        self.assertEquals(u"foo.b.boo", cs.genusername(u"Föö Büü".split(), u"Böö"))
        # Second duplicate uses the full middle name.
        self.assertEquals(u"foo.buu.boo", cs.genusername(u"Föö Büü".split(), u"Böö"))
        # Subsequent duplicates use an increasing suffix without the middle name.
        self.assertEquals(u"foo.boo2", cs.genusername(u"Föö Büü".split(), u"Böö"))
        self.assertEquals(u"foo.boo3", cs.genusername(u"Föö Büü".split(), u"Böö"))
Beispiel #13
0
    def test_login__form_submission__invalid_password(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.views.login import login

        session = DBSession()
        session.add(Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers'))
        session.flush()
        self.assertEquals(
            session.query(Voter).filter_by(username=u'buck.rogers').first().fullname(),
            u'Bück Rögers')

        request = testing.DummyRequest(statsd=False)
        token = request.session.new_csrf_token()
        request.POST = {
            'form.submitted': u'1',
            'username': u'buck.rogers',
            'password': u'thisiswrong',
            'csrf_token': token,
        }

        options = login(request)
        self.assertEquals(options, {
            'action_url': 'http://example.com/tunnistaudu',
            'csrf_token': token,
            'error': u'Tunnistautuminen epäonnistui. Kokeile tunnistautua uudelleen!'})
Beispiel #14
0
    def test_login__form_submission__success(self, remember):
        from nuvavaalit.models import Voter
        from nuvavaalit.views.login import login

        session = DBSession()
        session.add(Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers'))
        session.flush()
        self.assertEquals(
            session.query(Voter).filter_by(username=u'buck.rogers').first().fullname(),
            u'Bück Rögers')

        remember.return_value = [('X-Login', 'buck.rogers')]
        request = testing.DummyRequest(statsd=False)
        token = request.session.new_csrf_token()
        request.POST = {
            'form.submitted': u'1',
            'username': u'buck.rogers',
            'password': u'secret',
            'csrf_token': token,
        }

        response = login(request)
        self.assertEquals(dict(response.headers), {
            'Content-Length': '0',
            'Content-Type': 'text/html; charset=UTF-8',
            'Location': 'http://example.com/valitse',
            'X-Login': '******'})
Beispiel #15
0
def results(request):
    """Calculates the results of the election.
    """
    session = DBSession()
    votes = defaultdict(int)

    # TODO: Merge the following two queries into a single outer join to calculate the votes.
    votes.update(dict(session.query(Vote.candidate_id, func.COUNT(Vote.candidate_id))\
                        .group_by(Vote.candidate_id).all()))

    total_votes = float(sum(votes.values()))
    results = []
    for candidate in session.query(Candidate):
        if not candidate.is_empty():
            results.append({
                'name': candidate.fullname(),
                'number': candidate.number,
                'votes': votes[candidate.id],
                'percentage': format_percentage(votes[candidate.id], total_votes),
            })

    # Sort the list of results so that equal votes are ordered in
    # random but stable ordering.
    results.sort(
        key=lambda r: (r['votes'], sort_hash(r['name'], r['number'])),
        reverse=True)

    voting_percentage = format_percentage(
        session.query(func.COUNT(Vote.id)).scalar(), session.query(func.COUNT(Voter.id)).scalar())
    threshold = int(request.registry.settings['nuvavaalit.num_selected_candidates'].strip())

    return {
        'selected': results[:threshold],
        'others': results[threshold:],
        'voting_percentage': voting_percentage,
        'total_votes': int(total_votes),
        'threshold': threshold,
        'show_leftovers': asbool(request.registry.settings.get('nuvavaalit.show_all_results', 'false')),
    }
Beispiel #16
0
def system_check(request):
    """System check for the persistency machinery.

    The purpose of the view is to check that we can access the session
    machinery and database. This view is meant to be called periodically by an
    external monitoring system.
    """
    session = DBSession()

    # Make a query to each model we have
    session.query(Candidate).count()
    session.query(Vote).count()
    session.query(Voter).count()
    session.query(VotingLog).count()

    # Exercise the session
    request.session.get_csrf_token()

    return Response("OK", content_type="text/plain")
Beispiel #17
0
    def run(self):
        session = DBSession()
        session.bind.echo = False

        # Join the repoze.filesafe manager in the transaction so that files will
        # be written only when a transaction commits successfully.
        filesafe = FileSafeDataManager()
        transaction.get().join(filesafe)

        # Query the currently existing usernames to avoid UNIQUE violations.
        self.usernames.update(username for cols in session.query(Voter.username).all() for username in cols)

        fh_itella = filesafe.createFile(self.output, 'w')

        # Excel worksheet for Itella
        wb_itella = xlwt.Workbook(encoding='utf-8')
        ws_itella = wb_itella.add_sheet('Hexagon IT')
        text_formatting = xlwt.easyxf(num_format_str='@')
        for col, header in enumerate([u'Tunnus', u'Salasana', u'Nimi', u'Osoite', u'Postinumero', u'Postitoimipaikka']):
            ws_itella.write(0, col, header, text_formatting)
        rows_itella = count(1)
        voter_count = 0

        self.header('Starting to process {}'.format(self.source_file))
        src_sheet = xlrd.open_workbook(self.source_file).sheet_by_index(0)
        pbar = progressbar.ProgressBar(widgets=[progressbar.Percentage(), progressbar.Bar()], maxval=src_sheet.nrows).start()

        for row in xrange(1, src_sheet.nrows):
            token, lastname, names, address, zipcode, city = [c.value for c in src_sheet.row_slice(row, 0, 6)]
            names = names.split()
            firstname = names[0]

            username = self.genusername(names, lastname)
            password = self.genpasswd()

            # Create the voter instance.
            session.add(Voter(username, password, firstname, lastname, token))

            # Write the Itella information
            row = rows_itella.next()

            for col, item in enumerate([username, password, u'{} {}'.format(u' '.join(names), lastname), address, zipcode, city]):
                ws_itella.write(row, col, item, text_formatting)

            voter_count += 1
            pbar.update(voter_count)

        wb_itella.save(fh_itella)
        fh_itella.close()

        session.flush()
        transaction.commit()
        pbar.finish()

        self.header('Finished processing')
        print 'Processed', voter_count, 'voters.'
Beispiel #18
0
    def test_already_voted_user_is_logged_out(self):
        from nuvavaalit.models import Voter
        from nuvavaalit.models import VotingLog

        session = DBSession()
        voter = Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers')
        session.add(voter)
        session.flush()
        session.add(VotingLog(voter))
        session.flush()

        self.assertTrue(voter.has_voted())
        self.authenticate(u'buck.rogers')
        response = self.testapp.get('/valitse')

        # An already voted user is redirected to the thank you page.
        self.assertEquals(response.status, '302 Found')
        self.assertEquals(response.location, 'http://localhost/kiitos')

        # Assert that we get cookies that log out the user.
        self.assertLogoutCookies(response)
Beispiel #19
0
def populate_candidates():
    """Populates the database with candidate information."""
    parser = argparse.ArgumentParser(description='Generates candidates in the database.')
    parser.add_argument('config', help='Path to a Paste configuration file, e.g. development.ini.')
    parser.add_argument('candidates', nargs='?', help='Path to a CSV file containing the candidate information.')
    args = parser.parse_args()

    env = bootstrap(relpath(args.config))

    session = DBSession()
    session.bind.echo = False
    # Create the candidate for empty votes.
    session.add(Candidate(Candidate.EMPTY_CANDIDATE, u'Tyhjä', u'', u'', u''))

    csv_file = relpath(args.candidates)
    reader = csv.reader(open(csv_file))

    print 'Reading candidates from', csv_file

    cnt = 0
    reader.next()

    for row in reader:
        number, firstname, lastname, slogan = row[:4]
        try:
            number = int(number.strip())
        except (TypeError, ValueError):
            print 'Invalid data: {}'.format(str(row))
            continue

        session.add(Candidate(number, univalue(firstname), univalue(lastname), univalue(slogan), u''))
        print 'Added {} {}, {}'.format(firstname, lastname, number)
        cnt += 1

    session.flush()
    transaction.commit()

    print "Created {} candidates.".format(cnt)
    env['closer']()
Beispiel #20
0
 def tearDown(self):
     DBSession.remove()
     testing.tearDown()
Beispiel #21
0
    def test_encrypted_link_matches_selection(self):
        from nuvavaalit.models import Candidate
        from nuvavaalit.models import Voter

        session = DBSession()
        session.add(Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers'))
        session.add(Candidate(2, u'Steven', u'Seagal', u'No pain, no game', u''))
        session.add(Candidate(7, u'Jet', u'Li', u'Yin, yang', u''))
        session.add(Candidate(11, u'Jean-Claude', u'van Damme', u'No engrish', u''))
        session.flush()

        response = self.testapp.get('/tunnistaudu')
        response.form['username'] = u'buck.rogers'
        response.form['password'] = u'secret'
        response = response.form.submit()

        self.assertEquals(response.status, '302 Found')
        self.assertEquals(response.location, 'http://localhost/valitse')
        response = response.follow()

        self.assertEquals(response.request.url, 'http://localhost/valitse')
        voting_links = response.html.findAll('a', attrs={'href': re.compile('^http://localhost/aanesta/')})
        self.assertEquals(voting_links[1]['href'], voting_links[2]['href'])
        self.assertEquals(voting_links[2].text, u'Seagal, Steven, 2')
        # Click the voting link
        response = self.testapp.get(voting_links[2]['href'])
        # Assert that we get a matching candidate selection
        self.assertEquals(response.html.find('p', attrs={'class': 'candidate-name'}).text, u'2 Seagal, Steven')
Beispiel #22
0
 def tearDown(self):
     DBSession.remove()
     shutil.rmtree(self.lock_dir)
Beispiel #23
0
    def test_successful_voting_process(self):
        from nuvavaalit.models import Candidate
        from nuvavaalit.models import Vote
        from nuvavaalit.models import Voter
        from nuvavaalit.models import VotingLog

        # Create a user we will authenticate as.
        session = DBSession()
        session.add(Voter(u'buck.rogers', u'secret', u'Bück', u'Rögers'))
        session.add(Candidate(2, u'Steven', u'Seägäl', u'No pain, no game', u''))
        session.add(Candidate(7, u'Jët', u'Li', u'Yin, yang', u''))
        session.add(Candidate(11, u'Jeän-Claüde', u'van Damme', u'No engrish', u''))
        session.flush()

        self.assertEquals(0, session.query(Vote).count())
        self.assertEquals(0, session.query(VotingLog).count())

        self.authenticate(u'buck.rogers')

        # Accessing the system front page will redirect us to the login page
        # during the elections.
        response = self.testapp.get('/')
        self.assertEquals(response.status, '302 Found')
        self.assertEquals(response.location, 'http://localhost/tunnistaudu')
        response = response.follow()

        # Logging in the system successfully will bring us to the candidate
        # selection page.
        response.form['username'] = u'buck.rogers'
        response.form['password'] = u'secret'
        response = response.form.submit()
        self.assertEquals(response.status, '302 Found')
        self.assertEquals(response.location, 'http://localhost/valitse')
        response = response.follow()

        # Selecting a candidate brings us to the voting page.
        self.assertEquals(response.request.url, 'http://localhost/valitse')
        voting_links = response.html.findAll('a', attrs={'href': re.compile('^http://localhost/aanesta/')})
        # Assert we have two links to the candidate.
        self.assertEquals(voting_links[1]['href'], voting_links[2]['href'])
        self.assertEquals(voting_links[2].text, u'Seägäl, Steven, 2')
        # Click the voting link
        response = self.testapp.get(voting_links[2]['href'])
        self.assertTrue(response.request.url.startswith('http://localhost/aanesta/'))
        # Assert that we get a matching candidate selection
        self.assertEquals(response.html.find('p', attrs={'class': 'candidate-name'}).text, u'2 Seägäl, Steven')

        # Attempting to vote a non-selected candidate gives us an error message.
        response.form['vote'] = u'69'
        response = response.form.submit()
        self.assertTrue(response.html.find('div', attrs={'class': 'error'}).text.startswith(u'Antamasi ehdokasnumero ei vastaa valintaasi'))
        self.assertTrue(response.request.url.startswith('http://localhost/aanesta/'))

        # Voting for the selected candidate stores the vote and logs out the user.
        response.form['vote'] = u'2'
        response = response.form.submit()
        self.assertEquals(response.status, '302 Found')
        self.assertEquals(response.location, 'http://localhost/kiitos')
        # Assert that we get cookies that log out the user.
        self.assertLogoutCookies(response)

        # Assert that the vote was recorded correctly.
        self.assertEquals(2, session.query(Vote).one().candidate.number)
        self.assertEquals(u'buck.rogers', session.query(VotingLog).one().voter.username)

        # Assert that subsequent attempts to login results in automatic logout.
        response = self.testapp.get('/tunnistaudu')
        response.form['username'] = u'buck.rogers'
        response.form['password'] = u'secret'
        response = response.form.submit()

        self.assertEquals(response.status, '302 Found')
        self.assertEquals(response.location, 'http://localhost/kiitos')
        # Assert that we get cookies that log out the user.
        self.assertLogoutCookies(response)
Beispiel #24
0
    def test_no_votes(self):
        from nuvavaalit.models import Candidate
        from nuvavaalit.models import Voter
        from nuvavaalit.views.results import results

        session = DBSession()
        session.add(Voter(u"buck.rogers", u"secret", u"Bück", u"Rögers"))
        session.add(Candidate(2, u"Steven", u"Seagal", u"No pain, no game", u""))
        session.add(Candidate(7, u"Jet", u"Li", u"Yin, yang", u""))
        session.add(Candidate(11, u"Jean-Claude", u"van Damme", u"No engrish", u""))
        session.flush()

        self.assertEquals(
            results(testing.DummyRequest(statsd=False)),
            {
                "others": [],
                "selected": [
                    {"name": u"Li, Jet", "number": 7, "percentage": "0.0", "votes": 0},
                    {"name": u"Seagal, Steven", "number": 2, "percentage": "0.0", "votes": 0},
                    {"name": u"van Damme, Jean-Claude", "number": 11, "percentage": "0.0", "votes": 0},
                ],
                "threshold": 8,
                "total_votes": 0,
                "show_leftovers": False,
                "voting_percentage": "0.0",
            },
        )
Beispiel #25
0
    def test_stable_sorting_of_equals(self):
        """Test that in case of a tie we get a stable but random sorting of candidates."""
        from nuvavaalit.models import Candidate
        from nuvavaalit.models import Vote
        from nuvavaalit.models import Voter
        from nuvavaalit.views.results import results

        session = DBSession()
        session.add(Voter(u"buck.rogers", u"secret", u"Bück", u"Rögers"))
        session.add(Candidate(2, u"Steven", u"Seagal", u"No pain, no game", u""))
        session.add(Candidate(7, u"Jet", u"Li", u"Yin, yang", u""))
        session.add(Candidate(11, u"Jean-Claude", u"van Damme", u"No engrish", u""))
        session.flush()

        candidate_two = session.query(Candidate).filter_by(number=2).first()
        candidate_seven = session.query(Candidate).filter_by(number=7).first()

        session.add(Vote(candidate_two))
        session.add(Vote(candidate_seven))
        session.flush()

        self.assertEquals(
            results(testing.DummyRequest(statsd=False)),
            {
                "others": [],
                "selected": [
                    {"name": u"Li, Jet", "number": 7, "percentage": "50.0", "votes": 1},
                    {"name": u"Seagal, Steven", "number": 2, "percentage": "50.0", "votes": 1},
                    {"name": u"van Damme, Jean-Claude", "number": 11, "percentage": "0.0", "votes": 0},
                ],
                "threshold": 8,
                "total_votes": 2,
                "show_leftovers": False,
                "voting_percentage": "200.0",
            },
        )

        # Rendering the results again will result in the same relative ordering
        # of the equal votes.
        self.assertEquals(
            results(testing.DummyRequest(statsd=False)),
            {
                "others": [],
                "selected": [
                    {"name": u"Li, Jet", "number": 7, "percentage": "50.0", "votes": 1},
                    {"name": u"Seagal, Steven", "number": 2, "percentage": "50.0", "votes": 1},
                    {"name": u"van Damme, Jean-Claude", "number": 11, "percentage": "0.0", "votes": 0},
                ],
                "threshold": 8,
                "total_votes": 2,
                "show_leftovers": False,
                "voting_percentage": "200.0",
            },
        )
Beispiel #26
0
    def test_empty_votes_removed_from_results_listing(self):
        """Test that in case of a tie we get a stable but random sorting of candidates."""
        from nuvavaalit.models import Candidate
        from nuvavaalit.models import Vote
        from nuvavaalit.models import Voter
        from nuvavaalit.views.results import results

        session = DBSession()
        session.add(Voter(u"buck.rogers", u"secret", u"Bück", u"Rögers"))
        session.add(Candidate(Candidate.EMPTY_CANDIDATE, u"empty", u"empty", u"empty", u"empty"))
        session.add(Candidate(2, u"Steven", u"Seagal", u"No pain, no game", u""))
        session.add(Candidate(7, u"Jet", u"Li", u"Yin, yang", u""))
        session.add(Candidate(11, u"Jean-Claude", u"van Damme", u"No engrish", u""))
        session.flush()

        candidate_two = session.query(Candidate).filter_by(number=2).first()
        candidate_seven = session.query(Candidate).filter_by(number=7).first()
        candidate_empty = session.query(Candidate).filter_by(number=Candidate.EMPTY_CANDIDATE).first()

        session.add(Vote(candidate_two))
        session.add(Vote(candidate_seven))
        session.add(Vote(candidate_empty))
        session.add(Vote(candidate_empty))
        session.flush()

        # The empty votes do not show up in the results listing but do
        # affect the number of votes, percentages, etc.
        self.assertEquals(
            results(testing.DummyRequest(statsd=False)),
            {
                "others": [],
                "selected": [
                    {"name": u"Li, Jet", "number": 7, "percentage": "25.0", "votes": 1},
                    {"name": u"Seagal, Steven", "number": 2, "percentage": "25.0", "votes": 1},
                    {"name": u"van Damme, Jean-Claude", "number": 11, "percentage": "0.0", "votes": 0},
                ],
                "threshold": 8,
                "total_votes": 4,
                "show_leftovers": False,
                "voting_percentage": "400.0",
            },
        )
Beispiel #27
0
 def tearDown(self):
     from nuvavaalit.models import DBSession
     DBSession.remove()
Beispiel #28
0
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 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('nuvavaalit')
    request.add_response_callback(disable_caching)
    localizer = get_localizer(request)

    # The user must be authenticated at this time
    if voter is None:
        log.warn('Unauthenticated attempt to vote.')
        raise HTTPForbidden()

    # The user may vote only once
    if voter.has_voted():
        log.warn('User "{}" attempted to vote a second time.'.format(voter.username))
        return exit_voting(request)

    # Find the selected candidate
    try:
        number = int(decrypt(request.matchdict['id'], request.session['encryption_key']))
    except (ValueError, TypeError):
        log.warn('Candidate number decryption failed')
        raise HTTPNotFound

    candidate = session.query(Candidate)\
            .filter(Candidate.number == number)\
            .first()

    if candidate is None:
        log.warn('User "{}" attempted to vote for a non-existing candidate "{}".'.format(
            voter.username, number))
        raise HTTPNotFound

    # 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() == str(number):

            session.add(Vote(candidate))
            session.add(VotingLog(voter))

            log.info('Stored vote cast by "{}".'.format(voter.username))
            if request.statsd:
                statsd.increment('vote.success')
            return exit_voting(request)
        else:
            error = True

    if request.statsd and error:
        statsd.increment('vote.error')

    options = {
        'action_url': request.path_url,
        'select_url': route_url('select', request),
        'candidate': {
            'number': candidate.number,
            'name': candidate.fullname() if not candidate.is_empty() else _(u'Tyhjä'),
            },
        'voter': {
            'fullname': voter.fullname(),
        },
        'error': error,
        'csrf_token': request.session.get_csrf_token(),
        'unload_confirmation': localizer.translate(
            _(u'Et ole vielä äänestänyt. Oletko varma, että haluat poistua sivulta?')),
    }

    return options
Beispiel #29
0
 def tearDown(self):
     shutil.rmtree(self.basedir)
     DBSession.remove()
Beispiel #30
0
    def test_run__no_existing_voters(self):
        """Tests a successful operation which creates the Voter instances and
        the associated filesystem artifacts. There are now existing Voters in
        the database.
        """
        from nuvavaalit.models import Voter
        from nuvavaalit.scripts.populate import CreateVoters

        session = DBSession()

        xlsx_file = self.filename("input.xlsx")
        output = self.filename("voters.xls")

        data = [
            ("Rögers", "Bück Williäm", "Sömewhere street", "04320", "Helsinki"),
            ("Li", "Jët Huan", "Chinä road 23", "04300", "Helsinki"),
            ("Cöllisiön", "Näme Föö", "Cöllider curve 4", "05400", "Helsinki"),
            ("Cöllisiön", "Näme Bär", "Cöllider curve 7", "05400", "Helsinki"),
        ]
        wb = openpyxl.Workbook()
        ws = wb.get_active_sheet()
        for row, values in enumerate(data, start=1):
            for col, value in enumerate(values, start=1):
                ws.cell(row=row, column=col).value = value
        wb.save(filename=xlsx_file)

        cs = CreateVoters(xlsx_file, output)

        # Make sure we don't have any existing voters.
        self.assertEquals(0, session.query(Voter).count())
        self.assertEquals(0, len(cs.usernames))

        cs.run()

        # Make sure the Excel sheet got created.
        self.assertTrue(os.path.exists(output))

        # Check the Itella list. All voters without a GSM or email must be present.
        wb = xlrd.open_workbook(filename=output)
        ws = wb.sheet_by_index(0)
        self.assertEquals(5, ws.nrows)
        self.assertEquals(6, ws.ncols)
        self.assertEquals(ws.cell_value(1, 0), u"buck.rogers")
        self.assertEquals(ws.cell_value(1, 1), u"7xs464kc")
        self.assertEquals(ws.cell_value(1, 2), u"Bück Williäm Rögers")
        self.assertEquals(ws.cell_value(1, 3), u"Sömewhere street")
        self.assertEquals(ws.cell_value(1, 4), u"04320")
        self.assertEquals(ws.cell_value(1, 5), u"Helsinki")

        # Check created Voter objects. All voters must be present.
        voters = session.query(Voter).order_by(Voter.username).all()
        self.assertEquals(4, len(voters))

        self.assertEquals(voters[0].username, u"buck.rogers")
        self.assertEquals(voters[0].firstname, u"Bück")
        self.assertEquals(voters[0].lastname, u"Rögers")

        self.assertEquals(voters[1].username, u"jet.li")
        self.assertEquals(voters[1].firstname, u"Jët")
        self.assertEquals(voters[1].lastname, u"Li")

        self.assertEquals(voters[2].username, u"name.b.collision")
        self.assertEquals(voters[2].firstname, u"Näme")
        self.assertEquals(voters[2].lastname, u"Cöllisiön")

        self.assertEquals(voters[3].username, u"name.collision")
        self.assertEquals(voters[3].firstname, u"Näme")
        self.assertEquals(voters[3].lastname, u"Cöllisiön")