Compare commits

...

10 Commits

Author SHA1 Message Date
8a0f27476c changes 2023-10-18 15:31:10 -04:00
c77b1ee890 changes 2023-10-18 15:17:31 -04:00
4a7defc1a4 changes 2023-10-18 15:13:23 -04:00
11b8deed1d Changes for release 2023-10-18 15:00:45 -04:00
67f4b581bc changes 2023-10-18 13:35:13 -04:00
69bb15c46d changes 2023-10-18 08:52:39 -04:00
e2a30c4858 changes 2023-10-18 08:17:57 -04:00
956bc23ee4 changes 2023-10-17 16:30:56 -04:00
7f0dbc610f changes 2023-10-15 22:14:32 -04:00
a3ee24800c changes 2023-10-03 08:51:42 -04:00
6 changed files with 194 additions and 53 deletions

24
filechunker.js Normal file
View 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;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,15 +32,21 @@ 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;
Experiment.RangeVotes=new Map();
Experiment.RangePower=new Map(); fs.readFile('./rangevote.json', 'utf8', (err, data) => {
Experiment.RangeResult=new Map(); if (err) {
Experiment.PlurVotes=new Map(); Experiment={};
Experiment.PlurResult=new Map(); Experiment.RangeVotes=new Map();
Experiment.PlurCount=0; Experiment.RangePower=new Map();
for (let c of Candidates) Experiment.RangeResult=new Map();
{ Experiment.PlurVotes=new Map();
Experiment.PlurResult=new Map();
Experiment.PlurCount=0;
}
else Experiment=JSON.parse(data,MapReviver);
for (let c of Candidates)
{
let k=CanId(c); let k=CanId(c);
let v=Experiment.RangePower.get(k); let v=Experiment.RangePower.get(k);
if (typeof(v)!='number' || v==NaN || v==Infinity) Experiment.RangePower.set(k,0); if (typeof(v)!='number' || v==NaN || v==Infinity) Experiment.RangePower.set(k,0);
@@ -44,6 +54,39 @@ for (let c of Candidates)
if (typeof(v)!='number' || v==NaN || v==Infinity) Experiment.RangeResult.set(k,0); if (typeof(v)!='number' || v==NaN || v==Infinity) Experiment.RangeResult.set(k,0);
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
View 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>

7
test.js Normal file
View File

@@ -0,0 +1,7 @@
import { Client } from 'pg'
const client = new Client()
await client.connect()
const res = await client.query('SELECT $1::text as message', ['Hello world!'])
console.log(res.rows[0].message) // Hello world!
await client.end()