Support comment on bookmarks

This commit is contained in:
Arun Prakash Jana 2016-05-18 01:41:31 +05:30
parent cb48798c3d
commit c252981bd2
No known key found for this signature in database
GPG Key ID: C0A712ED95043DCB
3 changed files with 89 additions and 19 deletions

View File

@ -27,6 +27,7 @@ Copyright (C) 2015-2016 [Arun Prakash Jana](mailto:engineerarun@gmail.com).
- Add, tag, search, update, remove bookmarks - Add, tag, search, update, remove bookmarks
- Fetch page title from the web (default) or add a custom page title manually - Fetch page title from the web (default) or add a custom page title manually
- Add comments (description) to bookmarks
- Open search results in browser - Open search results in browser
- Manual password protection using AES256 encryption - Manual password protection using AES256 encryption
- Handle piped input (combine with `xsel` and add bookmarks directly from browser) - Handle piped input (combine with `xsel` and add bookmarks directly from browser)
@ -122,6 +123,8 @@ Shell completion scripts for Bash, Fish and Zsh can be found in respective subdi
title of bookmark at index N from web title of bookmark at index N from web
-t, --title [...] manually set title, works with -a, -u -t, --title [...] manually set title, works with -a, -u
do not set title, if no arguments do not set title, if no arguments
-c, --comment [...] description of the bookmark, works with
-a, -u; clears comment, if no arguments
-d, --delete [N] delete bookmark at DB index N -d, --delete [N] delete bookmark at DB index N
delete all bookmarks, if no arguments delete all bookmarks, if no arguments
-h, --help show this information -h, --help show this information
@ -179,9 +182,9 @@ Shell completion scripts for Bash, Fish and Zsh can be found in respective subdi
- Encryption is optional and manual. AES256 algorithm is used. If you choose to use encryption, the database file should be unlocked (`-k`) before using buku and locked (`-l`) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is <i>unencrypted on creation</i>. - Encryption is optional and manual. AES256 algorithm is used. If you choose to use encryption, the database file should be unlocked (`-k`) before using buku and locked (`-l`) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is <i>unencrypted on creation</i>.
# Examples # Examples
1. **Add** a bookmark with **tags** `linux news` and `open source`, **fetch page title** from the web: 1. **Add** a bookmark with **tags** `linux news` and `open source`, **comment** `Informative website on Linux and open source`, **fetch page title** from the web:
$ buku -a http://tuxdiary.com linux news, open source $ buku -a http://tuxdiary.com linux news, open source -c Informative website on Linux and open source
Title: [TuxDiary | Linux, open source and a pinch of leisure.] Title: [TuxDiary | Linux, open source and a pinch of leisure.]
Added at index 15012014 Added at index 15012014
2. **Add** a bookmark with tags `linux news` and `open source` & **custom title** `Linux magazine`: 2. **Add** a bookmark with tags `linux news` and `open source` & **custom title** `Linux magazine`:

90
buku
View File

@ -50,6 +50,7 @@ except ImportError:
update = False # Update a bookmark in DB update = False # Update a bookmark in DB
titleData = None # Title fetched from a page titleData = None # Title fetched from a page
titleManual = None # Manually add a title offline titleManual = None # Manually add a title offline
description = None # Description of the bookmark
jsonOutput = False # Output json formatted result jsonOutput = False # Output json formatted result
showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag
debug = False # Enable debug logs debug = False # Enable debug logs
@ -163,12 +164,19 @@ def initdb():
# Create table if it doesn't exist # Create table if it doesn't exist
cur.execute('''CREATE TABLE if not exists bookmarks \ cur.execute('''CREATE TABLE if not exists bookmarks \
(id integer PRIMARY KEY, URL text NOT NULL UNIQUE, metadata text, tags text)''') (id integer PRIMARY KEY, URL text NOT NULL UNIQUE, metadata text, tags text, desc text)''')
conn.commit() conn.commit()
except Exception as e: except Exception as e:
print("\x1b[1mEXCEPTION\x1b[21m [initdb]: (%s) %s" % (type(e).__name__, e)) print("\x1b[1mEXCEPTION\x1b[21m [initdb]: (%s) %s" % (type(e).__name__, e))
sys.exit(1) sys.exit(1)
# Add description column in existing DB (from version 2.1)
try:
cur.execute("""ALTER TABLE bookmarks ADD COLUMN desc text default \'\'""")
conn.commit()
except:
pass
return (conn, cur) return (conn, cur)
@ -348,6 +356,7 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0):
""" """
global titleManual global titleManual
global description
tags = ',' tags = ','
meta = '' meta = ''
url = keywords[0] url = keywords[0]
@ -390,11 +399,14 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0):
print("Title: [%s]" % meta) print("Title: [%s]" % meta)
if updateindex == 0: # Add or insert a new entry if updateindex == 0: # Add or insert a new entry
if description is None:
description = ''
try: try:
if insertindex == 0: # insertindex is index number to insert record at if insertindex == 0: # insertindex is index number to insert record at
cur.execute('INSERT INTO bookmarks(URL, metadata, tags) VALUES (?, ?, ?)', (url, meta, tags,)) cur.execute('INSERT INTO bookmarks(URL, metadata, tags, desc) VALUES (?, ?, ?, ?)', (url, meta, tags, description))
else: else:
cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (insertindex, url, meta, tags,)) cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags, desc) VALUES (?, ?, ?, ?, ?)', (insertindex, url, meta, tags, description))
conn.commit() conn.commit()
print("Added at index %d\n" % cur.lastrowid) print("Added at index %d\n" % cur.lastrowid)
printdb(cur, cur.lastrowid) printdb(cur, cur.lastrowid)
@ -406,7 +418,10 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0):
print("Index %d exists" % insertindex) print("Index %d exists" % insertindex)
else: # Update an existing entry else: # Update an existing entry
try: try:
cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, updateindex,)) if description is None:
cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, updateindex,))
else:
cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ?, desc = ? WHERE id = ?", (url, meta, tags, description, updateindex,))
conn.commit() conn.commit()
if cur.rowcount == 1: if cur.rowcount == 1:
print("Updated index %d\n" % updateindex) print("Updated index %d\n" % updateindex)
@ -466,18 +481,20 @@ def searchdb(cur, keywords, all_keywords=False):
global jsonOutput global jsonOutput
arguments = [] arguments = []
placeholder = "'%' || ? || '%'" placeholder = "'%' || ? || '%'"
query = "SELECT id, url, metadata, tags FROM bookmarks WHERE" query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE"
if all_keywords == True: # Match all keywords in URL or Title if all_keywords == True: # Match all keywords in URL or Title
for token in keywords: for token in keywords:
query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s)) AND" % (placeholder, placeholder, placeholder) query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s)) AND" % (placeholder, placeholder, placeholder, placeholder)
arguments.append(token)
arguments.append(token) arguments.append(token)
arguments.append(token) arguments.append(token)
arguments.append(token) arguments.append(token)
query = query[:-4] query = query[:-4]
else: # Match any keyword in URL or Title else: # Match any keyword in URL or Title
for token in keywords: for token in keywords:
query += " tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR" % (placeholder, placeholder, placeholder) query += " tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s) OR" % (placeholder, placeholder, placeholder, placeholder)
arguments.append(token)
arguments.append(token) arguments.append(token)
arguments.append(token) arguments.append(token)
arguments.append(token) arguments.append(token)
@ -495,7 +512,7 @@ def searchdb(cur, keywords, all_keywords=False):
count = 0 count = 0
for row in results: for row in results:
count += 1 count += 1
print("\x1B[1m\x1B[93m%d. \x1B[0m\x1B[92m%s\x1B[0m [%d]\n\t%s\n\t\x1B[91m[TAGS]\x1B[0m %s\n" % (count, row[1], row[0], row[2], row[3][1:-1])) printRecord(row, count)
if count == 0: if count == 0:
return return
@ -537,12 +554,12 @@ def compactDB(conn, cur, index):
results = cur.fetchall() results = cur.fetchall()
for row in results: for row in results:
if row[0] > index: if row[0] > index:
cur.execute('SELECT id, URL, metadata, tags FROM bookmarks WHERE id = ?', (row[0],)) cur.execute('SELECT id, URL, metadata, tags, desc FROM bookmarks WHERE id = ?', (row[0],))
results = cur.fetchall() results = cur.fetchall()
for row in results: for row in results:
cur.execute('DELETE FROM bookmarks WHERE id = ?', (row[0],)) cur.execute('DELETE FROM bookmarks WHERE id = ?', (row[0],))
conn.commit() conn.commit()
cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (index, row[1], row[2], row[3],)) cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags, desc) VALUES (?, ?, ?, ?, ?)', (index, row[1], row[2], row[3], row[4],))
conn.commit() conn.commit()
print("Index %d moved to %d" % (row[0], index)) print("Index %d moved to %d" % (row[0], index))
@ -575,6 +592,35 @@ def cleardb(conn, cur, index):
print("Index out of bound") print("Index out of bound")
def printRecord(row, count=0):
"""Print a single DB record
Handles differently for search and print (count = 0)
"""
# Print index and URL
if count != 0:
print("\x1B[1m\x1B[93m%d. \x1B[0m\x1B[92m%s\x1B[0m\t[%d]" % (count, row[1], row[0]))
else:
print("\x1B[1m\x1B[93m%d. \x1B[0m\x1B[92m%s\x1B[0m" % (row[0], row[1]))
# Print title
if row[2] != '':
print(" %s" % row[2])
print("")
# Print description
if row[4] != '':
print(" %s" % row[4])
#print("\t\x1B[91m[DESC]\x1B[0m %s" % row[4])
# Print tags
if row[3] != ',':
print(" \x1B[91m[TAGS]\x1B[0m %s" % row[3][1:-1])
print("")
def printdb(cur, index, empty=False): def printdb(cur, index, empty=False):
"""Print bookmark details at index or all bookmarks if index is None """Print bookmark details at index or all bookmarks if index is None
Print only bookmarks with blank title or tag if empty is True Print only bookmarks with blank title or tag if empty is True
@ -599,7 +645,7 @@ def printdb(cur, index, empty=False):
if jsonOutput == False: if jsonOutput == False:
if showOpt == 0: if showOpt == 0:
for row in resultset: for row in resultset:
print("\x1B[1m\x1B[93m%s. \x1B[0m\x1B[92m%s\x1B[0m\n\t%s\n\t\x1B[91m[TAGS]\x1B[0m %s\n" % (row[0], row[1], row[2], row[3][1:-1])) printRecord(row)
elif showOpt == 1: elif showOpt == 1:
for row in resultset: for row in resultset:
print("%s %s" % (row[0], row[1])) print("%s %s" % (row[0], row[1]))
@ -618,7 +664,7 @@ def printdb(cur, index, empty=False):
if jsonOutput == False: if jsonOutput == False:
for row in resultset: for row in resultset:
print("\x1B[1m\x1B[93m%s. \x1B[0m\x1B[92m%s\x1B[0m\n\t%s\n\t\x1B[91m[TAGS]\x1B[0m %s" % (row[0], row[1], row[2], row[3][1:-1])) printRecord(row)
return return
print("No matching index") print("No matching index")
else: else:
@ -982,6 +1028,19 @@ class customTitleAction(argparse.Action):
setattr(args, self.dest, values) setattr(args, self.dest, values)
class customDescAction(argparse.Action):
"""Class to capture if an optional param
is actually used, even if sans arguments
"""
def __call__(self, parser, args, values, option_string=None):
global description
description = ''
# NOTE: the following converts a None argument to an empty array []
setattr(args, self.dest, values)
class ExtendedArgumentParser(argparse.ArgumentParser): class ExtendedArgumentParser(argparse.ArgumentParser):
"""Extend classic argument parser""" """Extend classic argument parser"""
@ -1029,7 +1088,7 @@ argparser = ExtendedArgumentParser(
description='A private command-line bookmark manager. Your mini web!', description='A private command-line bookmark manager. Your mini web!',
formatter_class=argparse.RawTextHelpFormatter, formatter_class=argparse.RawTextHelpFormatter,
usage='''buku [-a URL [tags ...]] [-u [N [URL tags ...]]] usage='''buku [-a URL [tags ...]] [-u [N [URL tags ...]]]
[-t [...]] [-d [N]] [-h] [-t [...]] [-c [...]] [-d [N]] [-h]
[-s keyword [...]] [-S keyword [...]] [-s keyword [...]] [-S keyword [...]]
[-k [N]] [-l [N]] [-p [N]] [-f N] [-k [N]] [-l [N]] [-p [N]] [-f N]
[-r oldtag [newtag ...]] [-j] [-o N] [-z]''', [-r oldtag [newtag ...]] [-j] [-o N] [-z]''',
@ -1047,12 +1106,15 @@ general_group = argparser.add_argument_group(title="general options",
title of bookmark at index N from web title of bookmark at index N from web
-t, --title [...] manually set title, works with -a, -u -t, --title [...] manually set title, works with -a, -u
do not set title, if no arguments do not set title, if no arguments
-c, --comment [...] description of the bookmark, works with
-a, -u; clears comment, if no arguments
-d, --delete [N] delete bookmark at DB index N -d, --delete [N] delete bookmark at DB index N
delete all bookmarks, if no arguments delete all bookmarks, if no arguments
-h, --help show this information''') -h, --help show this information''')
general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help=argparse.SUPPRESS) general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help=argparse.SUPPRESS)
general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customUpdateAction, metavar=('N', 'URL tags'), help=argparse.SUPPRESS) general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customUpdateAction, metavar=('N', 'URL tags'), help=argparse.SUPPRESS)
general_group.add_argument('-t', '--title', nargs='*', dest='title', action=customTitleAction, metavar='title', help=argparse.SUPPRESS) general_group.add_argument('-t', '--title', nargs='*', dest='title', action=customTitleAction, metavar='title', help=argparse.SUPPRESS)
general_group.add_argument('-c', '--comment', nargs='*', dest='desc', type=str, action=customDescAction, metavar='desc', help=argparse.SUPPRESS)
general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help=argparse.SUPPRESS) general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help=argparse.SUPPRESS)
general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS) general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS)
@ -1121,6 +1183,8 @@ if args.showOpt is not None:
showOpt = args.showOpt showOpt = args.showOpt
if titleManual is not None and len(args.title) > 0: if titleManual is not None and len(args.title) > 0:
titleManual = " ".join(args.title) titleManual = " ".join(args.title)
if description is not None and len(args.desc) > 0:
description = " ".join(args.desc)
if args.jsonOutput: if args.jsonOutput:
import json import json
jsonOutput = args.jsonOutput jsonOutput = args.jsonOutput

11
buku.1
View File

@ -5,7 +5,7 @@ buku \- A private command-line bookmark manager. Your mini web!
.B buku .B buku
[-a URL [tags ...]] [-u [N [URL tags ...]]] [-a URL [tags ...]] [-u [N [URL tags ...]]]
.br .br
[-t [...]] [-d [N]] [-h] [-t [...]] [-c [...]] [-d [N]] [-h]
.br .br
[-s keyword [...]] [-S keyword [...]] [-s keyword [...]] [-S keyword [...]]
.br .br
@ -60,7 +60,10 @@ is refreshed from the web. If
is omitted, all titles are refreshed from the web. is omitted, all titles are refreshed from the web.
.TP .TP
.BI \-t " " \--title " [...]" .BI \-t " " \--title " [...]"
Manually specify the title, works with -a, -i, -u. `-m none` clears title. Manually specify the title, works with -a, -u. Omits or clears the title, if no arguments passed.
.TP
.BI \-c " " \--comment " [...]"
Add comment or description on the bookmark, works with -a, -u. Clears the comment, if no arguments passed.
.TP .TP
.BI \-d " " \--delete " [N]" .BI \-d " " \--delete " [N]"
Delete bookmark at index Delete bookmark at index
@ -151,11 +154,11 @@ Overrides the default browser. Ref:
.SH EXAMPLES .SH EXAMPLES
.PP .PP
.IP 1. 4 .IP 1. 4
\fBAdd\fR a bookmark with \fBtags\fR 'linux news' and 'open source', \fBfetch page title\fR from the web: \fBAdd\fR a bookmark with \fBtags\fR 'linux news' and 'open source', \fBcomment\fR 'Informative website on Linux and open source', \fBfetch page title\fR from the web:
.PP .PP
.EX .EX
.IP .IP
.B buku -a http://tuxdiary.com linux news, open source .B buku -a http://tuxdiary.com linux news, open source -c Informative website on Linux and open source
.PP .PP
.IP 2. 4 .IP 2. 4
\fBAdd\fR a bookmark with tags 'linux news' and 'open source' & \fBcustom title\fR 'Linux magazine': \fBAdd\fR a bookmark with tags 'linux news' and 'open source' & \fBcustom title\fR 'Linux magazine':