25 | This is a standard ASF PMC vote with binding and non-binding votes.
26 | If you agree with the motion put forward, vote Yes. If you disagree, vote No.
27 | If you neither agree nor disagree, you can mark your ballot as blank by clicking on Abstain. F
28 | or some elections where the number of votes
29 | are important, abstaining on a vote you have no preference for/against can help reach quorum.
30 |
31 |
32 |
33 |
34 | Loading issue, please wait...
35 |
36 |
37 |
38 |
39 | Powered by Apache STeVe.
40 | Copyright 2016, the Apache Software Foundation.
41 | Licensed under the Apache License 2.0
42 |
25 | This is a standard Candidate or Party vote.
26 | To vote, click on the candidate or party of your choice and then click on Cast vote.
27 | Should you reconsider later, you can always recast your vote for as long as the election remains open.
28 |
29 |
30 |
31 |
32 | Loading issue, please wait...
33 |
34 |
35 |
36 |
37 | Powered by Apache STeVe.
38 | Copyright 2016, the Apache Software Foundation.
39 | Licensed under the Apache License 2.0
40 |
26 | This is a standard D'Hondt (Jefferson) based vote with N candidates and
27 | N seats available. Select the candidate/party you would like to vote for and click Cast vote to cast your vote.
28 | You can change your vote as often as you like, should you reconsider your choice.
29 |
30 |
31 |
32 |
33 | Loading issue, please wait...
34 |
35 |
36 |
37 |
38 | Powered by Apache STeVe.
39 | Copyright 2016, the Apache Software Foundation.
40 | Licensed under the Apache License 2.0
41 |
24 | This is a First in Class Vote ballot with N candidates and
25 | N points available.
26 | All the nominees are placed in random order on the candidate list.
27 | If the canidate has prepared a statement, you can view it by clicking on the
28 | statement link to the right of the candidate's name.
29 |
30 |
31 | How to vote:
32 | Drag a candidate from the candidate list to the ballot box to place them in
33 | the vote. Place the person you wish to give the highest score at the top,
34 | then the person with the second highest score and so on.
35 | You can rearrange your votes as you see fit, by dragging
36 | candidates up/down on the ballot box list. You may only votes for as many
37 | canidates as there are points to give out. If you want to
38 | remove a single candidate from your ballot box, simply drag the
39 | canidate back to the list of remaining candidates to the left.
40 |
41 |
42 |
43 |
44 | Loading issues, please wait...
45 |
46 |
47 |
48 |
49 | Powered by Apache STeVe.
50 | Copyright 2016, the Apache Software Foundation.
51 | Licensed under the Apache License 2.0
52 |
26 | This is a standard First Past the Post election. The person/party with the most votes wins the election.
27 | To vote, click on the candidate of your choice and then click on Cast vote.
28 | Should you reconsider later, you can always recast your vote for as long as the election remains open.
29 |
30 |
31 |
32 |
33 | Loading issue, please wait...
34 |
35 |
36 |
37 |
38 | Powered by Apache STeVe.
39 | Copyright 2016, the Apache Software Foundation.
40 | Licensed under the Apache License 2.0
41 |
24 | This is a Multiple Non-Transferable Vote ballot with N candidates and
25 | N seats available.
26 | All the nominees are placed in random order on the candidate list.
27 | If the canidate has prepared a statement, you can view it by clicking on the
28 | statement link to the right of the candidate's name.
29 |
30 |
31 | How to vote:
32 | Drag a candidate from the candidate list to the ballot box to place them in
33 | the vote. You can rearrange your votes as you see fit, by dragging
34 | candidates up/down on the ballot box list. You may only votes for as many
35 | canidates as there are seats. If you want to
36 | remove a single candidate from your ballot box, simply drag the
37 | canidate back to the list of remaining candidates to the left.
38 |
39 |
40 |
41 |
42 | Loading issues, please wait...
43 |
44 |
45 |
46 |
47 | Powered by Apache STeVe.
48 | Copyright 2016, the Apache Software Foundation.
49 | Licensed under the Apache License 2.0
50 |
24 | This is a ballot with N candidates and
25 | N seats available.
26 | The red line denotes the cutaway, should all your choices be voted in.
27 | All the nominees are placed in random order on the candidate list.
28 | If the candidate has prepared a statement, you can view it by clicking on the
29 | statement link to the right of the candidate's name.
30 |
31 |
32 | How to vote:
33 | Drag a candidate from the candidate list to the ballot box to place them in
34 | the vote. ORDER MATTERS: Put your candidates in the order of your preference,
35 | with the first preference at the top, the second below that and so on.
36 | You can rearrange your votes as you see fit, by dragging
37 | candidates up/down on the ballot box list. You may place as many candidates
38 | in the ballot box as you see fit. If you want to
39 | remove a single candidate from your ballot box, simply drag the
40 | candidate back to the list of remaining candidates to the left.
41 |
42 |
43 |
44 |
45 | Loading issues, please wait...
46 |
47 |
48 |
49 |
50 | Powered by Apache STeVe.
51 | Copyright 2016, the Apache Software Foundation.
52 | Licensed under the Apache License 2.0
53 |
25 | This is a standard YNA (Yes/No/Abstain) vote. If you agree with the motion put forward, vote Yes. If you disagree, vote No.
26 | If you neither agree nor disagree, you can mark your ballot as blank by clicking on Abstain. For some elections where the number of votes
27 | are important, abstaining on a vote you have no preference for/against can help reach quorum.
28 |
29 |
30 |
31 |
32 | Loading issue, please wait...
33 |
34 |
35 |
36 |
37 | Powered by Apache STeVe.
38 | Copyright 2016, the Apache Software Foundation.
39 | Licensed under the Apache License 2.0
40 |
24 | Here you can perform a quick vote for each of the yes/no issues in this
25 | election. Click on yes, no or abstain to cast your vote. Your vote is cast
26 | and registered once you click the respective button, and can be changed at
27 | any time, as long as the election is still open. When you vote, an icon will
28 | appear next to the vote options to indicate the vote that was recorded by
29 | the system.
30 |
31 |
When you click on a button, your vote is registered immediately. There is no 'done' button here. Also note that this page only lists Yes/No/Abstain issues. There may be other issues in need of your vote on the main election page.
32 |
33 |
34 |
35 | Loading issues, please wait...
36 |
37 |
38 |
39 |
40 | Powered by Apache STeVe.
41 | Copyright 2016, the Apache Software Foundation.
42 | Licensed under the Apache License 2.0
43 |
15 |
16 |
--------------------------------------------------------------------------------
/pysteve/www/htdocs/js/steve_ap.js:
--------------------------------------------------------------------------------
1 | /*
2 | Licensed to the Apache Software Foundation (ASF) under one or more
3 | contributor license agreements. See the NOTICE file distributed with
4 | this work for additional information regarding copyright ownership.
5 | The ASF licenses this file to You under the Apache License, Version 2.0
6 | (the "License"); you may not use this file except in compliance with
7 | the License. You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 |
18 | function displayIssueAP(code, response, state) {
19 | election_data = response
20 | var obj = document.getElementById('preloaderWrapper')
21 | obj.setAttribute("id", "ynavote")
22 | if (code != 200) {
23 | obj.innerHTML = "
20 | Leave this page open during the entire election. There is no need to refresh the page, it does that by itself.
21 | If you disconnect from the Internet or hibernate your machine (or close this page), it will resume once the connection is re-established, but
22 | should you decide to do this, some debug data could theoretically be lost.
23 |
24 |
25 |
Timestamps shown on this page are browser-local (your computer's timezone).
26 |
27 |
28 |
29 | Loading issues, please wait...
30 |
31 |
32 |
33 |
34 | Powered by Apache STeVe.
35 | Copyright 2016, the Apache Software Foundation.
36 | Licensed under the Apache License 2.0
37 |
36 | In open elections, anyone can request a voter ID and participate.
37 | To receive a voter ID, please enter your email address in the field below and click 'Request link'.
38 | An email will be sent to you with your own personalized voter ID.
39 | With this ID, you may vote as many times as you wish on any of the issues in this election,
40 | however only the last vote you cast on any given issue will be your final vote.
41 |
42 |
43 |
Email address:
44 |
48 |
49 |
50 |
51 |
52 | Powered by Apache STeVe.
53 | Copyright 2016, the Apache Software Foundation.
54 | Licensed under the Apache License 2.0
55 |
56 |
57 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | argparse
2 | nose
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | #
20 |
21 | from io import open
22 | import subprocess
23 | import sys
24 |
25 | from setuptools import find_packages, setup, Command, os
26 |
27 |
28 | VERSION = '1.0'
29 |
30 |
31 | class doc(Command):
32 | description = 'generate or test documentation'
33 | user_options = [('test', 't', 'run doctests instead of generating documentation')]
34 | boolean_options = ['test']
35 |
36 | def initialize_options(self):
37 | self.test = False
38 |
39 | def finalize_options(self):
40 | pass
41 |
42 | def run(self):
43 | if self.test:
44 | path = 'docs/build/doctest'
45 | mode = 'doctest'
46 | else:
47 | path = 'docs/build/%s' % VERSION
48 | mode = 'html'
49 |
50 | try:
51 | os.makedirs(path)
52 | except OSError:
53 | pass
54 |
55 | status = subprocess.call(['sphinx-build', '-E', '-b', mode, '-d', 'docs/build/doctrees', 'docs/source', path])
56 |
57 | if status:
58 | raise RuntimeError('documentation step "%s" failed' % (mode,))
59 |
60 | sys.stdout.write('\nDocumentation step "%s" performed, results here:\n'
61 | ' %s/\n' % (mode, path))
62 |
63 |
64 | class test(Command):
65 | description = 'run nosetests'
66 | user_options = [('verbose', 'v', 'run nosetests with -v option')]
67 | boolean_options = ['verbose']
68 |
69 | def initialize_options(self):
70 | self.verbose = False
71 |
72 | def finalize_options(self):
73 | pass
74 |
75 | def run(self):
76 | if self.verbose:
77 | verbose = '-v'
78 | else:
79 | verbose = ''
80 |
81 | status = subprocess.call(['nosetests', verbose])
82 |
83 | if status:
84 | raise RuntimeError('nosetests step failed')
85 |
86 |
87 | with open('requirements.txt') as f:
88 | install_requires = f.read().splitlines()
89 |
90 | tests_requires = install_requires + [
91 | 'nose >= 1.2',
92 | ]
93 |
94 | setup(
95 | name='apache-steve',
96 | version=VERSION,
97 | url='https://svn.apache.org/repos/asf/steve',
98 | license='Apache Software License (http://www.apache.org/licenses/LICENSE-2.0)',
99 | author='Apache Software Foundation',
100 | author_email='dev@steve.apache.org',
101 | description='Apache Steve',
102 | # don't ever depend on refcounting to close files anywhere else
103 | long_description=open('README', encoding='utf-8').read(),
104 |
105 | # scripts=["bin/votegroup"],
106 |
107 | # namespace_packages=['steve'],
108 | package_dir={'': 'lib'},
109 | packages=find_packages('lib'),
110 |
111 | zip_safe=False,
112 | platforms='any',
113 | install_requires=install_requires,
114 |
115 | tests_require=tests_requires,
116 | test_suite='nose.collector',
117 |
118 | classifiers=[
119 | 'Intended Audience :: Developers',
120 | 'Intended Audience :: System Administrators',
121 | 'License :: OSI Approved :: Apache Software License',
122 | 'Operating System :: OS Independent',
123 | 'Programming Language :: Python',
124 | 'Programming Language :: Python :: 2.6',
125 | 'Programming Language :: Python :: 2.7',
126 | 'Topic :: Software Development :: Libraries :: Python Modules'
127 | ],
128 | cmdclass={'doc': doc, 'test': test},
129 | )
130 |
--------------------------------------------------------------------------------
/site/css/steve.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 0;
3 | background-color: #F4F4F4;
4 | font-family: sans-serif;
5 | height: 100%;
6 | min-height: 100%;
7 | }
8 |
9 | #main_splash {
10 | width: 1200px;
11 | background-image: url(../images/front-splash.png);
12 | background-position-y: 48px;
13 | background-repeat: no-repeat;
14 | margin: 0 auto;
15 | background-color: #FFF;
16 | box-shadow: 0px 0px 3px #0006;
17 | position: relative;
18 | padding-top: 700px;
19 | }
20 |
21 | #main {
22 | width: 1200px;
23 | min-height: 100%;
24 | margin: 0 auto;
25 | background-color: #FFF;
26 | box-shadow: 0px 0px 3px #0006;
27 | position: relative;
28 | }
29 |
30 | nav {
31 | position: absolute;
32 | top: 0px;
33 | text-align: right;
34 | width: calc(100% - 20px);
35 | margin-right: 20px;
36 | background-image: url(../images/logo-bright.png);
37 | background-size: 100px;
38 | background-repeat: no-repeat;
39 | background-position: 5px 5px;
40 | }
41 |
42 | nav > ul {
43 | list-style: none;
44 | display: inline-block;
45 | }
46 | nav > ul > li {
47 | display: inline-block;
48 | color: #444;
49 | font-weight: bold;
50 | text-transform: uppercase;
51 | margin-left: 24px;
52 | font-size: 0.8rem;
53 | }
54 |
55 | nav > ul > li > a {
56 | color: #444;
57 | font-width: bold;
58 | text-decoration: none;
59 | }
60 |
61 | nav > ul > li > a:hover {
62 | color: #222;
63 | font-width: bold;
64 | text-decoration: underline;
65 | }
66 |
67 | #contents {
68 | padding: 20px;
69 | }
70 |
71 | #main > #contents {
72 | padding-top: 80px;
73 | }
74 |
75 | footer {
76 | border-top: 1px solid #3336;
77 | font-size: 0.75rem;
78 | text-align: left;
79 | padding: 16px;
80 | }
81 |
--------------------------------------------------------------------------------
/site/images/front-splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/site/images/front-splash.png
--------------------------------------------------------------------------------
/site/images/logo-bright.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/site/images/logo-bright.png
--------------------------------------------------------------------------------
/site/images/steve.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/site/images/steve.png
--------------------------------------------------------------------------------
/site/images/tat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/site/images/tat.png
--------------------------------------------------------------------------------
/site/js/empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/site/js/empty
--------------------------------------------------------------------------------
/site/pages/community.md:
--------------------------------------------------------------------------------
1 | Title: Community and Support
2 |
3 | Simple support.
4 |
5 | ## Mailing Lists
6 |
7 | * [Users](mailto:user@steve.apache.org) ([Subscribe](mailto:user-subscribe@steve.apache.org), [Unsubscribe](mailto:user-unsubscribe@steve.apache.org))
8 | * [Developers](mailto:dev@steve.apache.org) ([Subscribe](mailto:dev-subscribe@steve.apache.org), [Unsubscribe](mailto:dev-unsubscribe@steve.apache.org))
9 | * [Commits](mailto:commits@steve.apache.org) ([Subscribe](mailto:commits-subscribe@steve.apache.org), [Unsubscribe](mailto:commits-unsubscribe@steve.apache.org))
10 | * [Issues](mailto:issues@steve.apache.org) ([Subscribe](mailto:issues-subscribe@steve.apache.org), [Unsubscribe](mailto:issues-unsubscribe@steve.apache.org))
11 |
12 |
13 |
14 | ## Archives
15 | * [users](http://steve.markmail.org/search/?q=#query:%20list%3Aorg.apache.steve.user)
16 | * [development](http://steve.markmail.org/search/?q=#query:%20list%3Aorg.apache.steve.dev)
17 | * [commits](http://steve.markmail.org/search/?q=#query:%20list%3Aorg.apache.steve.commits)
18 | * [issues](http://steve.markmail.org/search/?q=#query:%20list%3Aorg.apache.steve.issues)
19 |
20 |
21 | ## JIRA
22 |
23 | * [STEVE](https://issues.apache.org/jira/browse/STEVE)
24 |
25 |
26 | ## Contributing
27 |
28 | Contributing to the project is a great way to support the community. Some great links for getting involved
29 |
30 | - [Source Code](dev/source-code.html)
31 | - [Contribution Tips](dev/contribution-tips.html)
32 | - [Developer Documentation](dev/index.html)
33 | - [IRC](http://webchat.freenode.net/?channels=apache-steve): #apache-steve on freenode.
34 |
35 | ## PMC Members
36 |
37 |
84 |
--------------------------------------------------------------------------------
/site/pages/demo.md:
--------------------------------------------------------------------------------
1 | Title: Quickstart Guide
2 |
3 | **TODO**: rework this for our new git-based repository.
4 |
5 | ----
6 |
7 | ## pySTeVe
8 |
9 | ### Quickstart Guide
10 |
11 | * `svn co https://svn.apache.org/repos/asf/steve/trunk/pysteve/`
12 | * Edit `steve.cfg` to suit your needs (karma, DB backend etc)
13 | * IF you choose ElasticSearch as backend, install the python module (pip install elasticsearch)
14 | * OR IF you choose files as your backend, run setup.py in the CLI directory.
15 | * Edit `httpd.conf`, add it to your existing httpd configuration
16 | * Set up authorization using htpasswd for admins, monitors etc
17 | * Go to `http://steve.example.org/admin` and set up an election
18 | * Start voting!
19 |
20 |
21 | ### Building a Docker image
22 |
23 | You can also build pySTeVe as a Docker image using the Dockerfile locate in the `docker` directory:
24 |
25 | * `svn co https://svn.apache.org/repos/asf/steve/trunk/pysteve/`
26 | * `docker build -t pysteve docker/`
27 | * `docker run -i -p 127.0.0.1:80:80 pysteve`
28 | * Navigate to `http://localhost/admin` to set up stuff, using the default credentials (admin/demo)
29 |
--------------------------------------------------------------------------------
/site/pages/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/site/pages/dev/source-code.mdtext:
--------------------------------------------------------------------------------
1 | Title: Source Code
2 |
3 | ## pySTeVe
4 | PySTeVe can be browsed via the [web interface](https://svn.apache.org/viewvc/steve/trunk/pysteve/).
5 |
6 | You can check out the source code at: [https://svn.apache.org/repos/asf/steve/trunk/pysteve/](https://svn.apache.org/repos/asf/steve/trunk/pysteve/):
7 |
8 |
9 | $svn co https://svn.apache.org/repos/asf/steve/trunk/pysteve/
10 |
11 |
12 | PySTeVe requires no building/compiling, but does require a setup, as explained on the [try it out](/demo.html) page.
13 |
14 |
15 |
16 |
17 | ## Older sources
18 | You can browse the source code via the [web interface](http://svn.apache.org/viewvc/steve/trunk/)
19 | .
20 |
21 | You can download (aka checkout) the sources of Apache STeVe with
22 | Subversion client using the following URL [http://svn.apache.org/repos/asf/steve/trunk](http://svn.apache.org/repos/asf/steve/trunk)
23 | .
24 |
25 | Performing the checkout from the command line using the subversion client
26 | is as easy as executing the following command:
27 | > $ svn co [http://svn.apache.org/repos/asf/steve/trunk](http://svn.apache.org/repos/asf/steve/trunk)
28 |
29 | If you are experiencing problems with errors like "400 Bad Request
30 | (http://svn.apache.org)", try using:
31 | > $ svn co [https://svn.apache.org/repos/asf/steve/trunk](https://svn.apache.org/repos/asf/steve/trunk)
32 |
33 |
34 | # Continuous integration
35 |
36 | Apache STeVe continuous integration relies on [Apache Buildbot](http://ci.apache.org/).
37 | All builders are available [from the page](http://ci.apache.org/builders).
38 |
39 | * [Apache STeVe under Ubuntu](http://ci.apache.org/builders/steve-trunk-ubuntu)
40 | * [Apache STeVe under Windows](http://ci.apache.org/builders/steve-trunk-win)
41 |
42 |
43 | # Building
44 |
45 | *TODO fill in*
46 |
47 |
--------------------------------------------------------------------------------
/site/pages/documentation.md:
--------------------------------------------------------------------------------
1 | Title: Index
2 |
3 | * [List of implemented vote types](vote_types.html)
4 | * [Getting started with pySTeVe](demo.html)
5 |
--------------------------------------------------------------------------------
/site/pages/downloads.md:
--------------------------------------------------------------------------------
1 | title: Downloads
2 |
3 | Apache STeVe has not produced any releases yet. Please ask on our dev@ list.
4 |
--------------------------------------------------------------------------------
/site/pages/index.md:
--------------------------------------------------------------------------------
1 | Title: Home
2 |
3 |
4 | Apache STeVe is the ASF's Python-based voting system that the
5 | Foundation uses to handle things like
6 | [voting in our new Board of Directors](https://www.apache.org/foundation/governance/meetings#boardvoting)
7 |
8 |
9 | Apache STeVe supports many types of voting, including (but not limited to):
10 |
11 | * [Single Transferable Votes](vote_types.html#stv)
12 | * [Single-motion voting (Yes/No/Abstain)](vote_types.html#yna)
13 | * [D'Hondt (Jefferson) style voting](vote_types.html#dh)
14 | * [Multiple Non-Transferable Votes](vote_types.html#mntv)
15 | * [First Past the Post (presidential elections)](vote_types.html#fpp)
16 | * [Candidate or Party voting with preferential trickle-down](vote_types.html#cop)
17 | * [First in Class (N-x-points based voting)](vote_types.html#fic)
18 | * [Apache-style Single-motion Voting](vote_types.html#ap)
19 |
20 |
21 | It also supports multiple database backends:
22 |
23 | * File-based
24 | * ElasticSearch
25 | * More to come...
26 |
--------------------------------------------------------------------------------
/site/pages/privacy-policy.md:
--------------------------------------------------------------------------------
1 | Title: Privacy Policy
2 |
3 | Information about your use of this website is collected using server access logs and a tracking cookie. The collected information consists of:
4 |
5 | * The IP address from which you access the website
6 | * The type of browser and operating system you use to access our site
7 | * The date and time you access our site
8 | * The pages you visit
9 | * The addresses of pages from where you followed a link to our site
10 |
11 |
12 | We gather part of this information using a tracking cookie set by
13 | the [Google Analytics](http://www.google.com/analytics/)
14 | service and handled by Google, as described in
15 | their [privacy policy](http://www.google.com/policies/privacy/).
16 | See your browser documentation for instructions on how to
17 | disable the cookie if you prefer not to share this data with Google.
18 |
19 | We use the gathered information to help us make our site more useful
20 | to visitors and to better understand how and when our site is used. We
21 | do not track or collect personally identifiable information or
22 | associate the data we collect with any personally identifiable information
23 | from other sources.
24 |
25 | By using this website, you consent to the collection of this data in
26 | the manner and for the purposes described above.
27 |
--------------------------------------------------------------------------------
/site/pages/vote_types.md:
--------------------------------------------------------------------------------
1 | Title: Vote Types
2 |
3 |
4 |
Single motion voting (Yes/No/Abstain)
5 |
6 | This is a simple tally vote. Voters can vote either Yes or No on an
7 | issue, or they can abstain. Votes are tallied, and the result is
8 | presented. It is up to the election committee to interpret the result.
9 |
10 | ----
11 |
12 |
Apache-style Single motion voting (Yes/No/Abstain with binding votes)
13 |
14 | This is a simple tally vote. Voters can vote either Yes or No on an
15 | issue, or they can abstain; however, certain people (committee members,
16 | for instance) may cast binding votes whereas others may only cast
17 | non-binding votes. Votes are tallied, and the result is presented. It
18 | is up to the election committee to interpret the result (for example, are there sufficient 'binding' votes to match any requirement that has been set, regardless of the total number of 'yes' and 'no' votes?).
19 |
20 | ----
21 |
22 |
First Past the Post (presidential election style)
23 |
24 | FPP is a voting system with multiple candidates. The candidate with
25 | the most votes wins, regardless of whether they received more than
26 | half the votes.
27 |
28 | ----
29 |
30 |
31 |
Single Transferable Vote
32 |
33 | The single transferable vote (STV) system is designed to achieve
34 | proportional representation through ranked voting in multi-seat
35 | elections. It does so by allowing every voter one vote, that is
36 | transferable to other candidates based on necessity of votes and the
37 | preference of the voter. Thus, if a candidate in an election is voted
38 | in (or in case of a tie), excess votes for that candidate are allocated to other candidates
39 | according to the preference of the voter. STV is designed to minimize
40 | 'wasted votes' in an election by reallocating votes (and thus the
41 | wishes of the voters) proportionally to the priority they stated.
42 |
43 | See the
44 | [Wikipedia article on STV voting](https://en.wikipedia.org/wiki/Single_transferable_vote#Voting)
45 | for more insight into how STV works.
46 |
47 | For calculating results, we use Meek's Method with a quota derived from
48 | the Droop Quota, but with implementation changes such as those
49 | proposed by New Zealand. See
50 | [this paper](http://svn.apache.org/repos/asf/steve/trunk/stv_background/meekm.pdf)
51 | for details.
52 |
53 | ----
54 |
55 |
D'Hondt (Jefferson) Voting
56 |
57 | The D'Hondt method, also known as the Jefferson method, is a *highest
58 | average* method for calculating proportional representation of parties
59 | at an election. In essence, this is done by calculating a quotient
60 | per party for each number of seats available and finding the highest
61 | values. The quotient is determined as `V/(s+1)` where `V` is the
62 | number of votes received and `s` is the number of seats won. Thus, for
63 | each party, the quotient is calculated for the number of seats
64 | available:
65 |
66 | #### Example result for election with 4 seats:
67 |
68 | | Party | Votes | 1 seat | 2 seats | 3 seats | 4 seats | seats won |
69 | |-------|-------|--------|---------|---------|---------|-----------|
70 | | Gnomes | 25,000 | 25,000/(0+1) = 25,000 | 25,000/(1+1) = 12,500 | 25,000/(2+1) = 8,333 | 25,000/(3+1) = 6,250 | 2 |
71 | | Elves | 15,000 | 15,000/(0+1) = 15,000 | 15,000/(1+1) = 7,500 | 15,000/(2+1) = 5,000 | 15,000/(3+1) = 3,750 | 1 |
72 | | Dwarves | 10,000 | 10,000/(0+1) = 10,000 | 10,000/(1+1) = 5,000 | 10,000/(2+1) = 3,333 | 10,000/(3+1) = 2,500 | 1 |
73 |
74 |
75 | For more information on the D'Hondt Method, see
76 | [this Wikipedia article](https://en.wikipedia.org/wiki/D'Hondt_method)
77 |
78 | ----
79 |
80 |
Candidate or Party voting with preferential trickle-down
81 |
82 | This information is not yet available.
83 |
84 | ----
85 |
86 |
First in Class (N-x-points based voting)
87 |
88 | This information is not yet available
89 |
90 | ----
91 |
92 |
Multiple Non-Transferable Votes
93 |
94 | Multiple Non-Transferable Voting (MNTV) is a group of voting systems in which voters elect several representatives at once, with each voter having more than one vote. MNTV uses multi-member electoral 'districts' or some other term for dividing the eligible voters into coherent groups, or only one district which contains all voters, which is used to provide at-large representation.
95 |
--------------------------------------------------------------------------------
/site/test-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Run a local Pelican build of the site, for dev/test.
4 | #
5 | # REQUIRED ENV VARS:
6 | #
7 | # BUILDSITE=/path/to/infrastructure-pelican/bin/buildsite.py
8 | # LIBCMARKDIR=/path/to/cmark-gfm.0.28.3.gfm.12/lib
9 | #
10 | # ### DOCCO on using infra-pel/bin/build-cmark.sh for the lib/
11 | #
12 | # ALSO NEEDED
13 | # $ pip3 install ezt pelican
14 | #
15 |
16 | THIS_SCRIPT=`realpath "$0"`
17 | SITE_DIR=`dirname "$THIS_SCRIPT"`
18 | ROOT_DIR=`dirname "$SITE_DIR"`
19 | #echo $ROOT_DIR
20 |
21 | cd "$SITE_DIR"
22 |
23 | # export BUILDSITE=~/src/asf/infra-pelican/bin/buildsite.py
24 | # export LIBCMARKDIR=~/src/asf/infra/tools/bintray/cmark-gfm.0.28.3.gfm.12-libs
25 |
26 | $BUILDSITE dir --output=/tmp/steve-site.$$ "--yaml-dir=$ROOT_DIR" "--content-dir=$SITE_DIR"
27 |
--------------------------------------------------------------------------------
/site/theme/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Apache STeVe
7 |
8 |
9 |
10 |
20 |
21 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec tincidunt hendrerit ornare. Curabitur ac sem eget orci condimentum convallis vitae sed nisl. Nullam cursus luctus ipsum, et viverra leo interdum ac. Duis in ante efficitur, tincidunt sapien dictum, tempus risus. Ut et convallis ante. Donec sagittis congue nisl, ac maximus eros hendrerit at. In vulputate at ex et imperdiet. Morbi in ligula risus. Aliquam nec efficitur sem. Nam sodales maximus purus, nec blandit odio interdum quis.
22 |
23 |
Ut eu interdum lorem, eu auctor lectus. Duis vitae imperdiet orci, ac condimentum neque. Nam sit amet hendrerit nisi. Sed ut tortor justo. Donec suscipit placerat dolor, in bibendum sem cursus eu. Aenean elementum dictum neque. Nam ullamcorper pretium nibh, imperdiet rhoncus odio aliquam in. Donec dapibus nulla nec lacinia scelerisque. Morbi non suscipit ipsum, id lobortis dolor. Sed pretium libero non tellus finibus tincidunt. Aenean ultricies lacus tortor, eget dictum est laoreet non. Fusce volutpat augue ut velit scelerisque, quis imperdiet erat viverra. Maecenas pretium scelerisque mi condimentum efficitur. Donec commodo dapibus lectus at rutrum. Donec sed ultrices tellus.
24 |
25 |
Curabitur venenatis dui eget nunc fermentum placerat. Fusce semper turpis eu cursus vulputate. Duis non ex ut metus luctus porttitor quis eget magna. Mauris tristique pellentesque tortor, congue semper justo lacinia euismod. In luctus fermentum metus in posuere. Donec auctor ipsum vel tortor lobortis lobortis. Proin gravida purus mi, vel ornare nibh congue eu. Phasellus ipsum diam, vestibulum eget pharetra non, eleifend non sem.
40 |
41 |
42 |
--------------------------------------------------------------------------------
/steve_logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/steve_logo.psd
--------------------------------------------------------------------------------
/stv9_template.txt:
--------------------------------------------------------------------------------
1 |
2 | The following candidates have been nominated for the nine
3 | available positions of Director within the Board of Directors of
4 | The Apache Software Foundation, as defined by Article V of the
5 | ASF Bylaws. In order to maximize effective representation
6 | of all members of the ASF, the 2002-2003 Board of Directors
7 | has resolved that such an election will be decided according
8 | to the Single Transferable Vote (STV) election process, as
9 | described below.
10 |
11 | STV:
12 | Board elections are performed via Single Transferable Vote (STV). Each
13 | voter lists all the people they want to see elected in order of
14 | preference. Excess votes for people who have already reached their quota
15 | (i.e., enough votes to ensure election) and also for the candidate with
16 | the least votes are redistributed to lower-numbered choices. This process
17 | is repeated until the required number are elected. Please see:
18 |
19 | http://en.wikipedia.org/wiki/Single_transferable_vote
20 |
21 | Election results are calculated using Meek's method for STV:
22 |
23 | http://en.wikipedia.org/wiki/Counting_Single_Transferable_Votes#Meek.27s_method
24 |
25 | The key idea to keep in mind is that the ordering of your votes
26 | is crucial. Those who you *really* want to be elected should
27 | be at the beginning/start of the list. For example:
28 |
29 | Aragorn, Frodo, Bilbo, Sauron, Gandalf, Treebeard, Gollum, Gimli
30 |
31 | means that you really want Aragorn to be elected (he's
32 | your primary preferred person), followed by Frodo (you want
33 | Frodo on the board more than you want Bilbo and Sauron, but
34 | not as much as you want Aragorn), followed by Bilbo, etc...
35 |
36 | There are 9 slots available; you should select all the candidates you
37 | want to be on the board, in the order in which you prefer them. You can
38 | select less than 9 people, and you can also select more than 9 people.
39 |
40 | More information can be found at: foundation:/voter
41 |
42 | ---
43 | Nominations:
44 |
45 | Label Name
46 |
47 |
48 |
49 | Candidate nomination and position statements can be found at:
50 |
51 | https://svn.apache.org/repos/private/foundation/Meetings/YYYYMMDD/board_ballot_YYYY_MM.txt
52 | https://svn.apache.org/repos/private/foundation/Meetings/YYYYMMDD/board_nominations.txt
53 |
54 |
--------------------------------------------------------------------------------
/stv_background/meekm.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/stv_background/meekm.pdf
--------------------------------------------------------------------------------
/v3/bin/.placeholder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/v3/bin/.placeholder
--------------------------------------------------------------------------------
/v3/queries.yaml:
--------------------------------------------------------------------------------
1 | queries:
2 | salt_issue: UPDATE ISSUES SET salt = ? WHERE _ROWID_ = ?
3 | salt_person: UPDATE PERSON SET salt = ? WHERE _ROWID_ = ?
4 | open_election: UPDATE METADATA SET salt = ?, opened_key = ?
5 | close_election: UPDATE METADATA SET closed = 1
6 | add_issue: |
7 | INSERT INTO ISSUES VALUES (?, ?, ?, ?, ?, ?)
8 | ON CONFLICT DO UPDATE SET
9 | title=excluded.title,
10 | description=excluded.description,
11 | type=excluded.type,
12 | kv=excluded.kv
13 | add_person: |
14 | INSERT INTO PERSON VALUES (?, ?, ?, ?)
15 | ON CONFLICT DO UPDATE SET
16 | name=excluded.name,
17 | email=excluded.email
18 | delete_issue: DELETE FROM ISSUES WHERE iid = ?
19 | delete_person: DELETE FROM PERSON WHERE pid = ?
20 | add_vote: INSERT INTO VOTES VALUES (NULL, ?, ?, ?, ?)
21 | has_voted: |
22 | SELECT 1 FROM VOTES
23 | WHERE person_token = ? AND issue_token = ?
24 | LIMIT 1
25 | metadata: SELECT * FROM METADATA
26 | issues: SELECT * FROM ISSUES ORDER BY iid
27 | person: SELECT * FROM PERSON ORDER BY pid
28 | get_issue: SELECT * FROM ISSUES WHERE iid = ?
29 | get_person: SELECT * FROM PERSON WHERE pid = ?
30 | by_issue: SELECT * FROM VOTES WHERE issue_token = ? ORDER BY _ROWID_
31 |
--------------------------------------------------------------------------------
/v3/requirements.txt:
--------------------------------------------------------------------------------
1 | passlib
2 | argon2-cffi
3 | cryptography
4 |
5 | # strictly speaking, optional:
6 | coverage
7 |
--------------------------------------------------------------------------------
/v3/schema.sql:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | /* ### TBD DOCCO. */
21 |
22 | /* ### $ sqlite3 testing.db < schema.sql
23 | ###
24 | ### OR:
25 | ### >>> import sqlite3
26 | ### >>> conn = sqlite3.connect('testing.db')
27 | ### >>> conn.executescript(open('schema.sql').read())
28 | ###
29 | ### ? maybe: conn.commit() and/or conn.close() ... the DML statements
30 | ### don't seem to require full closure of connection.
31 | */
32 |
33 |
34 | /* --------------------------------------------------------------------- */
35 |
36 | /* Various metadata about the Election contained in this database.
37 | Only one row will exist.
38 |
39 | An Election has three states:
40 |
41 | 1. Editable. The election is being set up. Issues and persons of
42 | record can be added, edited, and deleted. The Election's title
43 | may be changed (EID is fixed, however).
44 | DEFINITION: salt and opened_key are NULL. closed is n/a.
45 |
46 | 2. Open. The election is now open for voting.
47 | DEFINITION: salt and opened_key are NOT NULL. closed is NULL or 0.
48 |
49 | 3. Closed. The election is closed.
50 | DEFINITION: salt and opened_key are NOT NULL. closed is 1.
51 | */
52 | CREATE TABLE METADATA (
53 |
54 | /* The Election ID. This value might be replicated in the
55 | filesystem holding this database. To remain independent of
56 | application file choices, the ID is stored here. */
57 | eid TEXT PRIMARY KEY NOT NULL,
58 |
59 | /* Title of this election. */
60 | title TEXT NOT NULL,
61 |
62 | /* ### if we have monitors, they go here. */
63 | /* ### maybe add an owner? */
64 |
65 | /* A salt value to use for hashing this Election. 16 bytes.
66 | This will be NULL until the Election is opened. */
67 | salt BLOB,
68 |
69 | /* If this Election has been opened for voting, then we store
70 | the OpenedKey here to avoid recomputing. 32 bytes.
71 | This will be NULL until the Election is opened. */
72 | opened_key BLOB,
73 |
74 | /* Has this election been closed? NULL or 0 for not-closed (see
75 | SALT and OPENED_KEY to determine if the election has been
76 | opened). 1 for closed (implies it was opened). */
77 | closed INTEGER
78 |
79 | ) STRICT;
80 |
81 | /* --------------------------------------------------------------------- */
82 |
83 | /* The set of issues to vote upon in this Election. */
84 | CREATE TABLE ISSUES (
85 |
86 | /* The Issue ID, matching [-a-zA-Z0-9]+ */
87 | iid TEXT PRIMARY KEY NOT NULL,
88 |
89 | /* Simple one-line title for this issue. */
90 | title TEXT NOT NULL,
91 |
92 | /* An optional, longer description of the issue. */
93 | description TEXT,
94 |
95 | /* The type of this issue's vote mechanism (eg. yna, stv, ...). This
96 | is one of an enumerated set of values.
97 | ### see for the enumeration. */
98 | type TEXT NOT NULL,
99 |
100 | /* Per-type set of key/value pairs specifying additional data. This
101 | value is JSON-formatted */
102 | kv TEXT,
103 |
104 | /* A salt value to use for hashing this Issue. 16 bytes.
105 | This will be NULL until the Election is opened. */
106 | salt BLOB
107 |
108 | ) STRICT;
109 |
110 | /* --------------------------------------------------------------------- */
111 |
112 | /* The set of people "on record" for this Election. Only these people
113 | may vote. */
114 | CREATE TABLE PERSON (
115 |
116 | /* An id assigned to the person (eg. an LDAP username). */
117 | pid TEXT PRIMARY KEY NOT NULL,
118 |
119 | /* Optional human-readable name for this person. */
120 | name TEXT,
121 |
122 | /* How to contact this person (ie. to send a ballot link). */
123 | email TEXT NOT NULL,
124 |
125 | /* A salt value to use for hashing this Person. 16 bytes.
126 | This will be NULL until the Election is opened. */
127 | salt BLOB
128 |
129 | ) STRICT;
130 |
131 | /* --------------------------------------------------------------------- */
132 |
133 | /* The registered votes, once the Election has been opened. Note that
134 | duplicates of (person, issue) may occur, as re-voting is allowed. Only
135 | the latest is used. */
136 | CREATE TABLE VOTES (
137 |
138 | /* The key is auto-incrementing to provide a record of insert-order,
139 | so that we have an ordering to find the "most recent" when
140 | re-voting on an issue.
141 | Note: an integer primary key is an alias for _ROWID_. */
142 | vid INTEGER PRIMARY KEY AUTOINCREMENT,
143 |
144 | /* A hashed token representing a single Person. 32 bytes. */
145 | person_token BLOB NOT NULL,
146 |
147 | /* A hashed token representing an issue. 32 bytes. */
148 | issue_token BLOB NOT NULL,
149 |
150 | /* A binary value used to salt the token's encryption. 16 bytes. */
151 | salt BLOB NOT NULL,
152 |
153 | /* An encrypted form of the vote. */
154 | token BLOB NOT NULL
155 |
156 | ) STRICT;
157 |
158 | CREATE INDEX I_BY_PERSON ON VOTES (person_token);
159 | CREATE INDEX I_BY_ISSUE ON VOTES (issue_token);
160 |
161 | /* --------------------------------------------------------------------- */
162 |
--------------------------------------------------------------------------------
/v3/server/main.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/v3/server/main.py
--------------------------------------------------------------------------------
/v3/server/static/.placeholder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/v3/server/static/.placeholder
--------------------------------------------------------------------------------
/v3/server/templates/.placeholder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/v3/server/templates/.placeholder
--------------------------------------------------------------------------------
/v3/steve/__init__.py:
--------------------------------------------------------------------------------
1 | # TBD
2 |
--------------------------------------------------------------------------------
/v3/steve/crypto.py:
--------------------------------------------------------------------------------
1 | #
2 | # Licensed to the Apache Software Foundation (ASF) under one or more
3 | # contributor license agreements. See the NOTICE file distributed with
4 | # this work for additional information regarding copyright ownership.
5 | # The ASF licenses this file to You under the Apache License, Version 2.0
6 | # (the "License"); you may not use this file except in compliance with
7 | # the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # ### TBD docco
18 | #
19 | #
20 |
21 | import base64
22 | import random
23 |
24 | import passlib.hash # note that .argon2 is proxy in this pkg
25 | import passlib.utils # for the RNG, to create Salt values
26 |
27 | import cryptography.fernet
28 |
29 | # All salt values will be 16 bytes in length. After base64 encoding, they
30 | # will be represented with 22 characters.
31 | SALT_LEN = 16
32 |
33 |
34 | def gen_salt() -> bytes:
35 | "Generate bytes to be used as a salt, for hashing."
36 | return passlib.utils.getrandbytes(passlib.utils.rng, SALT_LEN)
37 |
38 |
39 | def gen_opened_key(edata: bytes, salt: bytes) -> bytes:
40 | "Generate the OpenedKey for this election."
41 | return _hash(edata, salt)
42 |
43 |
44 | def gen_token(opened_key: bytes, value: str, salt: bytes) -> bytes:
45 | "Generate a person or issue token."
46 | return _hash(opened_key + value.encode(), salt)
47 |
48 |
49 | ### fix return type, to be a tuple
50 | def create_vote(person_token: bytes,
51 | issue_token: bytes,
52 | votestring: str) -> bytes:
53 | "Create a vote tuple, to record the VOTESTRING."
54 | salt = gen_salt()
55 | key = _hash(person_token + issue_token, salt)
56 | b64key = base64.urlsafe_b64encode(key)
57 | f = cryptography.fernet.Fernet(b64key)
58 | return salt, f.encrypt(votestring.encode())
59 |
60 |
61 | def decrypt_votestring(person_token: bytes,
62 | issue_token: bytes,
63 | salt: bytes,
64 | token: bytes) -> str:
65 | "Decrypt TOKEN into a VOTESTRING."
66 | key = _hash(person_token + issue_token, salt)
67 | b64key = base64.urlsafe_b64encode(key)
68 | f = cryptography.fernet.Fernet(b64key)
69 | return f.decrypt(token).decode()
70 |
71 |
72 | def _hash(data: bytes, salt: bytes) -> bytes:
73 | "Apply our desired hashing function."
74 | ph = passlib.hash.argon2.using(type='d', salt=salt)
75 | h = ph.hash(data)
76 | return base64.standard_b64decode(h.split('$')[-1] + '==')
77 |
78 |
79 | def shuffle(x):
80 | "Ensure we use the strongest RNG available for shuffling."
81 | return random.shuffle(x, passlib.utils.rng.random)
82 |
--------------------------------------------------------------------------------
/v3/steve/db.py:
--------------------------------------------------------------------------------
1 | #
2 | # Licensed to the Apache Software Foundation (ASF) under one or more
3 | # contributor license agreements. See the NOTICE file distributed with
4 | # this work for additional information regarding copyright ownership.
5 | # The ASF licenses this file to You under the Apache License, Version 2.0
6 | # (the "License"); you may not use this file except in compliance with
7 | # the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # ----
18 | #
19 | # Convenience wrapper for working with a SQLite database.
20 | #
21 | # This wrapper has several primary purposes:
22 | #
23 | # 1. Easily create a cursor for each statement that might be
24 | # executed by the application.
25 | # 2. Remember the specific string object for those statements,
26 | # and re-use them in cursor.execute() for better performance.
27 | # 3. Rows fetched with SELECT statements are wrapped into a
28 | # namedtuple() instance, such that columns can be easily
29 | # accessed as attributes or numerically indexed as a tuple,
30 | #
31 |
32 | import sqlite3
33 | import collections
34 | import functools
35 |
36 |
37 | class DB:
38 |
39 | def __init__(self, fname):
40 |
41 | def row_factory(cursor, row):
42 | "Possibly apply namedtuple() to the returned row."
43 | return self.factories.get(cursor, lambda *row: row)(*row)
44 |
45 | # Note: isolation_level=None means autocommit mode.
46 | self.conn = sqlite3.connect(fname, isolation_level=None)
47 | self.conn.row_factory = row_factory
48 |
49 | # CURSOR : FACTORY
50 | self.factories = { }
51 |
52 | def _cursor_for(self, statement):
53 | return self.conn.cursor(functools.partial(NamedTupleCursor,
54 | statement))
55 |
56 | def add_query(self, table, query):
57 | "Return a cursor to use for this QUERY against TABLE."
58 |
59 | # The query must select all columns.
60 | assert query[:9].lower() == 'select * '
61 |
62 | # Get all column names for TABLE.
63 | cur = self.conn.execute(f'select * from {table} limit 1')
64 | names = [ info[0] for info in cur.description ]
65 |
66 | # We don't need the results, but cannot leave the cursor hanging,
67 | # as it establishes a lock on this table. This likely closes as
68 | # this method exits, but let's not rely upon that.
69 | cur.close()
70 |
71 | # Create a factory for turning rows into namedtuples.
72 | factory = collections.namedtuple(f'row_factory_{len(self.factories)}',
73 | names, rename=True,
74 | module=DB.__module__)
75 |
76 | # Register the row-wrapper factory for this cursor.
77 | cursor = self._cursor_for(query)
78 | self.factories[cursor] = factory
79 | return cursor
80 |
81 | def add_statement(self, statement):
82 | "Return a cursor for use with a DML SQL statement."
83 |
84 | # Note: rows should not be returned for this statement, and
85 | # (thus) the row_factory should not be called. If called, the
86 | # original row will be returned.
87 | return self._cursor_for(statement)
88 |
89 |
90 | class NamedTupleCursor(sqlite3.Cursor):
91 |
92 | def __init__(self, statement, *args, **kw):
93 | super().__init__(*args, **kw)
94 | self.statement = statement
95 |
96 | def perform(self, params=()):
97 | "Perform the statement with PARAMs, or prepare the query."
98 |
99 | # Use the exact same STATEMENT each time. Python's SQLite module
100 | # caches the parsed statement, if the string is the same object.
101 | self.execute(self.statement, params)
102 |
103 | def first_row(self, params=()):
104 | "Helper method to fetch the first row of a query."
105 | self.perform(params)
106 | row = self.fetchone()
107 | _ = self.fetchall() # run the cursor to completion; should be empty
108 | return row
109 |
--------------------------------------------------------------------------------
/v3/steve/vtypes/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 | # Licensed to the Apache Software Foundation (ASF) under one or more
3 | # contributor license agreements. See the NOTICE file distributed with
4 | # this work for additional information regarding copyright ownership.
5 | # The ASF licenses this file to You under the Apache License, Version 2.0
6 | # (the "License"); you may not use this file except in compliance with
7 | # the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 |
18 | # NOTE: not using a dynamic system. Just define/import what we know.
19 | TYPES = { 'yna', 'stv' }
20 |
21 | from . import yna
22 | from . import stv
23 |
24 |
25 | def vtype_module(vtype):
26 | "Return the vote type's module."
27 | assert vtype in TYPES
28 |
29 | return globals()[vtype]
30 |
--------------------------------------------------------------------------------
/v3/steve/vtypes/stv.py:
--------------------------------------------------------------------------------
1 | #
2 | # Licensed to the Apache Software Foundation (ASF) under one or more
3 | # contributor license agreements. See the NOTICE file distributed with
4 | # this work for additional information regarding copyright ownership.
5 | # The ASF licenses this file to You under the Apache License, Version 2.0
6 | # (the "License"); you may not use this file except in compliance with
7 | # the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # ----
18 | #
19 | # ### TBD: DOCCO
20 | #
21 |
22 | import os.path
23 | import importlib
24 |
25 | # Where can we find the stv_tool module?
26 | STV_RELPATH = '../../../monitoring/stv_tool.py'
27 |
28 |
29 | def load_stv():
30 | pathname = os.path.join(os.path.dirname(__file__), STV_RELPATH)
31 | spec = importlib.util.spec_from_file_location('stv_tool', pathname)
32 | module = importlib.util.module_from_spec(spec)
33 | spec.loader.exec_module(module)
34 | return module
35 |
36 |
37 | # load_stv() loads the module (again) on each call. Each one is separate,
38 | # with (eg.) a distinct VERBOSE flag. Note that sys.modules is completely
39 | # uninvolved in this process. Thus, let's load this once. (this is
40 | # effectively a fancy "import" statement)
41 | stv_tool = load_stv()
42 |
43 |
44 | def tally(votestrings, kv, names=None):
45 | "Run the STV tally process."
46 |
47 | # NOTE: the NAMES parameter is usually not passed, but is available
48 | # to compare operation against custom ordering of NAMES in the
49 | # LABELMAP. This function takes a specific approach, which differs
50 | # from historical orderings.
51 |
52 | # kv['labelmap'] should be: LABEL: NAME
53 | # for example: { 'a': 'John Doe', }
54 | labelmap = kv['labelmap']
55 |
56 | seats = kv['seats']
57 |
58 | # Remap all votestrings from a string sequence of label characters,
59 | # into a sequence of NAMEs.
60 | votes = [[labelmap[c] for c in v] for v in votestrings]
61 |
62 | # NOTE: it is important that the names are sorted, to create a
63 | # reproducible list of names. Callers may specify a custom ordering.
64 | if names is None:
65 | names = sorted(labelmap.values())
66 | results = stv_tool.run_stv(names, votes, seats)
67 |
68 | human = '\n'.join(
69 | f'{c.name:40}{" " if c.status == stv_tool.ELECTED else " not "}elected'
70 | for c in results.l
71 | )
72 | data = { 'raw': results, }
73 | return human, data
74 |
--------------------------------------------------------------------------------
/v3/steve/vtypes/yna.py:
--------------------------------------------------------------------------------
1 | #
2 | # Licensed to the Apache Software Foundation (ASF) under one or more
3 | # contributor license agreements. See the NOTICE file distributed with
4 | # this work for additional information regarding copyright ownership.
5 | # The ASF licenses this file to You under the Apache License, Version 2.0
6 | # (the "License"); you may not use this file except in compliance with
7 | # the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 | # ----
18 | #
19 | # ### TBD: DOCCO
20 | #
21 |
22 | # The votestring will be lower-cased. What is the result?
23 | YES = { 'y', 'yes', '1', 'true', }
24 | NO = { 'n', 'no', '0', 'false', }
25 | # "abstain" is any other (non-affirmative) value.
26 |
27 |
28 | def tally(votestrings, kv):
29 | y = n = a = 0
30 | for v in votestrings:
31 | if v in YES:
32 | y += 1
33 | elif v in NO:
34 | n += 1
35 | else:
36 | a += 1
37 |
38 | human = f'''\
39 | Yes: {y:#4}
40 | No: {n:#4}
41 | Abstain: {a:#4}'''
42 |
43 | return human, {'y': y, 'n': n, 'a': a,}
44 |
--------------------------------------------------------------------------------
/v3/test/check_coverage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | #
20 | # ----
21 | #
22 | # ### TBD: DOCCO
23 | #
24 |
25 |
26 | import sys
27 | import os.path
28 | import sqlite3
29 | import json
30 |
31 | import coverage
32 |
33 | # Ensure that we can import the "steve" package.
34 | THIS_DIR = os.path.realpath(os.path.dirname(__file__))
35 | PARENT_DIR = os.path.dirname(THIS_DIR)
36 | sys.path.insert(0, PARENT_DIR)
37 |
38 | TESTING_DB = os.path.join(THIS_DIR, 'covtest.db')
39 | SCHEMA_FILE = os.path.join(PARENT_DIR, 'schema.sql')
40 |
41 |
42 | def touch_every_line():
43 | "A minimal test to run each line in the 'steve' package."
44 |
45 | # Do the import *WITHIN* the coverage test.
46 | import steve.election
47 |
48 | eid = steve.election.new_eid()
49 |
50 | # Start the election, and open it.
51 | try:
52 | os.remove(TESTING_DB)
53 | except OSError:
54 | pass
55 | conn = sqlite3.connect(TESTING_DB)
56 | conn.executescript(open(SCHEMA_FILE).read())
57 | conn.execute('INSERT INTO METADATA'
58 | f' VALUES ("{eid}", "title", NULL, NULL, NULL)')
59 | conn.commit()
60 |
61 | # Ready to load up the Election and exercise it.
62 | e = steve.election.Election(TESTING_DB)
63 |
64 | _ = e.get_metadata() # while EDITABLE
65 |
66 | e.add_person('alice', 'Alice', 'alice@example.org')
67 | e.add_person('bob', None, 'bob@example.org')
68 | e.add_person('carlos', 'Carlos', 'carlos@example.org')
69 | e.add_person('david', None, 'david@example.org')
70 | _ = e.list_persons()
71 | e.delete_person('david')
72 | _ = e.get_person('alice')
73 |
74 | e.add_issue('a', 'issue A', None, 'yna', None)
75 | e.add_issue('b', 'issue B', None, 'stv', {
76 | 'seats': 3,
77 | 'labelmap': {
78 | 'a': 'Alice',
79 | 'b': 'Bob',
80 | 'c': 'Carlos',
81 | 'd': 'David',
82 | 'e': 'Eve',
83 | },
84 | })
85 | _ = e.list_issues()
86 | e.add_issue('c', 'issue C', None, 'yna', None)
87 | e.delete_issue('c')
88 | _ = e.get_issue('a')
89 |
90 | e.open()
91 | _ = e.get_metadata() # while OPEN
92 | e.add_vote('alice', 'a', 'y')
93 | e.add_vote('bob', 'a', 'n')
94 | e.add_vote('carlos', 'a', 'a') # use each of Y/N/A
95 | e.add_vote('alice', 'b', 'bc')
96 | e.add_vote('bob', 'b', 'ad')
97 | _ = e.has_voted_upon('alice')
98 | _ = e.is_tampered()
99 |
100 | e.close()
101 | _ = e.get_metadata() # while CLOSED
102 | _ = e.tally_issue('a')
103 | _ = e.tally_issue('b')
104 |
105 |
106 | def main():
107 | cov = coverage.Coverage(
108 | data_file=None, branch=True, config_file=False,
109 | source_pkgs=['steve'], messages=True,
110 | )
111 | cov.start()
112 |
113 | try:
114 | touch_every_line()
115 | finally:
116 | cov.stop()
117 |
118 | cov.report(file=sys.stdout)
119 | cov.html_report(directory='covreport')
120 |
121 |
122 | if __name__ == '__main__':
123 | main()
124 |
--------------------------------------------------------------------------------
/v3/test/populate_v2_stv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | #
20 |
21 | #
22 | # Gather "all" known STV voting processing into a reference directory,
23 | # with full debug output. These files can then be used as a comparison
24 | # to future developments on STV tooling, to ensure consistency.
25 | #
26 |
27 | if test "$1" = ""; then echo "USAGE: $0 MEETINGS_DIR"; exit 1; fi
28 | MEETINGS_DIR="$1"
29 |
30 | REFERENCE_DIR="v2-stv-ref"
31 | mkdir "$REFERENCE_DIR" || /bin/true
32 |
33 | V3_DIR="v3-stv"
34 | mkdir "$V3_DIR" || /bin/true
35 |
36 | THIS_FILE=`realpath $0`
37 | THIS_DIR=`dirname "$THIS_file"`
38 | #echo $THIS_FILE
39 | STV_TOOL=`realpath "$THIS_DIR/../../monitoring/stv_tool.py"`
40 | echo $STV_TOOL
41 | V3_TOOL="${THIS_DIR}/run_stv.py"
42 |
43 | #echo "ls $MEETINGS_DIR/*/raw_board_votes.txt"
44 |
45 | for v in `ls $MEETINGS_DIR/*/raw_board_votes.txt`; do
46 | #echo $v
47 | DATE=`echo $v | sed -n 's#.*/\([0-9]*\)/.*#\1#p'`
48 | echo $DATE
49 | "$STV_TOOL" -v "$v" > "$REFERENCE_DIR/$DATE"
50 |
51 | MTG_DIR=`dirname "$v"`
52 | #echo $MTG_DIR
53 | "$V3_TOOL" "${MTG_DIR}" > "${V3_DIR}/$DATE"
54 | done
55 |
--------------------------------------------------------------------------------
/v3/test/run_stv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | #
20 | # ----
21 | #
22 | # ### TBD: DOCCO
23 | # USAGE: run_stv.py .../Meetings/yyyymmdd
24 | #
25 |
26 | import sys
27 | import os.path
28 |
29 | # Ensure that we can import the "steve" package.
30 | THIS_DIR = os.path.realpath(os.path.dirname(__file__))
31 | sys.path.insert(0, os.path.dirname(THIS_DIR))
32 | import steve.vtypes.stv
33 |
34 | # The stv module loads the stv_tool module. Tweak it.
35 | stv_tool = steve.vtypes.stv.stv_tool
36 | stv_tool.VERBOSE = True
37 |
38 |
39 | def main(mtgdir):
40 | rawfile = os.path.join(mtgdir, 'raw_board_votes.txt')
41 | labelfile = os.path.join(mtgdir, 'board_nominations.ini')
42 |
43 | assert os.path.exists(rawfile)
44 | assert os.path.exists(labelfile)
45 |
46 | labelmap = stv_tool.read_labelmap(labelfile)
47 | votes = stv_tool.read_votefile(rawfile).values()
48 |
49 | # Construct a label-sorted list of names from the labelmap.
50 | names = [name for _, name in sorted(labelmap.items())]
51 |
52 | kv = {
53 | 'labelmap': labelmap,
54 | 'seats': 9,
55 | }
56 |
57 | # NOTE: for backwards-compat, the tally() function accepts a
58 | # list of names with caller-defined sorting.
59 | human, _ = steve.vtypes.stv.tally(votes, kv, names)
60 |
61 | # For the comparison purposes:
62 | print(human)
63 | print('Done!')
64 |
65 |
66 | if __name__ == '__main__':
67 | main(sys.argv[1])
68 |
--------------------------------------------------------------------------------
/whatif.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | #####
4 | # Licensed to the Apache Software Foundation (ASF) under one or more
5 | # contributor license agreements. See the NOTICE file distributed with
6 | # this work for additional information regarding copyright ownership.
7 | # The ASF licenses this file to You under the Apache License, Version 2.0
8 | # (the "License"); you may not use this file except in compliance with
9 | # the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an "AS IS" BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 | #####
19 |
20 | # Run alternate voting scenarios: vary the number of seats, remove candidates,
21 | # or do a run-off with only the specified candidates. Candidate names can
22 | # be specified using either their first name, last name, or full name without
23 | # any spaces (independent of case). Examples:
24 | #
25 | # Examples:
26 | # whatif.py ../Meetings/20110712/raw_board_votes.txt 10
27 | # whatif.py ../Meetings/20110712/raw_board_votes.txt -LawrenceRosen
28 | # whatif.py ../Meetings/20110712/raw_board_votes.txt 1 kulp noirin geir chris
29 |
30 | import os.path
31 | import sys
32 | import re
33 |
34 | sys.path.append(os.path.join(os.path.dirname(__file__), 'monitoring'))
35 | import stv_tool
36 |
37 | def usage():
38 | print('Usage: %s [-v] RAW_VOTES_FILE [seats] [-]name...' % scriptname)
39 | sys.exit(1)
40 |
41 | if __name__ == '__main__':
42 | scriptname = sys.argv.pop(0)
43 |
44 | if sys.argv and sys.argv[0] == '-v':
45 | stv_tool.VERBOSE = True
46 | sys.argv.pop(0)
47 |
48 | # extract required vote file argument, and load votes from it
49 | if not sys.argv or not os.path.exists(sys.argv[0]): usage()
50 | votefile = sys.argv.pop(0)
51 | names, votes = stv_tool.load_votes(votefile)
52 |
53 | # extract optional number of seats argument
54 | if sys.argv and sys.argv[0].isdigit():
55 | seats = int(sys.argv.pop(0))
56 | else:
57 | seats = 9
58 |
59 | # extract an alias list of first, last, and joined names
60 | alias = {}
61 | for name in names:
62 | lname = re.sub(r'[^\w ]', '', name.lower())
63 | alias[lname.replace(' ', '')] = name
64 | for part in lname.split(' '):
65 | alias[part] = name
66 |
67 | # validate input
68 | for arg in sys.argv:
69 | if arg.lstrip('-').lower() not in alias:
70 | sys.stderr.write('invalid selection: %s\n' % arg)
71 | usage()
72 |
73 | if not sys.argv:
74 | # no changes to the candidates running
75 | pass
76 | elif sys.argv[0][0] == '-':
77 | # remove candidates from vote
78 | for name in sys.argv: names.remove(alias[name.lstrip('-').lower()])
79 | else:
80 | # only include specified candidates
81 | # NOTE: order is important, so sort the names for repeatability
82 | names = [ alias[n.lower()] for n in sorted(sys.argv) ]
83 |
84 | # Trim the raw votes based on cmdline params. Eliminate votes that
85 | # are not for one of the allowed names. Do not include voters who
86 | # did not vote for anybody [post-trimming].
87 | trimmed = [ ]
88 | for voteseq in votes:
89 | newseq = [ v for v in voteseq if v in names ]
90 | if newseq:
91 | trimmed.append(newseq)
92 |
93 | # run the vote
94 | candidates = stv_tool.run_stv(names, trimmed, seats)
95 | candidates.print_results()
96 | print('Done!')
97 |
--------------------------------------------------------------------------------
/www/cgi-bin/cast-vote.pl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apache/steve/14288373e8db8832fd566f0d82fd879b21ef47c7/www/cgi-bin/cast-vote.pl
--------------------------------------------------------------------------------
/www/cgi-bin/redirect.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -T
2 | #####
3 | # Licensed to the Apache Software Foundation (ASF) under one or more
4 | # contributor license agreements. See the NOTICE file distributed with
5 | # this work for additional information regarding copyright ownership.
6 | # The ASF licenses this file to You under the Apache License, Version 2.0
7 | # (the "License"); you may not use this file except in compliance with
8 | # the License. You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #####
18 |
19 | #
20 | # redirect.pl - simple redirect CGI script that changes the referer header
21 | #
22 |
23 | use strict;
24 | use warnings;
25 | use CGI;
26 | use CGI::Carp qw/fatalsToBrowser/;
27 |
28 | my $q = CGI->new;
29 | my $uri = $q->param("uri") or die "Can't find uri param";
30 | print $q->header;
31 | print <
33 |
34 |
35 |
36 |
37 | Redirecting to $uri ...
38 |
39 |