Author: humbedooh
Date: Fri Mar 27 12:24:17 2015
New Revision: 1669560

URL: http://svn.apache.org/r1669560
Log:
Let's try an online monitoring of elections.
WITH BIG RED LETTERS

Added:
    steve/trunk/pysteve/www/htdocs/js/steve_monitor.js
    steve/trunk/pysteve/www/htdocs/monitor.html
Modified:
    steve/trunk/pysteve/lib/election.py
    steve/trunk/pysteve/www/cgi-bin/rest_admin.py
    steve/trunk/pysteve/www/htdocs/css/steve_interactive.css

Modified: steve/trunk/pysteve/lib/election.py
URL: 
http://svn.apache.org/viewvc/steve/trunk/pysteve/lib/election.py?rev=1669560&r1=1669559&r2=1669560&view=diff
==============================================================================
--- steve/trunk/pysteve/lib/election.py (original)
+++ steve/trunk/pysteve/lib/election.py Fri Mar 27 12:24:17 2015
@@ -273,4 +273,5 @@ def getHash(electionID):
         output.append("Issue #%s: %s\n- Checksum: %s\n- Votes cast: %u\n" % 
(issue, issuedata['title'], issuedata['hash'], len(votes)))
     tothash = hashlib.sha224(ihash).hexdigest()
     output.insert(0, ("You are receiving this data because you are listed as a 
monitor for this election.\nThe following data shows the state of the election 
data on disk. If any of these checksums change, especially the main checksum, 
then the election has been edited (rigged?) after invites were sent 
out.\n\nMain Election Checksum : %s\n\n" % tothash))
+    output.append("\nYou can monitor votes and recasts online at: 
%s/monitor.html?%s" % (config.get("general", "rooturl"), electionID))
     return tothash, "\n".join(output)

Modified: steve/trunk/pysteve/www/cgi-bin/rest_admin.py
URL: 
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/cgi-bin/rest_admin.py?rev=1669560&r1=1669559&r2=1669560&view=diff
==============================================================================
--- steve/trunk/pysteve/www/cgi-bin/rest_admin.py (original)
+++ steve/trunk/pysteve/www/cgi-bin/rest_admin.py Fri Mar 27 12:24:17 2015
@@ -536,7 +536,35 @@ else:
             for vtype in constants.VOTE_TYPES:
                 types[vtype['key']] = vtype['description']
             response.respond(200, {'types': types})
-            
+        
+        # Get vote data
+        elif action == "monitor" and electionID:
+            issue = l[2] if len(l) > 2 else None
+            if electionID and issue:
+                basedata = election.getBasedata(electionID, hideHash=True)
+                if karma >= 2 or ('owner' in basedata and basedata['owner'] == 
whoami):
+                    issuedata = election.getIssue(electionID, issue)
+                    votes = election.getVotesRaw(electionID, issue)
+                    if issuedata and votes:
+                        if election.validType(issuedata['type']):
+                            ehash, blergh = election.getHash(electionID)
+                            response.respond(200, {
+                                'issue': issuedata,
+                                'base': basedata,
+                                'votes': votes,
+                                'hash': ehash
+                            })
+                        else:
+                            response.respond(500, {'message': "Unknown vote 
type"})
+                    elif not votes:
+                        response.respond(404, {'message': "No votes found"})
+                    else:
+                        response.respond(404, {'message': "Issue not found"})
+                else:
+                    response.respond(403, {'message': "You do not have karma 
to tally the votes here"})
+            else:
+                    response.respond(404, {'message': 'No such election or 
issue'})
+                    
         else:
             response.respond(400, {'message': "No (or invalid) action 
supplied"})
     else:

Modified: steve/trunk/pysteve/www/htdocs/css/steve_interactive.css
URL: 
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/htdocs/css/steve_interactive.css?rev=1669560&r1=1669559&r2=1669560&view=diff
==============================================================================
--- steve/trunk/pysteve/www/htdocs/css/steve_interactive.css (original)
+++ steve/trunk/pysteve/www/htdocs/css/steve_interactive.css Fri Mar 27 
12:24:17 2015
@@ -258,6 +258,15 @@ pre {
   word-wrap: break-word;
   white-space: pre-wrap;
 }
+
+.monitor_details {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  font-family: monospace;
+  background: #FFD;
+  border: 1px dotted #333;
+}
+
 .modal-header,
 .modal-footer {
   padding: 10px 20px;

Added: steve/trunk/pysteve/www/htdocs/js/steve_monitor.js
URL: 
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/htdocs/js/steve_monitor.js?rev=1669560&view=auto
==============================================================================
--- steve/trunk/pysteve/www/htdocs/js/steve_monitor.js (added)
+++ steve/trunk/pysteve/www/htdocs/js/steve_monitor.js Fri Mar 27 12:24:17 2015
@@ -0,0 +1,168 @@
+// Function for fetching JSON from the REST API
+function getJSON(theUrl, xstate, callback) {
+       var xmlHttp = null;
+       if (window.XMLHttpRequest) {
+               xmlHttp = new XMLHttpRequest();
+       } else {
+               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+       }
+       xmlHttp.open("GET", theUrl, true);
+       xmlHttp.send(null);
+       xmlHttp.onreadystatechange = function(state) {
+               if (xmlHttp.readyState == 4 && xmlHttp.status && xmlHttp.status 
>= 200) {
+                       if (callback) {
+                               window.setTimeout(callback, 0.01, 
xmlHttp.status, (xmlHttp.responseText && xmlHttp.responseText.length > 1) ? 
JSON.parse(xmlHttp.responseText) : null, xstate);
+                       }
+               }
+       }
+}
+
+function getIssues() {
+       election = document.location.search.substr(1)
+       getJSON("/steve/admin/view/" + election, election, listIssues)
+}
+
+var ehash = null
+var eid = null
+var issues = []
+var basedata = {}
+var votes = {}
+var oldvotes = {}
+var recasts = {}
+var recasters = {}
+var rigged = false
+
+function listIssues(code, response, election) {
+       if (code == 200) {
+               eid = election
+               issues = response.issues
+               basedata = response.base_data
+               document.getElementById('title').innerHTML += " " + 
basedata.title
+               var obj = document.getElementById('preloaderWrapper')
+               obj.innerHTML = ""
+               obj.setAttribute("id", "contents")
+               for (i in issues) {
+                       window.setTimeout(showChanges, 1000, issues[i])
+               }
+       } else {
+               alert(response.message)
+       }
+}
+
+function updateVotes(code, response, issue) {
+       if (code == 200) {
+               recasters[issue] = recasters[issue] ? recasters[issue] : {}
+               oldvotes[issue] = votes[issue] ? votes[issue] : {}
+               votes[issue] = response.votes
+               if (ehash == null) {
+                       ehash = response.hash
+               }
+               if (ehash != response.hash) {
+                       rigged = true
+               }
+               recasts[issue] = recasts[issue] ? recasts[issue] : 0
+       } else {
+               alert(response.message)
+       }
+}
+
+function calcChanges(issue, oldv, newv) {
+       sinceLast = 0;
+       
+       
+       // Find new votes cast since last update
+       if (!oldv || !newv) {
+               return [0,0]
+       }
+       for (i in newv) {
+               if (!oldv[i]) {
+                       sinceLast++;
+               } else if (oldv[i].timestamp != undefined && newv[i].timestamp 
!= undefined && oldv[i].timestamp != newv[i].timestamp) {
+                       recasts[issue]++;
+                       recasters[issue][i] = (recasters[issue][i] ? 
recasters[issue][i] : 0) + 1
+               }
+       }
+       var nrc = 0;
+       for (i in recasters[issue]) nrc++;
+       
+       return [sinceLast, nrc]
+}
+
+function showDetails(issueid, update) {
+       var obj = document.getElementById('issue_' + issueid + '_details')
+       if (obj.innerHTML.length > 0 && !update) {
+               obj.innerHTML = ""
+       } else {
+               obj.innerHTML = ""
+               for (i in votes[issueid]) {
+                       var rawvote = votes[issueid][i]
+                       var vote = null
+                       var nrc = recasters[issueid][i] ? recasters[issueid][i] 
: 0
+                       var add = ""
+                       if (rawvote.timestamp) {
+                               vote = rawvote.vote
+                               add = ". Cast at " + new 
Date(rawvote.timestamp*1000).toLocaleString()
+                       } else {
+                               vote = rawvote
+                       }
+                       if (nrc > 0) {
+                               nrc = "Vote recast " + nrc + " time(s)"
+                       } else {
+                               nrc = "No recasts yet"
+                       }
+                       obj.innerHTML += "<b>" + i + ": </b> " + vote + " - " + 
nrc + add + "<br/>"
+               }
+               window.setTimeout(showDetails, 2500, issueid, true)
+       }
+       
+}
+
+
+function showChanges(issue) {
+       var parent = document.getElementById('issue_' + issue.id)
+       var header = document.getElementById('issue_' + issue.id + "_header")
+       if (rigged) {
+               document.getElementById('title').innerHTML = "<font 
color='red'>ELECTION HAS BEEN CHANGED SINCE IT OPENED, POSSIBLE RIGGING 
ATTEMPT!</font>"
+       }
+       if (!parent) {
+               parent = document.createElement('div')
+               parent.setAttribute("id", "issue_" + issue.id)
+               parent.setAttribute("class", "monitor_issue")
+               document.getElementById('contents').appendChild(parent)
+               
+               parent.innerHTML = "<h3>Issue #" + issue.id + ": " + 
issue.title + "</h3>"
+               
+               header = document.createElement('div')
+               header.setAttribute("id", "issue_" + issue.id + "_header")
+               header.innerHTML = "Awaiting vote data...hang on!"
+               parent.appendChild(header)
+               
+               details = document.createElement('div')
+               details.setAttribute("id", "issue_" + issue.id + "_details")
+               details.setAttribute("class", "monitor_details")
+               parent.appendChild(details)
+               
+               window.setTimeout(showChanges, 2000, issue)
+       } else {
+               window.setTimeout(showChanges, 5000, issue)
+               numvotes = 0;
+               if (votes[issue.id]) {
+                       for (i in votes[issue.id]) numvotes++;
+               }
+               if (numvotes > 0) {
+                       var v = votes[issue.id]
+                       var a = calcChanges(issue.id, oldvotes[issue.id], v)
+                       sinceLast = a[0]
+                       nrc = a[1]
+                       header.innerHTML = numvotes + " votes cast, " + 
sinceLast + " new votes cast since last update. " + recasts[issue.id] + " votes 
have been recast, split among " + nrc + " voters."
+                       header.innerHTML += " <a 
href='javascript:void(showDetails(\"" + issue.id + "\"));'>Show details</a>"
+                       header.innerHTML += " &nbsp; <a 
href='/steve/admin/monitor/" + eid + "/" + issue.id + "'>Get JSON</a>"
+               } else {
+                       header.innerHTML = "No votes cast yet..!"
+               }
+       }
+       getJSON("/steve/admin/monitor/" + eid + "/" + issue.id, issue.id, 
updateVotes)
+       
+       
+       
+}
\ No newline at end of file

Added: steve/trunk/pysteve/www/htdocs/monitor.html
URL: 
http://svn.apache.org/viewvc/steve/trunk/pysteve/www/htdocs/monitor.html?rev=1669560&view=auto
==============================================================================
--- steve/trunk/pysteve/www/htdocs/monitor.html (added)
+++ steve/trunk/pysteve/www/htdocs/monitor.html Fri Mar 27 12:24:17 2015
@@ -0,0 +1,30 @@
+ <!DOCTYPE HTML>
+<html>
+<head>
+<link rel="stylesheet" href="/css/steve_interactive.css">
+<script src="/js/steve_monitor.js" type="text/javascript"></script>
+<title>Election monitor</title>
+</head>
+<body onload="window.setTimeout(getIssues, 500);">
+    <div id="popups"></div>
+    <p style="text-align: center;">
+        <img src="/images/steve_logo.png"/>
+    </p>
+<div class="formbox">
+    
+<h2 id="title">Election monitor:</h2>
+
+
+<div id="preloaderWrapper">
+    <img src="/images/steve_spinner.gif"/><br/>
+        Loading issues, please wait...
+</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


Reply via email to