Compare commits
10 Commits
f6508ca91a
...
8a0f27476c
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a0f27476c | |||
| c77b1ee890 | |||
| 4a7defc1a4 | |||
| 11b8deed1d | |||
| 67f4b581bc | |||
| 69bb15c46d | |||
| e2a30c4858 | |||
| 956bc23ee4 | |||
| 7f0dbc610f | |||
| a3ee24800c |
24
filechunker.js
Normal file
24
filechunker.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
fs=require("fs")
|
||||||
|
|
||||||
|
class FileChunker {
|
||||||
|
constructor(fname) {
|
||||||
|
this.Chunks=[];
|
||||||
|
let Template=fs.readFileSync(fname).toString();
|
||||||
|
let pattern="<!-- INJECT -->";
|
||||||
|
let offset=0;
|
||||||
|
do {
|
||||||
|
let nextOffset=Template.indexOf(pattern,offset);
|
||||||
|
if (nextOffset>=0) {
|
||||||
|
this.Chunks.push(Template.substring(offset,nextOffset));
|
||||||
|
offset=nextOffset+pattern.length;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.Chunks.push(Template.substring(offset));
|
||||||
|
offset=-1;
|
||||||
|
}
|
||||||
|
} while (offset>0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports=FileChunker;
|
||||||
|
|
||||||
@@ -14,10 +14,10 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<form id="my-form" style="background-color:#E0E0E0" action="/plur_vote" method="post">
|
<form id="my-form" style="background-color:#E0E0E0" action="/plur_vote" method="post">
|
||||||
This page is an experiment in voting. Half of the people who visit this site vote using Plurality Voting
|
This page is an experiment in voting. Half of the people who visit this site will use Plurality Voting, and half will use Range Voting. I do ask that anyone unlikely to vote in the Republican primary use the "Just show me the Results" option below.<br><br>
|
||||||
where a voter selects their top candidate, and the winner is the candidate with the most votes. The other
|
In Plurality Voting, voters select their top candidate, and the winner is the candidate with the most votes. Most elections in the United States use Plurality Voting.<br><br>
|
||||||
half use Range Voting where voters score all familiar candidates based on how happy they would be should
|
In Range Voting, voters score all candidates <i>that they know</i> based on how happy they would be should that candidate win. <br><br>
|
||||||
that candidate win. You have been randomly selected to vote using Plurality Voting.
|
<b>You have been randomly selected to vote using Plurality Voting.</b>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -26,11 +26,12 @@
|
|||||||
<th> </th>
|
<th> </th>
|
||||||
<th>Candidate</th>
|
<th>Candidate</th>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- INJECT0 -->
|
<!-- INJECT -->
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<button type="submit" value="submit">Submit</button>
|
<button type="submit" value="submit">Submit</button><br>
|
||||||
|
Or, just show me the <a href="results">Results</a>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,10 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<form id="my-form" style="background-color:#E0E0E0" action="/range_vote" method="post" oninput=UpdateValues(event)>
|
<form id="my-form" style="background-color:#E0E0E0" action="/range_vote" method="post" oninput=UpdateValues(event)>
|
||||||
This page is an experiment in voting. Half of the people who visit this site will use Plurality Voting:
|
This page is an experiment in voting. Half of the people who visit this site will use Plurality Voting, and half will use Range Voting. I do ask that anyone unlikely to vote in the Republican primary use the "Just show me the Results" option below.<br><br>
|
||||||
Voters select their top candidate, and the winner is the candidate with the most votes. The other
|
In Plurality Voting, voters select their top candidate, and the winner is the candidate with the most votes. Most elections in the United States use Plurality Voting.<br><br>
|
||||||
half will use Range Voting: Voters score all candidates <i>that they know</i> based on how happy they would be should
|
In Range Voting, voters score all candidates <i>that they know</i> based on how happy they would be should that candidate win. <br><br>
|
||||||
that candidate win. <b>You have been randomly selected to vote using Range Voting.</b>
|
<b>You have been randomly selected to vote using Range Voting.</b>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -27,11 +27,12 @@
|
|||||||
<th style="width:50%"><span id="spanid"> </th>
|
<th style="width:50%"><span id="spanid"> </th>
|
||||||
<th>Happiness</th>
|
<th>Happiness</th>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- INJECT0 -->
|
<!-- INJECT -->
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<button type="submit" value="submit">Submit</button>
|
<button type="submit" value="submit">Submit</button><br>
|
||||||
|
Or, just show me the <a href="results">Results</a>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
119
rangevote.js
119
rangevote.js
@@ -2,9 +2,13 @@ var express = require('express')
|
|||||||
var bodyParser = require('body-parser')
|
var bodyParser = require('body-parser')
|
||||||
var fs=require('fs')
|
var fs=require('fs')
|
||||||
var hashindex=require('hash-index')
|
var hashindex=require('hash-index')
|
||||||
|
var fileChunker=require("./filechunker.js");
|
||||||
|
|
||||||
var app = express()
|
var app = express()
|
||||||
|
|
||||||
|
var TemplateVoteRange=new fileChunker("./rangevote.html");
|
||||||
|
var TemplateVotePlur =new fileChunker("./plurvote.html");
|
||||||
|
var TemplateResults =new fileChunker("./results.html");
|
||||||
|
|
||||||
var Candidates=[
|
var Candidates=[
|
||||||
"Chris Christie",
|
"Chris Christie",
|
||||||
@@ -28,13 +32,19 @@ function CanId(c)
|
|||||||
|
|
||||||
|
|
||||||
// Set up data structures for Range Votes and Plur Votes
|
// Set up data structures for Range Votes and Plur Votes
|
||||||
var Experiment={}
|
var Experiment;
|
||||||
|
|
||||||
|
fs.readFile('./rangevote.json', 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
Experiment={};
|
||||||
Experiment.RangeVotes=new Map();
|
Experiment.RangeVotes=new Map();
|
||||||
Experiment.RangePower=new Map();
|
Experiment.RangePower=new Map();
|
||||||
Experiment.RangeResult=new Map();
|
Experiment.RangeResult=new Map();
|
||||||
Experiment.PlurVotes=new Map();
|
Experiment.PlurVotes=new Map();
|
||||||
Experiment.PlurResult=new Map();
|
Experiment.PlurResult=new Map();
|
||||||
Experiment.PlurCount=0;
|
Experiment.PlurCount=0;
|
||||||
|
}
|
||||||
|
else Experiment=JSON.parse(data,MapReviver);
|
||||||
for (let c of Candidates)
|
for (let c of Candidates)
|
||||||
{
|
{
|
||||||
let k=CanId(c);
|
let k=CanId(c);
|
||||||
@@ -45,6 +55,39 @@ for (let c of Candidates)
|
|||||||
v=Experiment.PlurResult.get(k);
|
v=Experiment.PlurResult.get(k);
|
||||||
if (typeof(v)!='number' || v==NaN || v==Infinity) Experiment.PlurResult.set(k,0);
|
if (typeof(v)!='number' || v==NaN || v==Infinity) Experiment.PlurResult.set(k,0);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function LogErr(err)
|
||||||
|
{
|
||||||
|
if (err) console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MapReplacer(key, value) {
|
||||||
|
if(value instanceof Map) {
|
||||||
|
return {
|
||||||
|
dataType: 'Map',
|
||||||
|
value: Array.from(value.entries()), // or with spread: value: [...value]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function MapReviver(key, value) {
|
||||||
|
if(typeof value === 'object' && value !== null) {
|
||||||
|
if (value.dataType === 'Map') {
|
||||||
|
return new Map(value.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Persist()
|
||||||
|
{
|
||||||
|
var str=JSON.stringify(Experiment,MapReplacer);
|
||||||
|
fs.writeFile("./rangevote.json",str,LogErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -55,10 +98,8 @@ for (let c of Candidates)
|
|||||||
RangeHTML=RangeHTML+' <tr>'+
|
RangeHTML=RangeHTML+' <tr>'+
|
||||||
' <td>'+c+'</td><td><input style="width:95%" type="range" min="-1" max="100" value="-1" id="i-'+CanId(c)+'" name="n-'+CanId(c)+'" /></td><td><span id="o-'+CanId(c)+'">No Opinion</span></td></tr>\n';
|
' <td>'+c+'</td><td><input style="width:95%" type="range" min="-1" max="100" value="-1" id="i-'+CanId(c)+'" name="n-'+CanId(c)+'" /></td><td><span id="o-'+CanId(c)+'">No Opinion</span></td></tr>\n';
|
||||||
}
|
}
|
||||||
var TemplateHTML=fs.readFileSync("rangevote.html").toString();
|
RangeHTML=TemplateVoteRange.Chunks[0]+RangeHTML+TemplateVoteRange.Chunks[1];
|
||||||
var TemplateSearch="<!-- INJECT0 -->";
|
|
||||||
var TemplateIndex=TemplateHTML.indexOf(TemplateSearch);
|
|
||||||
RangeHTML=TemplateHTML.substr(0,TemplateIndex)+RangeHTML+TemplateHTML.substr(TemplateIndex+TemplateSearch.length,1000000);
|
|
||||||
|
|
||||||
// Construct the static page for Plurality Voting
|
// Construct the static page for Plurality Voting
|
||||||
var PlurHTML="";
|
var PlurHTML="";
|
||||||
@@ -67,10 +108,7 @@ for (let c of Candidates)
|
|||||||
PlurHTML=PlurHTML+' <tr>'+
|
PlurHTML=PlurHTML+' <tr>'+
|
||||||
' <td><input type="radio" value="v-'+CanId(c)+'" name="pick" /></td><td>'+c+'</td></tr>\n';
|
' <td><input type="radio" value="v-'+CanId(c)+'" name="pick" /></td><td>'+c+'</td></tr>\n';
|
||||||
}
|
}
|
||||||
TemplateHTML=fs.readFileSync("plurvote.html").toString();
|
PlurHTML=TemplateVotePlur.Chunks[0]+PlurHTML+TemplateVotePlur.Chunks[1];
|
||||||
TemplateSearch="<!-- INJECT0 -->";
|
|
||||||
TemplateIndex=TemplateHTML.indexOf(TemplateSearch);
|
|
||||||
PlurHTML=TemplateHTML.substr(0,TemplateIndex)+PlurHTML+TemplateHTML.substr(TemplateIndex+TemplateSearch.length,1000000);
|
|
||||||
|
|
||||||
|
|
||||||
// create application/json parser
|
// create application/json parser
|
||||||
@@ -102,7 +140,7 @@ function PlurVote(ip,NewChoice)
|
|||||||
NewChoice=CanId(NewChoice);
|
NewChoice=CanId(NewChoice);
|
||||||
if (typeof(NewChoice)=='string') { Experiment.PlurResult.set(NewChoice,Experiment.PlurResult.get(NewChoice)+1); Experiment.PlurCount+=1; }
|
if (typeof(NewChoice)=='string') { Experiment.PlurResult.set(NewChoice,Experiment.PlurResult.get(NewChoice)+1); Experiment.PlurCount+=1; }
|
||||||
Experiment.PlurVotes.set(ip,NewChoice);
|
Experiment.PlurVotes.set(ip,NewChoice);
|
||||||
console.log(Experiment);
|
console.log("Plurality vote from "+ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -116,9 +154,10 @@ function AddToRangeResult(vote,mult=1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function RangeVote(ip,entries)
|
function RangeVote(ip,entries)
|
||||||
{
|
{
|
||||||
console.log('IP: '+ip);
|
|
||||||
let r0=null,r1=null;
|
let r0=null,r1=null;
|
||||||
for (let [key,val] of entries) if (typeof(key)=="string" && val>=0)
|
for (let [key,val] of entries) if (typeof(key)=="string" && val>=0)
|
||||||
{
|
{
|
||||||
@@ -138,33 +177,59 @@ function RangeVote(ip,entries)
|
|||||||
if (OldVote) AddToRangeResult(OldVote,-1);
|
if (OldVote) AddToRangeResult(OldVote,-1);
|
||||||
Experiment.RangeVotes.set(ip,NewVote);
|
Experiment.RangeVotes.set(ip,NewVote);
|
||||||
AddToRangeResult(NewVote,1);
|
AddToRangeResult(NewVote,1);
|
||||||
|
console.log("Range vote from "+ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendResults(req,res)
|
||||||
|
{
|
||||||
|
let strPlur="";
|
||||||
|
for (let c of Candidates)
|
||||||
|
{
|
||||||
|
let key=CanId(c);
|
||||||
|
if (Experiment.PlurResult.get(key)>0)
|
||||||
|
strPlur=strPlur+"<tr>"+
|
||||||
|
"<td>"+c+"</td>"+
|
||||||
|
"<td>"+Experiment.PlurResult.get(key)+"</td>"+
|
||||||
|
"<td>"+Math.round(100.0*Experiment.PlurResult.get(key)/Experiment.PlurCount)+"%</td>"+
|
||||||
|
"</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
let strRange="";
|
||||||
|
for (let c of Candidates)
|
||||||
|
{
|
||||||
|
let key=CanId(c);
|
||||||
|
if (Experiment.RangePower.get(key)>0)
|
||||||
|
strRange=strRange+"<tr>"+
|
||||||
|
"<td>"+c+"</td>"+
|
||||||
|
"<td>"+Experiment.RangePower.get(key)+"</td>"+
|
||||||
|
"<td>"+Math.round(Experiment.RangeResult.get(key)/Experiment.RangePower.get(key))+"%</td>"+
|
||||||
|
"</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send( TemplateResults.Chunks[0]+Experiment.PlurVotes.size+
|
||||||
|
TemplateResults.Chunks[1]+strPlur+
|
||||||
|
TemplateResults.Chunks[2]+Experiment.RangeVotes.size+
|
||||||
|
TemplateResults.Chunks[3]+strRange+
|
||||||
|
TemplateResults.Chunks[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// POST /login gets urlencoded bodies
|
// POST /login gets urlencoded bodies
|
||||||
app.post('/range_vote', urlencodedParser, function (req, res) {
|
app.post('/range_vote', urlencodedParser, function (req, res) {
|
||||||
RangeVote(req.socket.remoteAddress,Object.entries(req.body));
|
RangeVote(req.socket.remoteAddress,Object.entries(req.body));
|
||||||
let str="Results from "+Experiment.RangeVotes.size+" Voters:<br>";
|
Persist();
|
||||||
for (let c of Candidates)
|
SendResults(req,res);
|
||||||
{
|
|
||||||
let key=CanId(c);
|
|
||||||
if (Experiment.RangePower.get(key)>0) str=str+c+': '+Math.round(Experiment.RangeResult.get(key)/Experiment.RangePower.get(key))+'% ('+Experiment.RangePower.get(key)+' Voters)<br>';
|
|
||||||
}
|
|
||||||
console.log(JSON.stringify(Experiment));
|
|
||||||
res.send(str);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
app.post('/plur_vote', urlencodedParser, function (req, res) {
|
app.post('/plur_vote', urlencodedParser, function (req, res) {
|
||||||
PlurVote(req.socket.remoteAddress,req.body.pick);
|
PlurVote(req.socket.remoteAddress,req.body.pick);
|
||||||
let str="Results from "+Experiment.PlurVotes.size+" Voters:<br>";
|
Persist();
|
||||||
for (let c of Candidates)
|
SendResults(req,res);
|
||||||
{
|
|
||||||
let key=CanId(c);
|
|
||||||
if (Experiment.PlurResult.get(key)>0) str=str+c+': '+Math.round(100.0*Experiment.PlurResult.get(key)/Experiment.PlurCount)+'%<br>';
|
|
||||||
}
|
|
||||||
res.send(str);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.get('/results',urlencodedParser,function(req,res) {
|
||||||
|
SendResults(req,res);
|
||||||
|
})
|
||||||
|
|
||||||
app.listen(3000,function(req,res) { console.log("Listening for connections"); })
|
app.listen(3000,function(req,res) { console.log("Listening for connections"); })
|
||||||
|
|
||||||
|
|||||||
43
results.html
Normal file
43
results.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Ballot Experiments Results</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<style>
|
||||||
|
form, table, th, td {
|
||||||
|
border:1px solid black;
|
||||||
|
border-collapse: collapse;
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
Here are the results by Plurality Voting based on <!-- INJECT --> voters:
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Candidate</th>
|
||||||
|
<th># Votes</th>
|
||||||
|
<th>% Votes</th>
|
||||||
|
</tr>
|
||||||
|
<!-- INJECT -->
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Here are the results by Range Voting based on <!-- INJECT --> voters:
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Candidate</th>
|
||||||
|
<th># Voters</th>
|
||||||
|
<th>% Happiness</th>
|
||||||
|
</tr>
|
||||||
|
<!-- INJECT -->
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
Reference in New Issue
Block a user