Skip to content

Commit

Permalink
Consolidate loops, use Notes.app icon, add environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean committed Aug 10, 2018
1 parent bfabac6 commit 10c42ff
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 43 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@

## Usage

Type `n[part of note title]` and press enter.
Type `n[part of note]` and press enter.

## Install

Download the Alfred workflow file from Releases and open it.

## Customize

### Search

By default, this searches the title + full text of the notes and orders results based on the last modification date of the note. If you want to search titles only or order results based on alphabetical order, change the [environment variables](https://www.alfredapp.com/help/workflows/advanced/variables/#environment).

### Icons

Icons are from [Emojitwo](https://emojitwo.github.io/) and will show up when they are the first character in the name of a folder, like `📘 GPI` in the screenshot above. Add your own icons to the workflow's `icons` folder and tweak `searchNotes.py` to see them in Alfred.

## Compatibility
Expand Down
9 changes: 9 additions & 0 deletions info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,15 @@ end run</string>
<integer>280</integer>
</dict>
</dict>
<key>variables</key>
<dict>
<key>searchTitlesOnly</key>
<string>0</string>
<key>sortByDate</key>
<string>1</string>
</dict>
<key>version</key>
<string></string>
<key>webaddress</key>
<string>https://sball.in</string>
</dict>
Expand Down
92 changes: 50 additions & 42 deletions searchNotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,72 @@
import re
import os

# Sort matches by title or modification date. Edit w/environment variable.
sortId = 3 if os.getenv('sortBy') != 'Title' else 0
sortInReverse = sortId is 3
# Sort matches by title or modification date, option to search titles only.
# Edit with Alfred workflow environment variable.
sortId = 2 if os.getenv('sortByDate') == '1' else 0
searchTitlesOnly = (os.getenv('searchTitlesOnly') == '1')
sortInReverse = (sortId == 2)

# Open notes database
home = '/'.join(__file__.split('/')[:3])
conn = sqlite3.connect(
home + '/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite')
db = home + '/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite'
conn = sqlite3.connect(db)
c = conn.cursor()

# Get uuid string required in full id
c.execute("SELECT z_uuid FROM z_metadata")
c.execute('SELECT z_uuid FROM z_metadata')
uuid = str(c.fetchone()[0])

# Get tuples of note title, folder code, snippet, modification date, & id#
# 432 is the zfolder id for 'Recently Deleted'
c.execute("""SELECT t1.ztitle1,t1.zfolder,t1.zsnippet,t1.zmodificationdate1,t1.z_pk,t1.znotedata,t2.zdata,t2.z_pk
# Get tuples of note title, folder code, modification date, & id#
# 432 is for at least one person the zfolder id for 'Recently Deleted'
c.execute("""SELECT t1.ztitle1,t1.zfolder,t1.zmodificationdate1,
t1.z_pk,t1.znotedata,t2.zdata,t2.z_pk
FROM ziccloudsyncingobject AS t1
INNER JOIN zicnotedata AS t2
ON t1.znotedata = t2.z_pk
WHERE t1.ztitle1 IS NOT NULL AND t1.zfolder IS NOT 432 AND t1.zmarkedfordeletion IS NOT 1""")
matches = c.fetchall()
matches = sorted(matches, key=lambda m: m[sortId], reverse=sortInReverse)
WHERE t1.ztitle1 IS NOT NULL AND t1.zfolder IS NOT 432
AND t1.zmarkedfordeletion IS NOT 1""")
# Get and check for d[5] because a New Note with no body can trip us up
dbItems = [d for d in c.fetchall() if d[5]]
dbItems = sorted(dbItems, key=lambda d: d[sortId], reverse=sortInReverse)

# Get ordered lists of folder codes and folder names
c.execute("""SELECT z_pk,ztitle2
FROM ziccloudsyncingobject
c.execute("""SELECT z_pk,ztitle2 FROM ziccloudsyncingobject
WHERE ztitle2 IS NOT NULL AND zmarkedfordeletion IS NOT 1""")
folderCodes, folderNames = zip(*c.fetchall())

conn.close()

# Alfred results: title = note title, arg = id to pass on, subtitle = folder name, match = the note contents
items = [{"title": m[0],
"arg": "x-coredata://" + uuid + "/ICNote/p" + str(m[4]),
"subtitle": folderNames[folderCodes.index(m[1])],
# + (" | " + m[2] if type(m[2]) is unicode and len(m[2]) > 0 else ""),
#
# decompress gzipped notes from the sqlite database, strip out gobbledygook footers.
"match": zlib.decompress(m[6], 16+zlib.MAX_WBITS).split('\x1a\x10', 1)[0]}
for m in matches]
# Custom icons to look for in folder names
icons = [u'\ud83d\udcd3', u'\ud83d\udcd5', u'\ud83d\udcd7', u'\ud83d\udcd8',
u'\ud83d\udcd9']

# Alfred results: title = note title, arg = id to pass on, subtitle = folder name,
# match = note contents from gzipped database entries after stripping footers.
items = [{} for d in dbItems]
for i, d in enumerate(dbItems):
# In body, strip weird characters, title & weird header artifacts,
# and replace line breaks with spaces
body = zlib.decompress(d[5], 16+zlib.MAX_WBITS).split('\x1a\x10', 1)[0]
body = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff]', '', body)
body = re.sub('^.*\n', ' ', body)
body = re.sub('\n', ' ', body)
body = re.sub('^ ', '', body)

subtitle = folderNames[folderCodes.index(d[1])] + " | " + body[:100]

# Custom icons for folder names that start with corresponding emoji
if any(x in subtitle[:2] for x in icons):
iconText = subtitle[:2].encode('raw_unicode_escape')
subtitle = subtitle[3:]
icon = {'type': 'image', 'path': 'icons/' + iconText + '.png'}
else:
icon = {'type': 'fileicon', 'path': '/Applications/Notes.app'}

items[i] = {'title': d[0],
'subtitle': subtitle,
'arg': 'x-coredata://' + uuid + '/ICNote/p' + str(d[3]),
'match': d[0] if searchTitlesOnly else d[0] + body,
'icon': icon}

# Do further clean up and additions to the match and subtitle fields.
for i, item in enumerate(items):
# strip weird characters, title & weird header artifacts,
# replace line breaks with spaces.
txt = re.sub('^ ', '', re.sub('\n', ' ', re.sub('^.*\n', ' ', re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff]', '', items[i]['match']))))
items[i]['match'] = items[i]['title'] + " " + items[i]['subtitle'] + " " + txt
items[i]['subtitle'] += " | " + txt[:100]

# Custom icons for folder names that start with corresponding emoji
icons = [u'\ud83d\udcd3', u'\ud83d\udcd5', u'\ud83d\udcd7', u'\ud83d\udcd8', u'\ud83d\udcd9']
for i in items:
if any(x in i['subtitle'] for x in icons):
subtitle = i['subtitle']
icon = subtitle[:2]
i['subtitle'] = subtitle[3:]
i['icon'] = {'type': 'image', 'path': 'icons/' + icon.encode('raw_unicode_escape') + '.png'}

output = {"items": items}
print json.dumps(output, sort_keys=True, indent=4, separators=(',', ': '))
print json.dumps({'items': items}, sort_keys=True)

0 comments on commit 10c42ff

Please sign in to comment.