Changes to 'fix_caching'
by Pierre-YvesChibon
New branch 'fix_caching' available with the following commits:
commit 1c5d618971da5e617cb9224f8546cbb8329fc3bc
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 13:11:21 2014 +0100
Let the about page of an election use the cached information
commit 888bbdf728ce454a2399a4cbe59a5a8d3ffd617a
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 13:10:54 2014 +0100
Fix displaying the real name retrieved from FAS present inthe usernamemap
9 years, 6 months
Changes to 'refs/tags/v2.4'
by Pierre-YvesChibon
Tag 'v2.4' created by Pierre-Yves Chibon <pingou(a)pingoured.fr> at 2014-11-20 12:13 +0000
Release 2.4
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iEYEABECAAYFAlRt2t0ACgkQ2HRbBxDocwqkoACeLn1sLtnOBp6z6w6H1cPPKZl1
qy4AoJXnavRkyi8CIZpOuDaFz8J378yL
=trk1
-----END PGP SIGNATURE-----
Changes since v2.3:
Joshua Santos (2):
Adding for abstain against feature
Adding unit tests
Pierre-Yves Chibon (37):
Fix displaying the information after the field when adding/editing an election
Fix the display for the start_date and end_date to always have them specify it opens at 00:00 and closes at 23:59:59
Fix link to the results page from the about page
Create a new jinja filter that rjust() some provided text
Add a new template presenting the results in the format of the announcements
Create a new endpoint providing the election results in text
Add a flash message for the election admins pointing them to the text version of the results
Let's make it simple: only admins can see the text version of the results page
Check is the user is authenticated before checking if he/she is an admin
Optimization: by using `and` instead of `or` will stop the condition at the first False
Rely on the is_authenticated() where it makes sense
Add unit-tests for the text output of the results
Add a `max_vote` entry in the stats table used for the result page
Adjust calculating the maximum number of votes to use `max_vote`
Simplify checking if the user is allowed to view the result page when the election is under embargo
Fix check for showing the election announce template
Fix typo in comment
Add unit-test checking the text output with some vote/result
Merge pull request #33 from fedora-infra/text_results
Simplify the form for get_for_abstain_against_voting_form
Simplify retrieving the candidate id submitted via the form
Fix get_election_stats for the `for_abstain_against` elections
Add some candidates to the election #7
Adjust again the form logic for vote_for_abstain_against
Adjust the unit-tests to handle the pro_abstain_against vote
Name the `for_abstain_against` election type `+1/0/-1 voting` in the UI
Change ``for_abstain_against`` voting to ``irc`` voting
Adjust test function names and docstrings for the change in the type
One more forAbstainAgainst to change
Merge pull request #34 from fedora-infra/for_abstain_against_vote
Activate the permanent session so that session time-out as specified in the configuration file
Add header and a default time-out period for the session in the default configuration file
Merge pull request #35 from fedora-infra/permanent_session
Merge pull request #36 from fedora-infra/feature/cache
pep8 clean up
Add missing encoding information
Release 2.4
Ralph Bean (1):
Use dogpile.cache to vastly speed up the app.
---
fedora_elections/__init__.py | 21 ++
fedora_elections/default_config.py | 18 ++
fedora_elections/elections.py | 137 +++++++++++------
fedora_elections/forms.py | 15 +
fedora_elections/models.py | 9 +
fedora_elections/templates/about.html | 3
fedora_elections/templates/admin/all_elections.html | 4
fedora_elections/templates/admin/election_form.html | 4
fedora_elections/templates/admin/view_election.html | 4
fedora_elections/templates/archive.html | 4
fedora_elections/templates/index.html | 4
fedora_elections/templates/results.html | 4
fedora_elections/templates/results_text.html | 60 +++++++
fedora_elections/utils.py | 30 +++
files/fedora-elections.spec | 16 +-
requirements.txt | 1
tests/test_candidate.py | 22 ++
tests/test_election.py | 32 +++-
tests/test_flask.py | 10 -
tests/test_flask_elections.py | 122 +++++++++++++++
tests/test_flask_irc.py | 157 ++++++++++++++++++++
tests/test_flask_range_voting.py | 3
tests/test_flask_select_voting.py | 3
tests/test_flask_simple_voting.py | 3
tests/test_vote.py | 12 +
25 files changed, 624 insertions(+), 74 deletions(-)
---
9 years, 6 months
40 commits - fedora_elections/default_config.py fedora_elections/elections.py fedora_elections/forms.py fedora_elections/__init__.py fedora_elections/models.py fedora_elections/templates fedora_elections/utils.py files/fedora-elections.spec requirements.txt tests/test_candidate.py tests/test_election.py tests/test_flask_elections.py tests/test_flask_irc.py tests/test_flask.py tests/test_flask_range_voting.py tests/test_flask_select_voting.py tests/test_flask_simple_voting.py tests/test_vot
by Pierre-YvesChibon
fedora_elections/__init__.py | 21 ++
fedora_elections/default_config.py | 18 ++
fedora_elections/elections.py | 137 +++++++++++------
fedora_elections/forms.py | 15 +
fedora_elections/models.py | 9 +
fedora_elections/templates/about.html | 3
fedora_elections/templates/admin/all_elections.html | 4
fedora_elections/templates/admin/election_form.html | 4
fedora_elections/templates/admin/view_election.html | 4
fedora_elections/templates/archive.html | 4
fedora_elections/templates/index.html | 4
fedora_elections/templates/results.html | 4
fedora_elections/templates/results_text.html | 60 +++++++
fedora_elections/utils.py | 30 +++
files/fedora-elections.spec | 16 +-
requirements.txt | 1
tests/test_candidate.py | 22 ++
tests/test_election.py | 32 +++-
tests/test_flask.py | 10 -
tests/test_flask_elections.py | 122 +++++++++++++++
tests/test_flask_irc.py | 157 ++++++++++++++++++++
tests/test_flask_range_voting.py | 3
tests/test_flask_select_voting.py | 3
tests/test_flask_simple_voting.py | 3
tests/test_vote.py | 12 +
25 files changed, 624 insertions(+), 74 deletions(-)
New commits:
commit cd9744663a40a8e2868f4a18368d0754d841e5e5
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 12:28:13 2014 +0100
Release 2.4
diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py
index 0cde879..157a435 100644
--- a/fedora_elections/__init__.py
+++ b/fedora_elections/__init__.py
@@ -24,7 +24,7 @@
# Pierre-Yves Chibon <pingou(a)fedoraproject.org>
#
-__version__ = '2.3'
+__version__ = '2.4'
import os
diff --git a/files/fedora-elections.spec b/files/fedora-elections.spec
index c1e9996..c8ab8ce 100644
--- a/files/fedora-elections.spec
+++ b/files/fedora-elections.spec
@@ -1,7 +1,7 @@
%define modname fedora_elections
Name: fedora-elections
-Version: 2.3
+Version: 2.4
Release: 1%{?dist}
Summary: Fedora elections application
@@ -24,6 +24,7 @@ BuildRequires: python-setuptools
BuildRequires: python-wtforms
BuildRequires: python-nose
BuildRequires: python-coverage
+BuildRequires: python-dogpile-cache
Requires: python-fedora
Requires: python-fedora-flask
@@ -33,6 +34,7 @@ Requires: python-openid
Requires: python-openid-teams
Requires: python-openid-cla
Requires: python-wtforms
+Requires: python-dogpile-cache
# EPEL6
%if ( 0%{?rhel} && 0%{?rhel} == 6 )
@@ -109,6 +111,18 @@ install -m 644 files/update_1_to_2.sql \
%changelog
+* Thu Nov 20 2014 Pierre-Yves Chibon <pingou(a)pingoured.fr> - 2.4-1
+- Update to 2.4
+- Fix the election creation/edit page to show all the labels
+- Display the start and end time for the date fields in the create/edit
+ election pages
+- Add a text result page that can be used as basis to send the announcement
+ email with the results
+- Add new election type: IRC allowing users to do +1/0/-1 votes
+- Add session time-out
+- Add caching for the information retrieved from FAS
+- Add python-dogpile-cache as BR and R
+
* Wed Jul 16 2014 Pierre-Yves Chibon <pingou(a)pingoured.fr> - 2.3-1
- Update to 2.3
- Restrict the groups asked upon login (Allows Dennis to log in)
commit 300f48a2ba7813b095cfe8be519aed11d7751cea
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 11:52:46 2014 +0100
Add missing encoding information
diff --git a/fedora_elections/utils.py b/fedora_elections/utils.py
index e6fd08c..66ce8cb 100644
--- a/fedora_elections/utils.py
+++ b/fedora_elections/utils.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
import fedora_elections
from fedora.client import AuthError
commit 0b8cc3a062d71c556447976dacea7ad31158024e
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 11:52:39 2014 +0100
pep8 clean up
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index b8f3d8d..9ec8df8 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -297,9 +297,9 @@ def vote_irc(election):
num_candidates = election.candidates.count()
next_action = 'confirm'
- form=forms.get_irc_voting_form(
- candidates=election.candidates,
- fasusers=election.candidates_are_fasusers)
+ form = forms.get_irc_voting_form(
+ candidates=election.candidates,
+ fasusers=election.candidates_are_fasusers)
if form.validate_on_submit():
if form.action.data == 'submit':
for candidate in form:
@@ -351,7 +351,8 @@ def election_results(election_alias):
"embargoed pending formal announcement.")
return safe_redirect_back()
- if is_authenticated() and (is_admin(flask.g.fas_user) \
+ if is_authenticated() and (
+ is_admin(flask.g.fas_user)
or is_election_admin(flask.g.fas_user, election.id)):
flask.flash(
"Check out the <a href='%s'>Text version</a> "
@@ -370,6 +371,7 @@ def election_results(election_alias):
stats=stats,
)
+
@APP.route('/results/<election_alias>/text')
def election_results_text(election_alias):
election = get_valid_election(election_alias, ended=True)
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index 7150564..2603973 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -167,7 +167,7 @@ def get_irc_voting_form(candidates, fasusers):
for candidate in candidates:
field = wtforms.SelectField(
candidate.name,
- choices=[('0',0),('1',1),('-1',-1)]
+ choices=[('0', 0), ('1', 1), ('-1', -1)]
)
setattr(IrcVoting, candidate.name, field)
diff --git a/tests/test_flask_elections.py b/tests/test_flask_elections.py
index 2d7c5d0..90a0230 100644
--- a/tests/test_flask_elections.py
+++ b/tests/test_flask_elections.py
@@ -361,7 +361,6 @@ candidates for running this elections!
self.assertTrue('<h3>Current elections</h3>' in output.data)
-
if __name__ == '__main__':
SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_range_voting.py b/tests/test_flask_range_voting.py
index 0cc94fd..d0be838 100644
--- a/tests/test_flask_range_voting.py
+++ b/tests/test_flask_range_voting.py
@@ -298,5 +298,6 @@ class FlaskRangeElectionstests(ModelFlasktests):
if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskRangeElectionstests)
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(
+ FlaskRangeElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_select_voting.py b/tests/test_flask_select_voting.py
index cc248b3..73d2ca0 100644
--- a/tests/test_flask_select_voting.py
+++ b/tests/test_flask_select_voting.py
@@ -170,5 +170,6 @@ class FlaskSimpleElectionstests(ModelFlasktests):
if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskSimpleElectionstests)
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(
+ FlaskSimpleElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_simple_voting.py b/tests/test_flask_simple_voting.py
index aa28980..1eaa339 100644
--- a/tests/test_flask_simple_voting.py
+++ b/tests/test_flask_simple_voting.py
@@ -246,5 +246,6 @@ class FlaskSimpleElectionstests(ModelFlasktests):
if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskSimpleElectionstests)
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(
+ FlaskSimpleElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_vote.py b/tests/test_vote.py
index 8d15279..92f1e71 100644
--- a/tests/test_vote.py
+++ b/tests/test_vote.py
@@ -178,11 +178,11 @@ class Votetests(Modeltests):
# Election 7
- obj = models.Vote( #id:1
- election_id=7,
- voter='toshio',
- candidate_id=12,
- value='1',
+ obj = models.Vote( # id:1
+ election_id=7,
+ voter='toshio',
+ candidate_id=12,
+ value='1',
)
self.session.add(obj)
self.session.commit()
commit 9b299fa5a70ff8abc1dccdce62354a135a14d17d
Merge: 6ff26d1 6eca259
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 11:47:52 2014 +0100
Merge pull request #36 from fedora-infra/feature/cache
Use dogpile.cache to vastly speed up the app.
commit 6eca25955d33d427bd6893705deeb9876dc58e2f
Author: Ralph Bean <rbean(a)redhat.com>
Date: Mon Nov 17 20:57:39 2014 -0500
Use dogpile.cache to vastly speed up the app.
Currently, every page load in the voting process requires that the
elections make ``N`` queries to FAS where ``N`` is the number of
candidates. It's being really slow in production tonight with the
opening of the Council election.
This should add some generic caching so that we can use an on-disk .dbm
cache if we like or alternatively memcached.
Tested locally.
diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py
index 131f320..0cde879 100644
--- a/fedora_elections/__init__.py
+++ b/fedora_elections/__init__.py
@@ -38,6 +38,8 @@ from fedora.client import AuthError, AppError
from fedora.client.fas2 import AccountSystem
from flask.ext.fas_openid import FAS
+import dogpile.cache
+
import fedora_elections.fedmsgshim
import fedora_elections.proxy
@@ -64,6 +66,9 @@ from fedora_elections import models
SESSION = models.create_session(APP.config['DB_URL'])
from fedora_elections import forms
+cache = dogpile.cache.make_region()
+cache.configure(**APP.config['DOGPILE_CACHE'])
+
def is_authenticated():
''' Return a boolean specifying if the user is authenticated or not.
diff --git a/fedora_elections/default_config.py b/fedora_elections/default_config.py
index 979dd9d..e887840 100644
--- a/fedora_elections/default_config.py
+++ b/fedora_elections/default_config.py
@@ -22,3 +22,10 @@ FAS_BASE_URL = 'https://admin.stg.fedoraproject.org/accounts/'
FAS_USERNAME = ''
FAS_PASSWORD = ''
FAS_CHECK_CERT = False
+
+DOGPILE_CACHE = {
+ 'backend': 'dogpile.cache.dbm',
+ 'arguments': {
+ 'filename': '/var/tmp/elections-cache.dbm',
+ },
+}
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 2ad0e1e..b8f3d8d 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -37,6 +37,7 @@ from fedora_elections import (
APP, SESSION, FAS2, is_authenticated, is_admin, is_election_admin,
is_safe_url, safe_redirect_back,
)
+from fedora_elections.utils import build_name_map
def login_required(f):
@@ -166,16 +167,7 @@ def vote_range(election):
flask.flash("Please confirm your vote!")
next_action = 'vote'
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[str(candidate.id)] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError), err:
- APP.logger.debug(err)
- # User has their name set to private or user doesn't exist.
- usernamemap[str(candidate.id)] = candidate.name
+ usernamemap = build_name_map(election)
return flask.render_template(
'vote_range.html',
@@ -239,16 +231,7 @@ def vote_select(election):
flask.flash("Please confirm your vote!")
next_action = 'vote'
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[candidate.name] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError), err:
- APP.logger.debug(err)
- # User has their name set to private or user doesn't exist.
- usernamemap[candidate.name] = candidate.name
+ usernamemap = build_name_map(election)
return flask.render_template(
'vote_simple.html',
@@ -376,15 +359,7 @@ def election_results(election_alias):
'election_results_text', election_alias=election.alias)
)
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[candidate.id] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError):
- # User has their name set to private or user doesn't exist.
- usernamemap[candidate.id] = candidate.name
+ usernamemap = build_name_map(election)
stats = models.Vote.get_election_stats(SESSION, election.id)
@@ -408,15 +383,7 @@ def election_results_text(election_alias):
"The text results are only available to the admins", "error")
return safe_redirect_back()
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[candidate.id] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError):
- # User has their name set to private or user doesn't exist.
- usernamemap[candidate.id] = candidate.name
+ usernamemap = build_name_map(election)
stats = models.Vote.get_election_stats(SESSION, election.id)
diff --git a/fedora_elections/utils.py b/fedora_elections/utils.py
new file mode 100644
index 0000000..e6fd08c
--- /dev/null
+++ b/fedora_elections/utils.py
@@ -0,0 +1,28 @@
+import fedora_elections
+
+from fedora.client import AuthError
+
+
+def build_name_map(election):
+ """ Returns a mapping of candidate ids to fas human_names. """
+ if not election.candidates_are_fasusers:
+ return {}
+
+ return dict([
+ (str(candidate.id), get_fas_human_name(candidate.name))
+ for candidate in election.candidates
+ ])
+
+
+(a)fedora_elections.cache.cache_on_arguments()
+def get_fas_human_name(username):
+ """ Given a fas username, return the fas human_name if possible.
+
+ If the user has their name set to private or they don't exist, we just
+ return the given username as a stand-in.
+ """
+ try:
+ return fedora_elections.FAS2.person_by_username(username)['human_name']
+ except (KeyError, AuthError), err:
+ fedora_elections.APP.logger.debug(err)
+ return username
diff --git a/requirements.txt b/requirements.txt
index 7bff0c9..a62a392 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,4 @@ python-openid-cla
python-openid-teams
SQLAlchemy
wtforms
+dogpile.cache
commit 6ff26d188985174fedadcf76538c9d2e7a386cf1
Merge: b1396a3 398d7d3
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 17:10:55 2014 +0200
Merge pull request #35 from fedora-infra/permanent_session
Permanent session
commit 398d7d3f4f1449f84189472e3718e75b92c3155d
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:46:57 2014 +0200
Add header and a default time-out period for the session in the default configuration file
diff --git a/fedora_elections/default_config.py b/fedora_elections/default_config.py
index 9a37e23..979dd9d 100644
--- a/fedora_elections/default_config.py
+++ b/fedora_elections/default_config.py
@@ -1,3 +1,14 @@
+# -*- coding: utf-8 -*-
+
+'''
+Fedora elections default configuration.
+'''
+
+from datetime import timedelta
+
+# Set the time after which the session expires
+PERMANENT_SESSION_LIFETIME = timedelta(hours=1)
+
FEDORA_ELECTIONS_ADMIN_GROUP = 'elections'
# url to the database server:
commit 32788ff1f7219ec341ea0421e3672debe51d094e
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:46:35 2014 +0200
Activate the permanent session so that session time-out as specified in the configuration file
diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py
index 2fd1222..131f320 100644
--- a/fedora_elections/__init__.py
+++ b/fedora_elections/__init__.py
@@ -152,6 +152,7 @@ def inject_variables():
return dict(is_admin=is_admin(user),
version=__version__)
+
@APP.template_filter('rjust')
def rjust_filter(text, length):
""" Run a rjust command on the text for the given length
@@ -159,6 +160,13 @@ def rjust_filter(text, length):
return str(text).rjust(length)
+# pylint: disable=W0613
+(a)APP.before_request
+def set_session():
+ """ Set the flask session as permanent. """
+ flask.session.permanent = True
+
+
# LIST VIEWS #############################################
@APP.route('/')
commit b1396a3567540aa03aaccabe8903fa97c805fa83
Merge: 1f21208 8a42ce6
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:34:51 2014 +0200
Merge pull request #34 from fedora-infra/for_abstain_against_vote
For abstain against vote
commit 8a42ce62abbefb172d0cf8c575b5f843d987b0e6
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:32:42 2014 +0200
One more forAbstainAgainst to change
diff --git a/tests/test_election.py b/tests/test_election.py
index 583ce13..b33bf86 100644
--- a/tests/test_election.py
+++ b/tests/test_election.py
@@ -172,7 +172,7 @@ class Electiontests(Modeltests):
self.session.add(obj)
self.session.commit()
self.assertNotEqual(obj, None)
- # Election - forAbstainAgainst voting - Open
+ # Election - IRC voting - Open
obj = models.Election( # id:7
shortdesc='test election 7 shortdesc',
alias='test_election7',
commit 8ffd821696d0409ebd03413b380434e1e091babb
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:30:17 2014 +0200
Adjust test function names and docstrings for the change in the type
diff --git a/tests/test_flask_irc.py b/tests/test_flask_irc.py
index 44d467d..9c45fa4 100644
--- a/tests/test_flask_irc.py
+++ b/tests/test_flask_irc.py
@@ -44,8 +44,8 @@ from tests import ModelFlasktests, Modeltests, TODAY, FakeUser, user_set
class FlaskIrcElectionstests(ModelFlasktests):
""" Flask application tests irc voting. """
- def test_vote_for_abstain_against(self):
- """ Test the vote_for_abstain_against function - the preview part. """
+ def test_vote_irc(self):
+ """ Test the vote_irc function - the preview part. """
output = self.app.get(
'/vote/test_election', follow_redirects=True)
self.assertEqual(output.status_code, 200)
@@ -105,8 +105,8 @@ class FlaskIrcElectionstests(ModelFlasktests):
'<li class="message">Please confirm your vote!</li>'
in output.data)
- def test_vote_for_abstain_against_process(self):
- """ Test the vote_for_abstain_against function - the voting part. """
+ def test_vote_irc_process(self):
+ """ Test the vote_irc function - the voting part. """
output = self.app.get(
'/vote/test_election', follow_redirects=True)
self.assertEqual(output.status_code, 200)
commit 64c3628ffa812ad5e642e3fd28932d63a1e1bacc
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:27:59 2014 +0200
Change ``for_abstain_against`` voting to ``irc`` voting
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index bfff1cd..2ad0e1e 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -117,8 +117,8 @@ def vote(election_alias):
return vote_simple(election)
elif election.voting_type == 'select':
return vote_select(election)
- elif election.voting_type == 'for_abstain_against':
- return vote_for_abstain_against(election)
+ elif election.voting_type == 'irc':
+ return vote_irc(election)
else: # pragma: no cover
flask.flash(
'Unknown election voting type: %s' % election.voting_type)
@@ -303,7 +303,7 @@ def vote_simple(election):
nextaction=next_action)
-def vote_for_abstain_against(election):
+def vote_irc(election):
votes = models.Vote.of_user_on_election(
SESSION, flask.g.fas_user.username, election.id, count=True)
@@ -314,7 +314,7 @@ def vote_for_abstain_against(election):
num_candidates = election.candidates.count()
next_action = 'confirm'
- form=forms.get_for_abstain_against_voting_form(
+ form=forms.get_irc_voting_form(
candidates=election.candidates,
fasusers=election.candidates_are_fasusers)
if form.validate_on_submit():
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index 50f676b..7150564 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -37,7 +37,7 @@ class ElectionForm(wtf.Form):
('range_3', 'Simplified Range Voting (max is set below)'),
('select', 'Select Voting (checkboxes for each candidate, '
'maximum number of votes set below)'),
- ('for_abstain_against', '+1/0/-1 voting'),
+ ('irc', '+1/0/-1 voting'),
],
default='range')
@@ -160,8 +160,8 @@ def get_simple_voting_form(candidates, fasusers):
return SimpleVoting()
-def get_for_abstain_against_voting_form(candidates, fasusers):
- class ForAbstainAgainstVoting(wtf.Form):
+def get_irc_voting_form(candidates, fasusers):
+ class IrcVoting(wtf.Form):
action = wtforms.HiddenField()
for candidate in candidates:
@@ -169,9 +169,9 @@ def get_for_abstain_against_voting_form(candidates, fasusers):
candidate.name,
choices=[('0',0),('1',1),('-1',-1)]
)
- setattr(ForAbstainAgainstVoting, candidate.name, field)
+ setattr(IrcVoting, candidate.name, field)
- return ForAbstainAgainstVoting()
+ return IrcVoting()
def get_select_voting_form(candidates, max_selection):
diff --git a/fedora_elections/models.py b/fedora_elections/models.py
index 1ff263f..74af82d 100644
--- a/fedora_elections/models.py
+++ b/fedora_elections/models.py
@@ -420,7 +420,7 @@ class Vote(BASE):
stats['max_vote'] = election.max_votes
elif election.voting_type == 'simple':
stats['max_vote'] = 1
- elif election.voting_type == 'for_abstain_against':
+ elif election.voting_type == 'irc':
stats['max_vote'] = 1
return stats
diff --git a/tests/test_election.py b/tests/test_election.py
index fe1e17e..583ce13 100644
--- a/tests/test_election.py
+++ b/tests/test_election.py
@@ -182,7 +182,7 @@ class Electiontests(Modeltests):
end_date=TODAY + timedelta(days=3),
seats_elected=1,
embargoed=1,
- voting_type='for_abstain_against',
+ voting_type='irc',
candidates_are_fasusers=0,
max_votes=1,
fas_user='nerdsville',
diff --git a/tests/test_flask_for_abstain_against_voting.py b/tests/test_flask_for_abstain_against_voting.py
deleted file mode 100644
index 243159d..0000000
--- a/tests/test_flask_for_abstain_against_voting.py
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-"""
- (c) 2014 - Copyright Joshua Santos
- Author: Joshua Santos <nerdsville(a)nerdsville.net>
-
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2, or (at your option) any later version. This
-# program is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY expressed or implied, including the implied warranties of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details. You should have received a copy of the GNU
-# General Public License along with this program; if not, write to the Free
-# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the source
-# code or documentation are not subject to the GNU General Public License and
-# may only be used or replicated with the express permission of Red Hat, Inc.
-
- fedora_elections.elections test script
-"""
-__requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4']
-import pkg_resources
-
-import logging
-import unittest
-import sys
-import os
-
-from datetime import time
-from datetime import timedelta
-
-import flask
-
-sys.path.insert(0, os.path.join(os.path.dirname(
- os.path.abspath(__file__)), '..'))
-
-import fedora_elections
-from tests import ModelFlasktests, Modeltests, TODAY, FakeUser, user_set
-
-
-# pylint: disable=R0904
-class FlaskSimpleElectionstests(ModelFlasktests):
- """ Flask application tests range voting. """
-
- def test_vote_for_abstain_against(self):
- """ Test the vote_for_abstain_against function - the preview part. """
- output = self.app.get(
- '/vote/test_election', follow_redirects=True)
- self.assertEqual(output.status_code, 200)
- self.assertTrue(
- '<title>OpenID transaction in progress</title>' in output.data
- or 'discoveryfailure' in output.data)
-
- self.setup_db()
-
- user = FakeUser(['packager'], username='toshio')
- with user_set(fedora_elections.APP, user):
- output = self.app.get(
- '/vote/test_election7', follow_redirects=True)
- self.assertTrue(
- 'class="message">You have already voted in the election!</'
- in output.data)
-
- user = FakeUser(['packager'], username='nerdsville')
- with user_set(fedora_elections.APP, user):
- output = self.app.get(
- '/vote/test_election7')
- self.assertTrue(
- '<h2>test election 7 shortdesc</h2>' in output.data)
- self.assertTrue(
- '<input type="hidden" name="action" value="preview" />'
- in output.data)
-
- csrf_token = output.data.split(
- 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
-
- # Invalid vote: No candidate
- data = {
- 'action': 'preview',
- }
-
- output = self.app.post('/vote/test_election7', data=data)
- self.assertEqual(output.status_code, 200)
- self.assertTrue(
- '<h2>test election 7 shortdesc</h2>' in output.data)
-
- # Valid input
- data = {
- 'Kevin': -1,
- 'Toshio': '0',
- 'action': 'preview',
- 'csrf_token': csrf_token,
- }
-
- output = self.app.post('/vote/test_election7', data=data)
- self.assertEqual(output.status_code, 200)
- self.assertTrue(
- '<h2>test election 7 shortdesc</h2>' in output.data)
- self.assertTrue(
- '<input type="hidden" name="action" value="submit" />'
- in output.data)
- self.assertTrue(
- '<li class="message">Please confirm your vote!</li>'
- in output.data)
-
- def test_vote_for_abstain_against_process(self):
- """ Test the vote_for_abstain_against function - the voting part. """
- output = self.app.get(
- '/vote/test_election', follow_redirects=True)
- self.assertEqual(output.status_code, 200)
- self.assertTrue(
- '<title>OpenID transaction in progress</title>' in output.data
- or 'discoveryfailure' in output.data)
-
- self.setup_db()
-
- user = FakeUser(['packager'], username='pingou')
- with user_set(fedora_elections.APP, user):
- # Invalid candidate id - no csrf
- data = {
- 'candidate': 1,
- 'action': 'submit',
- }
-
- output = self.app.post(
- '/vote/test_election7', data=data,
- follow_redirects=True)
- self.assertEqual(output.status_code, 200)
-
- csrf_token = output.data.split(
- 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
-
- # Valid input
- data = {
- 'Toshio': '0',
- 'Kevin': '1',
- 'action': 'submit',
- 'csrf_token': csrf_token,
- }
-
- output = self.app.post(
- '/vote/test_election7', data=data,
- follow_redirects=True)
- self.assertEqual(output.status_code, 200)
- self.assertTrue(
- 'class="message">Your vote has been recorded. Thank you!</'
- in output.data)
- self.assertTrue('<h3>Current elections</h3>' in output.data)
- self.assertTrue('<h3>Next 1 elections</h3>' in output.data)
- self.assertTrue('<h3>Last 2 elections</h3>' in output.data)
-
-
-if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskSimpleElectionstests)
- unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_irc.py b/tests/test_flask_irc.py
new file mode 100644
index 0000000..44d467d
--- /dev/null
+++ b/tests/test_flask_irc.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+ (c) 2014 - Copyright Joshua Santos
+ Author: Joshua Santos <nerdsville(a)nerdsville.net>
+
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2, or (at your option) any later version. This
+# program is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the GNU
+# General Public License along with this program; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the source
+# code or documentation are not subject to the GNU General Public License and
+# may only be used or replicated with the express permission of Red Hat, Inc.
+
+ fedora_elections.elections test script
+"""
+__requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4']
+import pkg_resources
+
+import logging
+import unittest
+import sys
+import os
+
+from datetime import time
+from datetime import timedelta
+
+import flask
+
+sys.path.insert(0, os.path.join(os.path.dirname(
+ os.path.abspath(__file__)), '..'))
+
+import fedora_elections
+from tests import ModelFlasktests, Modeltests, TODAY, FakeUser, user_set
+
+
+# pylint: disable=R0904
+class FlaskIrcElectionstests(ModelFlasktests):
+ """ Flask application tests irc voting. """
+
+ def test_vote_for_abstain_against(self):
+ """ Test the vote_for_abstain_against function - the preview part. """
+ output = self.app.get(
+ '/vote/test_election', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<title>OpenID transaction in progress</title>' in output.data
+ or 'discoveryfailure' in output.data)
+
+ self.setup_db()
+
+ user = FakeUser(['packager'], username='toshio')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/vote/test_election7', follow_redirects=True)
+ self.assertTrue(
+ 'class="message">You have already voted in the election!</'
+ in output.data)
+
+ user = FakeUser(['packager'], username='nerdsville')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/vote/test_election7')
+ self.assertTrue(
+ '<h2>test election 7 shortdesc</h2>' in output.data)
+ self.assertTrue(
+ '<input type="hidden" name="action" value="preview" />'
+ in output.data)
+
+ csrf_token = output.data.split(
+ 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
+
+ # Invalid vote: No candidate
+ data = {
+ 'action': 'preview',
+ }
+
+ output = self.app.post('/vote/test_election7', data=data)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<h2>test election 7 shortdesc</h2>' in output.data)
+
+ # Valid input
+ data = {
+ 'Kevin': -1,
+ 'Toshio': '0',
+ 'action': 'preview',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post('/vote/test_election7', data=data)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<h2>test election 7 shortdesc</h2>' in output.data)
+ self.assertTrue(
+ '<input type="hidden" name="action" value="submit" />'
+ in output.data)
+ self.assertTrue(
+ '<li class="message">Please confirm your vote!</li>'
+ in output.data)
+
+ def test_vote_for_abstain_against_process(self):
+ """ Test the vote_for_abstain_against function - the voting part. """
+ output = self.app.get(
+ '/vote/test_election', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<title>OpenID transaction in progress</title>' in output.data
+ or 'discoveryfailure' in output.data)
+
+ self.setup_db()
+
+ user = FakeUser(['packager'], username='pingou')
+ with user_set(fedora_elections.APP, user):
+ # Invalid candidate id - no csrf
+ data = {
+ 'candidate': 1,
+ 'action': 'submit',
+ }
+
+ output = self.app.post(
+ '/vote/test_election7', data=data,
+ follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+
+ csrf_token = output.data.split(
+ 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
+
+ # Valid input
+ data = {
+ 'Toshio': '0',
+ 'Kevin': '1',
+ 'action': 'submit',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/vote/test_election7', data=data,
+ follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ 'class="message">Your vote has been recorded. Thank you!</'
+ in output.data)
+ self.assertTrue('<h3>Current elections</h3>' in output.data)
+ self.assertTrue('<h3>Next 1 elections</h3>' in output.data)
+ self.assertTrue('<h3>Last 2 elections</h3>' in output.data)
+
+
+if __name__ == '__main__':
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskIrcElectionstests)
+ unittest.TextTestRunner(verbosity=2).run(SUITE)
commit 22e5cffc30c89dea14819fb4ac2201c3914f72b2
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 16:18:40 2014 +0200
Name the `for_abstain_against` election type `+1/0/-1 voting` in the UI
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index dda16b3..50f676b 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -37,7 +37,7 @@ class ElectionForm(wtf.Form):
('range_3', 'Simplified Range Voting (max is set below)'),
('select', 'Select Voting (checkboxes for each candidate, '
'maximum number of votes set below)'),
- ('for_abstain_against', 'For - Abstain - Against (+1, neutral, -1)'),
+ ('for_abstain_against', '+1/0/-1 voting'),
],
default='range')
commit d6ddf33bf2e83ca91386197ebf2adae9ed92f19c
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 15:00:57 2014 +0200
Adjust the unit-tests to handle the pro_abstain_against vote
diff --git a/tests/test_flask_for_abstain_against_voting.py b/tests/test_flask_for_abstain_against_voting.py
index 2c5e99c..243159d 100644
--- a/tests/test_flask_for_abstain_against_voting.py
+++ b/tests/test_flask_for_abstain_against_voting.py
@@ -85,9 +85,11 @@ class FlaskSimpleElectionstests(ModelFlasktests):
self.assertEqual(output.status_code, 200)
self.assertTrue(
'<h2>test election 7 shortdesc</h2>' in output.data)
+
# Valid input
data = {
'Kevin': -1,
+ 'Toshio': '0',
'action': 'preview',
'csrf_token': csrf_token,
}
@@ -132,7 +134,8 @@ class FlaskSimpleElectionstests(ModelFlasktests):
# Valid input
data = {
- 'Toshio': True,
+ 'Toshio': '0',
+ 'Kevin': '1',
'action': 'submit',
'csrf_token': csrf_token,
}
commit a58e7ea1c654edd9f74b04eb2532dc32f69e94c9
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 14:58:52 2014 +0200
Adjust again the form logic for vote_for_abstain_against
The name of the field will be the name of the candidate and adjust the
logic to handle it as such
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 7ba405d..bfff1cd 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -307,6 +307,10 @@ def vote_for_abstain_against(election):
votes = models.Vote.of_user_on_election(
SESSION, flask.g.fas_user.username, election.id, count=True)
+ cand_name = {}
+ for candidate in election.candidates:
+ cand_name[candidate.name] = candidate.id
+
num_candidates = election.candidates.count()
next_action = 'confirm'
@@ -318,12 +322,11 @@ def vote_for_abstain_against(election):
for candidate in form:
if candidate.short_name in ['csrf_token', 'action']:
continue
- cand_id = candidate.short_name.replace('candidate_', '')
new_vote = models.Vote(
election_id=election.id,
voter=flask.g.fas_user.username,
timestamp=datetime.now(),
- candidate_id=cand_id,
+ candidate_id=cand_name[candidate.short_name],
value=candidate.data,
)
SESSION.add(new_vote)
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index fc82721..dda16b3 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -164,15 +164,12 @@ def get_for_abstain_against_voting_form(candidates, fasusers):
class ForAbstainAgainstVoting(wtf.Form):
action = wtforms.HiddenField()
- fields = []
for candidate in candidates:
- fields.append(wtforms.SelectField(
+ field = wtforms.SelectField(
candidate.name,
choices=[('0',0),('1',1),('-1',-1)]
- ))
-
- for i,field in enumerate(fields):
- setattr(ForAbstainAgainstVoting,'candidate_'+str(i),field)
+ )
+ setattr(ForAbstainAgainstVoting, candidate.name, field)
return ForAbstainAgainstVoting()
commit bfa0f558f1f30fdb4daf43bc20ea346109ffc1d9
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 14:46:38 2014 +0200
Add some candidates to the election #7
diff --git a/tests/test_candidate.py b/tests/test_candidate.py
index aa87e95..90bdc08 100644
--- a/tests/test_candidate.py
+++ b/tests/test_candidate.py
@@ -187,6 +187,28 @@ class Candidatetests(Modeltests):
self.session.commit()
self.assertNotEqual(obj, None)
+ #
+ # Election #7
+ #
+
+ obj = models.Candidate( # id:14
+ election_id=7,
+ name='Toshio',
+ url='https://fedoraproject.org/wiki/User:Toshio',
+ )
+ self.session.add(obj)
+ self.session.commit()
+ self.assertNotEqual(obj, None)
+
+ obj = models.Candidate( # id:15
+ election_id=7,
+ name='Kevin',
+ url='https://fedoraproject.org/wiki/User:Kevin',
+ )
+ self.session.add(obj)
+ self.session.commit()
+ self.assertNotEqual(obj, None)
+
def test_to_json(self):
""" Test the Candidate.to_json function. """
self.test_init_candidate()
commit 7c282c7622c248b1e2df0ed63c7e1b20a44a2a45
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 14:38:46 2014 +0200
Fix get_election_stats for the `for_abstain_against` elections
diff --git a/fedora_elections/models.py b/fedora_elections/models.py
index 1cc51f4..1ff263f 100644
--- a/fedora_elections/models.py
+++ b/fedora_elections/models.py
@@ -420,6 +420,8 @@ class Vote(BASE):
stats['max_vote'] = election.max_votes
elif election.voting_type == 'simple':
stats['max_vote'] = 1
+ elif election.voting_type == 'for_abstain_against':
+ stats['max_vote'] = 1
return stats
commit df53ac4639dddfb6d149de0664c1fb70e3827536
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 14:38:20 2014 +0200
Simplify retrieving the candidate id submitted via the form
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index b50951a..7ba405d 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -315,18 +315,17 @@ def vote_for_abstain_against(election):
fasusers=election.candidates_are_fasusers)
if form.validate_on_submit():
if form.action.data == 'submit':
- i=0
for candidate in form:
if candidate.short_name in ['csrf_token', 'action']:
continue
+ cand_id = candidate.short_name.replace('candidate_', '')
new_vote = models.Vote(
election_id=election.id,
voter=flask.g.fas_user.username,
timestamp=datetime.now(),
- candidate_id=form.candidate_ids[i-1],
+ candidate_id=cand_id,
value=candidate.data,
)
- i += 1
SESSION.add(new_vote)
SESSION.commit()
flask.flash("Your vote has been recorded. Thank you!")
commit 27037ebd30c58d5e8d380b840f569bbb68ace74d
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue Aug 26 14:38:01 2014 +0200
Simplify the form for get_for_abstain_against_voting_form
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index 68cbed3..fc82721 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -163,32 +163,17 @@ def get_simple_voting_form(candidates, fasusers):
def get_for_abstain_against_voting_form(candidates, fasusers):
class ForAbstainAgainstVoting(wtf.Form):
action = wtforms.HiddenField()
- titles = []
- candidate_ids = []
+
fields = []
for candidate in candidates:
- title = candidate.name
- if fasusers: # pragma: no cover
- # We can't cover FAS integration
- try:
- title = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError), err:
- APP.logger.debug(err)
- if candidate.url:
- title = '%s <a href="%s">[Info]</a>' % (title, candidate.url)
- ForAbstainAgainstVoting.titles.append((str(candidate.id), title))
- ForAbstainAgainstVoting.candidate_ids.append(str(candidate.id))
fields.append(wtforms.SelectField(
candidate.name,
choices=[('0',0),('1',1),('-1',-1)]
))
- def validate_field(form, field):
- if field.data != '1' and field.data != '-1' and field.data != '0':
- raise wtforms.ValidationError(
- 'Not a valid integer value for vote.')
+
for i,field in enumerate(fields):
- setattr(ForAbstainAgainstVoting,'candidate'+str(i),field)
+ setattr(ForAbstainAgainstVoting,'candidate_'+str(i),field)
+
return ForAbstainAgainstVoting()
commit 59097d23b275b43240419ba927d96a2effd89257
Author: Joshua Santos <nerdsville(a)nerdsville.net>
Date: Mon Aug 25 07:54:08 2014 -0700
Adding unit tests
diff --git a/tests/test_election.py b/tests/test_election.py
index 2370716..fe1e17e 100644
--- a/tests/test_election.py
+++ b/tests/test_election.py
@@ -172,6 +172,24 @@ class Electiontests(Modeltests):
self.session.add(obj)
self.session.commit()
self.assertNotEqual(obj, None)
+ # Election - forAbstainAgainst voting - Open
+ obj = models.Election( # id:7
+ shortdesc='test election 7 shortdesc',
+ alias='test_election7',
+ description='test election 7 description',
+ url='https://fedoraproject.org',
+ start_date=TODAY - timedelta(days=1),
+ end_date=TODAY + timedelta(days=3),
+ seats_elected=1,
+ embargoed=1,
+ voting_type='for_abstain_against',
+ candidates_are_fasusers=0,
+ max_votes=1,
+ fas_user='nerdsville',
+ )
+ self.session.add(obj)
+ self.session.commit()
+ self.assertNotEqual(obj, None)
def test_get_election(self):
""" Test the Election.get function. """
@@ -246,13 +264,14 @@ class Electiontests(Modeltests):
obj = models.Election.search(self.session)
self.assertNotEqual(obj, None)
self.assertNotEqual(obj, [])
- self.assertEqual(len(obj), 6)
+ self.assertEqual(len(obj), 7)
self.assertEqual(obj[0].description, 'test election 4 description')
self.assertEqual(obj[1].description, 'test election 5 description')
self.assertEqual(obj[2].description, 'test election 6 description')
- self.assertEqual(obj[3].description, 'test election 3 description')
- self.assertEqual(obj[4].description, 'test election 2 description')
- self.assertEqual(obj[5].description, 'test election description')
+ self.assertEqual(obj[3].description, 'test election 7 description')
+ self.assertEqual(obj[4].description, 'test election 3 description')
+ self.assertEqual(obj[5].description, 'test election 2 description')
+ self.assertEqual(obj[6].description, 'test election description')
def test_get_older_election(self):
""" Test the Election.get_older_election function. """
@@ -270,10 +289,11 @@ class Electiontests(Modeltests):
obj = models.Election.get_open_election(self.session, limit=TODAY)
self.assertNotEqual(obj, None)
self.assertNotEqual(obj, [])
- self.assertEqual(len(obj), 3)
+ self.assertEqual(len(obj), 4)
self.assertEqual(obj[0].shortdesc, 'test election 5 shortdesc')
self.assertEqual(obj[1].shortdesc, 'test election 6 shortdesc')
- self.assertEqual(obj[2].shortdesc, 'test election 3 shortdesc')
+ self.assertEqual(obj[2].shortdesc, 'test election 7 shortdesc')
+ self.assertEqual(obj[3].shortdesc, 'test election 3 shortdesc')
def test_get_next_election(self):
""" Test the Election.get_next_election function. """
diff --git a/tests/test_flask.py b/tests/test_flask.py
index b9da2cd..9e7efbe 100644
--- a/tests/test_flask.py
+++ b/tests/test_flask.py
@@ -99,7 +99,7 @@ class Flasktests(ModelFlasktests):
self.assertTrue('<a href="/vote/' in output.data)
self.assertTrue(
'<span class="text">logged in as </span>' in output.data)
- self.assertEqual(output.data.count('Vote now!'), 3)
+ self.assertEqual(output.data.count('Vote now!'), 4)
user = FakeUser([], username='toshio')
with user_set(fedora_elections.APP, user):
@@ -296,7 +296,7 @@ class Flasktests(ModelFlasktests):
output = self.app.get('/open')
self.assertEqual(output.status_code, 200)
self.assertTrue('<title>Fedora elections</title>' in output.data)
- self.assertTrue('<h3>Next 3 elections</h3>' in output.data)
+ self.assertTrue('<h3>Next 4 elections</h3>' in output.data)
self.assertTrue('<td>test election 3 shortdesc</td>' in output.data)
self.assertTrue('<a href="/vote/' in output.data)
self.assertTrue('<a href="/login">login</a>' in output.data)
@@ -306,18 +306,18 @@ class Flasktests(ModelFlasktests):
output = self.app.get('/open')
self.assertEqual(output.status_code, 200)
self.assertTrue('<title>Fedora elections</title>' in output.data)
- self.assertTrue('<h3>Next 3 elections</h3>' in output.data)
+ self.assertTrue('<h3>Next 4 elections</h3>' in output.data)
self.assertTrue('<a href="/vote/' in output.data)
self.assertTrue(
'<span class="text">logged in as </span>' in output.data)
- self.assertEqual(output.data.count('Vote now!'), 3)
+ self.assertEqual(output.data.count('Vote now!'), 4)
user = FakeUser([], username='toshio')
with user_set(fedora_elections.APP, user):
output = self.app.get('/open')
self.assertEqual(output.status_code, 200)
self.assertTrue('<title>Fedora elections</title>' in output.data)
- self.assertTrue('<h3>Next 3 elections</h3>' in output.data)
+ self.assertTrue('<h3>Next 4 elections</h3>' in output.data)
self.assertTrue(
'<span class="text">logged in as </span>' in output.data)
self.assertEqual(output.data.count('Vote now!'), 0)
diff --git a/tests/test_flask_for_abstain_against_voting.py b/tests/test_flask_for_abstain_against_voting.py
new file mode 100644
index 0000000..2c5e99c
--- /dev/null
+++ b/tests/test_flask_for_abstain_against_voting.py
@@ -0,0 +1,154 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+ (c) 2014 - Copyright Joshua Santos
+ Author: Joshua Santos <nerdsville(a)nerdsville.net>
+
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2, or (at your option) any later version. This
+# program is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the GNU
+# General Public License along with this program; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the source
+# code or documentation are not subject to the GNU General Public License and
+# may only be used or replicated with the express permission of Red Hat, Inc.
+
+ fedora_elections.elections test script
+"""
+__requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4']
+import pkg_resources
+
+import logging
+import unittest
+import sys
+import os
+
+from datetime import time
+from datetime import timedelta
+
+import flask
+
+sys.path.insert(0, os.path.join(os.path.dirname(
+ os.path.abspath(__file__)), '..'))
+
+import fedora_elections
+from tests import ModelFlasktests, Modeltests, TODAY, FakeUser, user_set
+
+
+# pylint: disable=R0904
+class FlaskSimpleElectionstests(ModelFlasktests):
+ """ Flask application tests range voting. """
+
+ def test_vote_for_abstain_against(self):
+ """ Test the vote_for_abstain_against function - the preview part. """
+ output = self.app.get(
+ '/vote/test_election', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<title>OpenID transaction in progress</title>' in output.data
+ or 'discoveryfailure' in output.data)
+
+ self.setup_db()
+
+ user = FakeUser(['packager'], username='toshio')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/vote/test_election7', follow_redirects=True)
+ self.assertTrue(
+ 'class="message">You have already voted in the election!</'
+ in output.data)
+
+ user = FakeUser(['packager'], username='nerdsville')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/vote/test_election7')
+ self.assertTrue(
+ '<h2>test election 7 shortdesc</h2>' in output.data)
+ self.assertTrue(
+ '<input type="hidden" name="action" value="preview" />'
+ in output.data)
+
+ csrf_token = output.data.split(
+ 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
+
+ # Invalid vote: No candidate
+ data = {
+ 'action': 'preview',
+ }
+
+ output = self.app.post('/vote/test_election7', data=data)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<h2>test election 7 shortdesc</h2>' in output.data)
+ # Valid input
+ data = {
+ 'Kevin': -1,
+ 'action': 'preview',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post('/vote/test_election7', data=data)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<h2>test election 7 shortdesc</h2>' in output.data)
+ self.assertTrue(
+ '<input type="hidden" name="action" value="submit" />'
+ in output.data)
+ self.assertTrue(
+ '<li class="message">Please confirm your vote!</li>'
+ in output.data)
+
+ def test_vote_for_abstain_against_process(self):
+ """ Test the vote_for_abstain_against function - the voting part. """
+ output = self.app.get(
+ '/vote/test_election', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<title>OpenID transaction in progress</title>' in output.data
+ or 'discoveryfailure' in output.data)
+
+ self.setup_db()
+
+ user = FakeUser(['packager'], username='pingou')
+ with user_set(fedora_elections.APP, user):
+ # Invalid candidate id - no csrf
+ data = {
+ 'candidate': 1,
+ 'action': 'submit',
+ }
+
+ output = self.app.post(
+ '/vote/test_election7', data=data,
+ follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+
+ csrf_token = output.data.split(
+ 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
+
+ # Valid input
+ data = {
+ 'Toshio': True,
+ 'action': 'submit',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/vote/test_election7', data=data,
+ follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ 'class="message">Your vote has been recorded. Thank you!</'
+ in output.data)
+ self.assertTrue('<h3>Current elections</h3>' in output.data)
+ self.assertTrue('<h3>Next 1 elections</h3>' in output.data)
+ self.assertTrue('<h3>Last 2 elections</h3>' in output.data)
+
+
+if __name__ == '__main__':
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskSimpleElectionstests)
+ unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_vote.py b/tests/test_vote.py
index c3d81e5..8d15279 100644
--- a/tests/test_vote.py
+++ b/tests/test_vote.py
@@ -176,6 +176,18 @@ class Votetests(Modeltests):
self.session.commit()
self.assertNotEqual(obj, None)
+ # Election 7
+
+ obj = models.Vote( #id:1
+ election_id=7,
+ voter='toshio',
+ candidate_id=12,
+ value='1',
+ )
+ self.session.add(obj)
+ self.session.commit()
+ self.assertNotEqual(obj, None)
+
def test_vote_count_with_votes(self):
""" Test the Candidate.vote_count function with votes in. """
self.test_init_vote()
commit 96c803a6ddf46e31f140e0b194e21929dafb1abd
Author: Joshua Santos <nerdsville(a)nerdsville.net>
Date: Mon Aug 4 16:53:41 2014 -0700
Adding for abstain against feature
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 4abfbdc..b50951a 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -117,6 +117,8 @@ def vote(election_alias):
return vote_simple(election)
elif election.voting_type == 'select':
return vote_select(election)
+ elif election.voting_type == 'for_abstain_against':
+ return vote_for_abstain_against(election)
else: # pragma: no cover
flask.flash(
'Unknown election voting type: %s' % election.voting_type)
@@ -301,6 +303,47 @@ def vote_simple(election):
nextaction=next_action)
+def vote_for_abstain_against(election):
+ votes = models.Vote.of_user_on_election(
+ SESSION, flask.g.fas_user.username, election.id, count=True)
+
+ num_candidates = election.candidates.count()
+
+ next_action = 'confirm'
+ form=forms.get_for_abstain_against_voting_form(
+ candidates=election.candidates,
+ fasusers=election.candidates_are_fasusers)
+ if form.validate_on_submit():
+ if form.action.data == 'submit':
+ i=0
+ for candidate in form:
+ if candidate.short_name in ['csrf_token', 'action']:
+ continue
+ new_vote = models.Vote(
+ election_id=election.id,
+ voter=flask.g.fas_user.username,
+ timestamp=datetime.now(),
+ candidate_id=form.candidate_ids[i-1],
+ value=candidate.data,
+ )
+ i += 1
+ SESSION.add(new_vote)
+ SESSION.commit()
+ flask.flash("Your vote has been recorded. Thank you!")
+ return safe_redirect_back()
+
+ if form.action.data == 'preview':
+ flask.flash("Please confirm your vote!")
+ next_action = 'vote'
+
+ return flask.render_template(
+ 'vote_simple.html',
+ election=election,
+ form=form,
+ num_candidates=num_candidates,
+ nextaction=next_action)
+
+
@APP.route('/results/<election_alias>')
def election_results(election_alias):
election = get_valid_election(election_alias, ended=True)
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index 46c14ee..68cbed3 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -37,6 +37,7 @@ class ElectionForm(wtf.Form):
('range_3', 'Simplified Range Voting (max is set below)'),
('select', 'Select Voting (checkboxes for each candidate, '
'maximum number of votes set below)'),
+ ('for_abstain_against', 'For - Abstain - Against (+1, neutral, -1)'),
],
default='range')
@@ -159,6 +160,38 @@ def get_simple_voting_form(candidates, fasusers):
return SimpleVoting()
+def get_for_abstain_against_voting_form(candidates, fasusers):
+ class ForAbstainAgainstVoting(wtf.Form):
+ action = wtforms.HiddenField()
+ titles = []
+ candidate_ids = []
+ fields = []
+ for candidate in candidates:
+ title = candidate.name
+ if fasusers: # pragma: no cover
+ # We can't cover FAS integration
+ try:
+ title = \
+ FAS2.person_by_username(candidate.name)['human_name']
+ except (KeyError, AuthError), err:
+ APP.logger.debug(err)
+ if candidate.url:
+ title = '%s <a href="%s">[Info]</a>' % (title, candidate.url)
+ ForAbstainAgainstVoting.titles.append((str(candidate.id), title))
+ ForAbstainAgainstVoting.candidate_ids.append(str(candidate.id))
+ fields.append(wtforms.SelectField(
+ candidate.name,
+ choices=[('0',0),('1',1),('-1',-1)]
+ ))
+ def validate_field(form, field):
+ if field.data != '1' and field.data != '-1' and field.data != '0':
+ raise wtforms.ValidationError(
+ 'Not a valid integer value for vote.')
+ for i,field in enumerate(fields):
+ setattr(ForAbstainAgainstVoting,'candidate'+str(i),field)
+ return ForAbstainAgainstVoting()
+
+
def get_select_voting_form(candidates, max_selection):
class SelectVoting(wtf.Form):
action = wtforms.HiddenField()
commit 1f21208e5cf2d4680c4fdcdaca21ca2701efaab7
Merge: 7954b66 9dbe3fe
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Aug 1 11:52:03 2014 +0200
Merge pull request #33 from fedora-infra/text_results
Text results
commit 9dbe3fe02383e20ddc619c7ed10940424057a088
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Aug 1 11:45:03 2014 +0200
Add unit-test checking the text output with some vote/result
diff --git a/tests/test_flask_elections.py b/tests/test_flask_elections.py
index 65268c9..2d7c5d0 100644
--- a/tests/test_flask_elections.py
+++ b/tests/test_flask_elections.py
@@ -272,6 +272,47 @@ class FlaskElectionstests(ModelFlasktests):
username='toshio')
with user_set(fedora_elections.APP, user):
output = self.app.get(
+ '/results/test_election/text', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ exp = """<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Fedora elections</title>
+ <link rel="shortcut icon" type="image/vnd.microsoft.icon"
+ href="//fedoraproject.org/static/images/favicon.ico"/>
+ </head>
+ <body>
+<pre>
+Greetings, all!
+
+The elections for test election shortdesc have concluded, and the results
+are shown below.
+
+XXX is electing 1 seats this time.
+A total of 2 ballots were cast, meaning a candidate
+could accumulate up to 6 votes (2 * 3).
+
+The results for the elections are as follows:
+
+ # votes | name
+- --------+----------------------
+ 8 | Kevin
+- --------+----------------------
+ 7 | Ralph
+ 6 | Toshio
+
+
+Congratulations to the winning candidates, and thank you all
+candidates for running this elections!
+</pre>
+
+</body>
+</html>"""
+ self.assertEqual(output.data, exp)
+
+ output = self.app.get(
'/results/test_election2/text', follow_redirects=True)
self.assertEqual(output.status_code, 200)
exp = """<!DOCTYPE html>
commit 53316711f81df4730140087e3ec3c7e0010141d2
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Aug 1 11:26:47 2014 +0200
Fix typo in comment
Thanks @puiterwijk
diff --git a/fedora_elections/templates/results.html b/fedora_elections/templates/results.html
index 9183faf..435073b 100644
--- a/fedora_elections/templates/results.html
+++ b/fedora_elections/templates/results.html
@@ -23,7 +23,7 @@
available), check if the number of votes for this candidate is lower than
the number of votes for the last candidate and if the Flag is False
So this takes care of the case where there are 10 seats elected and the 11th
- candidate as the same score as the 10th one.
+ candidate has the same score as the 10th one.
In this case we would end up with one more person that the number of seats
available and we'll need to either find a way to select one over the other
or deal with having one more candidate accepted #}
diff --git a/fedora_elections/templates/results_text.html b/fedora_elections/templates/results_text.html
index f3947ec..9c99734 100644
--- a/fedora_elections/templates/results_text.html
+++ b/fedora_elections/templates/results_text.html
@@ -34,7 +34,7 @@ for candidate in election.candidates|sort(attribute='vote_count', reverse=True)
than the number of votes for the last candidate and if the Flag is
False.
So this takes care of the case where there are 10 seats elected and
- the 11th candidate as the same score as the 10th one.
+ the 11th candidate has the same score as the 10th one.
In this case we would end up with one more person that the number of
seats available and we'll need to either find a way to select one
over the other or deal with having one more candidate accepted -#}
commit 8f1aec4281996acff1794c5835a1ffa803dbf96c
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Aug 1 11:24:27 2014 +0200
Fix check for showing the election announce template
You may be either an election admin or a plain all-mighty admin
Thanks @puiterwijk
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 70ea77f..4abfbdc 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -357,8 +357,8 @@ def election_results_text(election_alias):
if not isinstance(election, models.Election): # pragma: no cover
return election
- if not (is_authenticated() and is_admin(flask.g.fas_user)
- and is_election_admin(flask.g.fas_user, election.id)):
+ if not (is_authenticated() and (is_admin(flask.g.fas_user)
+ or is_election_admin(flask.g.fas_user, election.id))):
flask.flash(
"The text results are only available to the admins", "error")
return safe_redirect_back()
commit 7fce149fbb8f2b324e3c779dd9cc97b1ba5acebd
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Aug 1 11:07:11 2014 +0200
Simplify checking if the user is allowed to view the result page when the election is under embargo
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 355eabb..70ea77f 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -309,26 +309,19 @@ def election_results(election_alias):
return election
elif election.embargoed:
- if not is_authenticated():
+ if is_authenticated() and (
+ is_admin(flask.g.fas_user)
+ or is_election_admin(flask.g.fas_user, election.id)):
+ flask.flash("You are only seeing this page because you are "
+ "an admin.", "warning")
+ flask.flash("The results for this election are currently "
+ "embargoed pending formal announcement.",
+ "warning")
+ else:
flask.flash("We are sorry. The results for this election "
"cannot be viewed because they are currently "
"embargoed pending formal announcement.")
return safe_redirect_back()
- else:
- if is_admin(flask.g.fas_user) \
- or is_election_admin(flask.g.fas_user, election.id):
- flask.flash("You are only seeing this page because you are "
- "an admin.", "warning")
- flask.flash("The results for this election are currently "
- "embargoed pending formal announcement.",
- "warning")
- pass
- else:
- flask.flash(
- "We are sorry. The results for this election "
- "cannot be viewed because they are currently "
- "embargoed pending formal announcement.")
- return safe_redirect_back()
if is_authenticated() and (is_admin(flask.g.fas_user) \
or is_election_admin(flask.g.fas_user, election.id)):
commit 88b340563c8ee5bf5bbbfc6a397dccc2f07d5bf1
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Sat Jul 26 12:43:31 2014 +0200
Adjust calculating the maximum number of votes to use `max_vote`
diff --git a/fedora_elections/templates/results.html b/fedora_elections/templates/results.html
index c4581b1..9183faf 100644
--- a/fedora_elections/templates/results.html
+++ b/fedora_elections/templates/results.html
@@ -68,7 +68,7 @@
</tr>
<tr>
<th title="Maximum number of votes possible">Maximum of votes</th>
- <td>{{ stats['n_voters'] * stats['n_candidates'] }}</td>
+ <td>{{ stats['n_voters'] * stats['max_vote'] }}</td>
</tr>
{% if stats['candidate_voters'] %}
<tr>
diff --git a/fedora_elections/templates/results_text.html b/fedora_elections/templates/results_text.html
index 59fbba0..f3947ec 100644
--- a/fedora_elections/templates/results_text.html
+++ b/fedora_elections/templates/results_text.html
@@ -16,7 +16,7 @@ are shown below.
XXX is electing {{ election.seats_elected }} seats this time.
A total of {{stats['n_voters'] }} ballots were cast, meaning a candidate
-could accumulate up to {{ stats['n_voters'] * stats['n_candidates'] }} votes ({{stats['n_voters'] }} * {{ stats['n_candidates'] }}).
+could accumulate up to {{ stats['n_voters'] * stats['max_vote'] }} votes ({{stats['n_voters'] }} * {{ stats['max_vote'] }}).
The results for the elections are as follows:
commit 9e82fe2336d0f4790f30b06a65968a688516f232
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Sat Jul 26 12:43:02 2014 +0200
Add a `max_vote` entry in the stats table used for the result page
diff --git a/fedora_elections/models.py b/fedora_elections/models.py
index d35096f..1cc51f4 100644
--- a/fedora_elections/models.py
+++ b/fedora_elections/models.py
@@ -414,6 +414,13 @@ class Vote(BASE):
stats['candidate_voters'] = candidate_voters
stats['n_candidates'] = cnt
+ if election.voting_type in ['range', 'select']:
+ stats['max_vote'] = cnt
+ if election.max_votes:
+ stats['max_vote'] = election.max_votes
+ elif election.voting_type == 'simple':
+ stats['max_vote'] = 1
+
return stats
commit 4359ef64463bbf915f48404e3912f9a958aaf46b
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:52:51 2014 +0200
Add unit-tests for the text output of the results
diff --git a/tests/test_flask_elections.py b/tests/test_flask_elections.py
index 397db4a..65268c9 100644
--- a/tests/test_flask_elections.py
+++ b/tests/test_flask_elections.py
@@ -238,6 +238,88 @@ class FlaskElectionstests(ModelFlasktests):
' and you may not see its results yet.</li>' in output.data)
self.assertTrue('<h3>Current elections</h3>' in output.data)
+ def test_election_results_text(self):
+ """ Test the election_results_text function - the preview part. """
+ output = self.app.get(
+ '/results/test_election/text', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ 'class="message">The election, test_election, does not exist.</'
+ in output.data)
+
+ self.setup_db()
+
+ output = self.app.get(
+ '/results/test_election/text', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ 'class="error">The text results are only available to the '
+ 'admins</li>' in output.data)
+ self.assertTrue('<h2>Elections</h2>' in output.data)
+
+ user = FakeUser(['packager'], username='toshio')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/results/test_election2/text', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ 'class="error">The text results are only available to the '
+ 'admins</li>' in output.data)
+ self.assertTrue('<h2>Elections</h2>' in output.data)
+
+ user = FakeUser(
+ fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'],
+ username='toshio')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/results/test_election2/text', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ exp = """<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Fedora elections</title>
+ <link rel="shortcut icon" type="image/vnd.microsoft.icon"
+ href="//fedoraproject.org/static/images/favicon.ico"/>
+ </head>
+ <body>
+<pre>
+Greetings, all!
+
+The elections for test election 2 shortdesc have concluded, and the results
+are shown below.
+
+XXX is electing 1 seats this time.
+A total of 0 ballots were cast, meaning a candidate
+could accumulate up to 0 votes (0 * 0).
+
+The results for the elections are as follows:
+
+ # votes | name
+- --------+----------------------
+
+
+Congratulations to the winning candidates, and thank you all
+candidates for running this elections!
+</pre>
+
+</body>
+</html>"""
+
+ self.assertEqual(output.data, exp)
+
+ user = FakeUser(['gitr2spec'], username='kevin')
+ with user_set(fedora_elections.APP, user):
+ output = self.app.get(
+ '/results/test_election3/text', follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertTrue(
+ '<li class="message">Sorry but this election is in progress,'
+ ' and you may not see its results yet.</li>' in output.data)
+ self.assertTrue('<h3>Current elections</h3>' in output.data)
+
+
if __name__ == '__main__':
SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskElectionstests)
commit 86578361f7e9dbb40fabbe4f08bfdc78d13202ea
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:45:04 2014 +0200
Rely on the is_authenticated() where it makes sense
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 5227ddb..355eabb 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -309,7 +309,7 @@ def election_results(election_alias):
return election
elif election.embargoed:
- if not hasattr(flask.g, 'fas_user') or not flask.g.fas_user:
+ if not is_authenticated():
flask.flash("We are sorry. The results for this election "
"cannot be viewed because they are currently "
"embargoed pending formal announcement.")
commit 950db261e90a6244f667dcc6b5bc00836b3f8354
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:44:14 2014 +0200
Optimization: by using `and` instead of `or` will stop the condition at the first False
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index e7b9893..5227ddb 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -364,8 +364,8 @@ def election_results_text(election_alias):
if not isinstance(election, models.Election): # pragma: no cover
return election
- if not is_authenticated() or not is_admin(flask.g.fas_user) \
- or not is_election_admin(flask.g.fas_user, election.id):
+ if not (is_authenticated() and is_admin(flask.g.fas_user)
+ and is_election_admin(flask.g.fas_user, election.id)):
flask.flash(
"The text results are only available to the admins", "error")
return safe_redirect_back()
commit 0d63467564d94d5497ac2da10002ba3d1438bc6d
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:43:43 2014 +0200
Check is the user is authenticated before checking if he/she is an admin
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index a4ca812..e7b9893 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -330,8 +330,8 @@ def election_results(election_alias):
"embargoed pending formal announcement.")
return safe_redirect_back()
- if is_admin(flask.g.fas_user) \
- or is_election_admin(flask.g.fas_user, election.id):
+ if is_authenticated() and (is_admin(flask.g.fas_user) \
+ or is_election_admin(flask.g.fas_user, election.id)):
flask.flash(
"Check out the <a href='%s'>Text version</a> "
"to send the annoucement" % flask.url_for(
commit cbad3d6573394df0b9c85e619956e6ab464db6b7
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:36:36 2014 +0200
Let's make it simple: only admins can see the text version of the results page
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 30a7c8f..a4ca812 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -364,12 +364,10 @@ def election_results_text(election_alias):
if not isinstance(election, models.Election): # pragma: no cover
return election
- elif election.embargoed and (
- not is_admin(flask.g.fas_user)
- or not is_election_admin(flask.g.fas_user, election.id)):
+ if not is_authenticated() or not is_admin(flask.g.fas_user) \
+ or not is_election_admin(flask.g.fas_user, election.id):
flask.flash(
- "The text results are only available to admins when the election"
- " is under embargo")
+ "The text results are only available to the admins", "error")
return safe_redirect_back()
usernamemap = {}
commit 0f507e6a1f3b392f4b9a457093c299b5e7adbf66
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:30:58 2014 +0200
Add a flash message for the election admins pointing them to the text version of the results
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 9a00384..30a7c8f 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -330,6 +330,14 @@ def election_results(election_alias):
"embargoed pending formal announcement.")
return safe_redirect_back()
+ if is_admin(flask.g.fas_user) \
+ or is_election_admin(flask.g.fas_user, election.id):
+ flask.flash(
+ "Check out the <a href='%s'>Text version</a> "
+ "to send the annoucement" % flask.url_for(
+ 'election_results_text', election_alias=election.alias)
+ )
+
usernamemap = {}
if (election.candidates_are_fasusers): # pragma: no cover
for candidate in election.candidates:
commit 0265cac5246d6729e3518448c7e2d84f7c4ef785
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:30:25 2014 +0200
Create a new endpoint providing the election results in text
This endpoint can be used as a basis to make the announcement
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 2b5c407..9a00384 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -348,3 +348,37 @@ def election_results(election_alias):
usernamemap=usernamemap,
stats=stats,
)
+
+(a)APP.route('/results/<election_alias>/text')
+def election_results_text(election_alias):
+ election = get_valid_election(election_alias, ended=True)
+
+ if not isinstance(election, models.Election): # pragma: no cover
+ return election
+
+ elif election.embargoed and (
+ not is_admin(flask.g.fas_user)
+ or not is_election_admin(flask.g.fas_user, election.id)):
+ flask.flash(
+ "The text results are only available to admins when the election"
+ " is under embargo")
+ return safe_redirect_back()
+
+ usernamemap = {}
+ if (election.candidates_are_fasusers): # pragma: no cover
+ for candidate in election.candidates:
+ try:
+ usernamemap[candidate.id] = \
+ FAS2.person_by_username(candidate.name)['human_name']
+ except (KeyError, AuthError):
+ # User has their name set to private or user doesn't exist.
+ usernamemap[candidate.id] = candidate.name
+
+ stats = models.Vote.get_election_stats(SESSION, election.id)
+
+ return flask.render_template(
+ 'results_text.html',
+ election=election,
+ usernamemap=usernamemap,
+ stats=stats,
+ )
commit fd85d73c6d8f9bb31d4e711e0b7f433ce2038a74
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:29:28 2014 +0200
Add a new template presenting the results in the format of the announcements
diff --git a/fedora_elections/templates/results_text.html b/fedora_elections/templates/results_text.html
new file mode 100644
index 0000000..59fbba0
--- /dev/null
+++ b/fedora_elections/templates/results_text.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>{% block title %}Fedora elections{% endblock %}</title>
+ <link rel="shortcut icon" type="image/vnd.microsoft.icon"
+ href="//fedoraproject.org/static/images/favicon.ico"/>
+ </head>
+ <body>
+<pre>
+Greetings, all!
+
+The elections for {{ election.shortdesc }} have concluded, and the results
+are shown below.
+
+XXX is electing {{ election.seats_elected }} seats this time.
+A total of {{stats['n_voters'] }} ballots were cast, meaning a candidate
+could accumulate up to {{ stats['n_voters'] * stats['n_candidates'] }} votes ({{stats['n_voters'] }} * {{ stats['n_candidates'] }}).
+
+The results for the elections are as follows:
+
+ # votes | name
+- --------+----------------------{%
+for candidate in election.candidates|sort(attribute='vote_count', reverse=True) -%}
+ {% if loop.index <= election.seats_elected -%}
+ {# If we are below the number of user that will be selected,
+ get the number of votes and the flag to False -#}
+ {% set flag = False -%}
+ {% set votes = candidate.vote_count -%}
+ {%- elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag -%}
+ {# if we are above the number of user that will be selected (seats
+ available), check if the number of votes for this candidate is lower
+ than the number of votes for the last candidate and if the Flag is
+ False.
+ So this takes care of the case where there are 10 seats elected and
+ the 11th candidate as the same score as the 10th one.
+ In this case we would end up with one more person that the number of
+ seats available and we'll need to either find a way to select one
+ over the other or deal with having one more candidate accepted -#}
+ {% set flag = True -%}
+ {% set lastrow = True -%}
+ {%- else -%}
+ {# we are above the number of seats available, the number of votes is
+ below that of the last candidate above selected and the Flag is True
+ which means we already passed the condition above -#}
+ {% set lastrow = False -%} {%- endif %}{% if lastrow == True %}
+- --------+---------------------- {%- endif %}
+{{ candidate.vote_count | rjust(8) }} | {% if election.candidates_are_fasusers -%}
+ {{ usernamemap[candidate.id] }} {%- else -%} {{candidate.name}}
+ {%- endif %}
+{%- endfor %}
+
+
+Congratulations to the winning candidates, and thank you all
+candidates for running this elections!
+</pre>
+
+</body>
+</html>
commit 9aa5c7d0c5c3833a2ae6216381ab3ea2c7db0f8b
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Fri Jul 25 15:28:19 2014 +0200
Create a new jinja filter that rjust() some provided text
diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py
index fd5f3e5..2fd1222 100644
--- a/fedora_elections/__init__.py
+++ b/fedora_elections/__init__.py
@@ -152,6 +152,12 @@ def inject_variables():
return dict(is_admin=is_admin(user),
version=__version__)
+(a)APP.template_filter('rjust')
+def rjust_filter(text, length):
+ """ Run a rjust command on the text for the given length
+ """
+ return str(text).rjust(length)
+
# LIST VIEWS #############################################
commit 7954b66939dd757bb3f493411cdc289f60cfd395
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Wed Jul 23 17:54:38 2014 +0200
Fix link to the results page from the about page
diff --git a/fedora_elections/templates/about.html b/fedora_elections/templates/about.html
index 7a604bb..e2008ff 100644
--- a/fedora_elections/templates/about.html
+++ b/fedora_elections/templates/about.html
@@ -7,7 +7,8 @@
<p><a href="{{ election.url }}">[More Information]</a></p>
{% endif %}
{% if election.status == 'Ended' %}
- <a href="/results/{{ election.alias }}">Show Results!</a>
+ <a href="{{ url_for('election_results',
+ election_alias=election.alias) }}">Show Results!</a>
{% endif %}
<table border="1" cellpadding="1">
{% for candidate in election.candidates %}
commit 38dbc1107e3c6a4a16225dbf2021b970a0bbb7e2
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Wed Jul 23 14:28:29 2014 +0200
Fix the display for the start_date and end_date to always have them specify it opens at 00:00 and closes at 23:59:59
diff --git a/fedora_elections/templates/admin/all_elections.html b/fedora_elections/templates/admin/all_elections.html
index c19c831..952245b 100644
--- a/fedora_elections/templates/admin/all_elections.html
+++ b/fedora_elections/templates/admin/all_elections.html
@@ -30,8 +30,8 @@
{{ election.shortdesc }}
</a>
</td>
- <td>{{ election.start_date }}</td>
- <td>{{ election.end_date }}</td>
+ <td>{{ election.start_date.strftime('%Y-%m-%d') }} 00:00 </td>
+ <td>{{ election.end_date.strftime('%Y-%m-%d') }} 23:59 </td>
<td>{{ election.status }}</td>
</tr>
{% endfor %}
diff --git a/fedora_elections/templates/admin/view_election.html b/fedora_elections/templates/admin/view_election.html
index 2247754..c283de7 100644
--- a/fedora_elections/templates/admin/view_election.html
+++ b/fedora_elections/templates/admin/view_election.html
@@ -11,8 +11,8 @@
</a>
</p>
<ul>
- <li>Start date: {{ election.start_date }}</li>
- <li>End date: {{ election.end_date }}</li>
+ <li>Start date: {{ election.start_date.strftime('%Y-%m-%d') }} 00:00 </li>
+ <li>End date: {{ election.end_date.strftime('%Y-%m-%d') }} 23:59 </li>
<li>Number elected: {{ election.seats_elected }}</li>
<li>Election type: {{ election.voting_type }}</li>
<li>Maximum number of votes: {{ election.max_votes }}</li>
diff --git a/fedora_elections/templates/archive.html b/fedora_elections/templates/archive.html
index ea5d6db..61699de 100644
--- a/fedora_elections/templates/archive.html
+++ b/fedora_elections/templates/archive.html
@@ -15,8 +15,8 @@
{% for election in elections %}
<tr>
<td>{{election.shortdesc}}</td>
- <td>{{election.start_date}}</td>
- <td>{{election.end_date}}</td>
+ <td>{{ election.start_date.strftime('%Y-%m-%d') }} 00:00 </td>
+ <td>{{ election.end_date.strftime('%Y-%m-%d') }} 23:59 </td>
<td>
<a href="{{ url_for('about_election', election_alias=election.alias) }}">
More information
diff --git a/fedora_elections/templates/index.html b/fedora_elections/templates/index.html
index 1381b41..64b526b 100644
--- a/fedora_elections/templates/index.html
+++ b/fedora_elections/templates/index.html
@@ -18,8 +18,8 @@
{% for election in election_list %}
<tr>
<td>{{ election.shortdesc }}</td>
- <td>{{ election.start_date }}</td>
- <td>{{ election.end_date }}</td>
+ <td>{{ election.start_date.strftime('%Y-%m-%d') }} 00:00 </td>
+ <td>{{ election.end_date.strftime('%Y-%m-%d') }} 23:59 </td>
<td>{{ election.status }}</td>
<td>
<a href="{{ url_for('about_election', election_alias=election.alias) }}">
commit c7ecd6e25352ef4c5f6f2b5395d102cffda2b8c6
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Wed Jul 23 14:27:28 2014 +0200
Fix displaying the information after the field when adding/editing an election
diff --git a/fedora_elections/templates/admin/election_form.html b/fedora_elections/templates/admin/election_form.html
index fceb75a..a3dcde8 100644
--- a/fedora_elections/templates/admin/election_form.html
+++ b/fedora_elections/templates/admin/election_form.html
@@ -17,8 +17,8 @@
{{ render_field_in_row(form.voting_type) }}
{{ render_field_in_row(form.max_votes) }}
{{ render_field_in_row(form.url) }}
- {{ render_field_in_row(form.start_date, " at 00:00:00 UTC") }}
- {{ render_field_in_row(form.end_date, " at 23:59:59 UTC") }}
+ {{ render_field_in_row(form.start_date, after=" at 00:00:00 UTC") }}
+ {{ render_field_in_row(form.end_date, after=" at 23:59:59 UTC") }}
{{ render_field_in_row(form.seats_elected) }}
{{ render_field_in_row(form.candidates_are_fasusers) }}
{{ render_field_in_row(form.embargoed) }}
9 years, 6 months
Branch 'develop' - fedora_elections/__init__.py files/fedora-elections.spec
by Pierre-YvesChibon
fedora_elections/__init__.py | 2 +-
files/fedora-elections.spec | 16 +++++++++++++++-
2 files changed, 16 insertions(+), 2 deletions(-)
New commits:
commit cd9744663a40a8e2868f4a18368d0754d841e5e5
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 12:28:13 2014 +0100
Release 2.4
diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py
index 0cde879..157a435 100644
--- a/fedora_elections/__init__.py
+++ b/fedora_elections/__init__.py
@@ -24,7 +24,7 @@
# Pierre-Yves Chibon <pingou(a)fedoraproject.org>
#
-__version__ = '2.3'
+__version__ = '2.4'
import os
diff --git a/files/fedora-elections.spec b/files/fedora-elections.spec
index c1e9996..c8ab8ce 100644
--- a/files/fedora-elections.spec
+++ b/files/fedora-elections.spec
@@ -1,7 +1,7 @@
%define modname fedora_elections
Name: fedora-elections
-Version: 2.3
+Version: 2.4
Release: 1%{?dist}
Summary: Fedora elections application
@@ -24,6 +24,7 @@ BuildRequires: python-setuptools
BuildRequires: python-wtforms
BuildRequires: python-nose
BuildRequires: python-coverage
+BuildRequires: python-dogpile-cache
Requires: python-fedora
Requires: python-fedora-flask
@@ -33,6 +34,7 @@ Requires: python-openid
Requires: python-openid-teams
Requires: python-openid-cla
Requires: python-wtforms
+Requires: python-dogpile-cache
# EPEL6
%if ( 0%{?rhel} && 0%{?rhel} == 6 )
@@ -109,6 +111,18 @@ install -m 644 files/update_1_to_2.sql \
%changelog
+* Thu Nov 20 2014 Pierre-Yves Chibon <pingou(a)pingoured.fr> - 2.4-1
+- Update to 2.4
+- Fix the election creation/edit page to show all the labels
+- Display the start and end time for the date fields in the create/edit
+ election pages
+- Add a text result page that can be used as basis to send the announcement
+ email with the results
+- Add new election type: IRC allowing users to do +1/0/-1 votes
+- Add session time-out
+- Add caching for the information retrieved from FAS
+- Add python-dogpile-cache as BR and R
+
* Wed Jul 16 2014 Pierre-Yves Chibon <pingou(a)pingoured.fr> - 2.3-1
- Update to 2.3
- Restrict the groups asked upon login (Allows Dennis to log in)
9 years, 6 months
Branch 'develop' - 4 commits - fedora_elections/default_config.py fedora_elections/elections.py fedora_elections/forms.py fedora_elections/__init__.py fedora_elections/utils.py requirements.txt tests/test_flask_elections.py tests/test_flask_range_voting.py tests/test_flask_select_voting.py tests/test_flask_simple_voting.py tests/test_vote.py
by Pierre-YvesChibon
fedora_elections/__init__.py | 5 +++
fedora_elections/default_config.py | 7 ++++
fedora_elections/elections.py | 53 +++++++------------------------------
fedora_elections/forms.py | 2 -
fedora_elections/utils.py | 30 ++++++++++++++++++++
requirements.txt | 1
tests/test_flask_elections.py | 1
tests/test_flask_range_voting.py | 3 +-
tests/test_flask_select_voting.py | 3 +-
tests/test_flask_simple_voting.py | 3 +-
tests/test_vote.py | 10 +++---
11 files changed, 66 insertions(+), 52 deletions(-)
New commits:
commit 300f48a2ba7813b095cfe8be519aed11d7751cea
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 11:52:46 2014 +0100
Add missing encoding information
diff --git a/fedora_elections/utils.py b/fedora_elections/utils.py
index e6fd08c..66ce8cb 100644
--- a/fedora_elections/utils.py
+++ b/fedora_elections/utils.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
import fedora_elections
from fedora.client import AuthError
commit 0b8cc3a062d71c556447976dacea7ad31158024e
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 11:52:39 2014 +0100
pep8 clean up
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index b8f3d8d..9ec8df8 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -297,9 +297,9 @@ def vote_irc(election):
num_candidates = election.candidates.count()
next_action = 'confirm'
- form=forms.get_irc_voting_form(
- candidates=election.candidates,
- fasusers=election.candidates_are_fasusers)
+ form = forms.get_irc_voting_form(
+ candidates=election.candidates,
+ fasusers=election.candidates_are_fasusers)
if form.validate_on_submit():
if form.action.data == 'submit':
for candidate in form:
@@ -351,7 +351,8 @@ def election_results(election_alias):
"embargoed pending formal announcement.")
return safe_redirect_back()
- if is_authenticated() and (is_admin(flask.g.fas_user) \
+ if is_authenticated() and (
+ is_admin(flask.g.fas_user)
or is_election_admin(flask.g.fas_user, election.id)):
flask.flash(
"Check out the <a href='%s'>Text version</a> "
@@ -370,6 +371,7 @@ def election_results(election_alias):
stats=stats,
)
+
@APP.route('/results/<election_alias>/text')
def election_results_text(election_alias):
election = get_valid_election(election_alias, ended=True)
diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py
index 7150564..2603973 100644
--- a/fedora_elections/forms.py
+++ b/fedora_elections/forms.py
@@ -167,7 +167,7 @@ def get_irc_voting_form(candidates, fasusers):
for candidate in candidates:
field = wtforms.SelectField(
candidate.name,
- choices=[('0',0),('1',1),('-1',-1)]
+ choices=[('0', 0), ('1', 1), ('-1', -1)]
)
setattr(IrcVoting, candidate.name, field)
diff --git a/tests/test_flask_elections.py b/tests/test_flask_elections.py
index 2d7c5d0..90a0230 100644
--- a/tests/test_flask_elections.py
+++ b/tests/test_flask_elections.py
@@ -361,7 +361,6 @@ candidates for running this elections!
self.assertTrue('<h3>Current elections</h3>' in output.data)
-
if __name__ == '__main__':
SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_range_voting.py b/tests/test_flask_range_voting.py
index 0cc94fd..d0be838 100644
--- a/tests/test_flask_range_voting.py
+++ b/tests/test_flask_range_voting.py
@@ -298,5 +298,6 @@ class FlaskRangeElectionstests(ModelFlasktests):
if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskRangeElectionstests)
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(
+ FlaskRangeElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_select_voting.py b/tests/test_flask_select_voting.py
index cc248b3..73d2ca0 100644
--- a/tests/test_flask_select_voting.py
+++ b/tests/test_flask_select_voting.py
@@ -170,5 +170,6 @@ class FlaskSimpleElectionstests(ModelFlasktests):
if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskSimpleElectionstests)
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(
+ FlaskSimpleElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_flask_simple_voting.py b/tests/test_flask_simple_voting.py
index aa28980..1eaa339 100644
--- a/tests/test_flask_simple_voting.py
+++ b/tests/test_flask_simple_voting.py
@@ -246,5 +246,6 @@ class FlaskSimpleElectionstests(ModelFlasktests):
if __name__ == '__main__':
- SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskSimpleElectionstests)
+ SUITE = unittest.TestLoader().loadTestsFromTestCase(
+ FlaskSimpleElectionstests)
unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_vote.py b/tests/test_vote.py
index 8d15279..92f1e71 100644
--- a/tests/test_vote.py
+++ b/tests/test_vote.py
@@ -178,11 +178,11 @@ class Votetests(Modeltests):
# Election 7
- obj = models.Vote( #id:1
- election_id=7,
- voter='toshio',
- candidate_id=12,
- value='1',
+ obj = models.Vote( # id:1
+ election_id=7,
+ voter='toshio',
+ candidate_id=12,
+ value='1',
)
self.session.add(obj)
self.session.commit()
commit 9b299fa5a70ff8abc1dccdce62354a135a14d17d
Merge: 6ff26d1 6eca259
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Thu Nov 20 11:47:52 2014 +0100
Merge pull request #36 from fedora-infra/feature/cache
Use dogpile.cache to vastly speed up the app.
commit 6eca25955d33d427bd6893705deeb9876dc58e2f
Author: Ralph Bean <rbean(a)redhat.com>
Date: Mon Nov 17 20:57:39 2014 -0500
Use dogpile.cache to vastly speed up the app.
Currently, every page load in the voting process requires that the
elections make ``N`` queries to FAS where ``N`` is the number of
candidates. It's being really slow in production tonight with the
opening of the Council election.
This should add some generic caching so that we can use an on-disk .dbm
cache if we like or alternatively memcached.
Tested locally.
diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py
index 131f320..0cde879 100644
--- a/fedora_elections/__init__.py
+++ b/fedora_elections/__init__.py
@@ -38,6 +38,8 @@ from fedora.client import AuthError, AppError
from fedora.client.fas2 import AccountSystem
from flask.ext.fas_openid import FAS
+import dogpile.cache
+
import fedora_elections.fedmsgshim
import fedora_elections.proxy
@@ -64,6 +66,9 @@ from fedora_elections import models
SESSION = models.create_session(APP.config['DB_URL'])
from fedora_elections import forms
+cache = dogpile.cache.make_region()
+cache.configure(**APP.config['DOGPILE_CACHE'])
+
def is_authenticated():
''' Return a boolean specifying if the user is authenticated or not.
diff --git a/fedora_elections/default_config.py b/fedora_elections/default_config.py
index 979dd9d..e887840 100644
--- a/fedora_elections/default_config.py
+++ b/fedora_elections/default_config.py
@@ -22,3 +22,10 @@ FAS_BASE_URL = 'https://admin.stg.fedoraproject.org/accounts/'
FAS_USERNAME = ''
FAS_PASSWORD = ''
FAS_CHECK_CERT = False
+
+DOGPILE_CACHE = {
+ 'backend': 'dogpile.cache.dbm',
+ 'arguments': {
+ 'filename': '/var/tmp/elections-cache.dbm',
+ },
+}
diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py
index 2ad0e1e..b8f3d8d 100644
--- a/fedora_elections/elections.py
+++ b/fedora_elections/elections.py
@@ -37,6 +37,7 @@ from fedora_elections import (
APP, SESSION, FAS2, is_authenticated, is_admin, is_election_admin,
is_safe_url, safe_redirect_back,
)
+from fedora_elections.utils import build_name_map
def login_required(f):
@@ -166,16 +167,7 @@ def vote_range(election):
flask.flash("Please confirm your vote!")
next_action = 'vote'
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[str(candidate.id)] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError), err:
- APP.logger.debug(err)
- # User has their name set to private or user doesn't exist.
- usernamemap[str(candidate.id)] = candidate.name
+ usernamemap = build_name_map(election)
return flask.render_template(
'vote_range.html',
@@ -239,16 +231,7 @@ def vote_select(election):
flask.flash("Please confirm your vote!")
next_action = 'vote'
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[candidate.name] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError), err:
- APP.logger.debug(err)
- # User has their name set to private or user doesn't exist.
- usernamemap[candidate.name] = candidate.name
+ usernamemap = build_name_map(election)
return flask.render_template(
'vote_simple.html',
@@ -376,15 +359,7 @@ def election_results(election_alias):
'election_results_text', election_alias=election.alias)
)
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[candidate.id] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError):
- # User has their name set to private or user doesn't exist.
- usernamemap[candidate.id] = candidate.name
+ usernamemap = build_name_map(election)
stats = models.Vote.get_election_stats(SESSION, election.id)
@@ -408,15 +383,7 @@ def election_results_text(election_alias):
"The text results are only available to the admins", "error")
return safe_redirect_back()
- usernamemap = {}
- if (election.candidates_are_fasusers): # pragma: no cover
- for candidate in election.candidates:
- try:
- usernamemap[candidate.id] = \
- FAS2.person_by_username(candidate.name)['human_name']
- except (KeyError, AuthError):
- # User has their name set to private or user doesn't exist.
- usernamemap[candidate.id] = candidate.name
+ usernamemap = build_name_map(election)
stats = models.Vote.get_election_stats(SESSION, election.id)
diff --git a/fedora_elections/utils.py b/fedora_elections/utils.py
new file mode 100644
index 0000000..e6fd08c
--- /dev/null
+++ b/fedora_elections/utils.py
@@ -0,0 +1,28 @@
+import fedora_elections
+
+from fedora.client import AuthError
+
+
+def build_name_map(election):
+ """ Returns a mapping of candidate ids to fas human_names. """
+ if not election.candidates_are_fasusers:
+ return {}
+
+ return dict([
+ (str(candidate.id), get_fas_human_name(candidate.name))
+ for candidate in election.candidates
+ ])
+
+
+(a)fedora_elections.cache.cache_on_arguments()
+def get_fas_human_name(username):
+ """ Given a fas username, return the fas human_name if possible.
+
+ If the user has their name set to private or they don't exist, we just
+ return the given username as a stand-in.
+ """
+ try:
+ return fedora_elections.FAS2.person_by_username(username)['human_name']
+ except (KeyError, AuthError), err:
+ fedora_elections.APP.logger.debug(err)
+ return username
diff --git a/requirements.txt b/requirements.txt
index 7bff0c9..a62a392 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,4 @@ python-openid-cla
python-openid-teams
SQLAlchemy
wtforms
+dogpile.cache
9 years, 6 months