Skip to content

Commit 5041836

Browse files
committed
add vote count summaries
1 parent 04e90d2 commit 5041836

File tree

5 files changed

+208
-103
lines changed

5 files changed

+208
-103
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
"observable": "observable"
1010
},
1111
"dependencies": {
12-
"@observablehq/framework": "^1.9.0"
12+
"@observablehq/framework": "^1.9.0",
13+
"d3-time-format": "^4.1.0",
14+
"jzip": "^1.0.0"
1315
},
1416
"devDependencies": {
1517
"rimraf": "^5.0.5"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import JSZip from "jszip";
2+
import {timeParse} from "d3-time-format";
3+
4+
// Data from https://rcvresults.multco.us/
5+
const resultsUrls = [
6+
{office: "district 1", resultsNum: 1, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland%20Councilor%20District%201/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland,%20Councilor,%20District%201.json"},
7+
{office: "district 1", resultsNum: 2, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland%20Councilor%20District%201/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland,%20Councilor,%20District%201.json"},
8+
{office: "district 1", resultsNum: 3, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland%20Councilor%20District%201/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland,%20Councilor,%20District%201.json"},
9+
{office: "district 1", resultsNum: 4, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland%20Councilor%20District%201/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland,%20Councilor,%20District%201.json"},
10+
{office: "district 1", resultsNum: 5, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland%20Councilor%20District%201/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland,%20Councilor,%20District%201.json"},
11+
12+
{office: "district 2", resultsNum: 1, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland%20Councilor%20District%202/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland,%20Councilor,%20District%202.json"},
13+
{office: "district 2", resultsNum: 2, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland%20Councilor%20District%202/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland,%20Councilor,%20District%202.json"},
14+
{office: "district 2", resultsNum: 3, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland%20Councilor%20District%202/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland,%20Councilor,%20District%202.json"},
15+
{office: "district 2", resultsNum: 4, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland%20Councilor%20District%202/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland,%20Councilor,%20District%202.json"},
16+
{office: "district 2", resultsNum: 5, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland%20Councilor%20District%202/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland,%20Councilor,%20District%202.json"},
17+
18+
{office: "district 3", resultsNum: 1, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland%20Councilor%20District%203/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland,%20Councilor,%20District%203.json"},
19+
{office: "district 3", resultsNum: 2, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland%20Councilor%20District%203/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland,%20Councilor,%20District%203.json"},
20+
{office: "district 3", resultsNum: 3, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland%20Councilor%20District%203/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland,%20Councilor,%20District%203.json"},
21+
{office: "district 3", resultsNum: 4, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland%20Councilor%20District%203/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland,%20Councilor,%20District%203.json"},
22+
{office: "district 3", resultsNum: 5, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland%20Councilor%20District%203/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland,%20Councilor,%20District%203.json"},
23+
24+
{office: "district 4", resultsNum: 1, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland%20Councilor%20District%204/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland,%20Councilor,%20District%204.json"},
25+
{office: "district 4", resultsNum: 2, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland%20Councilor%20District%204/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland,%20Councilor,%20District%204.json"},
26+
{office: "district 4", resultsNum: 3, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland%20Councilor%20District%204/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland,%20Councilor,%20District%204.json"},
27+
{office: "district 4", resultsNum: 4, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland%20Councilor%20District%204/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland,%20Councilor,%20District%204.json"},
28+
{office: "district 4", resultsNum: 5, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland%20Councilor%20District%204/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland,%20Councilor,%20District%204.json"},
29+
30+
{office: "mayor", resultsNum: 1, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland%20Mayor/a3df36c7-9b95-4614-a357-759ae2ca223f_City%20of%20Portland,%20Mayor.json"},
31+
{office: "mayor", resultsNum: 2, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland%20Mayor/8ae97233-9022-48ba-9ff1-07cc5fb12141_City%20of%20Portland,%20Mayor.json"},
32+
{office: "mayor", resultsNum: 3, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland%20Mayor/09dd9240-5e25-4fc7-8159-26717db95079_City%20of%20Portland,%20Mayor.json"},
33+
{office: "mayor", resultsNum: 4, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland%20Mayor/0e40aff6-a890-4a8a-9bbc-ea6924944cb0_City%20of%20Portland,%20Mayor.json"},
34+
{office: "mayor", resultsNum: 5, url: "https://mcdcselectionsrcvprdst.z5.web.core.windows.net/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland%20Mayor/65380232-cc8b-4692-b331-d8792a07891f_City%20of%20Portland,%20Mayor.json"},
35+
];
36+
37+
const timestampParse = timeParse("%Y_%m_%d_%H_%M_%S");
38+
function cleanData(data) {
39+
const dateStr = data.config.contest
40+
.replace(/district_\d/i, "")
41+
.replace(/^[^\d]+/i, "");
42+
const timestamp = timestampParse(dateStr);
43+
44+
return {
45+
...data,
46+
config: {
47+
...data.config,
48+
threshold: +data.config.threshold,
49+
timestamp,
50+
},
51+
results: data.results.map(result => ({
52+
...result,
53+
tally: Object.fromEntries(Object.entries(result.tally).map(([k, v]) => [k, +v])),
54+
tallyResults: result.tallyResults.map(tallyResult => ({
55+
...tallyResult,
56+
transfers: Object.fromEntries(Object.entries(tallyResult.transfers).map(([k, v]) => [k, +v])),
57+
})),
58+
})),
59+
};
60+
}
61+
62+
const zip = new JSZip();
63+
const voteSummaries = [];
64+
65+
for (const {office, resultsNum, url} of resultsUrls) {
66+
const res = await fetch(url);
67+
if (!res.ok) throw new Error(`Error with request to ${url}: ${await res.body()}`);
68+
const rawData = await res.json();
69+
const data = cleanData(rawData);
70+
zip.file(`${office.replace(' ', '_')}/results_${resultsNum}.json`, JSON.stringify(data));
71+
72+
voteSummaries.push({
73+
office,
74+
resultsNum,
75+
timestamp: data.config.timestamp,
76+
totalVotes: Object.values(data.results[0].tally).reduce((acc, d) => acc + d, 0),
77+
})
78+
}
79+
80+
zip.file("summary.json", JSON.stringify(voteSummaries));
81+
82+
const buf = await zip.generateAsync({type: "nodebuffer"});
83+
process.stdout.write(buf);

src/2024-11-08-portland-elections/data/[race]/[round].json.js

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)