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 {


Reply via email to