[Fedora Elections] #36: Consider hiding historical elections
by fedora-badges
#36: Consider hiding historical elections
---------------------+------------------------------------------------------
Reporter: tremble | Owner: nigelj
Type: defect | Status: new
Priority: minor | Milestone: Future Release
Component: General | Version: 0.2.1
Keywords: |
---------------------+------------------------------------------------------
The list of elections is growing.
While we want to keep the information (probably), do we really want to
display more than the last X (15? - just over two releases, would show how
a current board member was elected) months worth of election, and just
tuck the rest behind a "historical eletcions" link
--
Ticket URL: <https://fedorahosted.org/elections/ticket/36>
Fedora Elections <https://fedorahosted.org/elections/>
Fedora Elections
10 years, 1 month
[Fedora Elections] #26: First submit should be preview
by fedora-badges
#26: First submit should be preview
-------------------------+--------------------------------------------------
Reporter: mmcgrath | Owner: nigelj
Type: enhancement | Status: new
Priority: major | Milestone:
Component: General | Version: 0.1.5
Keywords: |
-------------------------+--------------------------------------------------
Minor thing but I think it might help, when you vote. The first screen to
submit to should really say "preview" instead of submit since you're not
submitting the vote until the final screen.
--
Ticket URL: <https://fedorahosted.org/elections/ticket/26>
Fedora Elections <https://fedorahosted.org/elections/>
Fedora Elections
11 years, 11 months
[Fedora Elections] #28: Improvements for the Voting App: Allow voting multiple times
by fedora-badges
#28: Improvements for the Voting App: Allow voting multiple times
-------------------------+--------------------------------------------------
Reporter: ixs | Owner: nigelj
Type: enhancement | Status: new
Priority: major | Milestone:
Component: General | Version: 0.1.5
Keywords: |
-------------------------+--------------------------------------------------
Please allow voting multiple times with each later vote canceling the
previous votes. This easily allows to correct a mistake during the voting
procedure or accounts for changed opinion.
There has just been a bit of discussion on IRC where people accidentially
voted incorrectly and would have liked such a feature.
kthxbai
--
Ticket URL: <https://fedorahosted.org/elections/ticket/28>
Fedora Elections <https://fedorahosted.org/elections/>
Fedora Elections
11 years, 11 months
development.ini elections2/config elections2/controllers elections2/i18n elections2/__init__.py elections2/lib elections2/model elections2/public elections2/templates elections2/tests elections2/websetup elections/admin.py elections.cfg elections/commands.py elections/config elections/controllers.py elections/__init__.py elections/json.py elections/model.py elections/release.py elections.sql elections/static elections/templates elections/tests elections/vote.py examples/elections.conf exam
by Pierre-YvesChibon
MANIFEST.in | 10
README.txt | 24 +
dev/null |binary
development.ini | 132 +++++++++
elections.cfg | 81 ------
elections.sql | 90 ------
elections/admin.py | 189 --------------
elections/commands.py | 56 ----
elections/config/app.cfg | 102 -------
elections/config/log.cfg | 29 --
elections/controllers.py | 184 -------------
elections/json.py | 10
elections/model.py | 89 ------
elections/release.py | 38 --
elections/static/css/style.css | 57 ----
elections/templates/about.html | 50 ---
elections/templates/admedit.html | 68 -----
elections/templates/admin.html | 37 --
elections/templates/admnewc.html | 19 -
elections/templates/admnewe.html | 27 --
elections/templates/index.html | 111 --------
elections/templates/login.html | 13
elections/templates/master.html | 71 -----
elections/templates/results.html | 20 -
elections/templates/verify.html | 22 -
elections/templates/vote.html | 61 ----
elections/tests/test_controllers.py | 32 --
elections/tests/test_model.py | 22 -
elections/vote.py | 154 -----------
elections2/__init__.py | 2
elections2/config/__init__.py | 2
elections2/config/app_cfg.py | 75 +++++
elections2/config/deployment.ini | 99 +++++++
elections2/config/deployment.ini_tmpl | 98 +++++++
elections2/config/environment.py | 9
elections2/config/middleware.py | 39 ++
elections2/controllers/__init__.py | 2
elections2/controllers/controller.template | 23 +
elections2/controllers/error.py | 30 ++
elections2/controllers/root.py | 281 +++++++++++++++++++++
elections2/controllers/secure.py | 31 ++
elections2/controllers/template.py | 36 ++
elections2/i18n/ru/LC_MESSAGES/elections2.po | 24 +
elections2/lib/__init__.py | 2
elections2/lib/admin.py | 272 ++++++++++++++++++++
elections2/lib/app_globals.py | 18 +
elections2/lib/base.py | 32 ++
elections2/lib/helpers.py | 5
elections2/lib/vote.py | 195 ++++++++++++++
elections2/model/__init__.py | 125 +++++++++
elections2/model/auth.py | 227 ++++++++++++++++
elections2/model/model.template | 22 +
elections2/public/css/style.css | 57 ++++
elections2/public/favicon.ico |binary
elections2/public/images/favicon.ico |binary
elections2/public/images/header_inner.png |binary
elections2/public/images/info.png |binary
elections2/public/images/ok.png |binary
elections2/public/images/tg_under_the_hood.png |binary
elections2/public/images/under_the_hood_blue.png |binary
elections2/templates/about.html | 50 +++
elections2/templates/admedit.html | 74 +++++
elections2/templates/admin.html | 37 ++
elections2/templates/admnewc.html | 19 +
elections2/templates/admnewe.html | 27 ++
elections2/templates/error.html | 19 +
elections2/templates/fedora_login.html | 105 +++++++
elections2/templates/index.html | 111 ++++++++
elections2/templates/login.html | 20 +
elections2/templates/master.html | 81 ++++++
elections2/templates/results.html | 20 +
elections2/templates/verify.html | 22 +
elections2/templates/vote.html | 61 ++++
elections2/tests/__init__.py | 64 ++++
elections2/tests/functional/__init__.py | 2
elections2/tests/functional/test_authentication.py | 85 ++++++
elections2/tests/functional/test_root.py | 80 +++++
elections2/tests/models/__init__.py | 62 ++++
elections2/tests/models/test_auth.py | 51 +++
elections2/websetup/__init__.py | 19 +
elections2/websetup/bootstrap.py | 57 ++++
elections2/websetup/schema.py | 21 +
examples/elections.conf | 19 -
examples/elections.wsgi | 36 --
sample-prod.cfg | 84 ------
setup.cfg | 29 ++
setup.py | 257 +++----------------
start-elections.py | 18 -
test.cfg | 32 --
test.ini | 26 +
90 files changed, 3050 insertions(+), 2042 deletions(-)
New commits:
commit 5f3b851abecb7c0220578a38a7cb445a1d70aee0
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Tue May 10 21:04:53 2011 +0200
Import port of Elections to TG2.
This commit remove files from the version TG1 of elections and import/modify
files from the TG2 port of elections.
The README.txt is added as reference to update the README file.
It should be removed later on.
diff --git a/MANIFEST.in b/MANIFEST.in
index f168b6a..436cbca 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,4 @@
-include COPYING UPDATE-NOTES elections.cfg start-elections.py fedora-elections.spec sample-prod.cfg elections.sql
-include elections/config/*
-include elections/templates/*.html
-include elections/static/css/*.css
-include elections/static/images/*.png
-include examples/*
+recursive-include elections2/public *
+include elections2/public/favicon.ico
+recursive-include elections2/i18n *
+recursive-include elections2/templates *
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..ffec506
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,24 @@
+This file is for you to describe the elections2 application. Typically
+you would include information such as the information below:
+
+Installation and Setup
+======================
+
+Install ``elections2`` using the setup.py script::
+
+ $ cd elections2
+ $ python setup.py install
+
+Create the project database for any model classes defined::
+
+ $ paster setup-app development.ini
+
+Start the paste http server::
+
+ $ paster serve development.ini
+
+While developing you may want the server to reload after changes in package files (or its dependencies) are saved. This can be achieved easily by adding the --reload option::
+
+ $ paster serve --reload development.ini
+
+Then you are ready to go.
diff --git a/development.ini b/development.ini
new file mode 100644
index 0000000..280fe99
--- /dev/null
+++ b/development.ini
@@ -0,0 +1,132 @@
+#
+# elections2 - Pylons development environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+# This file is for deployment specific config options -- other configuration
+# that is always required for the app is done in the config directory,
+# and generally should not be modified by end users.
+
+[DEFAULT]
+debug = true
+# Uncomment and replace with the address which should receive any error reports
+#email_to = you(a)yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 127.0.0.1
+port = 8080
+
+[sa_auth]
+cookie_secret = d0dc7a4f-1d03-4e22-a6f6-ecd21b8a6464
+
+[app:main]
+use = egg:elections2
+full_stack = true
+#lang = ru
+cache_dir = %(here)s/data
+beaker.session.key = elections2
+beaker.session.secret = d0dc7a4f-1d03-4e22-a6f6-ecd21b8a6464
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+
+# pick the form for your database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+# sqlalchemy.url=postgres://username:password@hostname:port/databasename
+# sqlalchemy.url=mysql://username:password@hostname:port/databasename
+
+
+# If you have sqlite, here's a simple default to get you started
+# in development
+
+#sqlalchemy.url = sqlite:///%(here)s/devdata.db
+sqlalchemy.url=postgresql://elections:elections2@localhost/elections
+#echo shouldn't be used together with the logging module.
+sqlalchemy.echo = false
+sqlalchemy.echo_pool = false
+sqlalchemy.pool_recycle = 3600
+
+# if you are using Mako and want to be able to reload
+# the mako template from disk during the development phase
+# you should say 'true' here
+# This option is only used for mako templating engine
+# WARNING: if you want to deploy your application using a zipped egg
+# (ie: if your application's setup.py defines zip-safe=True, then you
+# MUST put "false" for the production environment because there will
+# be no disk and real files to compare time with.
+# On the contrary if your application defines zip-safe=False and is
+# deployed in an unzipped manner, then you can leave this option to true
+templating.mako.reloadfromdisk = true
+
+# the compiled template dir is a directory that must be readable by your
+# webserver. It will be used to store the resulting templates once compiled
+# by the TemplateLookup system.
+# During development you generally don't need this option since paste's HTTP
+# server will have access to you development directories, but in production
+# you'll most certainly want to have apache or nginx to write in a directory
+# that does not contain any source code in any form for obvious security reasons.
+templating.mako.compiled_templates_dir = %(here)s/data/templates
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, elections2, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_elections2]
+level = DEBUG
+handlers =
+qualname = elections2
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers =
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/elections.cfg b/elections.cfg
deleted file mode 100644
index b344223..0000000
--- a/elections.cfg
+++ /dev/null
@@ -1,81 +0,0 @@
-[global]
-fas.url = 'https://publictest10.fedoraproject.org/accounts/'
-fas.username = 'admin'
-fas.password = 'admin'
-
-# This is where all of your settings go for your development environment
-# Settings that are the same for both development and production
-# (such as template engine, encodings, etc.) all go in
-# elections/config/app.cfg
-
-# DATABASE
-
-# pick the form for your database
-# sqlobject.dburi="postgres://username@hostname/databasename"
-# sqlobject.dburi="mysql://username:password@hostname:port/databasename"
-# sqlobject.dburi="sqlite:///file_name_and_path"
-
-# If you have sqlite, here's a simple default to get you started
-# in development
-#sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
-sqlalchemy.dburi="postgres://njones@localhost/elections"
-sqlalchemy.convert_unicode=True
-
-
-# if you are using a database or table type without transactions
-# (MySQL default, for example), you should turn off transactions
-# by prepending notrans_ on the uri
-# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename"
-
-# for Windows users, sqlite URIs look like:
-# sqlobject.dburi="sqlite:///drive_letter:/path/to/file"
-
-# SERVER
-
-# Some server parameters that you may want to tweak
-server.socket_port=80
-
-# Enable the debug output at the end on pages.
-log_debug_info_filter.on = False
-
-server.environment="development"
-autoreload.package="elections"
-
-# Auto-Reload after code modification
-# autoreload.on = True
-
-server.log_to_screen = True
-
-# Set to True if you'd like to abort execution if a controller gets an
-# unexpected parameter. False by default
-tg.strict_parameters = True
-
-server.webpath = '/elections'
-base_url_filter.on = True
-base_url_filter.base_url='http://localhost/elections'
-base_url_filter.use_x_forwarded_host = False
-
-# LOGGING
-# Logging configuration generally follows the style of the standard
-# Python logging module configuration. Note that when specifying
-# log format messages, you need to use *() for formatting variables.
-# Deployment independent log configuration is in elections/config/log.cfg
-[logging]
-
-[[loggers]]
-[[[elections]]]
-level='DEBUG'
-qualname='elections'
-handlers=['debug_out']
-
-[[[allinfo]]]
-level='INFO'
-handlers=['debug_out']
-
-[[[access]]]
-level='INFO'
-qualname='turbogears.access'
-handlers=['access_out']
-propagate=0
-
-
diff --git a/elections.sql b/elections.sql
deleted file mode 100644
index 481e500..0000000
--- a/elections.sql
+++ /dev/null
@@ -1,90 +0,0 @@
-drop view electionvotes;
-drop view fvotecount;
-drop view votecount;
-drop view uservotes;
-
-drop table votes;
-drop table legalvoters;
-drop table candidates;
-drop table elections;
-
-CREATE TABLE elections (
- id serial NOT NULL,
--- was shortname
- shortdesc text NOT NULL,
--- was name
- alias text NOT NULL,
--- was info
- description text NOT NULL,
- url text NOT NULL,
- start_date timestamp without time zone NOT NULL,
- end_date timestamp without time zone NOT NULL,
--- was max_seats
- seats_elected integer NOT NULL,
- votes_per_user integer NOT NULL,
- embargoed integer default 0 NOT NULL,
- usefas integer default 0 NOT NULL,
- allow_nominations integer default 0 NOT NULL,
- nominations_until timestamp without time zone,
- unique (shortdesc),
- primary key (id)
-);
-
-CREATE TABLE candidates (
- id serial,
- election_id integer NOT NULL,
- name text NOT NULL,
- url text,
- formalname text,
- human integer,
- status integer,
- foreign key (election_id) references elections (id),
- unique (id),
- primary key (id, election_id)
-);
-
-CREATE TABLE legalvoters (
- id serial,
- election_id integer not null,
- group_name text not null,
- foreign key (election_id) references elections (id),
- primary key (id)
-);
-
-CREATE TABLE electionadmins (
- id serial,
- election_id integer not null,
- group_name text not null,
- foreign key (election_id) references elections (id),
- primary key (id)
-);
-
-CREATE TABLE votes (
- id serial,
- voter text NOT NULL,
- timestamp timestamp without time zone NOT NULL,
- candidate_id integer NOT NULL,
- weight integer NOT NULL,
- election_id integer NOT NULL,
- foreign key (candidate_id) references candidates(id),
- foreign key (election_id) references elections(id),
- primary key (id)
-);
-
-
-CREATE VIEW electionvotes AS
- SELECT votes.election_id, count(votes.election_id) AS novotes FROM votes GROUP BY votes.election_id;
-
-
-
-
-CREATE VIEW votecount AS
- SELECT votes.candidate_id, votes.election_id, sum(votes.weight) AS novotes FROM votes GROUP BY votes.candidate_id, votes.election_id ORDER BY sum(votes.weight) DESC;
-
-
-CREATE VIEW fvotecount AS
- SELECT c.id, c.name, v.election_id, v.novotes FROM votecount v, candidates c WHERE (c.id = v.candidate_id) ORDER BY v.novotes DESC;
-
-
-CREATE VIEW uservotes AS
- SELECT votes.election_id, votes.voter, count(votes.voter) AS novotes FROM votes GROUP BY votes.election_id, votes.voter;
diff --git a/elections/__init__.py b/elections/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/elections/admin.py b/elections/admin.py
deleted file mode 100644
index 2b4fbf8..0000000
--- a/elections/admin.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2008 Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano All rights reserved.
-#
-# 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. 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.
-#
-# Author(s): Nigel Jones <nigelj(a)fedoraproject.org>
-# Toshio Kuratomi <toshio(a)fedoraproject.org>
-# Ricky Zhou <ricky(a)fedoraproject.org>
-# Luca Foppiano <lfoppiano(a)fedoraproject.org>
-#
-# Report Bugs to https://www.fedorahosted.org/elections
-
-
-# Admin functions go here (i.e. add/delete elections)
-
-import turbogears
-from turbogears import controllers, expose, flash, redirect, config
-from turbogears import identity
-from fedora.client import AuthError, AppError
-from elections import model
-from elections.model import Elections, ElectionsTable, Candidates, LegalVoters, ElectionAdmins
-
-from datetime import datetime
-
-import sqlalchemy
-
-from turbogears.database import session
-
-class Admin(controllers.Controller):
- def __init__(self, fas, appTitle):
- self.fas = fas
- self.appTitle = appTitle
-
- @expose(allow_json=True)
- def list_elections(self, **kw):
- electlist = Elections.query.order_by(ElectionsTable.c.start_date).filter('id>0').all()
- elections = [{'id': e.id, 'alias': e.alias, 'shortdesc': e.shortdesc, 'start_date': e.start_date, 'end_date': e.end_date, 'legal_voters': [{'groupname': lv.group_name} for lv in LegalVoters.query.filter_by(election_id=e.id)]} for e in electlist]
- return dict(elections=elections, servertime=datetime.utcnow(), appTitle=self.appTitle)
-
- @expose(template='elections.templates.admin')
- @identity.require(identity.in_group("elections"))
- def index(self, **kw):
- electlist = Elections.query.order_by(ElectionsTable.c.start_date).filter('id>0').all()
- elections = [{'id': e.id, 'alias': e.alias, 'shortdesc': e.shortdesc, 'start_date': e.start_date, 'end_date': e.end_date, 'legal_voters': [{'groupname': lv.group_name} for lv in LegalVoters.query.filter_by(election_id=e.id)], 'embargoed' : e.embargoed} for e in electlist]
- return dict(elections=elections)
-
- @identity.require(identity.in_group("elections"))
- @expose(template="elections.templates.admnewe")
- def newe(self, **kw):
- if "submit" in kw:
- setembargo=1
- usefas=1
- if "embargoed" not in kw:
- setembargo=0
- if "usefas" not in kw:
- usefas=0
-
- election = Elections(alias=kw['alias'], status=0, method=0, shortdesc=kw['shortdesc'], description=kw['info'], url=kw['url'], start_date=kw['startdate'], end_date=kw['enddate'], embargoed=setembargo, seats_elected=kw['seats'],usefas=usefas,votes_per_user=1)
- turbogears.flash("New Election Created")
- session.save(election)
- raise turbogears.redirect("/admin/edit/"+str(election.alias))
- else:
- return dict()
-
- @identity.require(identity.in_group("elections"))
- @expose(template="elections.templates.admnewc")
- def newc(self, **kw):
- if "submit" in kw:
- for entry in kw['nameurl'].split("|"):
- candidate = entry.split("!")
- #Python doesn't have a good way of doing case/switch statements
- if len(candidate) == 1:
- Candidates(election_id=kw['id'],name=candidate[0], status=0, human=1)
- elif len(candidate) == 2:
- Candidates(election_id=kw['id'],name=candidate[0],url=candidate[1], status=0, human=1)
- else:
- turbogears.flash("There was an issue!")
- raise turbogears.redirect("/admin/newc")
- else:
- return dict()
-
- @identity.require(identity.in_group("elections"))
- @expose(template="elections.templates.admedit")
- def edit(self, eid=None, **kw):
- if "submit" in kw:
- for entry in kw['newgroups'].split("|"):
- entry.strip()
- if len(entry) :
- LegalVoters(election_id=kw['id'], group_name=entry)
- for entry in kw['newadmins'].split("|"):
- entry.strip()
- if len(entry) :
- ElectionAdmins(election_id=kw['id'], group_name=entry)
- for key, value in kw.items():
- if key.startswith('remove_'):
- group = key[len('remove_'):]
- for lv in LegalVoters.query.filter_by(election_id=kw['id'],group_name=group) :
- session.delete(lv)
- for key, value in kw.items():
- if key.startswith('removeadmin_'):
- group = key[len('removeadmin_'):]
- for admin in ElectionAdmins.query.filter_by(election_id=kw['id'],group_name=group) :
- session.delete(admin)
- for entry in kw['newcandidates'].split("|"):
- candidate = entry.split("!")
- #Python doesn't have a good way of doing case/switch statements
- if len(candidate) == 1:
- if len(candidate[0]) :
- Candidates(election_id=kw['id'],name=candidate[0], status=0, human=1)
- elif len(candidate) == 2:
- if len(candidate[0]) :
- Candidates(election_id=kw['id'],name=candidate[0],url=candidate[1], status=0, human=1)
- else:
- turbogears.flash("There was an issue!")
- setembargo=1
- usefas=1
- nominations=1
- if "embargoed" not in kw:
- setembargo=0
- if "usefas" not in kw:
- usefas=0
- if "allownominations" not in kw:
- nominations=0
- try:
- election = Elections.query.filter_by(id=int(eid)).all()[0]
- except ValueError:
- election = Elections.query.filter_by(alias=eid).all()[0]
- election.alias=kw['alias']
- election.status=0
- election.method=0
- election.shortdesc=kw['shortdesc']
- election.description=kw['info']
- election.url=kw['url']
- election.start_date=kw['startdate']
- election.end_date=kw['enddate']
- election.embargoed=setembargo
- election.seats_elected=kw['seats']
- election.usefas=usefas
- election.votes_per_user=kw['votes']
- election.allow_nominations=nominations
- raise turbogears.redirect("/admin/edit/"+kw['id'])
-
- try:
- eid = int(eid)
- election = Elections.query.filter_by(id=eid).all()[0]
- except ValueError:
- try:
- election = Elections.query.filter_by(alias=eid).all()[0]
- eid = election.id
- except IndexError:
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/admin/")
- except (IndexError, TypeError):
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/admin/")
-
- if "removeembargo" in kw:
- election.embargoed=0
- turbogears.flash("Embargo on election results removed")
- raise turbogears.redirect("/admin/")
-
- candidates = Candidates.query.filter_by(election_id=election.id).all()
- votergroups = LegalVoters.query.filter_by(election_id=election.id).all()
- admingroups = ElectionAdmins.query.filter_by(election_id=election.id).all()
- groupnamemap = {}
- for g in admingroups:
- try:
- groupnamemap[g.group_name] = g.group_name + " (" + self.fas.group_by_name(g.group_name)['display_name'] +")"
- except (AppError, AuthError, KeyError) :
- groupnamemap[g.group_name] = g.group_name
- for g in votergroups:
- try:
- groupnamemap[g.group_name] = g.group_name + " (" + self.fas.group_by_name(g.group_name)['display_name'] +")"
- except (AppError, AuthError, KeyError) :
- groupnamemap[g.group_name] = g.group_name
-
- return dict(e=election, candidates=candidates, admingroups=admingroups, groups=votergroups, groupnamemap=groupnamemap)
diff --git a/elections/commands.py b/elections/commands.py
deleted file mode 100644
index acac305..0000000
--- a/elections/commands.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-"""This module contains functions called from console script entry points."""
-
-import os
-import sys
-
-from os.path import dirname, exists, join
-
-import pkg_resources
-pkg_resources.require("TurboGears")
-
-import turbogears
-from turbogears import startup
-from fedora.tg.util import enable_csrf
-
-import cherrypy
-
-cherrypy.lowercase_api = True
-
-class ConfigurationError(Exception):
- pass
-
-def start():
- """Start the CherryPy application server."""
-
- setupdir = dirname(dirname(__file__))
- curdir = os.getcwd()
-
- # First look on the command line for a desired config file,
- # if it's not on the command line, then look for 'setup.py'
- # in the current directory. If there, load configuration
- # from a file called 'dev.cfg'. If it's not there, the project
- # is probably installed and we'll look first for a file called
- # 'prod.cfg' in the current directory and then for a default
- # config file called 'default.cfg' packaged in the egg.
- if len(sys.argv) > 1:
- configfile = sys.argv[1]
- elif exists(join(setupdir, "setup.py")):
- configfile = join(setupdir, "dev.cfg")
- elif exists(join(curdir, "prod.cfg")):
- configfile = join(curdir, "prod.cfg")
- else:
- try:
- configfile = pkg_resources.resource_filename(
- pkg_resources.Requirement.parse("elections"),
- "config/default.cfg")
- except pkg_resources.DistributionNotFound:
- raise ConfigurationError("Could not find default configuration.")
-
- turbogears.update_config(configfile=configfile,
- modulename="elections.config")
-
- startup.call_on_startup.append(enable_csrf)
- from elections.controllers import Root
-
- turbogears.start_server(Root())
diff --git a/elections/config/__init__.py b/elections/config/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/elections/config/app.cfg b/elections/config/app.cfg
deleted file mode 100644
index cd9d8fc..0000000
--- a/elections/config/app.cfg
+++ /dev/null
@@ -1,102 +0,0 @@
-[global]
-# The settings in this file should not vary depending on the deployment
-# environment. dev.cfg and prod.cfg are the locations for
-# the different deployment settings. Settings in this file will
-# be overridden by settings in those other files.
-
-# The commented out values below are the defaults
-
-# VIEW
-
-# which view (template engine) to use if one is not specified in the
-# template name
-# tg.defaultview = "kid"
-tg.defaultview = "genshi"
-
-# The following kid settings determine the settings used by the kid serializer.
-
-# Kid output method (e.g. html, html-strict, xhtml, xhtml-strict, xml, json)
-# and formatting (e.g. default, straight, compact, newlines, wrap, nice)
-# kid.outputformat="html default"
-genshi.outputformat = "xhtml"
-
-# kid.encoding="utf-8"
-genshi.encoding="utf-8"
-
-# The sitetemplate is used for overall styling of a site that
-# includes multiple TurboGears applications
-# tg.sitetemplate="<packagename.templates.templatename>"
-
-# Allow every exposed function to be called as json,
-# tg.allow_json = False
-
-# Suppress the inclusion of the shipped MochiKit version, which is rather outdated.
-# Attention: setting this to True and listing 'turbogears.mochikit' in 'tg.include_widgets'
-# is a contradiction. This option will overrule the default-inclusion to prevent version
-# mismatch bugs.
-# tg.mochikit_suppress = True
-
-# List of Widgets to include on every page.
-# for example ['turbogears.mochikit']
-# tg.include_widgets = []
-
-# Set to True if the scheduler should be started
-# tg.scheduler = False
-
-# Set to True to allow paginate decorator redirects when page number gets
-# out of bound. Useful for getting the real page id in the url
-# paginate.redirect_on_out_of_range = True
-
-# Set to True to allow paginate decorator redirects when last page is requested.
-# This is useful for getting the real last page id in the url
-# paginate.redirect_on_last_page = True
-
-# Set session or cookie
-# session_filter.on = True
-
-# VISIT TRACKING
-# Each visit to your application will be assigned a unique visit ID tracked via
-# a cookie sent to the visitor's browser.
-# --------------
-
-# Enable Visit tracking
-visit.on=True
-#visit.on=False
-
-# Number of minutes a visit may be idle before it expires.
-visit.timeout=20
-
-# The name of the cookie to transmit to the visitor's browser.
-# visit.cookie.name="tg-visit"
-
-# Domain name to specify when setting the cookie (must begin with . according to
-# RFC 2109). The default (None) should work for most cases and will default to
-# the machine to which the request was made. NOTE: localhost is NEVER a valid
-# value and will NOT WORK.
-# visit.cookie.domain=None
-
-identity.on = True
-
-# [REQUIRED] URL to which CherryPy will internally redirect when an access
-# control check fails. If Identity management is turned on, a value for this
-# option must be specified.
-identity.failure_url="/login"
-
-# FAS2 Auth
-# =========
-identity.provider="jsonfas2"
-visit.manager="jsonfas2"
-visit.cookie.secure = True
-
-# compress the data sends to the web browser
-# [/]
-# gzip_filter.on = True
-# gzip_filter.mime_types = ["application/x-javascript", "text/javascript", "text/html", "text/css", "text/plain"]
-
-[/static]
-static_filter.on = True
-static_filter.dir = "%(top_level_dir)s/static"
-
-[/favicon.ico]
-static_filter.on = True
-static_filter.file = "%(top_level_dir)s/static/images/favicon.ico"
diff --git a/elections/config/log.cfg b/elections/config/log.cfg
deleted file mode 100644
index ce776f8..0000000
--- a/elections/config/log.cfg
+++ /dev/null
@@ -1,29 +0,0 @@
-# LOGGING
-# Logging is often deployment specific, but some handlers and
-# formatters can be defined here.
-
-[logging]
-[[formatters]]
-[[[message_only]]]
-format='*(message)s'
-
-[[[full_content]]]
-format='*(asctime)s *(name)s *(levelname)s *(message)s'
-
-[[handlers]]
-[[[debug_out]]]
-class='StreamHandler'
-level='DEBUG'
-args='(sys.stdout,)'
-formatter='full_content'
-
-[[[access_out]]]
-class='StreamHandler'
-level='INFO'
-args='(sys.stdout,)'
-formatter='message_only'
-
-[[[error_out]]]
-class='StreamHandler'
-level='ERROR'
-args='(sys.stdout,)'
diff --git a/elections/controllers.py b/elections/controllers.py
deleted file mode 100644
index 94ee6f8..0000000
--- a/elections/controllers.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2008-2009 Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano All rights reserved.
-#
-# 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. 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.
-#
-# Author(s): Nigel Jones <nigelj(a)fedoraproject.org>
-# Toshio Kuratomi <toshio(a)fedoraproject.org>
-# Ricky Zhou <ricky(a)fedoraproject.org>
-# Luca Foppiano <lfoppiano(a)fedoraproject.org>
-#
-# Report Bugs to https://www.fedorahosted.org/elections
-
-import turbogears
-from turbogears import controllers, expose, flash, redirect, config
-from turbogears import identity
-from turbogears.database import session
-from cherrypy import request, response
-
-from fedora.client import AuthError, AppError
-from fedora.client.fas2 import AccountSystem
-from fedora.tg.controllers import login as fc_login
-from fedora.tg.controllers import logout as fc_logout
-from elections import model
-from elections.model import *
-from elections.admin import Admin
-from elections.vote import Vote
-
-import sqlalchemy
-
-from datetime import datetime
-import re
-
-class Root(controllers.RootController):
- appTitle = 'Fedora Elections'
-
- baseURL = config.get('fas.url', 'https://admin.fedoraproject.org/accounts/')
- username = config.get('fas.username', 'admin')
- password = config.get('fas.password', 'admin')
- fas = AccountSystem(baseURL, username=username, password=password)
-
- admin = Admin(fas, appTitle)
- vote = Vote(fas, appTitle)
-
- @expose(template="elections.templates.index")
- def index(self):
- elections = Elections.query.order_by(ElectionsTable.c.start_date).filter('id>0').all()
- past = []
- current = []
- future = []
- now = datetime.utcnow()
- for e in elections:
- if e.start_date > now :
- future.append(e)
- elif e.end_date < now :
- past.append(e)
- else :
- current.append(e)
- return dict(past=past, current=current, future=future, curtime=datetime.utcnow(), appTitle=self.appTitle)
-
- @expose(template="elections.templates.about")
- def about(self,eid=None):
- try:
- eid = int(eid)
- election = Elections.query.filter_by(id=eid).all()[0]
- except ValueError:
- try:
- election = Elections.query.filter_by(alias=eid).all()[0]
- eid = election.id
- except IndexError:
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/")
- except (IndexError, TypeError):
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/")
-
- votergroups = LegalVoters.query.filter_by(election_id=eid).all()
- candidates = Candidates.query.filter_by(election_id=eid).order_by(Candidates.name).all()
- usernamemap = {}
- groupnamemap = {}
-
- if election.usefas:
- for c in candidates:
- try:
- usernamemap[c.id] = self.fas.person_by_username(c.name)['human_name']
- except (KeyError, AuthError):
- # User has their name set to private or user doesn't exist
- usernamemap[c.id] = c.name
- for g in votergroups:
- try:
- groupnamemap[g.group_name] = g.group_name + " (" + self.fas.group_by_name(g.group_name)['display_name'] +")"
- except (AppError, AuthError, KeyError) :
- groupnamemap[g.group_name] = g.group_name
-
- curtime = datetime.utcnow()
-
- return dict(eid=eid, candidates=candidates, usernamemap=usernamemap, election=election, curtime=curtime, votergroups=votergroups, appTitle=self.appTitle, groupnamemap=groupnamemap)
-
- @expose(template="elections.templates.results")
- def results(self,eid=None):
- try:
- eid = int(eid)
- election = Elections.query.filter_by(id=eid).all()[0]
- except ValueError:
- try:
- election = Elections.query.filter_by(alias=eid).all()[0]
- eid = election.id
- except IndexError:
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/")
- except (IndexError, TypeError):
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/")
-
- usernamemap = {}
-
- if election.usefas:
- for c in election.candidates:
- try:
- usernamemap[c.id] = self.fas.person_by_username(c.name)['human_name']
- except (AuthError, KeyError) :
- # User has their name set to private
- usernamemap[c.id] = c.name
-
- curtime = datetime.utcnow()
- if election.end_date > curtime:
- turbogears.flash("We are sorry, the results for this election cannot be viewed at this time because the election is still in progress.")
- raise turbogears.redirect("/")
- elif election.start_date > curtime:
- turbogears.flash("We are sorry, the results for this election cannot be viewed at this time because the election has not started.")
- raise turbogears.redirect("/")
- elif election.embargoed == 1:
- if identity.in_group('elections') :
- pass
- else :
- match = 0
- admingroups = ElectionAdmins.query.filter_by(election_id=eid).all()
- for group in admingroups:
- if identity.in_group(group.group_name):
- match = 1
- if match == 0:
- turbogears.flash("Meep, We are sorry, the results for this election cannot be viewed because they are currently embargoed pending formal announcement.")
- raise turbogears.redirect("/")
- votecount = VoteTally.query.filter_by(election_id=eid).order_by(VoteTally.novotes.desc()).all()
- return dict(votecount=votecount, usernamemap=usernamemap, election=election, appTitle=self.appTitle)
-
- @identity.require(identity.not_anonymous())
- @expose(template="elections.templates.verify")
- def verify(self):
- validvotes = {}
- invalidvotes = {}
- c = 0
- allvotes = UserVoteCount.query.filter_by(voter=turbogears.identity.current.user_name).all()
- for v in allvotes:
- if len(v.election.candidates) == v.novotes:
- validvotes[c] = v
- c=c+1
- else:
- invalidvotes[c] = v
- c=c+1
- return dict(validvotes=validvotes, invalidvotes=invalidvotes, appTitle=self.appTitle)
-
-
- @expose(template="elections.templates.login", allow_json=True)
- def login(self, forward_url=None, *args, **kwargs):
- login_dict = fc_login(forward_url, args, kwargs)
- login_dict['appTitle'] = '%s -- Fedora Account System Login' % \
- self.appTitle
- return login_dict
-
- @expose(allow_json=True)
- def logout(self):
- return fc_logout()
diff --git a/elections/json.py b/elections/json.py
deleted file mode 100644
index 66d5cfb..0000000
--- a/elections/json.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# A JSON-based API(view) for your app.
-# Most rules would look like:
-# @jsonify.when("isinstance(obj, YourClass)")
-# def jsonify_yourclass(obj):
-# return [obj.val1, obj.val2]
-# @jsonify can convert your objects to following types:
-# lists, dicts, numbers and strings
-
-from turbojson.jsonify import jsonify
-
diff --git a/elections/model.py b/elections/model.py
deleted file mode 100644
index 4a024ea..0000000
--- a/elections/model.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from turbogears.database import metadata, mapper, get_engine,session
-# import some basic SQLAlchemy classes for declaring the data model
-# (see http://www.sqlalchemy.org/docs/04/ormtutorial.html)
-from sqlalchemy import Table, Column, ForeignKey
-from sqlalchemy.orm import relation
-# import some datatypes for table columns from SQLAlchemy
-# (see http://www.sqlalchemy.org/docs/04/types.html for more)
-from sqlalchemy import String, Unicode, Integer, DateTime, Boolean
-# A few sqlalchemy tricks:
-# Allow viewing foreign key relations as a dictionary
-from sqlalchemy.orm.collections import column_mapped_collection, attribute_mapped_collection
-# Allow us to reference the remote table of a many:many as a simple list
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy import select, and_
-
-from sqlalchemy.exceptions import InvalidRequestError
-
-from fedora.tg.json import SABase
-
-# Bind us to the database definedin the config file.
-get_engine()
-
-#
-# Tables mapped from the DB
-#
-
-ElectionsTable = Table('elections', metadata, autoload=True)
-VotesTable = Table('votes', metadata, autoload=True)
-CandidatesTable = Table('candidates', metadata, autoload=True)
-LegalVotersTable = Table('legalvoters', metadata, autoload=True)
-ElectionAdminsTable = Table('electionadmins', metadata, autoload=True)
-
-# View in the DB. Needs to have the column keys defined
-VoteTallyTable = Table('fvotecount', metadata,
- Column('id', Integer,
- ForeignKey('candidates.id'), primary_key=True),
- Column('election_id', Integer),
- Column('name', String, nullable=False),
- Column('novotes', Integer, nullable=False)
-)
-UserVoteCountTable = Table('uservotes', metadata,
- Column('election_id', Integer, ForeignKey('elections.id'), primary_key=True),
- Column('voter', String, nullable=False, primary_key=True),
- Column('novotes', Integer, nullable=False)
-)
-
-#
-# Classes to map to
-#
-
-class Elections(SABase):
- pass
-
-class Votes(SABase):
- pass
-
-class Candidates(SABase):
- pass
-
-class LegalVoters(SABase):
- pass
-
-class ElectionAdmins(SABase):
- pass
-
-class VoteTally(SABase):
- pass
-
-class UserVoteCount(SABase):
- pass
-
-#
-# set up mappers between tables and classes
-#
-
-mapper(Elections, ElectionsTable, properties = {
- 'legalVoters': relation(LegalVoters, backref='election'),
- 'candidates': relation(Candidates, backref='election'),
- 'uservotes': relation(UserVoteCount, backref='election')
- })
-mapper(Votes, VotesTable)
-mapper(Candidates, CandidatesTable, properties = {
- 'votes': relation(Votes, backref='candidate'),
- 'tally': relation(VoteTally, backref='candidate')
- })
-mapper(LegalVoters, LegalVotersTable)
-mapper(ElectionAdmins, ElectionAdminsTable)
-mapper(VoteTally, VoteTallyTable)
-mapper(UserVoteCount, UserVoteCountTable)
diff --git a/elections/release.py b/elections/release.py
deleted file mode 100644
index 6b12f00..0000000
--- a/elections/release.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2008 Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano All rights reserved.
-#
-# 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. 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.
-#
-# Author(s): Nigel Jones <nigelj(a)fedoraproject.org>
-# Toshio Kuratomi <toshio(a)fedoraproject.org>
-# Ricky Zhou <ricky(a)fedoraproject.org>
-# Luca Foppiano <lfoppiano(a)fedoraproject.org>
-#
-# Report Bugs to https://www.fedorahosted.org/elections
-
-# Release information about elections
-
-version = "0.4.0"
-
-description = "Elections Application for Fedora Project"
-# long_description = "More description about your plan"
-author = "Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano"
-email = "gitelections-members(a)fedoraproject.org"
-copyright = "Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano - 2008"
-
-# if it's open source, you might want to specify these
-url = "http://fedorahosted.org/elections"
-# download_url = "http://yourcool.site/download"
-license = "GPLv2"
diff --git a/elections/static/css/style.css b/elections/static/css/style.css
deleted file mode 100644
index fe03cd9..0000000
--- a/elections/static/css/style.css
+++ /dev/null
@@ -1,57 +0,0 @@
-.flash
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #000000;
- background: #EEFFBB;
-}
-
-.vthot
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #FFFFFF;
- background: #9C0000;
-}
-.vtwarm
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #000000;
- background: #FFA300;
-}
-.vtmedwarm
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #000000;
- background: #93FF63;
-}
-.vtmedcool
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #000000;
- background: #00DAFE;
-}
-.vtcool
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #000000;
- background: #0033FF;
-}
-.vtcold
-{
- border: 1px solid #DDDDDD;
- padding: 1.5ex;
- margin: 1ex 0;
- color: #FFFFFF;
- background: #000083;
-}
diff --git a/elections/static/images/favicon.ico b/elections/static/images/favicon.ico
deleted file mode 100644
index 332557b..0000000
Binary files a/elections/static/images/favicon.ico and /dev/null differ
diff --git a/elections/static/images/header_inner.png b/elections/static/images/header_inner.png
deleted file mode 100644
index 2b2d87d..0000000
Binary files a/elections/static/images/header_inner.png and /dev/null differ
diff --git a/elections/static/images/info.png b/elections/static/images/info.png
deleted file mode 100644
index 329c523..0000000
Binary files a/elections/static/images/info.png and /dev/null differ
diff --git a/elections/static/images/ok.png b/elections/static/images/ok.png
deleted file mode 100644
index fee6751..0000000
Binary files a/elections/static/images/ok.png and /dev/null differ
diff --git a/elections/static/images/tg_under_the_hood.png b/elections/static/images/tg_under_the_hood.png
deleted file mode 100644
index bc9c79c..0000000
Binary files a/elections/static/images/tg_under_the_hood.png and /dev/null differ
diff --git a/elections/static/images/under_the_hood_blue.png b/elections/static/images/under_the_hood_blue.png
deleted file mode 100644
index 90e84b7..0000000
Binary files a/elections/static/images/under_the_hood_blue.png and /dev/null differ
diff --git a/elections/templates/__init__.py b/elections/templates/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/elections/templates/about.html b/elections/templates/about.html
deleted file mode 100644
index 4994709..0000000
--- a/elections/templates/about.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections -- Election Information</title>
- </head>
- <body>
- <h1>${election.shortdesc}</h1>
- <p>${election.description}</p>
- <p py:if="election.url"><a href="${election.url}">[More Information]</a></p>
- <table border="1" cellpadding="1">
- <tr py:for="candidate in candidates">
- <td><py:choose test="election.usefas"><py:when test="0">${candidate.name}</py:when><py:when test="1">${usernamemap[candidate.id]}</py:when></py:choose> <small py:if="candidate.url"><a href="${candidate.url}">[info]</a></small></td>
- </tr>
- <py:choose test="curtime > election.end_date"><py:when test="0">
- <py:choose test="election.start_date > curtime"><py:when test="0">
- <tr>
- <td>
- <py:choose test="1">
- <py:when test="election.alias == ''">
- <a href="${tg.url('/vote/' + str(election.id))}">Vote Now!</a>
- </py:when>
- <py:otherwise>
- <a href="${tg.url('/vote/' + str(election.alias))}">Vote Now!</a>
- </py:otherwise>
- </py:choose>
- </td>
- </tr>
- </py:when></py:choose>
- </py:when><py:when test="1">
- <tr>
- <py:choose test="1">
- <py:when test="election.alias == ''">
- <a href="${tg.url('/results/' + str(election.id))}">Show Results!</a>
- </py:when>
- <py:otherwise>
- <a href="${tg.url('/results/' + str(election.alias))}">Show Results!</a>
- </py:otherwise>
- </py:choose>
- </tr>
- </py:when></py:choose>
- </table>
- <p>To vote in this election you must be a member of any one of the following groups:</p>
- <ul>
- <li py:for="g in votergroups"><py:choose test="g.group_name == 'anyany'"><py:when test="0"><py:choose test="g.group_name == 'anycla'"><py:when test="0">${groupnamemap[g.group_name]}</py:when><py:when test="1">Any non-CLA Group</py:when></py:choose></py:when><py:when test="1">Any Group</py:when></py:choose></li>
- </ul>
- </body>
-</html>
diff --git a/elections/templates/admedit.html b/elections/templates/admedit.html
deleted file mode 100644
index e036dda..0000000
--- a/elections/templates/admedit.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections -- Administration -- Election Details</title>
- </head>
- <body>
- <form action="${tg.url('/admin/edit/' + str(e.alias))}" method="post">
- <table>
- <tr>
- <th colspan="2"> Election Details - ${e.shortdesc} </th>
- </tr>
- <tr><td>Election ID:</td><td>${e.id}<input type="hidden" name="id" value="${e.id}" /></td></tr>
- <tr><td>Election Name:</td><td><input type="text" name="shortdesc" value="${e.shortdesc}" /></td></tr>
- <tr><td>Alias:</td><td><input type="text" name="alias" value="${e.alias}" /></td></tr>
- <tr><td>URL:</td><td><input type="text" name="url" value="${e.url}" /></td></tr>
- <tr><td>Election Info:</td><td><textarea name="info" rows="5" cols="40">${e.description}</textarea></td></tr>
- <tr><td>Start Date:</td><td><input type="text" name="startdate" value="${e.start_date}" /></td></tr>
- <tr><td>End Date:</td><td><input type="text" name="enddate" value="${e.end_date}" /></td></tr>
- <tr><td>Seats Elected:</td><td><input type="text" name="seats" value="${e.seats_elected}" /></td></tr>
- <tr><td>Votes Per User:</td><td><input type="text" name="votes" value="${e.votes_per_user}" /></td></tr>
- <tr><td>Allow Nominations?</td><td><input type="checkbox" name="allownominations" py:attrs="{'checked':tg.checker(e.allow_nominations)}" /> </td></tr>
- <tr><td>Until When?</td><td><input type="text" name="nominationend" value="" /> </td></tr>
- <tr><td>Use FAS?</td><td><input type="checkbox" name="usefas" py:attrs="{'checked':tg.checker(e.usefas)}" /> </td></tr>
- <tr><td>Embargoed?</td><td><input type="checkbox" name="embargoed" py:attrs="{'checked':tg.checker(e.embargoed)}" /> </td></tr>
- <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
-
- <tr>
- <th colspan="2"> Allowed Groups </th>
- </tr>
- <tr py:for="g in groups">
- <td>
- <py:choose test="g.group_name == 'anyany'"><py:when test="0"><py:choose test="g.group_name == 'anycla'"><py:when test="0">${groupnamemap[g.group_name]}</py:when><py:when test="1">Any non-CLA Group</py:when></py:choose></py:when><py:when test="1">Any Group</py:when></py:choose>
- </td>
- <td>Remove group: <input type="checkbox" name="remove_${g.group_name}" /> </td>
- </tr>
- <tr><td>New Groups:<br/>( entries seperated by | )</td><td><input type="text" name="newgroups" value="" /> </td></tr>
- <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
-
- <tr>
- <th colspan="2"> Admin Groups </th>
- </tr>
- <tr py:for="g in admingroups">
- <td> ${groupnamemap[g.group_name]} </td>
- <td>Remove group: <input type="checkbox" name="removeadmin_${g.group_name}" /> </td>
- </tr>
- <tr><td>New Groups:<br/>( entries seperated by | )</td><td><input type="text" name="newadmins" value="" /> </td></tr>
- <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
-
- <tr>
- <th colspan="2"> Candidates </th>
- </tr>
- <tr py:for="c in candidates">
- <td>
- ${c.name}
- </td>
- <td>
- ${c.url}
- </td>
- </tr>
- <tr><td>New Candidates:<br/>( Formated as [URL optional]: Name1!URL1|Name2!URL2 )</td><td><input type="text" name="newcandidates" value="" /> </td></tr>
- <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
- </table>
- </form>
- </body>
-</html>
diff --git a/elections/templates/admin.html b/elections/templates/admin.html
deleted file mode 100644
index 1bff42e..0000000
--- a/elections/templates/admin.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections -- Administration</title>
- </head>
- <body>
- <table>
- <tr>
- <th>ID</th>
- <th>Name</th>
- <th>Alias</th>
- <th>Start Date</th>
- <th>End Date</th>
- <th></th>
- </tr>
- <tr py:for="election in elections">
- <td>${election.id}</td>
- <td>${election.shortdesc}</td>
- <td>${election.alias}</td>
- <td>${election.start_date}</td>
- <td>${election.end_date}</td>
- <td>
- <a href="${tg.url('/admin/edit/' + str(election.id))}">More Information</a>
- <py:if test="election.embargoed">
- |
- <a href="${tg.url('/admin/edit/' + str(election.id) + '?removeembargo=1')}">Remove Embargo</a>
- </py:if>
- </td>
- </tr>
- </table>
- <p><a href="${tg.url('/admin/newe/')}">Create a new election</a></p>
-
- </body>
-</html>
diff --git a/elections/templates/admnewc.html b/elections/templates/admnewc.html
deleted file mode 100644
index a1393f6..0000000
--- a/elections/templates/admnewc.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections -- Administration -- New Candidates</title>
- </head>
- <body>
- <form action="${tg.url('/admin/newc')}" method="post">
- <table>
- <tr><td>Election Id (Numerical):</td><td><input type="text" name="id" /></td></tr>
- <tr><td>The string below needs to be formatted as: "Name1!URL1 (opt)|Name2!URL2 (opt)|Name3!URL3 (opt)"</td></tr>
- <tr><td>Candidate Name/URL String:</td><td><input type="text" name="nameurl" /></td></tr>
- <tr><td></td><td><input type="submit" name="submit" value="Submit" /></td></tr>
- </table>
- </form>
- </body>
-</html>
diff --git a/elections/templates/admnewe.html b/elections/templates/admnewe.html
deleted file mode 100644
index 2fe3b1e..0000000
--- a/elections/templates/admnewe.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections -- Administration -- New Election</title>
- </head>
- <body>
- <form action="${tg.url('/admin/newe')}" method="post">
- <table>
- <tr><td>Election Name:</td><td><input type="text" name="shortdesc" /></td></tr>
- <tr><td>Alias:</td><td><input type="text" name="alias" /></td></tr>
- <tr><td>Election Info:</td><td><textarea name="info" rows="5" cols="40"></textarea></td></tr>
- <tr><td>URL:</td><td><input type="text" name="url" /></td></tr>
- <tr><td>Start Date:</td><td><input type="text" name="startdate" value="yyyy-mm-dd hh:mm:ss"/></td></tr>
- <tr><td>End Date:</td><td><input type="text" name="enddate" value="yyyy-mm-dd hh:mm:ss"/></td></tr>
- <tr><td>Seats Elected:</td><td><input type="text" name="seats"/></td></tr>
- <tr><td>Embargoed?</td><td><input type="checkbox" name="embargoed" /> </td></tr>
- <tr><td>Allow Nominations?</td><td><input type="checkbox" name="allownominations" /> </td></tr>
- <tr><td>Until When?</td><td><input type="text" name="nominationend" /> </td></tr>
- <tr><td>Use FAS?</td><td><input type="checkbox" name="usefas" /> </td></tr>
- <tr><td></td><td><input type="submit" name="submit" value="Submit" /></td></tr>
- </table>
- </form>
- </body>
-</html>
diff --git a/elections/templates/index.html b/elections/templates/index.html
deleted file mode 100644
index 09176d5..0000000
--- a/elections/templates/index.html
+++ /dev/null
@@ -1,111 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections</title>
- </head>
- <body>
-
- <py:if test="len(current)">
- <h2>Current Elections</h2>
- <table>
- <tr>
- <th></th>
- <th>Start Date (UTC)</th>
- <th>End Date (UTC)</th>
- <th></th>
- </tr>
- <tr py:for="election in current">
- <td>${election.shortdesc}</td>
- <td>${election.start_date}</td>
- <td>${election.end_date}</td>
- <td>
- <py:choose test="1">
- <py:when test="election.alias == ''">
- <a href="${tg.url('/about/' + str(election.id))}">More Information</a>
- </py:when>
- <py:otherwise>
- <a href="${tg.url('/about/' + str(election.alias))}">More Information</a>
- </py:otherwise>
- </py:choose>
- </td>
- </tr>
- </table>
- </py:if>
-
- <py:if test="len(future)">
- <h2>Elections Coming soon...</h2>
- <table>
- <tr>
- <th></th>
- <th>Start Date (UTC)</th>
- <th>End Date (UTC)</th>
- <th></th>
- </tr>
- <tr py:for="election in future">
- <td>${election.shortdesc}</td>
- <td>${election.start_date}</td>
- <td>${election.end_date}</td>
- <td>
- <py:choose test="1">
- <py:when test="election.alias == ''">
- <a href="${tg.url('/about/' + str(election.id))}">More Information</a>
- </py:when>
- <py:otherwise>
- <a href="${tg.url('/about/' + str(election.alias))}">More Information</a>
- </py:otherwise>
- </py:choose>
- </td>
- </tr>
- </table>
- </py:if>
-
- <py:if test="len(past)">
- <h2>Past Elections</h2>
- <table>
- <tr>
- <th></th>
- <th>Start Date (UTC)</th>
- <th>End Date (UTC)</th>
- <th></th>
- <th></th>
- </tr>
- <tr py:for="election in past">
- <td>${election.shortdesc}</td>
- <td>${election.start_date}</td>
- <td>${election.end_date}</td>
- <td>
- <py:choose test="1">
- <py:when test="election.alias == ''">
- <a href="${tg.url('/about/' + str(election.id))}">More Information</a>
- </py:when>
- <py:otherwise>
- <a href="${tg.url('/about/' + str(election.alias))}">More Information</a>
- </py:otherwise>
- </py:choose>
- </td>
- <td>
- <py:choose test="1">
- <py:when test="0 == election.embargoed">
- <py:choose test="1">
- <py:when test="election.alias == ''">
- <a href="${tg.url('/results/' + str(election.id))}">Results</a>
- </py:when>
- <py:otherwise>
- <a href="${tg.url('/results/' + str(election.alias))}">Results</a>
- </py:otherwise>
- </py:choose>
- </py:when>
- <py:otherwise>
- Election Results under Embargo
- </py:otherwise>
- </py:choose>
- </td>
- </tr>
- </table>
- </py:if>
-
- </body>
-</html>
diff --git a/elections/templates/login.html b/elections/templates/login.html
deleted file mode 100644
index e77aada..0000000
--- a/elections/templates/login.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <xi:include href="${tg.fedora_template('login.html')}" />
- <head>
- <title>${appTitle}</title>
- </head>
- <body>
- <loginform>${message}</loginform>
- </body>
-</html>
diff --git a/elections/templates/master.html b/elections/templates/master.html
deleted file mode 100644
index 217e222..0000000
--- a/elections/templates/master.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:xi="http://www.w3.org/2001/XInclude"
- xmlns:py="http://genshi.edgewall.org/"
- py:strip="">
- <head py:match="head" py:attrs="select('@*')">
- <meta py:replace="select('*|text()')" />
- <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/static/css/style.css')}" />
- <link rel="stylesheet" type="text/css" media="screen" href="https://fedoraproject.org/static/css/fedora.css" />
- <jsglobals />
- </head>
- <body py:match="body" py:attrs="select('@*')">
- <div id="wrapper">
-
- <div id="head">
- <h1><a href="/en/index">Fedora</a></h1>
- </div>
-
- <div id="sidebar">
- <div id="nav">
- <h2>Navigation</h2>
- <ul>
- <li id="nav-home"><a href="http://fedoraproject.org/en/index">Home</a></li>
- <li id="nav-get"><a href="http://fedoraproject.org/en/get-fedora">Get Fedora</a></li>
- <li id="nav-join"><a href="http://fedoraproject.org/en/join-fedora">Join Fedora</a></li>
- <li id="nav-help"><a href="http://fedoraproject.org/en/get-help">Get Help</a></li>
- </ul>
- </div>
- </div>
-
- <div id="content">
-
- <ul class="toolbar" id="#main-toolbar">
- <logintoolitem href="${tg.url('/')}" />
- <li py:if="('elections' in tg.identity.groups)">Admin</li>
- </ul>
- <div py:if="tg_flash" class="flash">
- ${tg_flash}
- </div>
-
- <div py:replace="select('*|text()')" />
- </div>
- <p>
- Please report voting issues to the <a href="https://fedorahosted.org/fedora-infrastructure/">Fedora Infrastructure ticket system</a> selecting the Elections component.
- </p>
-
- </div>
-
- <div id="bottom">
- <div id="footer">
-
- <p class="copy">
- Copyright © 2008 Red Hat, Inc. and others. All Rights Reserved. Please send any comments or corrections to the <a href="mailto:webmaster@fedoraproject.org">websites team</a>.
- </p>
- <p class="disclaimer">
- The Fedora Project is maintained and driven by the community and sponsored by Red Hat. This is a community maintained site. Red Hat is not responsible for content.
- </p>
- <ul>
- <li class="first"><a href="/en/sponsors">Sponsors</a></li>
-
- <li><a href="http://fedoraproject.org/wiki/Legal">Legal</a></li>
- <li><a href="http://fedoraproject.org/wiki/Legal/TrademarkGuidelines">Trademark Guidelines</a></li>
- </ul>
- </div>
- </div>
- </body>
-
- <xi:include href="${tg.fedora_template('login.html')}" />
- <xi:include href="${tg.fedora_template('jsglobals.html')}" />
-
-</html>
diff --git a/elections/templates/results.html b/elections/templates/results.html
deleted file mode 100644
index d2d142d..0000000
--- a/elections/templates/results.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>${appTitle}</title>
- </head>
- <body>
- <h1>${election.shortdesc} Results</h1>
- <p>${election.description}</p>
- <p py:if="election.url"><a href="${election.url}">[More Information]</a></p>
- <table border="1" cellpadding="1">
- <tr py:for="candcount in votecount">
- <td><py:choose test="election.usefas"><py:when test="0">${candcount.candidate.name}</py:when><py:when test="1">${usernamemap[candcount.candidate.id]}</py:when></py:choose> <small py:if="candcount.candidate.url"><a href="${candcount.candidate.url}">[info]</a></small></td>
- <td>${candcount.novotes}</td>
- </tr>
- </table>
- </body>
-</html>
diff --git a/elections/templates/verify.html b/elections/templates/verify.html
deleted file mode 100644
index 0dbea59..0000000
--- a/elections/templates/verify.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>Fedora Elections -- Vote Verification -- ${election.shortdesc}</title>
- </head>
- <body>
- <h1>Vote Verification Page</h1>
- <p>Please check the below content to ensure that all of your cast votes are marked as 'valid', if you notice any elections showing that you have an invalid vote cast please contact the election administration team by creating a ticket at the <a href="https://fedorahosted.org/fedora-infrastructure/">Fedora Infrastructure ticket system</a> and select the 'Elections' component with your FAS username and the election that is marked as invalid.</p>
- <h2 py:if="validvotes">Valid Votes</h2>
- <ul py:if="validvotes">
- <li py:for="vote in validvotes"><a href="${tg.url('/about/' + validvotes[vote].election.id)}">${validvotes[vote].election.shortdesc}</a></li>
- </ul>
- <h2 py:if="invalidvotes">Invalid Votes</h2>
- <ul py:if="invalidvotes">
- <li py:for="vote in invalidvotes"><a href="${tg.url('/about/' + invalidvotes[vote].election.id)}">${invalidvotes[vote].election.shortdesc}</a></li>
- </ul>
-
- </body>
-</html>
diff --git a/elections/templates/vote.html b/elections/templates/vote.html
deleted file mode 100644
index 7887c69..0000000
--- a/elections/templates/vote.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<?python
- def gradient(nocands, voteweight):
- weightpercent = ((voteweight*100)/nocands)
- if weightpercent >=90:
- return "vthot"
- elif weightpercent >= 70:
- return "vtwarm"
- elif weightpercent >= 50:
- return "vtmedwarm"
- elif weightpercent >= 30:
- return "vtmedcool"
- elif weightpercent >= 10:
- return "vtcool"
- else:
- return "vtcold"
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
- <xi:include href="master.html" />
- <head>
- <title>${appTitle} -- Cast Your Vote -- ${election.shortdesc}</title>
- </head>
- <body>
- <h1>${election.shortdesc}</h1>
- <p>${election.description}</p>
- <p py:if="election.url"><a href="${election.url}">[More Information]</a></p>
- <div>
- <form action="${tg.url('/vote/' + str(election.id))}" method="post">
- <table border="1" cellpadding="1">
- <tr py:for="candidate in candidates">
- <td><py:choose test="election.usefas"><py:when test="0">${candidate.name}</py:when><py:when test="1">${usernamemap[candidate.id]}</py:when></py:choose> <small py:if="candidate.url"><a href="${candidate.url}">[info]</a></small></td>
- <py:if test="nextaction=='vote'">
- <td><select name="${candidate.id}">
- <option py:for="option in range(0,len(candidates)+1)" value="$option">$option</option>
- </select></td>
- </py:if>
- <py:if test="nextaction=='confirm'">
- <td><input type="hidden" name="${candidate.id}" value="${voteinfo[candidate.id]}" /><div class="${gradient(len(candidates),voteinfo[candidate.id])}">${voteinfo[candidate.id]}</div></td>
- </py:if>
- </tr>
- <tr>
- <td><input py:if="nextaction=='confirm'" type="button" value="Go Back" onClick="history.go(-1)" /></td>
- <td>
- <input py:if="nextaction!='confirm'" type="submit" name="${nextaction}" value="Preview" />
- <input py:if="nextaction=='confirm'" type="submit" name="${nextaction}" value="Submit" />
- </td>
- </tr>
- </table>
- </form>
- </div>
- <div>
- <h2>Information on Range Voting</h2>
- <p>Fedora Project has implemented <a href="http://rangevoting.org/">Range Voting</a> for this election, in particular the "Range (score-summing, blanks treated as zero score, no quorum rule)" range voting system.</p>
- <p>To cast your vote in this election simply select a value between 0 and ${len(candidates)} with 0 as 'least or no preference' and ${len(candidates)} as 'highest preference'.</p>
- <p>At the end of the election, the highest ranking candidate(s) are marked as the winners.</p>
- <p>For more information about Range Voting, visit the <a href="http://rangevoting.org/">Center for Range Voting</a>.</p>
- </div>
- </body>
-</html>
diff --git a/elections/tests/__init__.py b/elections/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/elections/tests/test_controllers.py b/elections/tests/test_controllers.py
deleted file mode 100644
index bce4c18..0000000
--- a/elections/tests/test_controllers.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import unittest
-import turbogears
-from turbogears import testutil
-from elections.controllers import Root
-import cherrypy
-
-cherrypy.root = Root()
-
-class TestPages(unittest.TestCase):
-
- def setUp(self):
- turbogears.startup.startTurboGears()
-
- def tearDown(self):
- """Tests for apps using identity need to stop CP/TG after each test to
- stop the VisitManager thread.
- See http://trac.turbogears.org/turbogears/ticket/1217 for details.
- """
- turbogears.startup.stopTurboGears()
-
- def test_method(self):
- "the index method should return a string called now"
- import types
- result = testutil.call(cherrypy.root.index)
- assert type(result["now"]) == types.StringType
-
- def test_indextitle(self):
- "The indexpage should have the right title"
- testutil.createRequest("/")
- response = cherrypy.response.body[0].lower()
- assert "<title>welcome to turbogears</title>" in response
-
diff --git a/elections/tests/test_model.py b/elections/tests/test_model.py
deleted file mode 100644
index ae649d0..0000000
--- a/elections/tests/test_model.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# If your project uses a database, you can set up database tests
-# similar to what you see below. Be sure to set the db_uri to
-# an appropriate uri for your testing database. sqlite is a good
-# choice for testing, because you can use an in-memory database
-# which is very fast.
-
-from turbogears import testutil, database
-# from elections.model import YourDataClass, User
-
-# database.set_db_uri("sqlite:///:memory:")
-
-# class TestUser(testutil.DBTest):
-# def get_model(self):
-# return User
-# def test_creation(self):
-# "Object creation should set the name"
-# obj = User(user_name = "creosote",
-# email_address = "spam(a)python.not",
-# display_name = "Mr Creosote",
-# password = "Wafer-thin Mint")
-# assert obj.display_name == "Mr Creosote"
-
diff --git a/elections/vote.py b/elections/vote.py
deleted file mode 100644
index 5b5f575..0000000
--- a/elections/vote.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2008 Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano All rights reserved.
-#
-# 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. 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.
-#
-# Author(s): Nigel Jones <nigelj(a)fedoraproject.org>
-# Toshio Kuratomi <toshio(a)fedoraproject.org>
-# Ricky Zhou <ricky(a)fedoraproject.org>
-# Luca Foppiano <lfoppiano(a)fedoraproject.org>
-#
-# Report Bugs to https://www.fedorahosted.org/elections
-
-
-# Voting functions
-
-import turbogears
-from turbogears import controllers, expose, flash, redirect, config
-from turbogears import identity
-from elections import model
-from elections.model import Elections, LegalVoters, Candidates, Votes, UserVoteCount
-
-import sqlalchemy
-
-from turbogears.database import session
-
-from datetime import datetime
-import re
-
-class Vote(controllers.Controller):
- def __init__(self, fas, appTitle):
- self.fas = fas
- self.appTitle = appTitle
-
- #TODO: This function will be split off into: default => submit => confirm functions, hopefully it was simplify everything
- @identity.require(identity.not_anonymous())
- @expose(template="elections.templates.vote")
- def default(self, eid=None, **kw):
- try:
- eid = int(eid)
- election = Elections.query.filter_by(id=eid).all()[0]
- except ValueError:
- try:
- election = Elections.query.filter_by(alias=eid).all()[0]
- eid = election.id
- except IndexError:
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/")
- except IndexError:
- turbogears.flash("This election does not exist, check if you have used the correct URL.")
- raise turbogears.redirect("/")
-
- votergroups = LegalVoters.query.filter_by(election_id=eid).all()
- foo = identity.current.groups
-
- match = 0
- for group in votergroups:
- if group.group_name == "anycla":
- if (len(identity.current.groups) > len([g for g in identity.current.groups if re.match("cla_.*",g)])):
- match = 1
- elif identity.in_group(group.group_name) or group.group_name == "anyany":
- match = 1
- if match == 0:
- turbogears.flash("You are not in a FAS group that can vote in this election, more information can be found at the bottom of this page.")
- raise turbogears.redirect("/about/" + str(eid))
-
- candidates = Candidates.query.filter_by(election_id=eid).order_by(Candidates.name).all()
- uservote = UserVoteCount.query.filter_by(election_id=eid, voter=turbogears.identity.current.user_name).all()
-
- usernamemap = {}
-
- if election.usefas:
- for c in candidates:
- try:
- usernamemap[c.id] = self.fas.person_by_username(c.name)['human_name']
- except KeyError:
- # User has human_name set to private
- usernamemap[c.id] = c.name
-
- uvotes = {}
- next_action = ""
-
- curtime = datetime.utcnow()
- if election.start_date > curtime:
- turbogears.flash("Voting has not yet started, sorry.")
- raise turbogears.redirect("/")
- elif election.end_date < curtime:
- turbogears.flash("You cannot vote in this election because the end date has passed. You have been redirected to the election results")
- raise turbogears.redirect("/results/" + election.shortname)
- elif len(uservote) != 0:
- turbogears.flash("You have already voted in this election!")
- raise turbogears.redirect("/")
-
- # Lets do this in reverse order
- if "confirm" in kw:
- for c in candidates:
- if str(c.id) in kw:
- try:
- range = int(kw[str(c.id)])
- if range >= 0 and range <= len(candidates):
- uvotes[c.id] = range
- else:
- turbogears.flash("Invalid Ballot!")
- raise turbogears.redirect("/")
- except ValueError:
- turbogears.flash("Invalid Ballot!")
- raise turbogears.redirect("/")
- for uvote in uvotes:
- Votes(voter=turbogears.identity.current.user_name, candidate_id=uvote, weight=uvotes[uvote], election_id=eid, timestamp=curtime)
- turbogears.flash("Your vote has been recorded, thank you!")
- raise turbogears.redirect("/")
- elif "vote" in kw:
- turbogears.flash("Please confirm your vote!")
- for c in candidates:
- if str(c.id) in kw:
- try:
- range = int(kw[str(c.id)])
- if range > len(candidates):
- turbogears.flash("Invalid data was detected for one or more candidates and was changed to zeros! Please correct and resubmit your ballot.")
- uvotes[c.id] = 0
- next_action = "vote"
- elif range >= 0:
- uvotes[c.id] = range
- else:
- turbogears.flash("Invalid data was detected for one or more candidates and was changed to zeros! Please correct and resubmit your ballot.")
- uvotes[c.id] = 0
- next_action = "vote"
- except ValueError:
- turbogears.flash("Invalid data was detected for one or more candidates and was changed to zeros! Please correct and resubmit your ballot.")
- uvotes[c.id] = 0
- next_action = "vote"
- else:
- turbogears.flash("Invalid Ballot!")
- raise turbogears.redirect("/")
- if next_action != "vote":
- next_action = "confirm"
- else:
- for c in candidates:
- uvotes[c.id] = ""
- next_action = "vote"
-
- return dict(eid=eid, candidates=candidates, usernamemap=usernamemap, election=election, nextaction=next_action, voteinfo=uvotes, appTitle=self.appTitle)
-
diff --git a/elections2/__init__.py b/elections2/__init__.py
new file mode 100644
index 0000000..05d80e0
--- /dev/null
+++ b/elections2/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+"""The elections2 package"""
diff --git a/elections2/config/__init__.py b/elections2/config/__init__.py
new file mode 100644
index 0000000..633f866
--- /dev/null
+++ b/elections2/config/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+
diff --git a/elections2/config/app_cfg.py b/elections2/config/app_cfg.py
new file mode 100644
index 0000000..b2dcdae
--- /dev/null
+++ b/elections2/config/app_cfg.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+"""
+Global configuration file for TG2-specific settings in elections2.
+
+This file complements development/deployment.ini.
+
+Please note that **all the argument values are strings**. If you want to
+convert them into boolean, for example, you should use the
+:func:`paste.deploy.converters.asbool` function, as in::
+
+ from paste.deploy.converters import asbool
+ setting = asbool(global_conf.get('the_setting'))
+
+"""
+
+from tg.configuration import AppConfig
+
+import elections2
+from elections2 import model
+from elections2.lib import app_globals, helpers
+from fedora.tg.tg2utils import add_fas_auth_middleware
+
+class MyAppConfig(AppConfig):
+ add_auth_middleware = add_fas_auth_middleware
+
+base_config = MyAppConfig()
+#base_config = AppConfig()
+
+base_config.renderers = []
+
+base_config.package = elections2
+
+#Enable json in expose
+base_config.renderers.append('json')
+#Set the default renderer
+base_config.default_renderer = 'genshi'
+base_config.renderers.append('genshi')
+# if you want raw speed and have installed chameleon.genshi
+# you should try to use this renderer instead.
+# warning: for the moment chameleon does not handle i18n translations
+#base_config.renderers.append('chameleon_genshi')
+
+#Configure the base SQLALchemy Setup
+base_config.use_sqlalchemy = True
+base_config.model = elections2.model
+base_config.DBSession = elections2.model.DBSession
+
+# Configure the authentication backend
+
+# YOU MUST CHANGE THIS VALUE IN PRODUCTION TO SECURE YOUR APP
+base_config.sa_auth.cookie_secret = "iqy4o#iyu7foi7y384tyorthifvy78q*@⁾$"
+
+base_config.auth_backend = 'sqlalchemy'
+base_config.sa_auth.dbsession = model.DBSession
+# what is the class you want to use to search for users in the database
+base_config.sa_auth.user_class = model.User
+# what is the class you want to use to search for groups in the database
+base_config.sa_auth.group_class = model.Group
+# what is the class you want to use to search for permissions in the database
+base_config.sa_auth.permission_class = model.Permission
+
+# override this if you would like to provide a different who plugin for
+# managing login and logout of your application
+base_config.sa_auth.form_plugin = None
+
+# override this if you are using a different charset for the login form
+base_config.sa_auth.charset = 'utf-8'
+
+# You may optionally define a page where you want users to be redirected to
+# on login:
+base_config.sa_auth.post_login_url = '/post_login'
+
+# You may optionally define a page where you want users to be redirected to
+# on logout:
+base_config.sa_auth.post_logout_url = '/post_logout'
diff --git a/elections2/config/deployment.ini b/elections2/config/deployment.ini
new file mode 100644
index 0000000..d2e3adb
--- /dev/null
+++ b/elections2/config/deployment.ini
@@ -0,0 +1,99 @@
+#
+# elections2 - TurboGears configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+# WARGING == If debug is not set to false, you'll get the interactive
+# debugger on production, which is a huge security hole.
+
+debug = false
+email_to = pingou(a)pingoured.fr
+smtp_server = localhost
+error_email_from = tg2(a)cyan.pingoured.fr
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 8080
+
+[sa_auth]
+cookie_secret = ${app_instance_secret}
+
+[app:main]
+use = egg:elections2
+full_stack = true
+cache_dir = %(here)s/data
+beaker.session.key = elections2
+beaker.session.secret = ${app_instance_secret}
+app_instance_uuid = ${app_instance_uuid}
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+# Specify the database for SQLAlchemy to use via
+# turbogears.database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+#sqlalchemy.url = sqlite:///%(here)s/somedb.db
+sqlalchemy.url="postgres://election@localhost/elections2"
+sqlalchemy.echo = False
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, elections2, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_elections2]
+level = INFO
+handlers =
+qualname = elections2
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers =
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/elections2/config/deployment.ini_tmpl b/elections2/config/deployment.ini_tmpl
new file mode 100644
index 0000000..0b0d67d
--- /dev/null
+++ b/elections2/config/deployment.ini_tmpl
@@ -0,0 +1,98 @@
+#
+# elections2 - TurboGears configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+# WARGING == If debug is not set to false, you'll get the interactive
+# debugger on production, which is a huge security hole.
+
+debug = false
+email_to = you(a)yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 8080
+
+[sa_auth]
+cookie_secret = ${app_instance_secret}
+
+[app:main]
+use = egg:elections2
+full_stack = true
+cache_dir = %(here)s/data
+beaker.session.key = elections2
+beaker.session.secret = ${app_instance_secret}
+app_instance_uuid = ${app_instance_uuid}
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+# Specify the database for SQLAlchemy to use via
+# turbogears.database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+sqlalchemy.url = sqlite:///%(here)s/somedb.db
+sqlalchemy.echo = False
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, elections2, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_elections2]
+level = INFO
+handlers =
+qualname = elections2
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers =
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/elections2/config/environment.py b/elections2/config/environment.py
new file mode 100644
index 0000000..32bd8c2
--- /dev/null
+++ b/elections2/config/environment.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+"""WSGI environment setup for elections2."""
+
+from elections2.config.app_cfg import base_config
+
+__all__ = ['load_environment']
+
+#Use base_config to setup the environment loader function
+load_environment = base_config.make_load_environment()
diff --git a/elections2/config/middleware.py b/elections2/config/middleware.py
new file mode 100644
index 0000000..ca09565
--- /dev/null
+++ b/elections2/config/middleware.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""WSGI middleware initialization for the elections2 application."""
+
+from elections2.config.app_cfg import base_config
+from elections2.config.environment import load_environment
+
+
+__all__ = ['make_app']
+
+# Use base_config to setup the necessary PasteDeploy application factory.
+# make_base_app will wrap the TG2 app with all the middleware it needs.
+make_base_app = base_config.setup_tg_wsgi_app(load_environment)
+
+
+def make_app(global_conf, full_stack=True, **app_conf):
+ """
+ Set elections2 up with the settings found in the PasteDeploy configuration
+ file used.
+
+ :param global_conf: The global settings for elections2 (those
+ defined under the ``[DEFAULT]`` section).
+ :type global_conf: dict
+ :param full_stack: Should the whole TG2 stack be set up?
+ :type full_stack: str or bool
+ :return: The elections2 application with all the relevant middleware
+ loaded.
+
+ This is the PasteDeploy factory for the elections2 application.
+
+ ``app_conf`` contains all the application-specific settings (those defined
+ under ``[app:main]``.
+
+
+ """
+ app = make_base_app(global_conf, full_stack=True, **app_conf)
+
+ # Wrap your base TurboGears 2 application with custom middleware here
+
+ return app
diff --git a/elections2/controllers/__init__.py b/elections2/controllers/__init__.py
new file mode 100644
index 0000000..80df58a
--- /dev/null
+++ b/elections2/controllers/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+"""Controllers for the elections2 application."""
diff --git a/elections2/controllers/controller.template b/elections2/controllers/controller.template
new file mode 100644
index 0000000..afbe3b9
--- /dev/null
+++ b/elections2/controllers/controller.template
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+"""Sample controller module"""
+
+# turbogears imports
+from tg import expose
+#from tg import redirect, validate, flash
+
+# third party imports
+#from pylons.i18n import ugettext as _
+#from repoze.what import predicates
+
+# project specific imports
+from elections2.lib.base import BaseController
+#from elections2.model import DBSession, metadata
+
+
+class SampleController(BaseController):
+ #Uncomment this line if your controller requires an authenticated user
+ #allow_only = authorize.not_anonymous()
+
+ @expose('elections2.templates.index')
+ def index(self):
+ return dict(page='index')
diff --git a/elections2/controllers/error.py b/elections2/controllers/error.py
new file mode 100644
index 0000000..8cf6074
--- /dev/null
+++ b/elections2/controllers/error.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+"""Error controller"""
+
+from tg import request, expose
+
+__all__ = ['ErrorController']
+
+
+class ErrorController(object):
+ """
+ Generates error documents as and when they are required.
+
+ The ErrorDocuments middleware forwards to ErrorController when error
+ related status codes are returned from the application.
+
+ This behaviour can be altered by changing the parameters to the
+ ErrorDocuments middleware in your config/middleware.py file.
+
+ """
+
+ @expose('elections2.templates.error')
+ def document(self, *args, **kwargs):
+ """Render the error document"""
+ resp = request.environ.get('pylons.original_response')
+ default_message = ("<p>We're sorry but we weren't able to process "
+ " this request.</p>")
+ values = dict(prefix=request.environ.get('SCRIPT_NAME', ''),
+ code=request.params.get('code', resp.status_int),
+ message=request.params.get('message', default_message))
+ return values
diff --git a/elections2/controllers/root.py b/elections2/controllers/root.py
new file mode 100644
index 0000000..7e84182
--- /dev/null
+++ b/elections2/controllers/root.py
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+"""Main Controller"""
+
+from tg import expose, flash, require, url, request, redirect
+from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from tgext.admin.tgadminconfig import TGAdminConfig
+from tgext.admin.controller import AdminController
+from repoze.what import predicates
+
+from elections2.lib.base import BaseController
+from elections2.model import DBSession, metadata
+from elections2.controllers.error import ErrorController
+from elections2 import model
+from elections2.controllers.secure import SecureController
+
+from fedora.client import AuthError, AppError
+from fedora.client.fas2 import AccountSystem
+
+from elections2 import model
+from elections2.model import *
+from elections2.lib.admin import Admin
+from elections2.lib.vote import Vote
+
+#import fedora.tg.tg2utils
+
+#from fedora.tg.controllers import login as fc_login
+#from fedora.tg.controllers import logout as fc_logout
+
+from datetime import datetime
+
+__all__ = ['RootController']
+
+
+class RootController(BaseController):
+ """
+ The root controller for the elections2 application.
+
+ All the other controllers and WSGI applications should be mounted on this
+ controller. For example::
+
+ panel = ControlPanelController()
+ another_app = AnotherWSGIApplication()
+
+ Keep in mind that WSGI applications shouldn't be mounted directly: They
+ must be wrapped around with :class:`tg.controllers.WSGIAppController`.
+
+ """
+ secc = SecureController()
+
+ admin = AdminController(model, DBSession, config_type=TGAdminConfig)
+
+ error = ErrorController()
+
+ appTitle = 'Fedora Elections'
+
+ baseURL = "https://admin.fedoraproject.org/accounts"
+ username = "elections"
+ password = "elections2"
+ fas = AccountSystem(baseURL, username=username, password=password)
+
+ admin = Admin(fas, appTitle)
+ vote = Vote(fas, appTitle)
+
+ request.identity = request.environ.get('repoze.who.identity')
+
+ @expose('elections2.templates.index')
+ def index(self):
+ """Handle the front-page."""
+ print "***************"
+ print request.identity
+ print "***************"
+ if request.identity:
+ print request.identity.keys()
+ print request.identity['username']
+ userid = request.identity['repoze.who.userid']
+ flash(_('Welcome back, %s!') % userid)
+
+ elections = model.DBSession.query(Elections).order_by(
+ Elections.start_date).filter('id>0').all()
+ past = []
+ current = []
+ future = []
+ now = datetime.utcnow()
+ for e in elections:
+ if e.start_date > now :
+ future.append(e)
+ elif e.end_date < now :
+ past.append(e)
+ else :
+ current.append(e)
+ return dict(past=past, current=current, future=future,
+ curtime=datetime.utcnow(), appTitle=self.appTitle)
+
+ @expose('elections2.templates.about')
+ def about(self, eid=None):
+ """Handle the 'about' page."""
+ try:
+ eid = int(eid)
+ election = model.DBSession.query(Elections
+ ).filter_by(id=eid).all()[0]
+ except ValueError:
+ try:
+ election = model.DBSession.query(Elections
+ ).filter_by(alias=eid).all()[0]
+ eid = election.id
+ except IndexError:
+ flash("This election does not exist, check" \
+ " if you have used the correct URL.")
+ raise redirect("/")
+ except (IndexError, TypeError):
+ flash("This election does not exist, check if" \
+ " you have used the correct URL.")
+ raise redirect("/")
+
+ candidates = model.DBSession.query(Candidates
+ ).filter_by(election_id=election.id).all()
+ votergroups = model.DBSession.query(LegalVoters
+ ).filter_by(election_id=election.id).all()
+ usernamemap = {}
+ groupnamemap = {}
+
+ if election.usefas:
+ for c in candidates:
+ try:
+ usernamemap[c.id] = \
+ self.fas.person_by_username(c.name)['human_name']
+ except (KeyError, AuthError):
+ # User has their name set to private or user doesn't exist
+ usernamemap[c.id] = c.name
+ for g in votergroups:
+ try:
+ groupnamemap[g.group_name] = g.group_name + " (" + \
+ self.fas.group_by_name(g.group_name)['display_name'] +")"
+ except (AppError, AuthError, KeyError) :
+ groupnamemap[g.group_name] = g.group_name
+
+ curtime = datetime.utcnow()
+
+ return dict(eid=eid, candidates=candidates,
+ usernamemap=usernamemap, election=election,
+ curtime=curtime, votergroups=votergroups,
+ appTitle=self.appTitle, groupnamemap=groupnamemap)
+
+ #(a)identity.require(identity.not_anonymous())
+ @expose(template="elections2.templates.verify")
+ def verify(self):
+ validvotes = {}
+ invalidvotes = {}
+ c = 0
+ allvotes = UserVoteCount.query.filter_by(voter=
+ turbogears.identity.current.user_name).all()
+ for v in allvotes:
+ if len(v.election.candidates) == v.novotes:
+ validvotes[c] = v
+ c=c+1
+ else:
+ invalidvotes[c] = v
+ c=c+1
+ return dict(validvotes=validvotes, invalidvotes=invalidvotes,
+ appTitle=self.appTitle)
+
+ #@expose(allow_json=True)
+ @expose()
+ def logout(self):
+ return fc_logout()
+
+ @expose('elections2.templates.login')
+ def login(self, came_from=url('/'), csrf_login = None):
+ """Start the user login."""
+ # code from elections v1
+ #login_dict = fc_login(forward_url, args, kwargs)
+ login_dict = {}
+ login_dict['appTitle'] = '%s -- Fedora Account System Login' % \
+ self.appTitle
+ login_dict['message'] = _("Please log-in")
+ login_dict['previous_url'] = came_from
+ login_dict['forward_url'] = "/post_login"
+ login_dict['original_parameters'] = request.params
+
+ login_counter = request.environ['repoze.who.logins']
+ if login_counter > 0:
+ flash(_('Wrong credentials'), 'warning')
+ if login_counter > 5:
+ flash(_('Too many wront attempt -- Blocked user'), 'warning')
+ login_dict['login_counter'] = str(login_counter)
+ login_dict['page'] = 'login'
+ login_dict['came_from'] = came_from
+ print csrf_login
+
+ return login_dict
+
+ ## Default code from TG2
+ #login_counter = request.environ['repoze.who.logins']
+ #return dict(page='login', login_counter=str(login_counter),
+ #came_from=came_from)
+
+ @expose(template="elections2.templates.results")
+ def results(self, eid=None):
+ try:
+ eid = int(eid)
+ election = model.DBSession.query(Elections).filter_by(id=eid).all()[0]
+ except ValueError:
+ try:
+ election = model.DBSession.query(Elections).filter_by(alias=eid).all()[0]
+ eid = election.id
+ except IndexError:
+ flash("This election does not exist, check"\
+ " if you have used the correct URL.")
+ raise redirect("/")
+ except (IndexError, TypeError):
+ flash("This election does not exist, check if"\
+ " you have used the correct URL.")
+ raise redirect("/")
+
+ usernamemap = {}
+
+ if election.usefas:
+ for c in election.candidates:
+ try:
+ usernamemap[c.id] = self.fas.person_by_username(
+ c.name)['human_name']
+ except (AuthError, KeyError) :
+ # User has their name set to private
+ usernamemap[c.id] = c.name
+
+ curtime = datetime.utcnow()
+ if election.end_date > curtime:
+ turbogears.flash("We are sorry, the results for this"\
+ " election cannot be viewed at this time because the"\
+ " election is still in progress.")
+ raise turbogears.redirect("/")
+ elif election.start_date > curtime:
+ turbogears.flash("We are sorry, the results for this election cannot be viewed at this time because the election has not started.")
+ raise turbogears.redirect("/")
+ elif election.embargoed == 1:
+ if identity.in_group('elections') :
+ pass
+ else :
+ match = 0
+ admingroups = model.DBSession.query(ElectionAdmins).filter_by(
+ election_id=eid).all()
+ for group in admingroups:
+ if identity.in_group(group.group_name):
+ match = 1
+ if match == 0:
+ turbogears.flash("Meep, We are sorry, the results"\
+ " for this election cannot be viewed because they"\
+ " are currently embargoed pending formal"
+ " announcement.")
+ raise turbogears.redirect("/")
+ votecount = model.DBSession.query(VoteTally).filter_by(election_id=eid
+ ).order_by(VoteTally.novotes.desc()).all()
+ return dict(votecount=votecount, usernamemap=usernamemap,
+ election=election, appTitle=self.appTitle)
+
+ @expose()
+ def post_login(self, came_from='/', *args, **kwargs):
+ """
+ Redirect the user to the initially requested page on successful
+ authentication or redirect her back to the login page if login failed.
+
+ """
+ ## default code from TG2:
+ print request.identity
+ if not request.identity:
+ login_counter = request.environ['repoze.who.logins'] + 1
+ redirect('/login', came_from=came_from, __logins=login_counter)
+ userid = request.identity['repoze.who.userid']
+ print request.identity.keys()
+ flash(_('Welcome back, %s!') % userid)
+ redirect(came_from)
+
+ @expose()
+ def post_logout(self, came_from='/'):
+ """
+ Redirect the user to the initially requested page on logout and say
+ goodbye as well.
+
+ """
+ flash(_('We hope to see you soon!'))
+ redirect(came_from)
diff --git a/elections2/controllers/secure.py b/elections2/controllers/secure.py
new file mode 100644
index 0000000..46d7654
--- /dev/null
+++ b/elections2/controllers/secure.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+"""Sample controller with all its actions protected."""
+from tg import expose, flash
+from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what.predicates import has_permission
+#from dbsprockets.dbmechanic.frameworks.tg2 import DBMechanic
+#from dbsprockets.saprovider import SAProvider
+
+from elections2.lib.base import BaseController
+#from elections2.model import DBSession, metadata
+
+__all__ = ['SecureController']
+
+
+class SecureController(BaseController):
+ """Sample controller-wide authorization"""
+
+ # The predicate that must be met for all the actions in this controller:
+ allow_only = has_permission('manage',
+ msg=l_('Only for people with the "manage" permission'))
+
+ @expose('elections2.templates.index')
+ def index(self):
+ """Let the user know that's visiting a protected controller."""
+ flash(_("Secure Controller here"))
+ return dict(page='index')
+
+ @expose('elections2.templates.index')
+ def some_where(self):
+ """Let the user know that this action is protected too."""
+ return dict(page='some_where')
diff --git a/elections2/controllers/template.py b/elections2/controllers/template.py
new file mode 100644
index 0000000..c22a566
--- /dev/null
+++ b/elections2/controllers/template.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""Fallback controller."""
+
+from elections2.lib.base import BaseController
+from tg import abort
+
+__all__ = ['TemplateController']
+
+
+class TemplateController(BaseController):
+ """
+ The fallback controller for elections2.
+
+ By default, the final controller tried to fulfill the request
+ when no other routes match. It may be used to display a template
+ when all else fails, e.g.::
+
+ def view(self, url):
+ return render('/%s' % url)
+
+ Or if you're using Mako and want to explicitly send a 404 (Not
+ Found) response code when the requested template doesn't exist::
+
+ import mako.exceptions
+
+ def view(self, url):
+ try:
+ return render('/%s' % url)
+ except mako.exceptions.TopLevelLookupException:
+ abort(404)
+
+ """
+
+ def view(self, url):
+ """Abort the request with a 404 HTTP status code."""
+ abort(404)
diff --git a/elections2/i18n/ru/LC_MESSAGES/elections2.po b/elections2/i18n/ru/LC_MESSAGES/elections2.po
new file mode 100644
index 0000000..36532d4
--- /dev/null
+++ b/elections2/i18n/ru/LC_MESSAGES/elections2.po
@@ -0,0 +1,24 @@
+# Russian translations for ${package}.
+# Copyright (C) 2008 ORGANIZATION
+# This file is distributed under the same license as the ${package} project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ${package} 0.0.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-01-13 14:00+0200\n"
+"PO-Revision-Date: 2008-01-13 14:00+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ru <LL(a)li.org>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.1\n"
+
+#: ${package}/controllers/root.py:13
+msgid "Your application is now running"
+msgstr "Ваши приложение успешно запущено"
+
diff --git a/elections2/lib/__init__.py b/elections2/lib/__init__.py
new file mode 100644
index 0000000..633f866
--- /dev/null
+++ b/elections2/lib/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+
diff --git a/elections2/lib/admin.py b/elections2/lib/admin.py
new file mode 100644
index 0000000..b049aa3
--- /dev/null
+++ b/elections2/lib/admin.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2008 Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano All rights reserved.
+#
+# 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. 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.
+#
+# Author(s): Nigel Jones <nigelj(a)fedoraproject.org>
+# Toshio Kuratomi <toshio(a)fedoraproject.org>
+# Ricky Zhou <ricky(a)fedoraproject.org>
+# Luca Foppiano <lfoppiano(a)fedoraproject.org>
+#
+# Report Bugs to https://www.fedorahosted.org/elections
+
+
+# Admin functions go here (i.e. add/delete elections)
+
+#import turbogears
+#from turbogears import controllers, expose, flash, redirect, config
+#from turbogears import identity
+from tg import expose, flash, require, url, request, redirect
+from elections2.lib.base import BaseController
+from fedora.client import AuthError, AppError
+from elections2 import model
+from elections2.model import Elections, Candidates, \
+ LegalVoters, ElectionAdmins
+
+from datetime import datetime
+
+import sqlalchemy, tg
+
+#from turbogears.database import session
+
+identity = request.environ.get('repoze.who.identity')
+
+class Admin(BaseController):
+ def __init__(self, fas, appTitle):
+ #print dir(fas), fas.username
+ self.fas = fas
+ self.appTitle = appTitle
+
+ @expose("json")
+ def list_elections(self, **kw):
+ electlist = model.DBSession.query(Elections).order_by(
+ Elections.start_date).filter('id>0').all()
+ elections = [{ 'id': e.id, 'alias': e.alias,
+ 'shortdesc': e.shortdesc,
+ 'start_date': e.start_date,
+ 'end_date': e.end_date,
+ 'legal_voters': [
+ {'groupname': lv.group_name} for lv in
+ model.DBSession.query(LegalVoters
+ ).filter_by(election_id=e.id)
+ ]} for e in electlist]
+ return dict(elections=elections, servertime=datetime.utcnow(),
+ appTitle=self.appTitle)
+
+ @expose(template='elections2.templates.admin')
+ def index(self, *kw, **args):
+ print "test"
+ electlist = model.DBSession.query(Elections).order_by(
+ Elections.start_date).filter('id>0').all()
+ elections = [{'id': e.id, 'alias': e.alias,
+ 'shortdesc': e.shortdesc,
+ 'start_date': e.start_date,
+ 'end_date': e.end_date,
+ 'legal_voters': [
+ {'groupname': lv.group_name} for lv in
+ model.DBSession.query(LegalVoters
+ ).filter_by(election_id=e.id)],
+ 'embargoed' : e.embargoed}
+ for e in electlist
+ ]
+ print "here"
+ return dict(elections=elections)
+
+ #print " -> ", request.environ.get("FAS_LOGIN_INFO")
+ #(a)identity.require(identity.in_group("elections"))
+ @expose(template="elections2.templates.admnewe")
+ def newe(self, **kw):
+ print "newe"
+ if "submit" in kw:
+ print kw
+ setembargo=1
+ usefas=1
+ if "embargoed" not in kw:
+ setembargo=0
+ if "usefas" not in kw:
+ usefas=0
+ if kw["seats"] == "":
+ flash("The number of seats given is incorrect.")
+ return dict()
+
+ election = Elections()
+ election.alias=kw['alias'] #status=0, method=0,
+ election.shortdesc=kw['shortdesc']
+ election.description=kw['info']
+ election.url=kw['url']
+ election.start_date=kw['startdate']
+ election.end_date=kw['enddate']
+ election.embargoed=setembargo
+ election.seats_elected=kw['seats']
+ election.usefas=usefas
+ election.votes_per_user=1
+ flash("New Election Created")
+ print dir(model.DBSession)
+ model.DBSession.add(election)
+ #model.DBSession.commit()
+ print "raise"
+ raise redirect("/admin/edit/"+str(election.alias))
+ else:
+ print "return"
+ return dict()
+
+ #(a)identity.require(identity.in_group("elections"))
+ @expose(template="elections2.templates.admnewc")
+ def newc(self, **kw):
+ if "submit" in kw:
+ for entry in kw['nameurl'].split("|"):
+ candidate = entry.split("!")
+ #Python doesn't have a good way of doing case/switch statements
+ if len(candidate) == 1:
+ Candidates(election_id=kw['id'], name=candidate[0],
+ status=0, human=1)
+ elif len(candidate) == 2:
+ Candidates(election_id=kw['id'], name=candidate[0],
+ url=candidate[1], status=0, human=1)
+ else:
+ flash("There was an issue!")
+ raise redirect("/admin/newc")
+ else:
+ return dict()
+
+ #(a)identity.require(identity.in_group("elections"))
+ @expose(template="elections2.templates.admedit")
+ def edit(self, eid=None, **kw):
+ print "**", eid, kw
+ if "submit" in kw:
+ for entry in kw['newgroups'].split("|"):
+ entry.strip()
+ if len(entry) :
+ lv = LegalVoters()
+ lv.election_id=kw['id']
+ lv.group_name=entry
+ model.DBSession.add(lv)
+ for entry in kw['newadmins'].split("|"):
+ entry.strip()
+ if len(entry) :
+ ea = ElectionAdmins()
+ ea.election_id=kw['id']
+ ea.group_name=entry
+ model.DBSession.add(ea)
+ for key, value in kw.items():
+ if key.startswith('remove_'):
+ group = key[len('remove_'):]
+ for lv in model.DBSession.query(LegalVoters).filter_by(
+ election_id=kw['id'],group_name=group) :
+ model.DBSession.delete(lv)
+ for key, value in kw.items():
+ if key.startswith('removeadmin_'):
+ group = key[len('removeadmin_'):]
+ for admin in model.DBSession.query(ElectionAdmins).filter_by(
+ election_id=kw['id'],group_name=group) :
+ model.DBSession.delete(admin)
+ for entry in kw['newcandidates'].split("|"):
+ candidate = entry.split("!")
+ #Python doesn't have a good way of doing case/switch statements
+ if len(candidate) == 1:
+ if len(candidate[0]) :
+ c=Candidates()
+ c.election_id=kw['id']
+ c.name=candidate[0]
+ c.status=0
+ c.human=1
+ model.DBSession.add(c)
+ elif len(candidate) == 2:
+ if len(candidate[0]) :
+ c = Candidates()
+ c.election_id=kw['id']
+ c.name=candidate[0],
+ c.url=candidate[1]
+ c.status=0
+ c.human=1
+ model.DBSession.add(c)
+ else:
+ flash("There was an issue!")
+ setembargo=1
+ usefas=1
+ nominations=1
+ if "embargoed" not in kw:
+ setembargo=0
+ if "usefas" not in kw:
+ usefas=0
+ if "allownominations" not in kw:
+ nominations=0
+ try:
+ election = model.DBSession.query(Elections).filter_by(
+ id=int(eid)).all()[0]
+ except ValueError:
+ election = model.DBSession.query(Elections).filter_by(
+ alias=eid).all()[0]
+ print kw.keys()
+ election.alias=kw['alias']
+ election.status=0
+ election.method=0
+ election.shortdesc=kw['shortdesc']
+ election.description=kw['info']
+ election.url=kw['url']
+ election.start_date=kw['startdate']
+ election.end_date=kw['enddate']
+ election.embargoed=setembargo
+ election.seats_elected=kw['seats']
+ election.usefas=usefas
+ election.votes_per_user=kw['votes']
+ election.allow_nominations=nominations
+ model.DBSession.add(election)
+ raise redirect("/admin/edit/"+kw['id'])
+
+ try:
+ eid = int(eid)
+ election = election = model.DBSession.query(Elections
+ ).filter_by(id=eid).all()[0]
+ except ValueError:
+ try:
+ election = election = model.DBSession.query(Elections
+ ).filter_by(alias=eid).all()[0]
+ eid = election.id
+ except IndexError:
+ flash("This election does not exist, check if you have used the correct URL.")
+ raise redirect("/admin/")
+ except (IndexError, TypeError):
+ flash("This election does not exist, check if you have used the correct URL.")
+ raise redirect("/admin/")
+
+ if "removeembargo" in kw:
+ election.embargoed=0
+ flash("Embargo on election results removed")
+ raise redirect("/admin/")
+
+ candidates = model.DBSession.query(Candidates
+ ).filter_by(election_id=election.id).all()
+ votergroups = model.DBSession.query(LegalVoters
+ ).filter_by(election_id=election.id).all()
+ admingroups = model.DBSession.query(ElectionAdmins
+ ).filter_by(election_id=election.id).all()
+ groupnamemap = {}
+ for g in admingroups:
+ try:
+ groupnamemap[g.group_name] = g.group_name + " ("
+ + self.fas.group_by_name(g.group_name)['display_name'] +")"
+ except (AppError, AuthError, KeyError) :
+ groupnamemap[g.group_name] = g.group_name
+ for g in votergroups:
+ try:
+ groupnamemap[g.group_name] = g.group_name + " ("
+ + self.fas.group_by_name(g.group_name)['display_name'] +")"
+ except (AppError, AuthError, KeyError) :
+ groupnamemap[g.group_name] = g.group_name
+
+ return dict(e=election, candidates=candidates,
+ admingroups=admingroups, groups=votergroups,
+ groupnamemap=groupnamemap)
diff --git a/elections2/lib/app_globals.py b/elections2/lib/app_globals.py
new file mode 100644
index 0000000..0bd3089
--- /dev/null
+++ b/elections2/lib/app_globals.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+"""The application's Globals object"""
+
+__all__ = ['Globals']
+
+
+class Globals(object):
+ """Container for objects available throughout the life of the application.
+
+ One instance of Globals is created during application initialization and
+ is available during requests via the 'app_globals' variable.
+
+ """
+
+ def __init__(self):
+ """Do nothing, by default."""
+ pass
diff --git a/elections2/lib/base.py b/elections2/lib/base.py
new file mode 100644
index 0000000..8ddcf23
--- /dev/null
+++ b/elections2/lib/base.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+"""The base Controller API."""
+
+from tg import TGController, tmpl_context
+from tg.render import render
+from tg import request
+from pylons.i18n import _, ungettext, N_
+from tw.api import WidgetBunch
+import elections2.model as model
+
+__all__ = ['BaseController']
+
+
+class BaseController(TGController):
+ """
+ Base class for the controllers in the application.
+
+ Your web application should have one of these. The root of
+ your application is used to compute URLs used by your app.
+
+ """
+
+ def __call__(self, environ, start_response):
+ """Invoke the Controller"""
+ # TGController.__call__ dispatches to the Controller method
+ # the request is routed to. This routing information is
+ # available in environ['pylons.routes_dict']
+
+ request.identity = request.environ.get('repoze.who.identity')
+ tmpl_context.identity = request.identity
+ return TGController.__call__(self, environ, start_response)
diff --git a/elections2/lib/helpers.py b/elections2/lib/helpers.py
new file mode 100644
index 0000000..8f35133
--- /dev/null
+++ b/elections2/lib/helpers.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+"""WebHelpers used in elections2."""
+
+from webhelpers import date, feedgenerator, html, number, misc, text
diff --git a/elections2/lib/vote.py b/elections2/lib/vote.py
new file mode 100644
index 0000000..3efe792
--- /dev/null
+++ b/elections2/lib/vote.py
@@ -0,0 +1,195 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2008 Nigel Jones, Toshio Kuratomi, Ricky Zhou, Luca Foppiano All rights reserved.
+#
+# 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. 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.
+#
+# Author(s): Nigel Jones <nigelj(a)fedoraproject.org>
+# Toshio Kuratomi <toshio(a)fedoraproject.org>
+# Ricky Zhou <ricky(a)fedoraproject.org>
+# Luca Foppiano <lfoppiano(a)fedoraproject.org>
+#
+# Report Bugs to https://www.fedorahosted.org/elections
+
+
+# Voting functions
+
+#import turbogears
+#from turbogears import controllers, expose, flash, redirect, config
+#from turbogears import identity
+from tg import expose, flash, require, url, request, redirect
+from elections2.lib.base import BaseController
+
+from elections2 import model
+from elections2.model import Elections, LegalVoters, Candidates, \
+ Votes, UserVoteCount
+
+import sqlalchemy
+
+#from turbogears.database import session
+
+from datetime import datetime
+import re
+
+class Vote(BaseController):
+ def __init__(self, fas, appTitle):
+ self.fas = fas
+ self.appTitle = appTitle
+
+ #TODO: This function will be split off into: default => submit =>
+ #confirm functions, hopefully it was simplify everything
+ #(a)identity.require(identity.not_anonymous())
+ @expose(template="elections2.templates.vote")
+ def default(self, eid=None, **kw):
+ print dir(Elections)
+ try:
+ eid = int(eid)
+ election = model.DBSession.query(Elections
+ ).filter_by(id=eid).all()[0]
+ except ValueError:
+ try:
+ election = model.DBSession.query(Elections
+ ).filter_by(alias=eid).all()[0]
+ eid = election.id
+ except IndexError:
+ flash("This election does not exist, check"\
+ " if you have used the correct URL.")
+ raise redirect("/")
+ except IndexError:
+ flash("This election does not exist, check if"\
+ " you have used the correct URL.")
+ raise redirect("/")
+
+ request.identity = request.environ.get('repoze.who.identity')
+ votergroups = model.DBSession.query(LegalVoters
+ ).filter_by(election_id=eid).all()
+ usergroups = request.identity["groups"]
+ print usergroups
+ print votergroups
+
+ match = 0
+ for group in votergroups:
+ if group.group_name == "anycla":
+ if (len(usergroups) > len([g for g in
+ usergroups if re.match("cla_.*",g)])
+ ):
+ match = 1
+ elif request.identity.in_group(group.group_name) or \
+ group.group_name == "anyany":
+ match = 1
+ if match == 0:
+ flash("You are not in a FAS group that can vote"\
+ " in this election, more information can be found at"\
+ " the bottom of this page.")
+ raise redirect("/about/" + str(eid))
+
+ candidates = model.DBSession.query(Candidates
+ ).filter_by(election_id=eid
+ ).order_by(Candidates.name).all()
+ uservote = model.DBSession.query(UserVoteCount
+ ).filter_by(election_id=eid,
+ voter=request.identity['username']).all()
+
+ usernamemap = {}
+
+ if election.usefas:
+ for c in candidates:
+ try:
+ usernamemap[c.id] = self.fas.person_by_username(
+ c.name)['human_name']
+ except KeyError:
+ # User has human_name set to private
+ usernamemap[c.id] = c.name
+
+ uvotes = {}
+ next_action = ""
+
+ curtime = datetime.utcnow()
+ if election.start_date > curtime:
+ flash("Voting has not yet started, sorry.")
+ raise redirect("/")
+ elif election.end_date < curtime:
+ flash("You cannot vote in this election because the end date has passed. You have been redirected to the election results")
+ raise redirect("/results/" + election.shortname)
+ elif len(uservote) != 0:
+ flash("You have already voted in this election!")
+ raise redirect("/")
+
+ # Lets do this in reverse order
+ if "confirm" in kw:
+ for c in candidates:
+ if str(c.id) in kw:
+ try:
+ range = int(kw[str(c.id)])
+ if range >= 0 and range <= len(candidates):
+ uvotes[c.id] = range
+ else:
+ flash("Invalid Ballot!")
+ raise redirect("/")
+ except ValueError:
+ flash("Invalid Ballot!")
+ raise redirect("/")
+ for uvote in uvotes:
+ v = Votes()
+ v.voter=request.identity['username']
+ v.candidate_id=uvote
+ v.weight=uvotes[uvote]
+ v.election_id=eid
+ v.timestamp=curtime
+ model.DBSession.add(v)
+ flash("Your vote has been recorded, thank you!")
+ raise redirect("/")
+ elif "vote" in kw:
+ flash("Please confirm your vote!")
+ for c in candidates:
+ if str(c.id) in kw:
+ try:
+ range = int(kw[str(c.id)])
+ if range > len(candidates):
+ flash("Invalid data was detected for one " \
+ "or more candidates and was changed to " \
+ "zeros! Please correct and resubmit your "\
+ "ballot.")
+ uvotes[c.id] = 0
+ next_action = "vote"
+ elif range >= 0:
+ uvotes[c.id] = range
+ else:
+ flash("Invalid data was " \
+ "detected for one or more candidates and" \
+ "was changed to zeros! Please correct " \
+ "and resubmit your ballot.")
+ uvotes[c.id] = 0
+ next_action = "vote"
+ except ValueError:
+ flash("Invalid data was detected for one or " \
+ "more candidates and was changed to zeros! " \
+ "Please correct and resubmit your ballot.")
+ uvotes[c.id] = 0
+ next_action = "vote"
+ else:
+ flash("Invalid Ballot!")
+ raise redirect("/")
+ if next_action != "vote":
+ next_action = "confirm"
+ else:
+ for c in candidates:
+ uvotes[c.id] = ""
+ next_action = "vote"
+
+ return dict(eid=eid, candidates=candidates,
+ usernamemap=usernamemap, election=election,
+ nextaction=next_action, voteinfo=uvotes,
+ appTitle=self.appTitle)
+
diff --git a/elections2/model/__init__.py b/elections2/model/__init__.py
new file mode 100644
index 0000000..c6df12a
--- /dev/null
+++ b/elections2/model/__init__.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+"""The application's model objects"""
+
+from zope.sqlalchemy import ZopeTransactionExtension
+from sqlalchemy.orm import scoped_session, sessionmaker, mapper, relation
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import Unicode, Integer, DateTime, String
+#from sqlalchemy import MetaData
+from sqlalchemy.ext.declarative import declarative_base
+
+from fedora.tg.json import SABase
+
+# Global session manager: DBSession() returns the Thread-local
+# session object appropriate for the current web request.
+maker = sessionmaker(autoflush=True, autocommit=False,
+ extension=ZopeTransactionExtension())
+DBSession = scoped_session(maker)
+
+# Base class for all of our model classes: By default, the data model is
+# defined with SQLAlchemy's declarative extension, but if you need more
+# control, you can switch to the traditional method.
+DeclarativeBase = declarative_base()
+
+# There are two convenient ways for you to spare some typing.
+# You can have a query property on all your model classes by doing this:
+# DeclarativeBase.query = DBSession.query_property()
+# Or you can use a session-aware mapper as it was used in TurboGears 1:
+# DeclarativeBase = declarative_base(mapper=DBSession.mapper)
+
+# Global metadata.
+# The default metadata is the one from the declarative base.
+metadata = DeclarativeBase.metadata
+
+# If you have multiple databases with overlapping table names, you'll need a
+# metadata for each database. Feel free to rename 'metadata2'.
+#metadata2 = MetaData()
+
+#####
+# Generally you will not want to define your table's mappers, and data objects
+# here in __init__ but will want to create modules them in the model directory
+# and import them at the bottom of this file.
+#
+######
+
+def init_model(engine):
+ """Call me before using any of the tables or classes in the model."""
+
+ DBSession.configure(bind=engine)
+ metadata.bind = engine
+ # If you are using reflection to introspect your database and create
+ # table objects for you, your tables must be defined and mapped inside
+ # the init_model function, so that the engine is available if you
+ # use the model outside tg2, you need to make sure this is called before
+ # you use the model.
+
+ #
+ # See the following example:
+
+ #global t_reflected
+
+ #t_reflected = Table("Reflected", metadata,
+ # autoload=True, autoload_with=engine)
+ ElectionsTable = Table('elections', metadata, autoload=True)
+ VotesTable = Table('votes', metadata, autoload=True)
+ CandidatesTable = Table('candidates', metadata, autoload=True)
+ LegalVotersTable = Table('legalvoters', metadata, autoload=True)
+ ElectionAdminsTable = Table('electionadmins', metadata, autoload=True)
+
+ # View in the DB. Needs to have the column keys defined
+ VoteTallyTable = Table('fvotecount', metadata,
+ Column('id', Integer,
+ ForeignKey('candidates.id'), primary_key=True),
+ Column('election_id', Integer),
+ Column('name', String, nullable=False),
+ Column('novotes', Integer, nullable=False)
+ )
+ UserVoteCountTable = Table('uservotes', metadata,
+ Column('election_id', Integer, ForeignKey('elections.id'), primary_key=True),
+ Column('voter', String, nullable=False, primary_key=True),
+ Column('novotes', Integer, nullable=False)
+ )
+
+ #mapper(Reflected, t_reflected)
+ mapper(Elections, ElectionsTable, properties = {
+ 'legalVoters': relation(LegalVoters, backref='election'),
+ 'candidates': relation(Candidates, backref='election'),
+ 'uservotes': relation(UserVoteCount, backref='election')
+ })
+ mapper(Votes, VotesTable)
+ mapper(Candidates, CandidatesTable, properties = {
+ 'votes': relation(Votes, backref='candidate'),
+ 'tally': relation(VoteTally, backref='candidate')
+ })
+ mapper(LegalVoters, LegalVotersTable)
+ mapper(ElectionAdmins, ElectionAdminsTable)
+ mapper(VoteTally, VoteTallyTable)
+ mapper(UserVoteCount, UserVoteCountTable)
+
+# Import your model modules here.
+from elections2.model.auth import User, Group, Permission
+
+#
+# Classes to map to
+#
+
+class Elections(SABase):
+ pass
+
+class Votes(SABase):
+ pass
+
+class Candidates(SABase):
+ pass
+
+class LegalVoters(SABase):
+ pass
+
+class ElectionAdmins(SABase):
+ pass
+
+class VoteTally(SABase):
+ pass
+
+class UserVoteCount(SABase):
+ pass
diff --git a/elections2/model/auth.py b/elections2/model/auth.py
new file mode 100644
index 0000000..65a8cbd
--- /dev/null
+++ b/elections2/model/auth.py
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+"""
+Auth* related model.
+
+This is where the models used by :mod:`repoze.who` and :mod:`repoze.what` are
+defined.
+
+It's perfectly fine to re-use this definition in the elections2 application,
+though.
+
+"""
+import os
+from datetime import datetime
+import sys
+try:
+ from hashlib import sha1
+except ImportError:
+ sys.exit('ImportError: No module named hashlib\n'
+ 'If you are on python2.4 this library is not part of python. '
+ 'Please install it. Example: easy_install hashlib')
+
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import Unicode, Integer, DateTime
+from sqlalchemy.orm import relation, synonym
+
+from elections2.model import DeclarativeBase, metadata, DBSession
+
+__all__ = ['User', 'Group', 'Permission']
+
+
+#{ Association tables
+
+
+# This is the association table for the many-to-many relationship between
+# groups and permissions. This is required by repoze.what.
+group_permission_table = Table('tg_group_permission', metadata,
+ Column('group_id', Integer, ForeignKey('tg_group.group_id',
+ onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
+ Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
+ onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)
+)
+
+# This is the association table for the many-to-many relationship between
+# groups and members - this is, the memberships. It's required by repoze.what.
+user_group_table = Table('tg_user_group', metadata,
+ Column('user_id', Integer, ForeignKey('tg_user.user_id',
+ onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
+ Column('group_id', Integer, ForeignKey('tg_group.group_id',
+ onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)
+)
+
+
+#{ The auth* model itself
+
+
+class Group(DeclarativeBase):
+ """
+ Group definition for :mod:`repoze.what`.
+
+ Only the ``group_name`` column is required by :mod:`repoze.what`.
+
+ """
+
+ __tablename__ = 'tg_group'
+
+ #{ Columns
+
+ group_id = Column(Integer, autoincrement=True, primary_key=True)
+
+ group_name = Column(Unicode(16), unique=True, nullable=False)
+
+ display_name = Column(Unicode(255))
+
+ created = Column(DateTime, default=datetime.now)
+
+ #{ Relations
+
+ users = relation('User', secondary=user_group_table, backref='groups')
+
+ #{ Special methods
+
+ def __repr__(self):
+ return '<Group: name=%r>' % self.group_name
+
+ def __unicode__(self):
+ return self.group_name
+
+ #}
+
+
+# The 'info' argument we're passing to the email_address and password columns
+# contain metadata that Rum (http://python-rum.org/) can use generate an
+# admin interface for your models.
+class User(DeclarativeBase):
+ """
+ User definition.
+
+ This is the user definition used by :mod:`repoze.who`, which requires at
+ least the ``user_name`` column.
+
+ """
+ __tablename__ = 'tg_user'
+
+ #{ Columns
+
+ user_id = Column(Integer, autoincrement=True, primary_key=True)
+
+ user_name = Column(Unicode(16), unique=True, nullable=False)
+
+ email_address = Column(Unicode(255), unique=True, nullable=False,
+ info={'rum': {'field':'Email'}})
+
+ display_name = Column(Unicode(255))
+
+ _password = Column('password', Unicode(80),
+ info={'rum': {'field':'Password'}})
+
+ created = Column(DateTime, default=datetime.now)
+
+ #{ Special methods
+
+ def __repr__(self):
+ return '<User: name=%r, email=%r, display=%r>' % (
+ self.user_name, self.email_address, self.display_name)
+
+ def __unicode__(self):
+ return self.display_name or self.user_name
+
+ #{ Getters and setters
+
+ @property
+ def permissions(self):
+ """Return a set with all permissions granted to the user."""
+ perms = set()
+ for g in self.groups:
+ perms = perms | set(g.permissions)
+ return perms
+
+ @classmethod
+ def by_email_address(cls, email):
+ """Return the user object whose email address is ``email``."""
+ return DBSession.query(cls).filter(cls.email_address == email).first()
+
+ @classmethod
+ def by_user_name(cls, username):
+ """Return the user object whose user name is ``username``."""
+ return DBSession.query(cls).filter(cls.user_name == username).first()
+
+ def _set_password(self, password):
+ """Hash ``password`` on the fly and store its hashed version."""
+ # Make sure password is a str because we cannot hash unicode objects
+ if isinstance(password, unicode):
+ password = password.encode('utf-8')
+ salt = sha1()
+ salt.update(os.urandom(60))
+ hash = sha1()
+ hash.update(password + salt.hexdigest())
+ password = salt.hexdigest() + hash.hexdigest()
+ # Make sure the hashed password is a unicode object at the end of the
+ # process because SQLAlchemy _wants_ unicode objects for Unicode cols
+ if not isinstance(password, unicode):
+ password = password.decode('utf-8')
+ self._password = password
+
+ def _get_password(self):
+ """Return the hashed version of the password."""
+ return self._password
+
+ password = synonym('_password', descriptor=property(_get_password,
+ _set_password))
+
+ #}
+
+ def validate_password(self, password):
+ """
+ Check the password against existing credentials.
+
+ :param password: the password that was provided by the user to
+ try and authenticate. This is the clear text version that we will
+ need to match against the hashed one in the database.
+ :type password: unicode object.
+ :return: Whether the password is valid.
+ :rtype: bool
+
+ """
+ hash = sha1()
+ if isinstance(password, unicode):
+ password = password.encode('utf-8')
+ hash.update(password + str(self.password[:40]))
+ return self.password[40:] == hash.hexdigest()
+
+
+class Permission(DeclarativeBase):
+ """
+ Permission definition for :mod:`repoze.what`.
+
+ Only the ``permission_name`` column is required by :mod:`repoze.what`.
+
+ """
+
+ __tablename__ = 'tg_permission'
+
+ #{ Columns
+
+ permission_id = Column(Integer, autoincrement=True, primary_key=True)
+
+ permission_name = Column(Unicode(63), unique=True, nullable=False)
+
+ description = Column(Unicode(255))
+
+ #{ Relations
+
+ groups = relation(Group, secondary=group_permission_table,
+ backref='permissions')
+
+ #{ Special methods
+
+ def __repr__(self):
+ return '<Permission: name=%r>' % self.permission_name
+
+ def __unicode__(self):
+ return self.permission_name
+
+ #}
+
+
+#}
diff --git a/elections2/model/model.template b/elections2/model/model.template
new file mode 100644
index 0000000..c1eebc4
--- /dev/null
+++ b/elections2/model/model.template
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+"""Sample model module."""
+
+from sqlalchemy import *
+from sqlalchemy.orm import mapper, relation
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import Integer, Unicode
+#from sqlalchemy.orm import relation, backref
+
+from elections2.model import DeclarativeBase, metadata, DBSession
+
+
+class SampleModel(DeclarativeBase):
+ __tablename__ = 'sample_model'
+
+ #{ Columns
+
+ id = Column(Integer, primary_key=True)
+
+ data = Column(Unicode(255), nullable=False)
+
+ #}
diff --git a/elections2/public/css/style.css b/elections2/public/css/style.css
new file mode 100644
index 0000000..fe03cd9
--- /dev/null
+++ b/elections2/public/css/style.css
@@ -0,0 +1,57 @@
+.flash
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #000000;
+ background: #EEFFBB;
+}
+
+.vthot
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #FFFFFF;
+ background: #9C0000;
+}
+.vtwarm
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #000000;
+ background: #FFA300;
+}
+.vtmedwarm
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #000000;
+ background: #93FF63;
+}
+.vtmedcool
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #000000;
+ background: #00DAFE;
+}
+.vtcool
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #000000;
+ background: #0033FF;
+}
+.vtcold
+{
+ border: 1px solid #DDDDDD;
+ padding: 1.5ex;
+ margin: 1ex 0;
+ color: #FFFFFF;
+ background: #000083;
+}
diff --git a/elections2/public/favicon.ico b/elections2/public/favicon.ico
new file mode 100644
index 0000000..840986e
Binary files /dev/null and b/elections2/public/favicon.ico differ
diff --git a/elections2/public/images/favicon.ico b/elections2/public/images/favicon.ico
new file mode 100644
index 0000000..332557b
Binary files /dev/null and b/elections2/public/images/favicon.ico differ
diff --git a/elections2/public/images/header_inner.png b/elections2/public/images/header_inner.png
new file mode 100644
index 0000000..2b2d87d
Binary files /dev/null and b/elections2/public/images/header_inner.png differ
diff --git a/elections2/public/images/info.png b/elections2/public/images/info.png
new file mode 100644
index 0000000..329c523
Binary files /dev/null and b/elections2/public/images/info.png differ
diff --git a/elections2/public/images/ok.png b/elections2/public/images/ok.png
new file mode 100644
index 0000000..fee6751
Binary files /dev/null and b/elections2/public/images/ok.png differ
diff --git a/elections2/public/images/tg_under_the_hood.png b/elections2/public/images/tg_under_the_hood.png
new file mode 100644
index 0000000..bc9c79c
Binary files /dev/null and b/elections2/public/images/tg_under_the_hood.png differ
diff --git a/elections2/public/images/under_the_hood_blue.png b/elections2/public/images/under_the_hood_blue.png
new file mode 100644
index 0000000..90e84b7
Binary files /dev/null and b/elections2/public/images/under_the_hood_blue.png differ
diff --git a/elections2/templates/__init__.py b/elections2/templates/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/elections2/templates/about.html b/elections2/templates/about.html
new file mode 100644
index 0000000..4994709
--- /dev/null
+++ b/elections2/templates/about.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections -- Election Information</title>
+ </head>
+ <body>
+ <h1>${election.shortdesc}</h1>
+ <p>${election.description}</p>
+ <p py:if="election.url"><a href="${election.url}">[More Information]</a></p>
+ <table border="1" cellpadding="1">
+ <tr py:for="candidate in candidates">
+ <td><py:choose test="election.usefas"><py:when test="0">${candidate.name}</py:when><py:when test="1">${usernamemap[candidate.id]}</py:when></py:choose> <small py:if="candidate.url"><a href="${candidate.url}">[info]</a></small></td>
+ </tr>
+ <py:choose test="curtime > election.end_date"><py:when test="0">
+ <py:choose test="election.start_date > curtime"><py:when test="0">
+ <tr>
+ <td>
+ <py:choose test="1">
+ <py:when test="election.alias == ''">
+ <a href="${tg.url('/vote/' + str(election.id))}">Vote Now!</a>
+ </py:when>
+ <py:otherwise>
+ <a href="${tg.url('/vote/' + str(election.alias))}">Vote Now!</a>
+ </py:otherwise>
+ </py:choose>
+ </td>
+ </tr>
+ </py:when></py:choose>
+ </py:when><py:when test="1">
+ <tr>
+ <py:choose test="1">
+ <py:when test="election.alias == ''">
+ <a href="${tg.url('/results/' + str(election.id))}">Show Results!</a>
+ </py:when>
+ <py:otherwise>
+ <a href="${tg.url('/results/' + str(election.alias))}">Show Results!</a>
+ </py:otherwise>
+ </py:choose>
+ </tr>
+ </py:when></py:choose>
+ </table>
+ <p>To vote in this election you must be a member of any one of the following groups:</p>
+ <ul>
+ <li py:for="g in votergroups"><py:choose test="g.group_name == 'anyany'"><py:when test="0"><py:choose test="g.group_name == 'anycla'"><py:when test="0">${groupnamemap[g.group_name]}</py:when><py:when test="1">Any non-CLA Group</py:when></py:choose></py:when><py:when test="1">Any Group</py:when></py:choose></li>
+ </ul>
+ </body>
+</html>
diff --git a/elections2/templates/admedit.html b/elections2/templates/admedit.html
new file mode 100644
index 0000000..3e179a8
--- /dev/null
+++ b/elections2/templates/admedit.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections -- Administration -- Election Details</title>
+ </head>
+ <body>
+ <form action="${tg.url('/admin/edit/' + str(e.id))}" method="post">
+ <table>
+ <tr>
+ <th colspan="2"> Election Details - ${e.shortdesc} </th>
+ </tr>
+ <tr><td>Election ID:</td><td>${e.id}<input type="hidden" name="id" value="${e.id}" /></td></tr>
+ <tr><td>Election Name:</td><td><input type="text" name="shortdesc" value="${e.shortdesc}" /></td></tr>
+ <tr><td>Alias:</td><td><input type="text" name="alias" value="${e.alias}" /></td></tr>
+ <tr><td>URL:</td><td><input type="text" name="url" value="${e.url}" /></td></tr>
+ <tr><td>Election Info:</td><td><textarea name="info" rows="5" cols="40">${e.description}</textarea></td></tr>
+ <tr><td>Start Date:</td><td><input type="text" name="startdate" value="${e.start_date}" /></td></tr>
+ <tr><td>End Date:</td><td><input type="text" name="enddate" value="${e.end_date}" /></td></tr>
+ <tr><td>Seats Elected:</td><td><input type="text" name="seats" value="${e.seats_elected}" /></td></tr>
+ <tr><td>Votes Per User:</td><td><input type="text" name="votes" value="${e.votes_per_user}" /></td></tr>
+<!--
+ <tr><td>Allow Nominations?</td><td><input type="checkbox" name="allownominations" py:attrs="{'checked':tg.checker(e.allow_nominations)}" /> </td></tr>
+-->
+ <tr><td>Until When?</td><td><input type="text" name="nominationend" value="" /> </td></tr>
+<!--
+ <tr><td>Use FAS?</td><td><input type="checkbox" name="usefas" py:attrs="{'checked':tg.checker(e.usefas)}" /> </td></tr>
+-->
+<!--
+ <tr><td>Embargoed?</td><td><input type="checkbox" name="embargoed" py:attrs="{'checked':tg.checker(e.embargoed)}" /> </td></tr>
+-->
+ <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
+
+ <tr>
+ <th colspan="2"> Allowed Groups </th>
+ </tr>
+ <tr py:for="g in groups">
+ <td>
+ <py:choose test="g.group_name == 'anyany'"><py:when test="0"><py:choose test="g.group_name == 'anycla'"><py:when test="0">${groupnamemap[g.group_name]}</py:when><py:when test="1">Any non-CLA Group</py:when></py:choose></py:when><py:when test="1">Any Group</py:when></py:choose>
+ </td>
+ <td>Remove group: <input type="checkbox" name="remove_${g.group_name}" /> </td>
+ </tr>
+ <tr><td>New Groups:<br/>( entries seperated by | )</td><td><input type="text" name="newgroups" value="" /> </td></tr>
+ <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
+
+ <tr>
+ <th colspan="2"> Admin Groups </th>
+ </tr>
+ <tr py:for="g in admingroups">
+ <td> ${groupnamemap[g.group_name]} </td>
+ <td>Remove group: <input type="checkbox" name="removeadmin_${g.group_name}" /> </td>
+ </tr>
+ <tr><td>New Groups:<br/>( entries seperated by | )</td><td><input type="text" name="newadmins" value="" /> </td></tr>
+ <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
+
+ <tr>
+ <th colspan="2"> Candidates </th>
+ </tr>
+ <tr py:for="c in candidates">
+ <td>
+ ${c.name}
+ </td>
+ <td>
+ ${c.url}
+ </td>
+ </tr>
+ <tr><td>New Candidates:<br/>( Formated as [URL optional]: Name1!URL1|Name2!URL2 )</td><td><input type="text" name="newcandidates" value="" /> </td></tr>
+ <tr><td></td><td><input type="submit" name="submit" value="Submit"/></td></tr>
+ </table>
+ </form>
+ </body>
+</html>
diff --git a/elections2/templates/admin.html b/elections2/templates/admin.html
new file mode 100644
index 0000000..1bff42e
--- /dev/null
+++ b/elections2/templates/admin.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections -- Administration</title>
+ </head>
+ <body>
+ <table>
+ <tr>
+ <th>ID</th>
+ <th>Name</th>
+ <th>Alias</th>
+ <th>Start Date</th>
+ <th>End Date</th>
+ <th></th>
+ </tr>
+ <tr py:for="election in elections">
+ <td>${election.id}</td>
+ <td>${election.shortdesc}</td>
+ <td>${election.alias}</td>
+ <td>${election.start_date}</td>
+ <td>${election.end_date}</td>
+ <td>
+ <a href="${tg.url('/admin/edit/' + str(election.id))}">More Information</a>
+ <py:if test="election.embargoed">
+ |
+ <a href="${tg.url('/admin/edit/' + str(election.id) + '?removeembargo=1')}">Remove Embargo</a>
+ </py:if>
+ </td>
+ </tr>
+ </table>
+ <p><a href="${tg.url('/admin/newe/')}">Create a new election</a></p>
+
+ </body>
+</html>
diff --git a/elections2/templates/admnewc.html b/elections2/templates/admnewc.html
new file mode 100644
index 0000000..a1393f6
--- /dev/null
+++ b/elections2/templates/admnewc.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections -- Administration -- New Candidates</title>
+ </head>
+ <body>
+ <form action="${tg.url('/admin/newc')}" method="post">
+ <table>
+ <tr><td>Election Id (Numerical):</td><td><input type="text" name="id" /></td></tr>
+ <tr><td>The string below needs to be formatted as: "Name1!URL1 (opt)|Name2!URL2 (opt)|Name3!URL3 (opt)"</td></tr>
+ <tr><td>Candidate Name/URL String:</td><td><input type="text" name="nameurl" /></td></tr>
+ <tr><td></td><td><input type="submit" name="submit" value="Submit" /></td></tr>
+ </table>
+ </form>
+ </body>
+</html>
diff --git a/elections2/templates/admnewe.html b/elections2/templates/admnewe.html
new file mode 100644
index 0000000..2fe3b1e
--- /dev/null
+++ b/elections2/templates/admnewe.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections -- Administration -- New Election</title>
+ </head>
+ <body>
+ <form action="${tg.url('/admin/newe')}" method="post">
+ <table>
+ <tr><td>Election Name:</td><td><input type="text" name="shortdesc" /></td></tr>
+ <tr><td>Alias:</td><td><input type="text" name="alias" /></td></tr>
+ <tr><td>Election Info:</td><td><textarea name="info" rows="5" cols="40"></textarea></td></tr>
+ <tr><td>URL:</td><td><input type="text" name="url" /></td></tr>
+ <tr><td>Start Date:</td><td><input type="text" name="startdate" value="yyyy-mm-dd hh:mm:ss"/></td></tr>
+ <tr><td>End Date:</td><td><input type="text" name="enddate" value="yyyy-mm-dd hh:mm:ss"/></td></tr>
+ <tr><td>Seats Elected:</td><td><input type="text" name="seats"/></td></tr>
+ <tr><td>Embargoed?</td><td><input type="checkbox" name="embargoed" /> </td></tr>
+ <tr><td>Allow Nominations?</td><td><input type="checkbox" name="allownominations" /> </td></tr>
+ <tr><td>Until When?</td><td><input type="text" name="nominationend" /> </td></tr>
+ <tr><td>Use FAS?</td><td><input type="checkbox" name="usefas" /> </td></tr>
+ <tr><td></td><td><input type="submit" name="submit" value="Submit" /></td></tr>
+ </table>
+ </form>
+ </body>
+</html>
diff --git a/elections2/templates/error.html b/elections2/templates/error.html
new file mode 100644
index 0000000..161ad82
--- /dev/null
+++ b/elections2/templates/error.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <xi:include href="master.html" />
+
+<head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title>A ${code} Error has Occurred </title>
+</head>
+
+<body>
+<h1>Error ${code}</h1>
+
+<div>${XML(message)}</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/elections2/templates/fedora_login.html b/elections2/templates/fedora_login.html
new file mode 100644
index 0000000..d04da12
--- /dev/null
+++ b/elections2/templates/fedora_login.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ py:strip="True">
+
+<py:match path="loginform" once="true">
+ <div class="panel">
+ <div id="login-box" class="login">
+ <h3>${_('Log In')}</h3>
+ <p py:content="str(select('*|text()'))" />
+ <form action="${tg.url('/login_handler',
+ params=dict(came_from=came_from.encode('utf-8'),
+ __logins=login_counter.encode('utf-8')))}"
+ method="post" accept-charset="UTF-8"
+ class="loginfields">
+ <div class="field"><label for="login">${_('User Name:')}</label>
+ <input type="text" id="login" name="login" />
+ </div>
+ <div class="field"><label for="password">${_('Password:')}</label>
+ <input type="password" id="password" name="password" />
+ </div>
+ <div class="field">
+ <input type="submit" name="login_b" class="button"
+ value="${_('Login')}" />
+ <hiddenvals />
+ </div>
+ </form>
+ <form py:if="tg.identity"
+ action="${tg.url(previous_url)}" method="post">
+ <p><a href="http://en.wikipedia.org/wiki/CSRF">${_('CSRF attacks')}</a>
+ ${_(''' are a means for a malicious website to make a request of another
+ web server as the user who contacted the malicious web site. The
+ purpose of this page is to help protect your account and this server
+ from attacks from such malicious web sites. By clicking below, you are
+ proving that you are a person rather than just the web browser
+ forwarding your authentication cookies on behalf of a malicious
+ website.''')}</p>
+ <input type="submit" name="csrf_login" class="button"
+ value="${_('I am a human')}" />
+ <hiddenvals />
+ </form>
+ <ul>
+ <li><a href="https://admin.fedoraproject.org/accounts/user/resetpass">${_('Forgot Password?')}</a></li>
+ <li><a href="https://admin.fedoraproject.org/accounts/user/new">${_('Sign Up')}</a></li>
+ </ul>
+ </div>
+ </div>
+</py:match>
+
+<py:match path="hiddenvals" once="true">
+ <input py:if="forward_url" type="hidden" name="forward_url"
+ value="${forward_url}" />
+ <div py:for="name,values in original_parameters.items()" py:strip="1">
+ <input py:for="value in isinstance(values, list) and values or [values]"
+ py:if="name != '_csrf_token'" type="hidden" name="${name}"
+ value="${value}"/>
+ </div>
+</py:match>
+
+<py:match path="logintoolitem" once="true">
+ <li py:if="request.identity" class="toolitem">
+ ${_('Welcome')}
+ <div py:choose="str(select('@href'))" py:strip="True">
+ <span py:when="''" py:choose="" py:strip="True">
+ <span py:when="hasattr(request.identity.user, 'display_name')"
+ py:replace="request.identity.user.display_name" />
+ <span py:when="hasattr(tg.identity.user, 'human_name')"
+ py:replace="request.identity.user.human_name" />
+ <span py:otherwise=""
+ py:replace="request.identity.user_name" />
+ </span>
+ <a py:otherwise="" href="${select('@href')}" py:choose=""
+ py:strip="True">
+ <span py:when="hasattr(request.identity , 'display_name')"
+ py:replace="request.identity.user.display_name" />
+ <span py:when="hasattr(request.identity, 'human_name')"
+ py:replace="request.identity.user.human_name" />
+ <span py:otherwise=""
+ py:replace="request.identity.userid" />
+ </a>
+ </div>
+ </li>
+ <li py:if="not request.identity"
+ id="login-toolitem" class="toolitem">
+ ${_('You are not logged in')}
+ <form action="${tg.url('/login')}" method="GET">
+ <input type="submit" value="${_('Login')}" class="button" />
+ </form>
+ </li>
+ <li py:if="not request.identity"
+ id="login-toolitem" class="toolitem">
+ ${_('CSRF protected')}
+ <form action="${tg.url(tg.request.path_info)}" method="POST">
+ <input type="submit" name="csrf_login" value="${_('Verify Login')}" class="button" />
+ </form>
+ </li>
+ <li py:if="not request.identity"
+ id="login-toolitem" class="toolitem">
+ <form action="${tg.url('/logout')}" method="GET">
+ <input type="submit" value="${_('Logout')}" class="button" />
+ </form>
+ </li>
+</py:match>
+</html>
diff --git a/elections2/templates/index.html b/elections2/templates/index.html
new file mode 100644
index 0000000..09176d5
--- /dev/null
+++ b/elections2/templates/index.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections</title>
+ </head>
+ <body>
+
+ <py:if test="len(current)">
+ <h2>Current Elections</h2>
+ <table>
+ <tr>
+ <th></th>
+ <th>Start Date (UTC)</th>
+ <th>End Date (UTC)</th>
+ <th></th>
+ </tr>
+ <tr py:for="election in current">
+ <td>${election.shortdesc}</td>
+ <td>${election.start_date}</td>
+ <td>${election.end_date}</td>
+ <td>
+ <py:choose test="1">
+ <py:when test="election.alias == ''">
+ <a href="${tg.url('/about/' + str(election.id))}">More Information</a>
+ </py:when>
+ <py:otherwise>
+ <a href="${tg.url('/about/' + str(election.alias))}">More Information</a>
+ </py:otherwise>
+ </py:choose>
+ </td>
+ </tr>
+ </table>
+ </py:if>
+
+ <py:if test="len(future)">
+ <h2>Elections Coming soon...</h2>
+ <table>
+ <tr>
+ <th></th>
+ <th>Start Date (UTC)</th>
+ <th>End Date (UTC)</th>
+ <th></th>
+ </tr>
+ <tr py:for="election in future">
+ <td>${election.shortdesc}</td>
+ <td>${election.start_date}</td>
+ <td>${election.end_date}</td>
+ <td>
+ <py:choose test="1">
+ <py:when test="election.alias == ''">
+ <a href="${tg.url('/about/' + str(election.id))}">More Information</a>
+ </py:when>
+ <py:otherwise>
+ <a href="${tg.url('/about/' + str(election.alias))}">More Information</a>
+ </py:otherwise>
+ </py:choose>
+ </td>
+ </tr>
+ </table>
+ </py:if>
+
+ <py:if test="len(past)">
+ <h2>Past Elections</h2>
+ <table>
+ <tr>
+ <th></th>
+ <th>Start Date (UTC)</th>
+ <th>End Date (UTC)</th>
+ <th></th>
+ <th></th>
+ </tr>
+ <tr py:for="election in past">
+ <td>${election.shortdesc}</td>
+ <td>${election.start_date}</td>
+ <td>${election.end_date}</td>
+ <td>
+ <py:choose test="1">
+ <py:when test="election.alias == ''">
+ <a href="${tg.url('/about/' + str(election.id))}">More Information</a>
+ </py:when>
+ <py:otherwise>
+ <a href="${tg.url('/about/' + str(election.alias))}">More Information</a>
+ </py:otherwise>
+ </py:choose>
+ </td>
+ <td>
+ <py:choose test="1">
+ <py:when test="0 == election.embargoed">
+ <py:choose test="1">
+ <py:when test="election.alias == ''">
+ <a href="${tg.url('/results/' + str(election.id))}">Results</a>
+ </py:when>
+ <py:otherwise>
+ <a href="${tg.url('/results/' + str(election.alias))}">Results</a>
+ </py:otherwise>
+ </py:choose>
+ </py:when>
+ <py:otherwise>
+ Election Results under Embargo
+ </py:otherwise>
+ </py:choose>
+ </td>
+ </tr>
+ </table>
+ </py:if>
+
+ </body>
+</html>
diff --git a/elections2/templates/login.html b/elections2/templates/login.html
new file mode 100644
index 0000000..2fc8b29
--- /dev/null
+++ b/elections2/templates/login.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <xi:include href="fedora_login.html" />
+ <head>
+ <title>${appTitle}</title>
+ </head>
+ <body>
+ <div id="loginform">
+<form action="${tg.url('/login_handler', params=dict(came_from=came_from.encode('utf-8'), __logins=login_counter.encode('utf-8')))}" method="post" accept-charset="UTF-8" class="loginfields">
+ <h2><span>Login</span></h2>
+ <label for="loginusername">Username:</label><input type="text" id="loginusername" name="login" class="text"></input><br/>
+ <label for="loginpassword">Password:</label><input type="password" id="loginpassword" name="password" class="text"></input>
+ <input type="submit" id="submit" value="Login" />
+</form>
+</div>
+</body>
+</html>
diff --git a/elections2/templates/master.html b/elections2/templates/master.html
new file mode 100644
index 0000000..84d4737
--- /dev/null
+++ b/elections2/templates/master.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/"
+ py:strip="">
+ <head py:match="head" py:attrs="select('@*')">
+ <meta py:replace="select('*|text()')" />
+ <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/static/css/style.css')}" />
+ <link rel="stylesheet" type="text/css" media="screen" href="https://fedoraproject.org/static/css/fedora.css" />
+ <jsglobals />
+ </head>
+ <body py:match="body" py:attrs="select('@*')">
+ <div id="wrapper">
+
+ <div id="head">
+ <h1><a href="/en/index">Fedora</a></h1>
+ </div>
+
+ <div id="sidebar">
+ <div id="nav">
+ <h2>Navigation</h2>
+ <ul>
+ <li id="nav-home"><a href="http://fedoraproject.org/en/index">Home</a></li>
+ <li id="nav-get"><a href="http://fedoraproject.org/en/get-fedora">Get Fedora</a></li>
+ <li id="nav-join"><a href="http://fedoraproject.org/en/join-fedora">Join Fedora</a></li>
+ <li id="nav-help"><a href="http://fedoraproject.org/en/get-help">Get Help</a></li>
+ </ul>
+ </div>
+ </div>
+
+ <div id="content">
+
+ <ul class="toolbar" id="#main-toolbar">
+ <span py:if="tg.auth_stack_enabled" py:strip="True">
+ <li py:if="not request.identity" id="login" class="loginlogout"><a href="${tg.url('/login')}">Login</a></li>
+ <li py:if="request.identity" id="login" class="loginlogout"><a href="${tg.url('/logout_handler')}">Logout</a></li>
+ <li py:if="request.identity" id="admin" class="loginlogout"><a href="${tg.url('/admin')}">Admin</a></li>
+ </span>
+<!--
+ <li py:if="('elections' in tg.identity.groups)">Admin</li>
+-->
+ </ul>
+ <div py:if="tg.flash" class="flash">
+ ${tg.flash}
+ </div>
+
+ <div py:replace="select('*|text()')" />
+ </div>
+ <p>
+ Please report voting issues to the <a href="https://fedorahosted.org/fedora-infrastructure/">Fedora Infrastructure ticket system</a> selecting the Elections component.
+ </p>
+
+ </div>
+
+ <div id="bottom">
+ <div id="footer">
+
+ <p class="copy">
+ Copyright © 2008 Red Hat, Inc. and others. All Rights Reserved. Please send any comments or corrections to the <a href="mailto:webmaster@fedoraproject.org">websites team</a>.
+ </p>
+ <p class="disclaimer">
+ The Fedora Project is maintained and driven by the community and sponsored by Red Hat. This is a community maintained site. Red Hat is not responsible for content.
+ </p>
+ <ul>
+ <li class="first"><a href="/en/sponsors">Sponsors</a></li>
+
+ <li><a href="http://fedoraproject.org/wiki/Legal">Legal</a></li>
+ <li><a href="http://fedoraproject.org/wiki/Legal/TrademarkGuidelines">Trademark Guidelines</a></li>
+ </ul>
+ </div>
+ </div>
+ </body>
+
+<!--
+ <xi:include href="fedora_login.html" />
+-->
+<!--
+ <xi:include href="${tg.fedora_template('jsglobals.html')}" />
+-->
+
+</html>
diff --git a/elections2/templates/results.html b/elections2/templates/results.html
new file mode 100644
index 0000000..d2d142d
--- /dev/null
+++ b/elections2/templates/results.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>${appTitle}</title>
+ </head>
+ <body>
+ <h1>${election.shortdesc} Results</h1>
+ <p>${election.description}</p>
+ <p py:if="election.url"><a href="${election.url}">[More Information]</a></p>
+ <table border="1" cellpadding="1">
+ <tr py:for="candcount in votecount">
+ <td><py:choose test="election.usefas"><py:when test="0">${candcount.candidate.name}</py:when><py:when test="1">${usernamemap[candcount.candidate.id]}</py:when></py:choose> <small py:if="candcount.candidate.url"><a href="${candcount.candidate.url}">[info]</a></small></td>
+ <td>${candcount.novotes}</td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/elections2/templates/verify.html b/elections2/templates/verify.html
new file mode 100644
index 0000000..0dbea59
--- /dev/null
+++ b/elections2/templates/verify.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>Fedora Elections -- Vote Verification -- ${election.shortdesc}</title>
+ </head>
+ <body>
+ <h1>Vote Verification Page</h1>
+ <p>Please check the below content to ensure that all of your cast votes are marked as 'valid', if you notice any elections showing that you have an invalid vote cast please contact the election administration team by creating a ticket at the <a href="https://fedorahosted.org/fedora-infrastructure/">Fedora Infrastructure ticket system</a> and select the 'Elections' component with your FAS username and the election that is marked as invalid.</p>
+ <h2 py:if="validvotes">Valid Votes</h2>
+ <ul py:if="validvotes">
+ <li py:for="vote in validvotes"><a href="${tg.url('/about/' + validvotes[vote].election.id)}">${validvotes[vote].election.shortdesc}</a></li>
+ </ul>
+ <h2 py:if="invalidvotes">Invalid Votes</h2>
+ <ul py:if="invalidvotes">
+ <li py:for="vote in invalidvotes"><a href="${tg.url('/about/' + invalidvotes[vote].election.id)}">${invalidvotes[vote].election.shortdesc}</a></li>
+ </ul>
+
+ </body>
+</html>
diff --git a/elections2/templates/vote.html b/elections2/templates/vote.html
new file mode 100644
index 0000000..7887c69
--- /dev/null
+++ b/elections2/templates/vote.html
@@ -0,0 +1,61 @@
+<?python
+ def gradient(nocands, voteweight):
+ weightpercent = ((voteweight*100)/nocands)
+ if weightpercent >=90:
+ return "vthot"
+ elif weightpercent >= 70:
+ return "vtwarm"
+ elif weightpercent >= 50:
+ return "vtmedwarm"
+ elif weightpercent >= 30:
+ return "vtmedcool"
+ elif weightpercent >= 10:
+ return "vtcool"
+ else:
+ return "vtcold"
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="master.html" />
+ <head>
+ <title>${appTitle} -- Cast Your Vote -- ${election.shortdesc}</title>
+ </head>
+ <body>
+ <h1>${election.shortdesc}</h1>
+ <p>${election.description}</p>
+ <p py:if="election.url"><a href="${election.url}">[More Information]</a></p>
+ <div>
+ <form action="${tg.url('/vote/' + str(election.id))}" method="post">
+ <table border="1" cellpadding="1">
+ <tr py:for="candidate in candidates">
+ <td><py:choose test="election.usefas"><py:when test="0">${candidate.name}</py:when><py:when test="1">${usernamemap[candidate.id]}</py:when></py:choose> <small py:if="candidate.url"><a href="${candidate.url}">[info]</a></small></td>
+ <py:if test="nextaction=='vote'">
+ <td><select name="${candidate.id}">
+ <option py:for="option in range(0,len(candidates)+1)" value="$option">$option</option>
+ </select></td>
+ </py:if>
+ <py:if test="nextaction=='confirm'">
+ <td><input type="hidden" name="${candidate.id}" value="${voteinfo[candidate.id]}" /><div class="${gradient(len(candidates),voteinfo[candidate.id])}">${voteinfo[candidate.id]}</div></td>
+ </py:if>
+ </tr>
+ <tr>
+ <td><input py:if="nextaction=='confirm'" type="button" value="Go Back" onClick="history.go(-1)" /></td>
+ <td>
+ <input py:if="nextaction!='confirm'" type="submit" name="${nextaction}" value="Preview" />
+ <input py:if="nextaction=='confirm'" type="submit" name="${nextaction}" value="Submit" />
+ </td>
+ </tr>
+ </table>
+ </form>
+ </div>
+ <div>
+ <h2>Information on Range Voting</h2>
+ <p>Fedora Project has implemented <a href="http://rangevoting.org/">Range Voting</a> for this election, in particular the "Range (score-summing, blanks treated as zero score, no quorum rule)" range voting system.</p>
+ <p>To cast your vote in this election simply select a value between 0 and ${len(candidates)} with 0 as 'least or no preference' and ${len(candidates)} as 'highest preference'.</p>
+ <p>At the end of the election, the highest ranking candidate(s) are marked as the winners.</p>
+ <p>For more information about Range Voting, visit the <a href="http://rangevoting.org/">Center for Range Voting</a>.</p>
+ </div>
+ </body>
+</html>
diff --git a/elections2/tests/__init__.py b/elections2/tests/__init__.py
new file mode 100644
index 0000000..6b2c015
--- /dev/null
+++ b/elections2/tests/__init__.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+"""Unit and functional test suite for elections2."""
+
+from os import path
+import sys
+
+from tg import config
+from paste.deploy import loadapp
+from paste.script.appinstall import SetupCommand
+from routes import url_for
+from webtest import TestApp
+from nose.tools import eq_
+
+from elections2 import model
+
+__all__ = ['setup_db', 'teardown_db', 'TestController', 'url_for']
+
+def setup_db():
+ """Method used to build a database"""
+ engine = config['pylons.app_globals'].sa_engine
+ model.init_model(engine)
+ model.metadata.create_all(engine)
+
+def teardown_db():
+ """Method used to destroy a database"""
+ engine = config['pylons.app_globals'].sa_engine
+ model.metadata.drop_all(engine)
+
+
+class TestController(object):
+ """
+ Base functional test case for the controllers.
+
+ The elections2 application instance (``self.app``) set up in this test
+ case (and descendants) has authentication disabled, so that developers can
+ test the protected areas independently of the :mod:`repoze.who` plugins
+ used initially. This way, authentication can be tested once and separately.
+
+ Check elections2.tests.functional.test_authentication for the repoze.who
+ integration tests.
+
+ This is the officially supported way to test protected areas with
+ repoze.who-testutil (http://code.gustavonarea.net/repoze.who-testutil/).
+
+ """
+
+ application_under_test = 'main_without_authn'
+
+ def setUp(self):
+ """Method called by nose before running each test"""
+ # Loading the application:
+ conf_dir = config.here
+ wsgiapp = loadapp('config:test.ini#%s' % self.application_under_test,
+ relative_to=conf_dir)
+ self.app = TestApp(wsgiapp)
+ # Setting it up:
+ test_file = path.join(conf_dir, 'test.ini')
+ cmd = SetupCommand('setup-app')
+ cmd.run([test_file])
+
+ def tearDown(self):
+ """Method called by nose after running each test"""
+ # Cleaning up the database:
+ teardown_db()
diff --git a/elections2/tests/functional/__init__.py b/elections2/tests/functional/__init__.py
new file mode 100644
index 0000000..74b869b
--- /dev/null
+++ b/elections2/tests/functional/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+"""Functional test suite for the controllers of the application."""
\ No newline at end of file
diff --git a/elections2/tests/functional/test_authentication.py b/elections2/tests/functional/test_authentication.py
new file mode 100644
index 0000000..f854a8b
--- /dev/null
+++ b/elections2/tests/functional/test_authentication.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+"""
+Integration tests for the :mod:`repoze.who`-powered authentication sub-system.
+
+As elections2 grows and the authentication method changes, only these tests
+should be updated.
+
+"""
+
+from elections2.tests import TestController
+
+
+class TestAuthentication(TestController):
+ """Tests for the default authentication setup.
+
+ By default in TurboGears 2, :mod:`repoze.who` is configured with the same
+ plugins specified by repoze.what-quickstart (which are listed in
+ http://code.gustavonarea.net/repoze.what-quickstart/#repoze.what.plugins....).
+
+ As the settings for those plugins change, or the plugins are replaced,
+ these tests should be updated.
+
+ """
+
+ application_under_test = 'main'
+
+ def test_forced_login(self):
+ """Anonymous users are forced to login
+
+ Test that anonymous users are automatically redirected to the login
+ form when authorization is denied. Next, upon successful login they
+ should be redirected to the initially requested page.
+
+ """
+ # Requesting a protected area
+ resp = self.app.get('/secc/', status=302)
+ assert resp.location.startswith('http://localhost/login')
+ # Getting the login form:
+ resp = resp.follow(status=200)
+ form = resp.form
+ # Submitting the login form:
+ form['login'] = u'manager'
+ form['password'] = 'managepass'
+ post_login = form.submit(status=302)
+ # Being redirected to the initially requested page:
+ assert post_login.location.startswith('http://localhost/post_login')
+ initial_page = post_login.follow(status=302)
+ assert 'authtkt' in initial_page.request.cookies, \
+ "Session cookie wasn't defined: %s" % initial_page.request.cookies
+ assert initial_page.location.startswith('http://localhost/secc/'), \
+ initial_page.location
+
+ def test_voluntary_login(self):
+ """Voluntary logins must work correctly"""
+ # Going to the login form voluntarily:
+ resp = self.app.get('/login', status=200)
+ form = resp.form
+ # Submitting the login form:
+ form['login'] = u'manager'
+ form['password'] = 'managepass'
+ post_login = form.submit(status=302)
+ # Being redirected to the home page:
+ assert post_login.location.startswith('http://localhost/post_login')
+ home_page = post_login.follow(status=302)
+ assert 'authtkt' in home_page.request.cookies, \
+ 'Session cookie was not defined: %s' % home_page.request.cookies
+ assert home_page.location == 'http://localhost/'
+
+ def test_logout(self):
+ """Logouts must work correctly"""
+ # Logging in voluntarily the quick way:
+ resp = self.app.get('/login_handler?login=manager&password=managepass',
+ status=302)
+ resp = resp.follow(status=302)
+ assert 'authtkt' in resp.request.cookies, \
+ 'Session cookie was not defined: %s' % resp.request.cookies
+ # Logging out:
+ resp = self.app.get('/logout_handler', status=302)
+ assert resp.location.startswith('http://localhost/post_logout')
+ # Finally, redirected to the home page:
+ home_page = resp.follow(status=302)
+ authtkt = home_page.request.cookies.get('authtkt')
+ assert not authtkt or authtkt == 'INVALID', \
+ 'Session cookie was not deleted: %s' % home_page.request.cookies
+ assert home_page.location == 'http://localhost/', home_page.location
diff --git a/elections2/tests/functional/test_root.py b/elections2/tests/functional/test_root.py
new file mode 100644
index 0000000..d912819
--- /dev/null
+++ b/elections2/tests/functional/test_root.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+"""
+Functional test suite for the root controller.
+
+This is an example of how functional tests can be written for controllers.
+
+As opposed to a unit-test, which test a small unit of functionality,
+functional tests exercise the whole application and its WSGI stack.
+
+Please read http://pythonpaste.org/webtest/ for more information.
+
+"""
+from nose.tools import assert_true
+
+from elections2.tests import TestController
+
+
+class TestRootController(TestController):
+ """Tests for the method in the root controller."""
+
+ def test_index(self):
+ """The front page is working properly"""
+ response = self.app.get('/')
+ msg = 'TurboGears 2 is rapid web application development toolkit '\
+ 'designed to make your life easier.'
+ # You can look for specific strings:
+ assert_true(msg in response)
+
+ # You can also access a BeautifulSoup'ed response in your tests
+ # (First run $ easy_install BeautifulSoup
+ # and then uncomment the next two lines)
+
+ #links = response.html.findAll('a')
+ #print links
+ #assert_true(links, "Mummy, there are no links here!")
+
+ def test_environ(self):
+ """Displaying the wsgi environ works"""
+ response = self.app.get('/environ.html')
+ assert_true('The keys in the environment are: ' in response)
+
+ def test_data(self):
+ """The data display demo works with HTML"""
+ response = self.app.get('/data.html?a=1&b=2')
+ expected = """\
+<table>
+ <tr>
+ <td>a</td>
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>b</td>
+ <td>2</td>
+ </tr>
+ </table>
+"""
+ assert expected in response, response
+
+ def test_data_json(self):
+ """The data display demo works with JSON"""
+ resp = self.app.get('/data.json?a=1&b=2')
+ assert '"a": "1", "b": "2"' in resp, resp
+
+ def test_secc_with_manager(self):
+ """The manager can access the secure controller"""
+ # Note how authentication is forged:
+ environ = {'REMOTE_USER': 'manager'}
+ resp = self.app.get('/secc', extra_environ=environ, status=200)
+ assert 'Secure Controller here' in resp.body, resp.body
+
+ def test_secc_with_editor(self):
+ """The editor cannot access the secure controller"""
+ environ = {'REMOTE_USER': 'editor'}
+ self.app.get('/secc', extra_environ=environ, status=403)
+ # It's enough to know that authorization was denied with a 403 status
+
+ def test_secc_with_anonymous(self):
+ """Anonymous users must not access the secure controller"""
+ self.app.get('/secc', status=401)
+ # It's enough to know that authorization was denied with a 401 status
diff --git a/elections2/tests/models/__init__.py b/elections2/tests/models/__init__.py
new file mode 100644
index 0000000..845220d
--- /dev/null
+++ b/elections2/tests/models/__init__.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+"""Unit test suite for the models of the application."""
+from nose.tools import assert_equals
+
+from elections2.model import DBSession
+from elections2.tests import setup_db, teardown_db
+
+__all__ = ['ModelTest']
+
+# Create an empty database before we start our tests for this module
+def setup():
+ """Function called by nose on module load"""
+ setup_db()
+
+# Tear down that database
+def teardown():
+ """Function called by nose after all tests in this module ran"""
+ teardown_db()
+
+
+class ModelTest(object):
+ """Base unit test case for the models."""
+
+ klass = None
+ attrs = {}
+
+ def setUp(self):
+ """Prepare model test fixture."""
+ try:
+ new_attrs = {}
+ new_attrs.update(self.attrs)
+ new_attrs.update(self.do_get_dependencies())
+ self.obj = self.klass(**new_attrs)
+ DBSession.add(self.obj)
+ DBSession.flush()
+ return self.obj
+ except:
+ DBSession.rollback()
+ raise
+
+ def tearDown(self):
+ """Finish model test fixture."""
+ DBSession.rollback()
+
+ def do_get_dependencies(self):
+ """Get model test dependencies.
+
+ Use this method to pull in other objects that need to be created
+ for this object to be build properly.
+
+ """
+ return {}
+
+ def test_create_obj(self):
+ """Model objects can be created"""
+ pass
+
+ def test_query_obj(self):
+ """Model objects can be queried"""
+ obj = DBSession.query(self.klass).one()
+ for key, value in self.attrs.iteritems():
+ assert_equals(getattr(obj, key), value)
diff --git a/elections2/tests/models/test_auth.py b/elections2/tests/models/test_auth.py
new file mode 100644
index 0000000..d9d0294
--- /dev/null
+++ b/elections2/tests/models/test_auth.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+"""Test suite for the TG app's models"""
+from nose.tools import eq_
+
+from elections2 import model
+from elections2.tests.models import ModelTest
+
+class TestGroup(ModelTest):
+ """Unit test case for the ``Group`` model."""
+ klass = model.Group
+ attrs = dict(
+ group_name = u"test_group",
+ display_name = u"Test Group"
+ )
+
+
+class TestUser(ModelTest):
+ """Unit test case for the ``User`` model."""
+
+ klass = model.User
+ attrs = dict(
+ user_name = u"ignucius",
+ email_address = u"ignucius(a)example.org"
+ )
+
+ def test_obj_creation_username(self):
+ """The obj constructor must set the user name right"""
+ eq_(self.obj.user_name, u"ignucius")
+
+ def test_obj_creation_email(self):
+ """The obj constructor must set the email right"""
+ eq_(self.obj.email_address, u"ignucius(a)example.org")
+
+ def test_no_permissions_by_default(self):
+ """User objects should have no permission by default."""
+ eq_(len(self.obj.permissions), 0)
+
+ def test_getting_by_email(self):
+ """Users should be fetcheable by their email addresses"""
+ him = model.User.by_email_address(u"ignucius(a)example.org")
+ eq_(him, self.obj)
+
+
+class TestPermission(ModelTest):
+ """Unit test case for the ``Permission`` model."""
+
+ klass = model.Permission
+ attrs = dict(
+ permission_name = u"test_permission",
+ description = u"This is a test Description"
+ )
diff --git a/elections2/websetup/__init__.py b/elections2/websetup/__init__.py
new file mode 100644
index 0000000..3038dee
--- /dev/null
+++ b/elections2/websetup/__init__.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+"""Setup the elections2 application"""
+
+import logging
+
+from elections2.config.environment import load_environment
+
+__all__ = ['setup_app']
+
+log = logging.getLogger(__name__)
+
+from schema import setup_schema
+import bootstrap
+
+def setup_app(command, conf, vars):
+ """Place any commands to setup elections2 here"""
+ load_environment(conf.global_conf, conf.local_conf)
+ setup_schema(command, conf, vars)
+ bootstrap.bootstrap(command, conf, vars)
diff --git a/elections2/websetup/bootstrap.py b/elections2/websetup/bootstrap.py
new file mode 100644
index 0000000..24050a3
--- /dev/null
+++ b/elections2/websetup/bootstrap.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""Setup the elections2 application"""
+
+import logging
+from tg import config
+from elections2 import model
+
+import transaction
+
+
+def bootstrap(command, conf, vars):
+ """Place any commands to setup elections2 here"""
+
+ # <websetup.bootstrap.before.auth
+ from sqlalchemy.exc import IntegrityError
+ try:
+ u = model.User()
+ u.user_name = u'manager'
+ u.display_name = u'Example manager'
+ u.email_address = u'manager(a)somedomain.com'
+ u.password = u'managepass'
+
+ model.DBSession.add(u)
+
+ g = model.Group()
+ g.group_name = u'managers'
+ g.display_name = u'Managers Group'
+
+ g.users.append(u)
+
+ model.DBSession.add(g)
+
+ p = model.Permission()
+ p.permission_name = u'manage'
+ p.description = u'This permission give an administrative right to the bearer'
+ p.groups.append(g)
+
+ model.DBSession.add(p)
+
+ u1 = model.User()
+ u1.user_name = u'editor'
+ u1.display_name = u'Example editor'
+ u1.email_address = u'editor(a)somedomain.com'
+ u1.password = u'editpass'
+
+ model.DBSession.add(u1)
+ model.DBSession.flush()
+ transaction.commit()
+ except IntegrityError:
+ print 'Warning, there was a problem adding your auth data, it may have already been added:'
+ import traceback
+ print traceback.format_exc()
+ transaction.abort()
+ print 'Continuing with bootstrapping...'
+
+
+ # <websetup.bootstrap.after.auth>
diff --git a/elections2/websetup/schema.py b/elections2/websetup/schema.py
new file mode 100644
index 0000000..0bfd27e
--- /dev/null
+++ b/elections2/websetup/schema.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+"""Setup the elections2 application"""
+
+import logging
+import transaction
+from tg import config
+
+def setup_schema(command, conf, vars):
+ """Place any commands to setup elections2 here"""
+ # Load the models
+
+ # <websetup.websetup.schema.before.model.import>
+ from elections2 import model
+ # <websetup.websetup.schema.after.model.import>
+
+
+ # <websetup.websetup.schema.before.metadata.create_all>
+ print "Creating tables"
+ model.metadata.create_all(bind=config['pylons.app_globals'].sa_engine)
+ # <websetup.websetup.schema.after.metadata.create_all>
+ transaction.commit()
diff --git a/examples/elections.conf b/examples/elections.conf
deleted file mode 100644
index b8d0155..0000000
--- a/examples/elections.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-# Apache2 mod_wsgi Configuration
-
-WSGISocketPrefix /tmp/wsgi
-
-WSGIRestrictStdout On
-WSGIRestrictSignal Off
-WSGIPythonOptimize 1
-
-WSGIDaemonProcess daemon threads=2 processes=2
-
-WSGIScriptAlias /elections /srv/elections/elections.wsgi/elections
-
-Alias /elections/static /srv/elections/elections/static
-
-<Location /elections>
-WSGIProcessGroup daemon
-Order deny,allow
-Allow from all
-</Location>
diff --git a/examples/elections.wsgi b/examples/elections.wsgi
deleted file mode 100644
index d6bf54a..0000000
--- a/examples/elections.wsgi
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python
-__requires__ = 'elections'
-
-import sys
-sys.path.append('/usr/share/elections/elections')
-sys.path.append('/usr/share/elections')
-sys.stdout = sys.stderr
-
-import pkg_resources
-pkg_resources.require('CherryPy <= 3.0alpha')
-
-import os
-#os.environ['PYTHON_EGG_CACHE'] = '/srv/elections/elections.eggcache/'
-
-import atexit
-import cherrypy
-import cherrypy._cpwsgi
-import turbogears
-from turbogears import startup
-from fedora.tg.util import enable_csrf
-
-turbogears.update_config(configfile="/etc/elections.cfg", modulename="elections.config")
-
-startup.call_on_startup.append(enable_csrf)
-
-import elections.controllers
-
-cherrypy.root = elections.controllers.Root()
-
-if cherrypy.server.state == 0:
- atexit.register(cherrypy.server.stop)
- cherrypy.server.start(init_only=True, server_class=None)
-
-def application(environ, start_response):
- environ['SCRIPT_NAME'] = ''
- return cherrypy._cpwsgi.wsgiApp(environ, start_response)
diff --git a/sample-prod.cfg b/sample-prod.cfg
deleted file mode 100644
index 2d7d887..0000000
--- a/sample-prod.cfg
+++ /dev/null
@@ -1,84 +0,0 @@
-[global]
-# This is where all of your settings go for your production environment.
-# You'll copy this file over to your production server and provide it
-# as a command-line option to your start script.
-# Settings that are the same for both development and production
-# (such as template engine, encodings, etc.) all go in
-# elections/config/app.cfg
-
-# DATABASE
-
-# pick the form for your database
-# sqlobject.dburi="postgres://username@hostname/databasename"
-# sqlobject.dburi="mysql://username:password@hostname:port/databasename"
-# sqlobject.dburi="sqlite:///file_name_and_path"
-
-# If you have sqlite, here's a simple default to get you started
-# in development
-sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
-
-
-# if you are using a database or table type without transactions
-# (MySQL default, for example), you should turn off transactions
-# by prepending notrans_ on the uri
-# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename"
-
-# for Windows users, sqlite URIs look like:
-# sqlobject.dburi="sqlite:///drive_letter:/path/to/file"
-
-
-# SERVER
-
-server.environment="production"
-
-# Sets the number of threads the server uses
-# server.thread_pool = 1
-
-# if this is part of a larger site, you can set the path
-# to the TurboGears instance here
-# server.webpath=""
-
-# Set to True if you are deploying your App behind a proxy
-# e.g. Apache using mod_proxy
-# base_url_filter.on = False
-
-# Set to True if your proxy adds the x_forwarded_host header
-# base_url_filter.use_x_forwarded_host = True
-
-# If your proxy does not add the x_forwarded_host header, set
-# the following to the *public* host url.
-# (Note: This will be overridden by the use_x_forwarded_host option
-# if it is set to True and the proxy adds the header correctly.
-# base_url_filter.base_url = "http://www.example.com"
-
-# Set to True if you'd like to abort execution if a controller gets an
-# unexpected parameter. False by default
-# tg.strict_parameters = False
-
-# LOGGING
-# Logging configuration generally follows the style of the standard
-# Python logging module configuration. Note that when specifying
-# log format messages, you need to use *() for formatting variables.
-# Deployment independent log configuration is in elections/config/log.cfg
-[logging]
-
-[[handlers]]
-
-[[[access_out]]]
-# set the filename as the first argument below
-args="('server.log',)"
-class='FileHandler'
-level='INFO'
-formatter='message_only'
-
-[[loggers]]
-[[[elections]]]
-level='ERROR'
-qualname='elections'
-handlers=['error_out']
-
-[[[access]]]
-level='INFO'
-qualname='turbogears.access'
-handlers=['access_out']
-propagate=0
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..a9acded
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,29 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
+
+[nosetests]
+with-pylons=test.ini
+
+# Babel configuration
+[compile_catalog]
+domain = elections2
+directory = elections2/i18n
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = elections2/i18n/elections2.pot
+width = 80
+keywords = l_
+
+[init_catalog]
+domain = elections2
+input_file = elections2/i18n/elections2.pot
+output_dir = elections2/i18n
+
+[update_catalog]
+domain = elections2
+input_file = elections2/i18n/elections2.pot
+output_dir = elections2/i18n
+previous = true
diff --git a/setup.py b/setup.py
index 009d2ef..082c680 100644
--- a/setup.py
+++ b/setup.py
@@ -1,220 +1,47 @@
# -*- coding: utf-8 -*-
-
-import os
-import glob
-import re
-import distutils
-from distutils.dep_util import newer
-from distutils import log
-
-from distutils.command.build_scripts import build_scripts as _build_scripts
-from distutils.command.build import build as _build
-from setuptools.command.install import install as _install
-from setuptools.command.install_lib import install_lib as _install_lib
-
-from setuptools import setup, find_packages
-from turbogears.finddata import find_package_data
-
-execfile(os.path.join("elections", "release.py"))
-
-class BuildScripts(_build_scripts, object):
- '''Build the package, changing the directories in start-pkgdb.'''
- substitutions = {'@CONFDIR@': '/usr/local/etc',
- '@DATADIR@': '/usr/local/share'}
- subRE = re.compile('('+'|'.join(substitutions.keys())+')+')
- # Set the correct directories in start-pkgdb
- user_options = _build_scripts.user_options
- user_options.extend([
- ('install-conf=', None, 'Installation directory for configuration files'),
- ('install-data=', None, 'Installation directory for data files')])
-
- def initialize_options(self):
- self.install_conf = None
- self.install_data = None
- super(BuildScripts, self).initialize_options()
-
- def finalize_options(self):
- self.set_undefined_options('build',
- ('install_conf', 'install_conf'),
- ('install_data', 'install_data'))
- self.substitutions['@CONFDIR@'] = self.install_conf or \
- '/usr/local/etc'
- self.substitutions['@DATADIR@'] = self.install_data or \
- '/usr/local/share'
- super(BuildScripts, self).finalize_options()
-
- def run(self):
- '''Substitute special variables with our install lcoations.
-
- '''
- for script in self.scripts:
- # If there's a script name with .in attached, make substitutions
- infile = script + '.in'
- if not os.path.exists(infile):
- continue
- if not self.force and not newer (infile, script):
- log.debug("not substituting in %s (up-to-date)", script)
- continue
- try:
- f = file(infile, 'r')
- except IOError:
- if not self.dry_run:
- raise
- f = None
- outf = open(script, 'w')
- for line in f.readlines():
- matches = self.subRE.search(line)
- if matches:
- for pattern in self.substitutions:
- line = line.replace(pattern,
- self.substitutions[pattern])
- outf.writelines(line)
- outf.close()
- f.close()
- super(BuildScripts, self).run()
-
-class Build(_build, object):
- '''Override setuptools and install the package in the correct place for
- an application.'''
-
- user_options = _build.user_options
- user_options.extend([
- ('install-conf=', None, 'Installation directory for configuration files'),
- ('install-data=', None, 'Installation directory for data files')])
-
- def initialize_options(self):
- self.install_conf = None
- self.install_data = None
- super(Build, self).initialize_options()
-
- def finalize_options(self):
- self.install_conf = self.install_conf or '/usr/local/etc'
- self.install_data = self.install_data or '/usr/local/share'
- super(Build, self).finalize_options()
-
- def run(self):
- super(Build, self).run()
-
-class Install(_install, object):
- '''Override setuptools and install the package in the correct place for
- an application.'''
- user_options = _install.user_options
- user_options.append( ('install-conf=', None,
- 'Installation directory for configuration files'))
- user_options.append( ('install-data=', None,
- 'Installation directory'))
- user_options.append( ('root=', None,
- 'Install root'))
-
- def initialize_options(self):
- self.install_conf = None
- self.install_data = None
- self.root = None
- super(Install, self).initialize_options()
-
- def finalize_options(self):
- self.install_conf = self.install_conf or '/usr/local/etc'
- self.install_data = self.install_data or '/usr/local/share'
- super(Install, self).finalize_options()
-
-class InstallApp(_install_lib, object):
- user_options = _install_lib.user_options
- user_options.append( ('install-conf=', None,
- 'Installation directory for configuration files'))
- user_options.append( ('install-data=', None,
- 'Installation directory'))
- user_options.append( ('root=', None,
- 'Install root'))
-
- def initialize_options(self):
- self.install_conf = None
- self.install_data = None
- self.root = None
- super(InstallApp, self).initialize_options()
-
- def finalize_options(self):
- self.set_undefined_options('install',
- ('root', 'root'),
- ('install_conf', 'install_conf'),
- ('install_data', 'install_data'))
- print dir(self.distribution.get_name())
- self.install_dir=os.path.join(self.install_data, self.distribution.get_name())
- super(InstallApp, self).finalize_options()
-
- def run(self):
- super(InstallApp, self).run()
-
- ### FIXME: Couldn't think of a better way to do this in limited time
-
- # Install the configuration file to the confdir
- confdir = self.root + os.path.sep + self.install_conf
- confdir.replace('//','/')
- self.mkpath(confdir)
- self.copy_file('elections.cfg', confdir)
-
+try:
+ from setuptools import setup, find_packages
+except ImportError:
+ from ez_setup import use_setuptools
+ use_setuptools()
+ from setuptools import setup, find_packages
setup(
- name="elections",
- version=version,
- # uncomment the following lines if you fill them out in release.py
- description=description,
- author=author,
- author_email=email,
- url=url,
- #download_url=download_url,
- license=license,
-
- cmdclass={'build_scripts': BuildScripts,
- 'build': Build,
- 'install': Install,
- 'install_lib': InstallApp},
+ name='elections2',
+ version='0.1',
+ description='',
+ author='',
+ author_email='',
+ #url='',
install_requires=[
- "TurboGears >= 1.0.4.3",
- "SQLAlchemy>=0.4"
- ],
- zip_safe=False,
- packages=find_packages(),
- package_data=find_package_data(where='elections', package='elections'),
- keywords=[
- # Use keywords if you'll be adding your package to the
- # Python Cheeseshop
-
- # if this has widgets, uncomment the next line
- # 'turbogears.widgets',
-
- # if this has a tg-admin command, uncomment the next line
- # 'turbogears.command',
-
- # if this has identity providers, uncomment the next line
- # 'turbogears.identity.provider',
-
- # If this is a template plugin, uncomment the next line
- # 'python.templating.engines',
-
- # If this is a full application, uncomment the next line
- 'turbogears.app',
- ],
- classifiers=[
- 'Development Status :: 3 - Alpha',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'Framework :: TurboGears',
- # if this is an application that you'll distribute through
- # the Cheeseshop, uncomment the next line
- 'Framework :: TurboGears :: Applications',
-
- # if this is a package that includes widgets that you'll distribute
- # through the Cheeseshop, uncomment the next line
- # 'Framework :: TurboGears :: Widgets',
- ],
+ "TurboGears2 >= 2.1a1",
+ "Babel >=0.9.4",
+ "zope.sqlalchemy >= 0.4 ",
+ "repoze.tm2 >= 1.0a4",
+
+ "repoze.what-quickstart >= 1.0",
+ "tgext.admin>=0.3.3",
+ ],
+ setup_requires=["PasteScript >= 1.7"],
+ paster_plugins=['PasteScript', 'Pylons', 'TurboGears2', 'tg.devtools'],
+ packages=find_packages(exclude=['ez_setup']),
+ include_package_data=True,
test_suite='nose.collector',
- entry_points = {
- 'console_scripts': [
- 'start-elections = elections.commands:start',
- ],
- },
- # Uncomment next line and create a default.cfg file in your project dir
- # if you want to package a default configuration in your egg.
- #data_files = [('config', ['default.cfg'])],
- )
+ tests_require=['WebTest', 'BeautifulSoup'],
+ package_data={'elections2': ['i18n/*/LC_MESSAGES/*.mo',
+ 'templates/*/*',
+ 'public/*/*']},
+ message_extractors={'elections2': [
+ ('**.py', 'python', None),
+ ('templates/**.mako', 'mako', None),
+ ('templates/**.html', 'genshi', None),
+ ('public/**', 'ignore', None)]},
+
+ entry_points="""
+ [paste.app_factory]
+ main = elections2.config.middleware:make_app
+
+ [paste.app_install]
+ main = pylons.util:PylonsInstaller
+ """,
+)
diff --git a/start-elections.py b/start-elections.py
deleted file mode 100755
index 77d834b..0000000
--- a/start-elections.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-"""Start script for the elections TurboGears project.
-
-This script is only needed during development for running from the project
-directory. When the project is installed, easy_install will create a
-proper start script.
-"""
-
-import sys
-from elections.commands import start, ConfigurationError
-
-if __name__ == "__main__":
- try:
- start()
- except ConfigurationError, exc:
- sys.stderr.write(str(exc))
- sys.exit(1)
diff --git a/test.cfg b/test.cfg
deleted file mode 100644
index ac62e50..0000000
--- a/test.cfg
+++ /dev/null
@@ -1,32 +0,0 @@
-[global]
-# You can place test-specific configuration options here (like test db uri, etc)
-
-# DATABASE
-
-sqlobject.dburi = "sqlite:///:memory:"
-
-# LOGGING
-
-[logging]
-
-[[formatters]]
-[[[full_content]]]
-format='*(asctime)s *(name)s *(levelname)s *(message)s'
-
-[[handlers]]
-[[[test_out]]]
-class='StreamHandler'
-level='DEBUG'
-args='(sys.stdout,)'
-formatter='full_content'
-
-[[loggers]]
-[[[elections]]]
-level='DEBUG'
-qualname='elections'
-handlers=['test_out']
-
-[[[turbogears]]]
-level='INFO'
-qualname='turbogears'
-handlers=['test_out']
diff --git a/test.ini b/test.ini
new file mode 100644
index 0000000..2dee11e
--- /dev/null
+++ b/test.ini
@@ -0,0 +1,26 @@
+#
+# elections2 - TurboGears 2 testing environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+debug = true
+# Uncomment and replace with the address which should receive any error reports
+# email_to = you(a)yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 5000
+
+[app:main]
+sqlalchemy.url = sqlite:///:memory:
+use = config:development.ini
+
+[app:main_without_authn]
+use = main
+skip_authentication = True
+
+# Add additional test specific configuration options as necessary.
13 years