Last active
April 23, 2023 14:31
-
-
Save maxpatiiuk/809c59bc2b40d53e3e07910088fb536b to your computer and use it in GitHub Desktop.
Export Goodreads reading stats for plotting by Google Sheets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This script is now deprecated as it integrated into my Google Chrome extension: | |
* https://chrome.google.com/webstore/detail/goodreads-stats/hdpkeldenopncgodhpjdlpngmnaijpjf?hl=en&authuser=0 | |
* | |
* The extension adds easy export functionality to Goodreads, along with plotting capabilities | |
*/ | |
/* | |
* Open this page: | |
* https://www.goodreads.com/review/list/83169518?ref=nav_mybooks | |
* (replace id with your user id) | |
* Turn on infinite scroll | |
* Scroll all the way down to load all entries | |
* Open Chrome DevTools | |
* Run the following script: | |
*/ | |
const monthFormatter = new Intl.DateTimeFormat(undefined, { month: 'long' }); | |
const months = Array.from({ length: 12 }, (_, month) => | |
monthFormatter.format(new Date(0, month, 2, 0, 0, 0)) | |
); | |
Object.entries( | |
document.body.getElementsBySelector('#booksBody .date_read .date_row span') | |
.map(d=>d.textContent) | |
.reduce((dates, date) => { | |
if (!date.includes(',')) { | |
const dateParts = date.split(' '); | |
date = `${dateParts[0]} 1, ${dateParts[1]}`; | |
} | |
let [monthDay, year] = date.split(','); | |
year = year.trim(); | |
if (year === 'set') return dates; | |
let [month, day] = monthDay.split(' '); | |
if (day[0] === '0') day = day.slice(1); | |
dates[year] ??= Object.fromEntries( | |
months.map((month) => [ | |
month, | |
Object.fromEntries( | |
Array.from({ | |
length: new Date(year, months.indexOf(month), 0).getDate() - 1, | |
}).map((_, day) => [day + 1, 0]) | |
), | |
]) | |
); | |
dates[year][month] ??= {}; | |
dates[year][month][day] ??= 0; | |
dates[year][month][day] += 1; | |
return dates; | |
}, {}) | |
) | |
.map(([year, yearData]) => | |
Object.entries(yearData).reduce( | |
([yearTotal, entries], [month, monthData]) => { | |
const [monthTotal, monthEntries] = Object.entries(monthData).reduce( | |
([total, entries], [day, count]) => [ | |
total + count, | |
[ | |
...entries, | |
[`${year} ${month} ${day}`, yearTotal + total + count], | |
], | |
], | |
[0, []] | |
); | |
return [yearTotal + monthTotal, [...entries, ...monthEntries]]; | |
}, | |
[0, []] | |
) | |
) | |
.flat(2) | |
.filter(Array.isArray) | |
.map((row) => row.join('\t')) | |
.join('\n'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment