Author: humbedooh
Date: Thu Mar 26 11:12:52 2015
New Revision: 1669319
URL: http://svn.apache.org/r1669319
Log:
Add Apache Style voting (yna with binding/non-binding votes)
Added:
steve/trunk/pysteve/lib/plugins/ap.py
steve/trunk/pysteve/www/htdocs/ballot_ap.html
steve/trunk/pysteve/www/htdocs/js/steve_ap.js
Modified:
steve/trunk/pysteve/lib/plugins/__init__.py
steve/trunk/pysteve/www/htdocs/js/steve_rest.js
Modified: steve/trunk/pysteve/lib/plugins/__init__.py
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/lib/plugins/__init__.py?rev=1669319&r1=1669318&r2=1669319&view=diff
==============================================================================
--- steve/trunk/pysteve/lib/plugins/__init__.py (original)
+++ steve/trunk/pysteve/lib/plugins/__init__.py Thu Mar 26 11:12:52 2015
@@ -24,6 +24,7 @@ CORE VOTE PLUGINS:
mntv: Multiple Non-Transferable Votes
cop: Candidate or Party Voting
fic: First in Class Voting
+ ap: Apache PMC Style voting
"""
-__all__ = ['yna','stv','dh','fpp','mntv','cop','fic']
\ No newline at end of file
+__all__ = ['yna','stv','dh','fpp','mntv','cop','fic','ap']
\ No newline at end of file
Added: steve/trunk/pysteve/lib/plugins/ap.py
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/lib/plugins/ap.py?rev=1669319&view=auto
==============================================================================
--- steve/trunk/pysteve/lib/plugins/ap.py (added)
+++ steve/trunk/pysteve/lib/plugins/ap.py Thu Mar 26 11:12:52 2015
@@ -0,0 +1,76 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+""" ASF PMC style voting plugin """
+from lib import constants, voter
+
+def tallyAP(votes, issue):
+ """ Simple YNA tallying
+ :param votes: The JSON object from $issueid.json.votes
+ :return: y,n,a as numbers
+ """
+ y = n = a = 0
+ by = bn = 0
+ for vote in votes.values():
+ if vote == 'y':
+ y += 1
+ if vote == 'n':
+ n += 1
+ if vote == 'a':
+ a += 1
+ if vote == 'by':
+ by += 1
+ if vote == 'bn':
+ bn += 1
+
+ return {
+ 'votes': len(votes),
+ 'yes': y,
+ 'no': n,
+ 'abstain': a,
+ 'binding_yes': by,
+ 'binding_no': bn
+ }
+
+
+def validateAP(vote, issue):
+ "Tries to invalidate a vote, returns why if succeeded, None otherwise"
+ letters = ['y','n','a', 'by', 'bn']
+ if len(vote) >= 3 or not vote in letters:
+ return "Invalid vote. Accepted votes are: %s" % ", ".join(letters)
+ return None
+
+
+# Verification process
+def verifyAP(basedata, issueID, voterID, vote):
+ "Invalidate a binding vote if not allowed to cast such"
+ email = voter.get(basedata['id'], basedata, voterID)
+ if vote.startswith('b'):
+ # Simple check example: if not apache committer, discard vote if
binding
+ if not email.endswith("@apache.org"):
+ raise Exception("You are not allowed to cast a binding vote!")
+
+
+constants.VOTE_TYPES += (
+ {
+ 'key': "ap",
+ 'description': "ASF PMC Style vote (YNA with binding votes)",
+ 'category': 'ap',
+ 'validate_func': validateAP,
+ 'vote_func': verifyAP,
+ 'tally_func': tallyAP
+ },
+)
\ No newline at end of file
Added: steve/trunk/pysteve/www/htdocs/ballot_ap.html
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/htdocs/ballot_ap.html?rev=1669319&view=auto
==============================================================================
--- steve/trunk/pysteve/www/htdocs/ballot_ap.html (added)
+++ steve/trunk/pysteve/www/htdocs/ballot_ap.html Thu Mar 26 11:12:52 2015
@@ -0,0 +1,41 @@
+ <!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="stylesheet" href="css/steve_interactive.css">
+<link rel="stylesheet" href="css/jquery-ui.css">
+<script src="js/steve_rest.js" type="text/javascript"></script>
+<script src="js/steve_ap.js" type="text/javascript"></script>
+<script src="js/jquery.js" type="text/javascript"></script>
+<script src="js/jquery-ui.js" type="text/javascript"></script>
+<title>Apache STeVe: ASF PMC style vote</title>
+
+</head>
+<body onload="window.setTimeout(loadIssue, 500, null, null, null,
displayIssueAP);">
+ <div id="popups"></div>
+ <p style="text-align: center;">
+ <img src="/images/steve_logo.png"/>
+ </p>
+ <a href="javascript:void(location.href='election.html' +
document.location.search);">Back to election front page</a>
+<div class="formbox">
+ <p>
+ This is a standard ASF PMC vote with binding and non-binding votes.
+ If you agree with the motion put forward, vote <kbd>Yes</kbd>. If you
disagree, vote <kbd>No</kbd>.
+ If you neither agree nor disagree, you can mark your ballot as blank
by clicking on <kbd>Abstain</kbd>. F
+ or some elections where the number of votes
+ are important, abstaining on a vote you have no preference for/against
can help reach quorum.
+ </p>
+<div id="preloaderWrapper">
+ <img src="/images/steve_spinner.gif"/>
+ <div id="preloader">
+ Loading issue, please wait...
+ </div>
+</div>
+</div>
+<p style="font-size: 12px; font-style: italic; text-align: center;">
+ Powered by <a href="https://steve.apache.org/">Apache STeVe</a>.
+ Copyright 2015, the Apache Software Foundation.
+ Licensed under the <a
href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
+</p>
+</body>
+</html>
\ No newline at end of file
Added: steve/trunk/pysteve/www/htdocs/js/steve_ap.js
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/htdocs/js/steve_ap.js?rev=1669319&view=auto
==============================================================================
--- steve/trunk/pysteve/www/htdocs/js/steve_ap.js (added)
+++ steve/trunk/pysteve/www/htdocs/js/steve_ap.js Thu Mar 26 11:12:52 2015
@@ -0,0 +1,102 @@
+function displayIssueAP(code, response, state) {
+ election_data = response
+ var obj = document.getElementById('preloaderWrapper')
+ obj.setAttribute("id", "ynavote")
+ if (code != 200) {
+ obj.innerHTML = "<h1>Could not load issue:</h1><h2>" +
response.message + "</h2>";
+ } else {
+ obj.innerHTML = ""
+
+ var title = document.createElement('h2')
+ title.innerHTML = response.issue.title;
+ obj.appendChild(title)
+
+ obj.appendChild(keyvaluepairText("nominatedby", "Put forward
(nominated) by:", response.issue.nominatedby))
+ obj.appendChild(keyvaluepairText("seconds", "Seconded by:",
response.issue.seconds.length > 0 ? response.issue.seconds.join(", ") :
"no-one" ))
+
+ var desc = document.createElement('pre')
+ desc.setAttribute("class", "statement")
+ desc.innerHTML = response.issue.description
+ obj.appendChild(desc)
+
+ var outer = document.createElement('div')
+ outer.setAttribute("class", "issueListItemWide")
+
+ var byes = document.createElement('input')
+ byes.setAttribute("type", "button")
+ byes.setAttribute("value", "Binding Yes (+1)")
+ byes.setAttribute("class", "btn-green")
+ byes.setAttribute("style", "float: right;");
+ byes.setAttribute("onclick", "castSingleVote('by');")
+
+ var yes = document.createElement('input')
+ yes.setAttribute("type", "button")
+ yes.setAttribute("value", "Yes (+1)")
+ yes.setAttribute("class", "btn-green")
+ yes.setAttribute("style", "float: right;");
+ yes.setAttribute("onclick", "castSingleVote('y');")
+
+ var no = document.createElement('input')
+ no.setAttribute("type", "button")
+ no.setAttribute("value", "No (-1)")
+ no.setAttribute("class", "btn-red")
+ no.setAttribute("style", " float: right;");
+ no.setAttribute("onclick", "castSingleVote('n');")
+
+ var bno = document.createElement('input')
+ bno.setAttribute("type", "button")
+ bno.setAttribute("value", "Binding No (-1)")
+ bno.setAttribute("class", "btn-red")
+ bno.setAttribute("style", " float: right;");
+ bno.setAttribute("onclick", "castSingleVote('bn');")
+
+ var abstain = document.createElement('input')
+ abstain.setAttribute("type", "button")
+ abstain.setAttribute("value", "Abstain (0)")
+ abstain.setAttribute("class", "btn-yellow")
+ abstain.setAttribute("style", "float: right;");
+ abstain.setAttribute("onclick", "castSingleVote('a');")
+
+ var p = document.createElement('p')
+ p.innerHTML = "Cast your vote by clicking on the respective
button below. You may recast your vote as many time as you like, should you
reconsider."
+
+ obj.appendChild(p)
+ outer.appendChild(bno)
+ outer.appendChild(no)
+ outer.appendChild(abstain)
+ outer.appendChild(yes)
+ outer.appendChild(byes)
+
+ obj.appendChild(outer)
+ }
+}
+
+function loadIssue(election, issue, uid, callback) {
+
+ var messages = ["Herding cats...", "Shaving yaks...", "Shooing some
cows away...", "Fetching election data...", "Loading issues..."]
+ if (!election || !uid) {
+ var l = document.location.search.substr(1).split("/");
+ election = l[0];
+ issue = l.length > 1 ? l[l.length-2] : "";
+ uid = l.length > 2 ? l[l.length-1] : "";
+ }
+ if (step == -1) {
+ getJSON("/steve/voter/view/" + election + "/" + issue + "?uid="
+ uid, [election, issue, uid], callback)
+ }
+
+ var obj = document.getElementById('preloader');
+ step++;
+ if (!election_data && obj) {
+ if (step % 2 == 1) obj.innerHTML =
messages[parseInt(Math.random()*messages.length-0.01)]
+ } else if (obj && (step % 2 == 1)) {
+ obj.innerHTML = "Ready..!"
+ }
+ if (step % 2 == 1) {
+ obj.style.transform = "translate(0,0)"
+ } else if (obj) {
+ obj.style.transform = "translate(0,-500%)"
+ }
+ if (!election_data|| (step % 2 == 0) ) {
+ window.setTimeout(loadElection, 750, election, uid, callback);
+ }
+}
Modified: steve/trunk/pysteve/www/htdocs/js/steve_rest.js
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/htdocs/js/steve_rest.js?rev=1669319&r1=1669318&r2=1669319&view=diff
==============================================================================
--- steve/trunk/pysteve/www/htdocs/js/steve_rest.js (original)
+++ steve/trunk/pysteve/www/htdocs/js/steve_rest.js Thu Mar 26 11:12:52 2015
@@ -153,11 +153,15 @@ function displayTally(code, response, is
obj.innerHTML += "<li>" + winner + ": " +
winnerName + pct + "</li>"
}
obj.innerHTML += "</ol>"
- } else if (response.yes && response.yes != undefined) {
+ } else if (response.yes != undefined) {
obj.innerHTML = "<i>(" + response.votes + " votes
cast)</i>\n\n"
- obj.innerHTML += "<b>Yes: </b>" + response.yes +
"\n"
- obj.innerHTML += "<b>No: </b>" + response.no + "\n"
- obj.innerHTML += "<b>Abstain: </b>" + response.abstain
+ "\n"
+ obj.innerHTML += "<b>Yes: </b>" +
response.yes + "\n"
+ obj.innerHTML += "<b>No: </b>" +
response.no + "\n"
+ obj.innerHTML += "<b>Abstain: </b>" +
response.abstain + "\n"
+ if (response.binding_yes != undefined) {
+ obj.innerHTML += "<b>Binding Yes: </b>" +
response.binding_yes + "\n"
+ obj.innerHTML += "<b>Binding No: </b>" +
response.binding_no + "\n"
+ }
} else {
obj.innerHTML = "Unknonwn vote type or no votes cast
yet"
}
@@ -391,6 +395,26 @@ function renderEditIssue(code, response,
btn.setAttribute("onclick", "saveYNA();")
div.appendChild(btn)
obj.appendChild(div)
+
+ } else if (edit_i.type == "ap") {
+ obj.innerHTML = "<h3>Editing a Apache PMC Style
issue</h3>"
+
+ obj.appendChild(keyvaluepair("id", "Issue ID:", "text",
edit_i.id, true))
+ obj.appendChild(keyvaluepair("ititle", "Issue title:",
"text", edit_i.title))
+ obj.appendChild(keyvaluepair("nominatedby", "Nominated
by:", "text", edit_i.nominatedby))
+ obj.appendChild(keyvaluepair("seconds", "Seconded by:",
"text", (edit_i.seconds ? edit_i.seconds : []).join(", ")))
+ obj.appendChild(document.createElement('hr'))
+ obj.appendChild(keyvaluepair("description",
"Description/statement:", "textarea", edit_i.description))
+
+ var div = document.createElement('div')
+ div.setAttribute("class", "keyvaluepair")
+ var btn = document.createElement('input')
+ btn.setAttribute("type", "button")
+ btn.setAttribute("class", "btn-green")
+ btn.setAttribute("value", "Save changes")
+ btn.setAttribute("onclick", "saveYNA();")
+ div.appendChild(btn)
+ obj.appendChild(div)
} else if (edit_i.type.match(/^stv/)) {
// base data
@@ -660,7 +684,7 @@ function peekCallback(code, response, el
}
function changeSTVType(type) {
- if (type == "yna") {
+ if (type == "yna" || type == "ap") {
document.getElementById('yna').style.display = "block";
document.getElementById('stv').style.display = "none";
} else {