tl;dr: I wrote a custom GPT that takes in a screenshot of my notebook and converts it into a JSON blob that I can copy paste into my Google Apps Script to upload to my calendar.
A few months ago [I started to note down](Pomodoro Everything) what I did in every 30 minute block between 7am and 10pm on a notebook. I’ve always found those people who calendar everything and analyze it later interesting, but I’ve never had the energy to make a calendar event for everything, nor come back and analyze it.
But I carry a notebook around every day, so I found it much easier to screenshot a page like this at the end of a day (or the next day)
After which, I send it to this custom GPT. I might make this private in the future and have it direct upload using an actions API to my calendar, so I’ll leave the source here for now:
- This GPT takes in an image from a notebook that is a description of what I did in a day from 7am to 10pm.
There is a number at the beginning of every 2 rows that denotes the hour, starting at 7am and the last one being for 10pm.
- The rows in between the numbers are :30.
- Each pomodoro is STRICTLY 30 minutes.
- This GPT reads the notes and converts the image into text.
- It makes common-sense corrections automatically and infers bad handwriting.
- The very first row is "-7", which is what I did before 7am. There are two pages (and therefore 2 images) for a day.
- The user may upload one or both. In the text, specify at the beginning whether it's the front or back page.
- The timestamps on paper are in 12h format. Convert it into 24h format for the JSON, e.g. "13:30"
- Some unusual words that I use a lot are "Archa", "Rey", "Bluxome", "Logistics", "Polymarket" (sometimes misspelled Polymart). I also use the ditto double quote. Use that to copy the line above if you see it.
- At the end of the pomodoros, there is a x-x break and below that are key notes from the day. Put those in a separate key notes section.
- Assume the date on top of the first page to be the date of the pomodoros.
This GPT outputs the contents of the pages as a JSON blob that is in the specified format below and is used to upload the data to a google calendar via a google apps script (separately). The overall document should look like:
# Google Apps Script JSON (in the following format as an example with descriptions)
```json
{
"2024-11-05": [
{"title": "Pomodoro 1 text", "startTime": "09:00", "endTime": "09:30"},
{"title": "Pomodoro 2 text", "startTime": "17:30", "endTime": "18:00"}
],
"2024-11-06": [
{"title": "Pomodoro 1 text", "startTime": "13:30", "endTime": "14:00"},
{"title": "Pomodoro 2 text", "startTime": "14:30", "endTime": "15:00"}
]
}
This GPT then spits out some JSON that looks like this. Since my handwriting is truly terrible where a SOTA human can’t understand it, I usually take this as an oppotunity to make corrections, often within the chat itself so that I have my feedback for later. That results in a fixed JSON blob like this
{
"2024-11-14": [
{"title": "Taft, Memento mori", "startTime": "06:00", "endTime": "06:30"},
{"title": "PopLeanne", "startTime": "06:30", "endTime": "07:00"},
{"title": "Shower, Weights - Pull", "startTime": "07:00", "endTime": "07:30"},
{"title": "More weights, Gruffalo with Rey", "startTime": "07:30", "endTime": "08:00"},
{"title": "Play with Rey", "startTime": "08:00", "endTime": "08:30"},
{"title": "Drop off Rey", "startTime": "08:30", "endTime": "09:00"},
{"title": "Drive to SF with Archa", "startTime": "09:00", "endTime": "09:30"},
{"title": "Drive, discuss AE & strategy Archa", "startTime": "09:30", "endTime": "10:00"},
{"title": "Walk South Park → Bluxome", "startTime": "10:00", "endTime": "10:30"},
{"title": "Waymo to OpenAI", "startTime": "15:00", "endTime": "15:30"},
{"title": "Sherwin at OpenAI", "startTime": "15:30", "endTime": "16:00"}
],
"Key Notes": []
}
Finally, I use a Google Apps script that I have bookmarked to upload it to my calendar
function writePomodorosToCalendar(data) {
const calendar = CalendarApp.getCalendarsByName("Pomodoros")[0];
if (!calendar) {
Logger.log("Calendar not found.");
return;
}
// Remove key_notes before processing dates
const { key_notes, ...dateData } = data;
for (const date in dateData) {
const pomodoros = dateData[date];
// Parse the date explicitly to ensure it's treated as Pacific Time
const [year, month, day] = date.split("-").map(Number);
const eventDate = new Date(year, month - 1, day); // Month is 0-indexed
pomodoros.forEach(pomodoro => {
// Skip if required fields are missing
if (!pomodoro.startTime || !pomodoro.endTime || !pomodoro.title) {
Logger.log(`Skipping invalid pomodoro entry: ${JSON.stringify(pomodoro)}`);
return;
}
try {
// Parse start and end times
const [startHour, startMinute] = pomodoro.startTime.split(":").map(Number);
const [endHour, endMinute] = pomodoro.endTime.split(":").map(Number);
// Create start and end Date objects in Pacific Time
const startTime = new Date(year, month - 1, day, startHour, startMinute, 0);
const endTime = new Date(year, month - 1, day, endHour, endMinute, 0);
// Create the event
calendar.createEvent(pomodoro.title, startTime, endTime);
Logger.log(`Created event: ${pomodoro.title} on ${startTime}`);
} catch (error) {
Logger.log(`Error creating event: ${error.message}`);
Logger.log(`Problematic entry: ${JSON.stringify(pomodoro)}`);
}
});
}
}
const data = {
// copy paste the JSON here every dayTODO
}
writePomodorosToCalendar(data)
… and that’s it, next up it’s just on my calendar, like magic
I’ll probably add some search and analysis as I need it later, but for now I’m just making it a point to get the data in with minimal friction.
As a small optimization, I’ll probably write a GPT action with a vercel edge function to just upload to my calendar directly from the GPT UI, but for now I don’t mind the quick copy-paste because some manual corrections need to be made every day, and the copy paste is a good forcing function to double check.