Skip to content

Instantly share code, notes, and snippets.

@dunhamsteve
Created December 5, 2018 02:55
Show Gist options
  • Save dunhamsteve/a4b1198a9f07d5425e6e5022188855d5 to your computer and use it in GitHub Desktop.
Save dunhamsteve/a4b1198a9f07d5425e6e5022188855d5 to your computer and use it in GitHub Desktop.
reads cached data from Notion.app's localStorage, collects incomplete todo items, and writes them to todo.html
#!/usr/bin/env python3
# This is written for OSX, python3, and the current (2018-12-04) version of the Notion app
# It writes incomplete todo items, found in Notion's localStorage, to a "todo.html" file,
# with links back to the Notion app
import os
import sqlite3
import json
from html import escape
from collections import defaultdict
# load cached blocks from Notion desktop's localStorage
blocks = {}
path = os.path.expanduser(
"~/Library/Application Support/Notion/Local Storage/https_www.notion.so_0.localstorage")
c = sqlite3.Connection(path)
for k, v in c.execute("select key,value from itemtable where key like 'LRU:LocalRecordStore3:%'"):
_, _, uuid, table = k.split(':')
data = json.loads(v.decode('utf16')).get('value', {}).get('value')
if data:
blocks[uuid] = data
def page_name(id):
"fetch name of page as text or 'Missinge Page'"
page = blocks.get(id, {})
if not page:
return "Missing Page"
return prop(page, 'title')
tags = {'b': 'b', 'i': 'em', 'c': 'code'}
def rich_prop(block, key):
"renders rich property to html"
parts = block.get('properties', {}).get(key, [])
rval = []
for part in parts:
text = part[0]
if len(part) > 1:
for seg in part[1]:
if seg[0] == 'p': text = page_name(seg[1])
if seg[0] == 'a': text = ['a', {'href': seg[1]}, text]
tag = tags.get(seg[0])
if tag:
text = [tag, text]
rval.append(text)
return rval
def prop(block, key):
"returns text from a property (sans page names)"
if block is not None:
parts = block.get('properties', {}).get(key, [])
return ''.join(part[0] for part in parts)
def get_page(block):
"return id of containing page or None"
while block != None:
if block.get('type') == 'page':
return block.get('id')
block = blocks.get(block.get('parent_id'))
def link(pid,bid=None):
"link to block in page"
rval = 'notion://-/'+pid.replace('-','')
if bid:
return rval + '#' + bid.replace('-','')
return rval
def todo(b, pid):
"render todo item"
bid = b.get('id')
title = rich_prop(b, 'title')
return ['div', ['a', {'href': link(pid,bid)},"\u2610"], *title]
def render_html(node):
"turn a pile of lists and dicts into html"
tag, *rest = node
attr = []
body = []
for x in rest:
if isinstance(x, dict):
for k, v in x.items():
attr.append(f' {k}="{escape(v)}"')
elif isinstance(x, list):
body.append(render_html(x))
else:
body.append(escape(str(x)))
return ''.join(['<', tag, *attr, '>', *body, '</', tag, '>'])
# collect todo items by page.
pages = defaultdict(list)
for id, b in sorted(blocks.items()):
if b.get('type') == 'to_do':
checked = prop(b, 'checked') == 'Yes'
if not checked:
pid = get_page(b)
pages[pid].append(b)
# render todo items
div = ['div', {'class': 'content'}]
# for now, sort by id to make order stable
for pid, items in sorted(pages.items()):
title = prop(blocks.get(pid), 'title')
items = [todo(item, pid) for item in items]
div.append(['h3', title])
div.append(['div', *items])
css = '''
body { font-family: sans-serif; }
div.content { width: 800px; margin: auto; }
a { text-decoration: none; color: #555; }
'''
out = render_html(['html',
['head', ['style', css]],
['body', div]])
print(out, file=open('todo.html', 'w'))
print('wrote todo.html')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment