commit cc241e583d345ed364a46bb57f677fdf028fd660
parent 58618398420ee21845e8d1ffb1d2aa3662ae4abf
Author: Daniel Moch <daniel@danielmoch.com>
Date: Wed, 25 Jul 2018 05:45:33 -0400
Refactor sncli into nncli
This completes the cosmetic refactoring. Next step is to refactor
nnotes_cli to make use of the NextCloud Notes API.
Diffstat:
5 files changed, 128 insertions(+), 68 deletions(-)
diff --git a/README.md b/README.md
@@ -72,23 +72,16 @@ Check your OS distribution for installation packages.
- flag note as markdown or not
- view and edit note tags
-### Screenshots
-
-
-
-
-
-
### HowTo
``` Usage: nncli [OPTIONS] [COMMAND] [COMMAND_ARGS]
OPTIONS: -h, --help - usage help -v, --verbose
- verbose output -n, --nosync - don't perform a server
- sync -r, --regex - search string is a regular
- expression -k <key>, --key=<key> - note key -t <title>,
- --title=<title> - title of note for create (cli mode) -c <file>,
- --config=<file> - config file to read from (defaults to ~/.nnclirc)
+ sync -r, --regex - search string is a regular
+ expression -k <key>, --key=<key> - note key -t <title>,
+ --title=<title> - title of note for create (cli mode) -c <file>,
+ --config=<file> - config file to read from (defaults to ~/.nnclirc)
COMMANDS: <none> - console gui mode when no
command specified sync - perform a full sync
@@ -111,9 +104,9 @@ Check your OS distribution for installation packages.
#### Configuration
-The current NextCloud Notes API does not support oauth authentication so your
-NextCloud Notes account information must live in the configuration file.
-Please be sure to protect this file.
+The current NextCloud Notes API does not support oauth authentication so
+your NextCloud Notes account information must live in the configuration
+file. Please be sure to protect this file.
nncli pulls in configuration from the `.nnclirc` file located in your
$HOME directory. At the very least, the following example `.nnclirc`
diff --git a/nnotes_cli/config.py b/nnotes_cli/config.py
@@ -1,3 +1,26 @@
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2018 Daniel Moch
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
# Copyright (c) 2014 Eric Davis
# Licensed under the MIT License
@@ -8,11 +31,31 @@ class Config:
def __init__(self, custom_file=None):
self.home = os.path.abspath(os.path.expanduser('~'))
+ if 'XDG_CONFIG_HOME' in os.environ.keys():
+ self.config_home = \
+ os.path.join(os.environ['XDG_CONFIG_HOME'], 'nncli')
+ else:
+ self.config_home = \
+ os.path.join(
+ os.path.expanduser('~'),
+ '.config',
+ 'nncli'
+ )
+ if 'XDG_CACHE_HOME' in os.environ.keys():
+ self.cache_home = \
+ os.path.join(os.environ['XDG_CACHE_HOME'], 'nncli')
+ else:
+ self.cache_home = \
+ os.path.join(
+ os.path.expanduser('~'),
+ '.cache',
+ 'nncli'
+ )
defaults = \
{
- 'cfg_sn_username' : '',
- 'cfg_sn_password' : '',
- 'cfg_db_path' : os.path.join(self.home, '.sncli'),
+ 'cfg_nn_username' : '',
+ 'cfg_nn_password' : '',
+ 'cfg_db_path' : self.cache_home,
'cfg_search_tags' : 'yes', # with regex searches
'cfg_sort_mode' : 'date', # 'alpha' or 'date'
'cfg_pinned_ontop' : 'yes',
@@ -26,7 +69,7 @@ def __init__(self, custom_file=None):
'cfg_max_logs' : '5',
'cfg_log_timeout' : '5',
'cfg_log_reversed' : 'yes',
- 'cfg_sn_host' : 'simple-note.appspot.com',
+ 'cfg_nn_host' : '',
'cfg_tempdir' : '',
'kb_help' : 'h',
@@ -125,23 +168,23 @@ def __init__(self, custom_file=None):
if custom_file is not None:
self.configs_read = cp.read([custom_file])
else:
- self.configs_read = cp.read([os.path.join(self.home, '.snclirc')])
+ self.configs_read = cp.read([os.path.join(self.config_home, 'config')])
- cfg_sec = 'sncli'
+ cfg_sec = 'nncli'
if not cp.has_section(cfg_sec):
cp.add_section(cfg_sec)
# special handling for password so we can retrieve it by running a command
- sn_password = cp.get(cfg_sec, 'cfg_sn_password', raw=True)
- if not sn_password:
- command = cp.get(cfg_sec, 'cfg_sn_password_eval', raw=True)
+ nn_password = cp.get(cfg_sec, 'cfg_nn_password', raw=True)
+ if not nn_password:
+ command = cp.get(cfg_sec, 'cfg_nn_password_eval', raw=True)
if command:
try:
- sn_password = subprocess.check_output(command, shell=True, universal_newlines=True)
+ nn_password = subprocess.check_output(command, shell=True, universal_newlines=True)
# remove trailing newlines to avoid requiring butchering shell commands (they can't usually be in passwords anyway)
- sn_password = sn_password.rstrip('\n')
+ nn_password = nn_password.rstrip('\n')
except subprocess.CalledProcessError as e:
print('Error evaluating command for password.')
print(e)
diff --git a/nnotes_cli/nncli.py b/nnotes_cli/nncli.py
@@ -1,3 +1,26 @@
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2018 Daniel Moch
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
# Copyright (c) 2014 Eric Davis
# Licensed under the MIT License
@@ -12,7 +35,7 @@
from .notes_db import NotesDB, ReadError, WriteError
from logging.handlers import RotatingFileHandler
-class sncli:
+class nncli:
def __init__(self, do_server_sync, verbose=False, config_file=None):
self.config = Config(config_file)
@@ -31,7 +54,7 @@ def __init__(self, do_server_sync, verbose=False, config_file=None):
force_full_sync = True
# configure the logging module
- self.logfile = os.path.join(self.config.get_config('db_path'), 'sncli.log')
+ self.logfile = os.path.join(self.config.get_config('db_path'), 'nncli.log')
self.loghandler = RotatingFileHandler(self.logfile, maxBytes=100000, backupCount=1)
self.loghandler.setLevel(logging.DEBUG)
self.loghandler.setFormatter(logging.Formatter(fmt='%(asctime)s [%(levelname)s] %(message)s'))
@@ -40,7 +63,7 @@ def __init__(self, do_server_sync, verbose=False, config_file=None):
self.logger.addHandler(self.loghandler)
self.config.logfile = self.logfile
- logging.debug('sncli logging initialized')
+ logging.debug('nncli logging initialized')
self.logs = []
@@ -56,7 +79,7 @@ def __init__(self, do_server_sync, verbose=False, config_file=None):
# with hundreds of notes will cause a recursion panic under
# urwid. This simple workaround gets the job done. :-)
self.verbose = True
- self.log('sncli database doesn\'t exist, forcing full sync...')
+ self.log('nncli database doesn\'t exist, forcing full sync...')
self.sync_notes()
self.verbose = verbose
@@ -168,11 +191,11 @@ def exec_diff_on_note(self, note, old_note):
def gui_header_clear(self):
self.master_frame.contents['header'] = ( None, None )
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_header_set(self, w):
self.master_frame.contents['header'] = ( w, None )
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_header_get(self):
return self.master_frame.contents['header'][0]
@@ -184,13 +207,13 @@ def gui_footer_log_clear(self):
ui = self.gui_footer_input_get()
self.master_frame.contents['footer'] = \
(urwid.Pile([ urwid.Pile([]), urwid.Pile([ui]) ]), None)
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_footer_log_set(self, pl):
ui = self.gui_footer_input_get()
self.master_frame.contents['footer'] = \
(urwid.Pile([ urwid.Pile(pl), urwid.Pile([ui]) ]), None)
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_footer_log_get(self):
return self.master_frame.contents['footer'][0].contents[0][0]
@@ -199,13 +222,13 @@ def gui_footer_input_clear(self):
pl = self.gui_footer_log_get()
self.master_frame.contents['footer'] = \
(urwid.Pile([ urwid.Pile([pl]), urwid.Pile([]) ]), None)
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_footer_input_set(self, ui):
pl = self.gui_footer_log_get()
self.master_frame.contents['footer'] = \
(urwid.Pile([ urwid.Pile([pl]), urwid.Pile([ui]) ]), None)
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_footer_input_get(self):
return self.master_frame.contents['footer'][0].contents[1][0]
@@ -216,12 +239,12 @@ def gui_footer_focus_input(self):
def gui_body_clear(self):
self.master_frame.contents['body'] = ( None, None )
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_body_set(self, w):
self.master_frame.contents['body'] = ( w, None )
self.gui_update_status_bar()
- self.sncli_loop.draw_screen()
+ self.nncli_loop.draw_screen()
def gui_body_get(self):
return self.master_frame.contents['body'][0]
@@ -276,7 +299,7 @@ def log(self, msg):
if self.verbose:
self.gui_footer_log_set(log_pile)
- self.sncli_loop.set_alarm_in(
+ self.nncli_loop.set_alarm_in(
int(self.config.get_config('log_timeout')),
self.log_timeout, None)
@@ -916,12 +939,12 @@ def gui_init_view(self, loop, view_note):
self.thread_sync.start()
def gui_clear(self):
- self.sncli_loop.widget = urwid.Filler(urwid.Text(''))
- self.sncli_loop.draw_screen()
+ self.nncli_loop.widget = urwid.Filler(urwid.Text(''))
+ self.nncli_loop.draw_screen()
def gui_reset(self):
- self.sncli_loop.widget = self.master_frame
- self.sncli_loop.draw_screen()
+ self.nncli_loop.widget = self.master_frame
+ self.nncli_loop.draw_screen()
def gui_stop(self):
# don't exit if there are any notes not yet saved to the disk
@@ -1044,14 +1067,14 @@ def gui(self, key):
urwid.Pile([]) ]),
focus_part='body')
- self.sncli_loop = urwid.MainLoop(self.master_frame,
+ self.nncli_loop = urwid.MainLoop(self.master_frame,
palette,
handle_mouse=False)
- self.sncli_loop.set_alarm_in(0, self.gui_init_view,
+ self.nncli_loop.set_alarm_in(0, self.gui_init_view,
True if key else False)
- self.sncli_loop.run()
+ self.nncli_loop.run()
def cli_list_notes(self, regex, search_string):
@@ -1280,7 +1303,7 @@ def SIGINT_handler(signum, frame):
def usage():
print ('''
Usage:
- sncli [OPTIONS] [COMMAND] [COMMAND_ARGS]
+ nncli [OPTIONS] [COMMAND] [COMMAND_ARGS]
OPTIONS:
-h, --help - usage help
@@ -1289,7 +1312,8 @@ def usage():
-r, --regex - search string is a regular expression
-k <key>, --key=<key> - note key
-t <title>, --title=<title> - title of note for create (cli mode)
- -c <file>, --config=<file> - config file to read from (defaults to ~/.snclirc)
+ -c <file>, --config=<file> - config file to read from (defaults to
+ ~/.config/nncli/config)
COMMANDS:
<none> - console gui mode when no command specified
@@ -1348,25 +1372,25 @@ def main(argv=sys.argv[1:]):
usage()
if not args:
- sncli(sync, verbose, config).gui(key)
+ nncli(sync, verbose, config).gui(key)
return
- def sncli_start(sync=sync, verbose=verbose, config=config):
- sn = sncli(sync, verbose, config)
+ def nncli_start(sync=sync, verbose=verbose, config=config):
+ sn = nncli(sync, verbose, config)
if sync: sn.sync_notes()
return sn
if args[0] == 'sync':
- sn = sncli_start(True)
+ sn = nncli_start(True)
elif args[0] == 'list':
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_list_notes(regex, ' '.join(args[1:]))
elif args[0] == 'dump':
- sn = sncli_start()
+ sn = nncli_start()
if key:
sn.cli_note_dump(key)
else:
@@ -1375,10 +1399,10 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
elif args[0] == 'create':
if len(args) == 1:
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_create(False, title)
elif len(args) == 2 and args[1] == '-':
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_create(True, title)
else:
usage()
@@ -1386,17 +1410,17 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
elif args[0] == 'import':
if len(args) == 1:
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_import(False)
elif len(args) == 2 and args[1] == '-':
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_import(True)
else:
usage()
elif args[0] == 'export':
- sn = sncli_start()
+ sn = nncli_start()
if key:
sn.cli_note_export(key)
else:
@@ -1407,7 +1431,7 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
if not key:
usage()
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_edit(key)
elif args[0] == 'trash' or args[0] == 'untrash':
@@ -1415,7 +1439,7 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
if not key:
usage()
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_trash(key, 1 if args[0] == 'trash' else 0)
elif args[0] == 'pin' or args[0] == 'unpin':
@@ -1423,7 +1447,7 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
if not key:
usage()
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_pin(key, 1 if args[0] == 'pin' else 0)
elif args[0] == 'markdown' or args[0] == 'unmarkdown':
@@ -1431,7 +1455,7 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
if not key:
usage()
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_markdown(key, 1 if args[0] == 'markdown' else 0)
# Tag API
@@ -1448,7 +1472,7 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
if args[1] == 'get':
- sn = sncli_start()
+ sn = nncli_start()
tags = sn.cli_note_tags_get(key)
if tags:
print(tags)
@@ -1456,19 +1480,19 @@ def sncli_start(sync=sync, verbose=verbose, config=config):
elif args[1] == 'set':
tags = args[2]
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_tags_set(key, tags)
elif args[1] == 'add':
new_tags = args[2]
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_tags_add(key, new_tags)
elif args[1] == 'rm':
rm_tags = args[2]
- sn = sncli_start()
+ sn = nncli_start()
sn.cli_note_tags_rm(key, rm_tags)
else:
diff --git a/nnotes_cli/notes_db.py b/nnotes_cli/notes_db.py
@@ -81,7 +81,7 @@ def __init__(self, config, log, update_view):
n['savedate'] = now
# set a localkey to each note in memory
# Note: 'key' is used only for syncing with server - 'localkey'
- # is used for everything else in sncli
+ # is used for everything else in nncli
n['localkey'] = localkey
# add the note to our database
diff --git a/setup.py b/setup.py
@@ -21,7 +21,7 @@
packages=['simplenote_cli'],
entry_points={
'console_scripts': [
- 'sncli = simplenote_cli.sncli:main'
+ 'nncli = nnotes_cli.nncli:main'
]
},
classifiers=[