math-off-vote-plot/script.js

151 lines
4.4 KiB
JavaScript
Raw Permalink Normal View History

2025-02-09 20:32:43 +00:00
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm"
import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm";
function clean_poll_data(data, rel_start_time) {
const {start_date, end_date, question, answers, votes} = data;
const num_votes = votes.length;
const totals = answers.map(a => 0);
totals.push(0);
const start_time = new Date(start_date);
const end_time = new Date(end_date);
answers.forEach((a,i) => {
votes.splice(0,0,{time: start_time, answer: i});
})
votes.forEach((v,i) => {
v.time = new Date(v.time);
v.total = totals[v.answer];
v.answer_name = answers[v.answer].answer;
totals[v.answer] += 1;
});
const last_vote_time = votes[votes.length-1].time;
const duration = last_vote_time - start_time;
answers.forEach((a,i) => {
votes.push({time: last_vote_time, answer: i, total: totals[i]-1, answer_name: answers[i].answer});
})
votes.forEach(v => {v.time = v.time - start_time + rel_start_time});
return {start_date, end_date, duration, question, answers, votes, last_vote_time};
}
async function plot_poll(ids, container) {
const reqs = await Promise.all(ids.map(id => fetch(`https://aperiodical.com/wp-json/wp-polls/v3/results/${id}`)));
const datas = await Promise.all(reqs.map(r => r.json()));
console.log(datas);
let all_votes = [];
const rel_start_time = 60*60*8 * 1000;
let max_duration = 0;
let title = '';
let num_answers = 0;
for(let data of datas) {
const {votes, answers, question, duration, last_vote_time} = clean_poll_data(data, rel_start_time);
votes.forEach(v => v.answer += num_answers);
title = question;
all_votes = all_votes.concat(votes);
max_duration = Math.max(duration, max_duration);
num_answers += answers.length;
}
console.table(all_votes);
container.innerHTML = '';
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'poll.css';
container.append(link);
const header = document.createElement('h2');
header.textContent = title;
container.append(header);
const plot = Plot.plot({
y: {
label: 'Votes',
grid: true,
},
x: {
label: 'Time',
tickFormat: t => {
t /= 1000;
const seconds = t % 60;
t = (t-seconds)/60;
const minutes = t % 60;
t = (t-minutes)/60;
const hours = t % 24;
return `${hours.toString().padStart(2,'0')}:${minutes.toString().padStart(2,'0')}`;
}
},
marks: [
Plot.line([{time: rel_start_time, total: 0}, {time: max_duration + rel_start_time, total: 0}],{x:'time', y:'total'}),
Plot.lineY(all_votes, {x: "time", y: "total", z: "answer", stroke: 'answer_name'}),
Plot.text(all_votes, Plot.selectLast({x:'time', y: 'total', z: 'answer', text: 'answer_name', textAnchor: 'end', dx: -6}))
]
})
container.append(plot);
}
class PollPlotElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode:'open'});
}
static observedAttributes = ['pollids'];
attributeChangedCallback() {
this.make_plot();
}
make_plot() {
const poll_ids = this.getAttribute('pollids').split(',').map(n=>parseInt(n));
if(poll_ids.some(id=>isNaN(id))) {
return;
}
plot_poll(poll_ids, this.shadowRoot);
}
}
customElements.define('poll-plot', PollPlotElement);
(async () => {
const polls = await (await fetch('https://aperiodical.com/wp-json/wp-polls/v3/polls')).json();
window.polls = polls;
polls.sort((a,b) => (new Date(b.opens)) - (new Date(a.opens)));
const params = new URLSearchParams(location.search);
let pollids, year;
if(params.has('year')) {
year = params.get('year');
console.log(year);
pollids = polls.filter(p => (new Date(p.opens)).getFullYear()==year).map(p=>p.id);
} else {
if(params.has('pollid')) {
pollids = params.get('pollid');
} else {
pollids = polls[0].id;
}
}
document.querySelector('poll-plot').setAttribute('pollids',pollids);
const polls_list = document.getElementById('other-matches');
for(let p of polls) {
const li = document.createElement('li');
const a = document.createElement('a');
a.href = `?pollid=${p.id}`;
if(pollids.includes(p.id)) {
a.setAttribute('aria-current', 'page');
}
const opens = new Date(p.opens);
a.textContent = `${opens.getFullYear()} - ${p.question}`;
li.append(a);
polls_list.append(li);
}
})();