commit bfcac97591b757230d6a23549d1023ac054218c2
parent 207767bc879a6eeda61cf3a96388d2504057e6b1
Author: Daniel Moch <daniel@danielmoch.com>
Date: Mon, 10 Sep 2018 06:05:53 -0400
Address pylint findings in the rest
Ref #10
Diffstat:
11 files changed, 678 insertions(+), 452 deletions(-)
diff --git a/nncli/clipboard.py b/nncli/clipboard.py
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
"""clipboard module"""
import os
-from distutils import spawn
+import subprocess
+from subprocess import CalledProcessError
class Clipboard:
"""Class implements copying note content to the clipboard"""
@@ -11,10 +12,19 @@ def __init__(self):
@staticmethod
def get_copy_command():
"""Defines the copy command based on the contents of $PATH"""
- if spawn.find_executable('xsel'):
+
+ try:
+ subprocess.check_output(['which', 'xsel'])
return 'echo "%s" | xsel -ib'
- if spawn.find_executable('pbcopy'):
+ except CalledProcessError:
+ pass
+
+ try:
+ subprocess.check_output(['which', 'pbcopy'])
return 'echo "%s" | pbcopy'
+ except CalledProcessError:
+ pass
+
return None
def copy(self, text):
diff --git a/nncli/config.py b/nncli/config.py
@@ -8,6 +8,7 @@
from appdirs import user_cache_dir, user_config_dir
+# pylint: disable=too-few-public-methods
class Config:
"""A class to contain all configuration data for nncli"""
class State:
diff --git a/nncli/nextcloud_note.py b/nncli/nextcloud_note.py
@@ -105,14 +105,14 @@ def update_note(self, note):
self.status = 'online'
except ConnectionError as ex:
self.status = 'offline, connection error'
- return ex, -1
+ raise ex
except RequestException as ex:
logging.debug('RESPONSE ERROR: %s', ex)
logging.debug(traceback.print_exc())
self.status = 'error updating note, check log'
- return ex, -1
+ raise ex
except ValueError as ex:
- return ex, -1
+ raise ex
#logging.debug('RESPONSE OK: ' + str(note))
return note, 0
@@ -199,7 +199,7 @@ def delete_note(self, note):
self.status = 'online'
except ConnectionError as ex:
self.status = 'offline, connection error'
- return ex, -1
+ raise ex
except RequestException as ex:
- return ex, -1
+ raise ex
return {}, 0
diff --git a/nncli/notes_db.py b/nncli/notes_db.py
@@ -1,14 +1,25 @@
# -*- coding: utf-8 -*-
+"""notes_db module"""
+import copy
+import glob
+import json
+import os
+import re
+import threading
+import time
+from requests.exceptions import RequestException
-import os, time, re, glob, json, copy, threading
from . import utils
from .nextcloud_note import NextcloudNote
-import logging
+# pylint: disable=too-many-instance-attributes, too-many-locals
+# pylint: disable=too-many-branches, too-many-statements
class ReadError(RuntimeError):
+ """Exception thrown on a read error"""
pass
class WriteError(RuntimeError):
+ """Exception thrown on a write error"""
pass
class NotesDB():
@@ -17,13 +28,13 @@ class NotesDB():
NextCloud Notes
"""
def __init__(self, config, log, update_view=None):
- self.config = config
- self.log = log
+ self.config = config
+ self.log = log
self.update_view = update_view
self.last_sync = 0 # set to zero to trigger a full sync
self.sync_lock = threading.Lock()
- self.go_cond = threading.Condition()
+ self.go_cond = threading.Condition()
# create db dir if it does not exist
if not os.path.exists(self.config.get_config('db_path')):
@@ -31,32 +42,35 @@ def __init__(self, config, log, update_view=None):
now = int(time.time())
# now read all .json files from disk
- fnlist = glob.glob(self.helper_key_to_fname('*'))
+ fnlist = glob.glob(self._helper_key_to_fname('*'))
self.notes = {}
- for fn in fnlist:
+ for func in fnlist:
try:
- n = json.load(open(fn, 'r'))
- except IOError as e:
- raise ReadError ('Error opening {0}: {1}'.format(fn, str(e)))
- except ValueError as e:
- raise ReadError ('Error reading {0}: {1}'.format(fn, str(e)))
+ note = json.load(open(func, 'r'))
+ except IOError as ex:
+ raise ReadError('Error opening {0}: {1}'.format(func, str(ex)))
+ except ValueError as ex:
+ raise ReadError('Error reading {0}: {1}'.format(func, str(ex)))
else:
# we always have a localkey, also when we don't have a
# note['id'] yet (no sync)
- localkey = n.get('localkey', os.path.splitext(os.path.basename(fn))[0])
+ localkey = note.get(
+ 'localkey',
+ os.path.splitext(os.path.basename(func))[0]
+ )
# we maintain in memory a timestamp of the last save
# these notes have just been read, so at this moment
# they're in sync with the disc.
- n['savedate'] = now
+ note['savedate'] = now
# set a localkey to each note in memory
# Note: 'id' is used only for syncing with server - 'localkey'
# is used for everything else in nncli
- n['localkey'] = localkey
+ note['localkey'] = localkey
# add the note to our database
- self.notes[localkey] = n
+ self.notes[localkey] = note
# initialise the NextCloud instance we're going to use
# this does not yet need network access
@@ -65,14 +79,19 @@ def __init__(self, config, log, update_view=None):
self.config.get_config('nn_host'))
def set_update_view(self, update_view):
+ """Set the update_view method"""
self.update_view = update_view
- def filtered_notes_sort(self, filtered_notes, sort_mode='date'):
+ def _filtered_notes_sort(self, filtered_notes, sort_mode='date'):
+ """Sort filtered note set"""
if sort_mode == 'date':
if self.config.get_config('favorite_ontop') == 'yes':
- filtered_notes.sort(key=utils.sort_by_modify_date_favorite, reverse=True)
+ filtered_notes.sort(key=utils.sort_by_modify_date_favorite,
+ reverse=True)
else:
- filtered_notes.sort(key=lambda o: -float(o.note.get('modified', 0)))
+ filtered_notes.sort(
+ key=lambda o: -float(o.note.get('modified', 0))
+ )
elif sort_mode == 'alpha':
if self.config.get_config('favorite_ontop') == 'yes':
filtered_notes.sort(key=utils.sort_by_title_favorite)
@@ -83,7 +102,8 @@ def filtered_notes_sort(self, filtered_notes, sort_mode='date'):
utils.sort_notes_by_categories(filtered_notes, \
favorite_ontop=favorite)
- def filter_notes(self, search_string=None, search_mode='gstyle', sort_mode='date'):
+ def filter_notes(self, search_string=None, search_mode='gstyle',
+ sort_mode='date'):
"""Return list of notes filtered with search string.
Based on the search mode that has been selected in self.config,
@@ -98,21 +118,24 @@ def filter_notes(self, search_string=None, search_mode='gstyle', sort_mode='date
if search_mode == 'gstyle':
filtered_notes, match_regexp, active_notes = \
- self.filter_notes_gstyle(search_string)
+ self._filter_notes_gstyle(search_string)
else:
filtered_notes, match_regexp, active_notes = \
- self.filter_notes_regex(search_string)
+ self._filter_notes_regex(search_string)
- self.filtered_notes_sort(filtered_notes, sort_mode)
+ self._filtered_notes_sort(filtered_notes, sort_mode)
return filtered_notes, match_regexp, active_notes
- def _helper_gstyle_categorymatch(self, cat_pats, note):
+ @staticmethod
+ def _helper_gstyle_categorymatch(cat_pats, note):
+ """Match categories using a Google-style search string"""
# Returns:
# 2 = match - no category patterns specified
# 1 = match - all category patterns match a category on this
# note
- # 0 = no match - note has no category or not all category patterns match
+ # 0 = no match - note has no category or not all category
+ # patterns match
if not cat_pats:
# match because no category patterns were specified
@@ -121,16 +144,17 @@ def _helper_gstyle_categorymatch(self, cat_pats, note):
note_category = note.get('category')
if not note_category:
- # category patterns specified but note has no categories, so no match
+ # category patterns specified but note has no categories,
+ # so no match
return 0
# for each cat_pat, we have to find a matching category
# .lower() used for case-insensitive search
cat_pats_matched = 0
- for tp in cat_pats:
- tp = tp.lower()
- for t in note_category:
- if tp in t.lower():
+ for cat_pat in cat_pats:
+ cat_pat = cat_pat.lower()
+ for pat in note_category:
+ if cat_pat in pat.lower():
cat_pats_matched += 1
break
@@ -141,32 +165,36 @@ def _helper_gstyle_categorymatch(self, cat_pats, note):
# note doesn't match
return 0
- def _helper_gstyle_wordmatch(self, word_pats, content):
+ @staticmethod
+ def _helper_gstyle_wordmatch(word_pats, content):
+ """Match note contents based no a Google-style search string"""
if not word_pats:
return True
word_pats_matched = 0
lowercase_content = content.lower() # case insensitive search
- for wp in word_pats:
- wp = wp.lower() # case insensitive search
- if wp in lowercase_content:
+ for word_pat in word_pats:
+ word_pat = word_pat.lower() # case insensitive search
+ if word_pat in lowercase_content:
word_pats_matched += 1
if word_pats_matched == len(word_pats):
- return True;
+ return True
return False
- def filter_notes_gstyle(self, search_string=None):
-
+ def _filter_notes_gstyle(self, search_string=None):
+ """Filter the notes based of a Google-style search string"""
filtered_notes = []
active_notes = 0
if not search_string:
- for k in self.notes:
- n = self.notes[k]
+ for key in self.notes:
+ note = self.notes[key]
active_notes += 1
- filtered_notes.append(utils.KeyValueObject(key=k, note=n, catfound=0))
+ filtered_notes.append(
+ utils.KeyValueObject(key=key, note=note, catfound=0)
+ )
return filtered_notes, [], active_notes
@@ -174,41 +202,48 @@ def filter_notes_gstyle(self, search_string=None):
# group1: multiple words in quotes
# group2: single words
- # example result for: 'category:category1 category:category2 word1 "word2 word3" category:category3'
+ # example result for: 'category:category1 category:category2
+ # word1 "word2 word3" category:category3'
# [ ('category1', '', ''),
# ('category2', '', ''),
# ('', '', 'word1'),
# ('', 'word2 word3', ''),
# ('category3', '', '') ]
- groups = re.findall('category:([^\s]+)|"([^"]+)"|([^\s]+)', search_string)
+ groups = re.findall(
+ r'category:([^\s]+)|"([^"]+)"|([^\s]+)', search_string
+ )
all_pats = [[] for _ in range(3)]
# we end up with [[cat_pats],[multi_word_pats],[single_word_pats]]
- for g in groups:
+ for group in groups:
for i in range(3):
- if g[i]: all_pats[i].append(g[i])
+ if group[i]:
+ all_pats[i].append(group[i])
- for k in self.notes:
- n = self.notes[k]
+ for key in self.notes:
+ note = self.notes[key]
active_notes += 1
- catmatch = self._helper_gstyle_categorymatch(all_pats[0], n)
+ catmatch = self._helper_gstyle_categorymatch(all_pats[0],
+ note)
word_pats = all_pats[1] + all_pats[2]
if catmatch and \
- self._helper_gstyle_wordmatch(word_pats, n.get('content')):
+ self._helper_gstyle_wordmatch(word_pats, note.get('content')):
# we have a note that can go through!
filtered_notes.append(
- utils.KeyValueObject(key=k,
- note=n,
- catfound=1 if catmatch == 1 else 0))
+ utils.KeyValueObject(key=key,
+ note=note,
+ catfound=1 \
+ if catmatch == 1 \
+ else 0))
return filtered_notes, '|'.join(all_pats[1] + all_pats[2]), active_notes
- def filter_notes_regex(self, search_string=None):
+ def _filter_notes_regex(self, search_string=None):
"""
Return a list of notes filtered using the regex search_string.
Each element in the list is a tuple (local_key, note).
@@ -218,32 +253,40 @@ def filter_notes_regex(self, search_string=None):
filtered_notes = []
active_notes = 0 # total number of notes, including deleted ones
- for k in self.notes:
- n = self.notes[k]
+ for key in self.notes:
+ note = self.notes[key]
active_notes += 1
if not sspat:
- filtered_notes.append(utils.KeyValueObject(key=k, note=n, catfound=0))
+ filtered_notes.append(
+ utils.KeyValueObject(key=key, note=note, catfound=0)
+ )
continue
if self.config.get_config('search_categories') == 'yes':
cat_matched = False
- for t in n.get('category'):
- if sspat.search(t):
+ for cat in note.get('category'):
+ if sspat.search(cat):
cat_matched = True
- filtered_notes.append(utils.KeyValueObject(key=k, note=n, catfound=1))
+ filtered_notes.append(
+ utils.KeyValueObject(key=key,
+ note=note, catfound=1)
+ )
break
if cat_matched:
continue
- if sspat.search(n.get('content')):
- filtered_notes.append(utils.KeyValueObject(key=k, note=n, catfound=0))
+ if sspat.search(note.get('content')):
+ filtered_notes.append(
+ utils.KeyValueObject(key=key, note=note, catfound=0)
+ )
match_regexp = search_string if sspat else ''
return filtered_notes, match_regexp, active_notes
def import_note(self, note):
+ """Import a note into the database"""
# need to get a key unique to this database. not really important
# what it is, as long as it's unique.
new_key = note['id'] if note.get('id') else utils.generate_random_key()
@@ -255,21 +298,23 @@ def import_note(self, note):
try:
modified = float(note.get('modified', timestamp))
except ValueError:
- raise ValueError('date fields must be numbers or string representations of numbers')
+ raise ValueError('date fields must be numbers or string'
+ 'representations of numbers')
# note has no internal key yet.
- new_note = {
- 'content' : note.get('content', ''),
- 'modified' : modified,
- 'title' : note.get('title'),
- 'category' : note.get('category') \
- if note.get('category') is not None \
- else '',
- 'savedate' : 0, # never been written to disc
- 'syncdate' : 0, # never been synced with server
- 'favorite' : False,
- 'deleted' : False
- }
+ new_note = \
+ {
+ 'content' : note.get('content', ''),
+ 'modified' : modified,
+ 'title' : note.get('title'),
+ 'category' : note.get('category') \
+ if note.get('category') is not None \
+ else '',
+ 'savedate' : 0, # never been written to disc
+ 'syncdate' : 0, # never been synced with server
+ 'favorite' : False,
+ 'deleted' : False
+ }
# sanity check all note values
if not isinstance(new_note['content'], str):
@@ -290,6 +335,7 @@ def import_note(self, note):
return new_key
def create_note(self, content):
+ """Create a new note in the database"""
# need to get a key unique to this database. not really important
# what it is, as long as it's unique.
new_key = utils.generate_random_key()
@@ -300,84 +346,95 @@ def create_note(self, content):
title = content.split('\n')[0]
# note has no internal key yet.
- new_note = {
- 'localkey' : new_key,
- 'content' : content,
- 'modified' : timestamp,
- 'category' : '',
- 'savedate' : 0, # never been written to disc
- 'syncdate' : 0, # never been synced with server
- 'favorite' : False,
- 'deleted' : False,
- 'title' : title
- }
+ new_note = \
+ {
+ 'localkey' : new_key,
+ 'content' : content,
+ 'modified' : timestamp,
+ 'category' : '',
+ 'savedate' : 0, # never been written to disc
+ 'syncdate' : 0, # never been synced with server
+ 'favorite' : False,
+ 'deleted' : False,
+ 'title' : title
+ }
self.notes[new_key] = new_note
return new_key
def get_note(self, key):
+ """Get a note from the database"""
return self.notes[key]
- def get_note_category(self, key):
+ def _get_note_category(self, key):
+ """Get a category for a note"""
return self.notes[key].get('category')
- def flag_what_changed(self, note, what_changed):
+ @staticmethod
+ def _flag_what_changed(note, what_changed):
+ """Flag a note field as changed"""
if 'what_changed' not in note:
note['what_changed'] = []
if what_changed not in note['what_changed']:
note['what_changed'].append(what_changed)
def set_note_deleted(self, key, deleted):
- n = self.notes[key]
- old_deleted = n['deleted'] if 'deleted' in n else 0
+ """Mark a note for deletion"""
+ note = self.notes[key]
+ old_deleted = note['deleted'] if 'deleted' in note else 0
if old_deleted != deleted:
- n['deleted'] = deleted
- n['modified'] = int(time.time())
- self.flag_what_changed(n, 'deleted')
+ note['deleted'] = deleted
+ note['modified'] = int(time.time())
+ self._flag_what_changed(note, 'deleted')
self.log('Note marked for deletion (key={0})'.format(key))
def set_note_content(self, key, content):
- n = self.notes[key]
- old_content = n.get('content')
+ """Set the content of a note in the database"""
+ note = self.notes[key]
+ old_content = note.get('content')
if content != old_content:
- n['content'] = content
- n['modified'] = int(time.time())
- self.flag_what_changed(n, 'content')
+ note['content'] = content
+ note['modified'] = int(time.time())
+ self._flag_what_changed(note, 'content')
self.log('Note content updated (key={0})'.format(key))
def set_note_category(self, key, category):
- n = self.notes[key]
- old_category = n.get('category')
+ """Set the category of a note in the database"""
+ note = self.notes[key]
+ old_category = note.get('category')
if category != old_category:
- n['category'] = category
- n['modified'] = int(time.time())
- self.flag_what_changed(n, 'category')
+ note['category'] = category
+ note['modified'] = int(time.time())
+ self._flag_what_changed(note, 'category')
self.log('Note category updated (key={0})'.format(key))
def set_note_favorite(self, key, favorite):
- n = self.notes[key]
- old_favorite = utils.note_favorite(n)
+ """Mark a note in the database as a favorite"""
+ note = self.notes[key]
+ old_favorite = utils.note_favorite(note)
if favorite != old_favorite:
- n['favorite'] = favorite
- n['modified'] = int(time.time())
- self.flag_what_changed(n, 'favorite')
+ note['favorite'] = favorite
+ note['modified'] = int(time.time())
+ self._flag_what_changed(note, 'favorite')
self.log('Note {0} (key={1})'. \
format('favorite' if favorite else \
'unfavorited', key))
- def helper_key_to_fname(self, k):
+ def _helper_key_to_fname(self, k):
+ """Convert a note key into a file name"""
return os.path.join(self.config.get_config('db_path'), str(k)) + '.json'
- def helper_save_note(self, k, note):
+ def _helper_save_note(self, k, note):
+ """Save a note to the file system"""
# Save a single note to disc.
- fn = self.helper_key_to_fname(k)
- json.dump(note, open(fn, 'w'), indent=2)
+ func = self._helper_key_to_fname(k)
+ json.dump(note, open(func, 'w'), indent=2)
# record that we saved this to disc.
note['savedate'] = int(time.time())
- def sync_notes(self, server_sync=True, full_sync=True):
+ def _sync_notes(self, server_sync=True, full_sync=True):
"""Perform a full bi-directional sync with server.
Psuedo-code algorithm for syncing:
@@ -412,15 +469,15 @@ def sync_notes(self, server_sync=True, full_sync=True):
# 1. for any note changed locally, including new notes:
# save note to server, update note with response
- for note_index, local_key in enumerate(self.notes.keys()):
- n = self.notes[local_key]
+ for _, local_key in enumerate(self.notes.keys()):
+ note = self.notes[local_key]
- if not n.get('id') or \
- float(n.get('modified')) > float(n.get('syncdate')):
+ if not note.get('id') or \
+ float(note.get('modified')) > float(note.get('syncdate')):
- savedate = float(n.get('savedate'))
- if float(n.get('modified')) > savedate or \
- float(n.get('syncdate')) > savedate:
+ savedate = float(note.get('savedate'))
+ if float(note.get('modified')) > savedate or \
+ float(note.get('syncdate')) > savedate:
# this will trigger a save to disk after sync algorithm
# we want this note saved even if offline or sync fails
local_updates[local_key] = True
@@ -431,77 +488,81 @@ def sync_notes(self, server_sync=True, full_sync=True):
continue
# only send required fields
- cn = copy.deepcopy(n)
- if 'what_changed' in n:
- del n['what_changed']
-
- if 'localkey' in cn:
- del cn['localkey']
-
- if 'minversion' in cn:
- del cn['minversion']
- del cn['syncdate']
- del cn['savedate']
- del cn['deleted']
- if 'etag' in cn:
- del cn['etag']
- if 'title' in cn:
- del cn['title']
-
- if 'what_changed' in cn:
- if 'content' not in cn['what_changed'] \
- and 'category' not in cn['what_changed']:
- del cn['content']
- if 'category' not in cn['what_changed']:
- del cn['category']
- if 'favorite' not in cn['what_changed']:
- del cn['favorite']
- del cn['what_changed']
-
- if n['deleted']:
- uret = self.note.delete_note(cn)
- else:
- uret = self.note.update_note(cn)
+ cnote = copy.deepcopy(note)
+ if 'what_changed' in note:
+ del note['what_changed']
+
+ if 'localkey' in cnote:
+ del cnote['localkey']
+
+ if 'minversion' in cnote:
+ del cnote['minversion']
+ del cnote['syncdate']
+ del cnote['savedate']
+ del cnote['deleted']
+ if 'etag' in cnote:
+ del cnote['etag']
+ if 'title' in cnote:
+ del cnote['title']
+
+ if 'what_changed' in cnote:
+ if 'content' not in cnote['what_changed'] \
+ and 'category' not in cnote['what_changed']:
+ del cnote['content']
+ if 'category' not in cnote['what_changed']:
+ del cnote['category']
+ if 'favorite' not in cnote['what_changed']:
+ del cnote['favorite']
+ del cnote['what_changed']
+
+ try:
+ if note['deleted']:
+ uret = self.note.delete_note(cnote)
+ else:
+ uret = self.note.update_note(cnote)
- if uret[1] == 0: # success
# if this is a new note our local key is not valid anymore
# merge the note we got back (content could be empty)
# record syncdate and save the note at the assigned key
del self.notes[local_key]
- k = uret[0].get('id')
- t = uret[0].get('title')
- c = uret[0].get('category')
- c = c if c is not None else ''
- n.update(uret[0])
- n['syncdate'] = now
- n['localkey'] = k
- n['category'] = c
- self.notes[k] = n
-
- local_updates[k] = True
- if local_key != k:
+ key = uret[0].get('id')
+ category = uret[0].get('category')
+ category = category if category is not None else ''
+ note.update(uret[0])
+ note['syncdate'] = now
+ note['localkey'] = key
+ note['category'] = category
+ self.notes[key] = note
+
+ local_updates[key] = True
+ if local_key != key:
# if local_key was a different key it should be deleted
local_deletes[local_key] = True
if local_key in local_updates:
del local_updates[local_key]
- self.log('Synced note to server (key={0})'.format(local_key))
- else:
- self.log('ERROR: Failed to sync note to server (key={0})'.format(local_key))
+ self.log(
+ 'Synced note to server (key={0})'.format(local_key)
+ )
+ except (ConnectionError, RequestException, ValueError):
+ self.log(
+ 'ERROR: Failed to sync note to server (key={0})'.
+ format(local_key)
+ )
sync_errors += 1
# 2. get the note index
if not server_sync:
- nl = []
+ note_list = []
else:
- nl = self.note.get_note_list()
+ note_list = self.note.get_note_list()
- if nl[1] == 0: # success
- nl = nl[0]
+ if note_list[1] == 0: # success
+ note_list = note_list[0]
else:
self.log('ERROR: Failed to get note list from server')
sync_errors += 1
- nl = []
+ note_list = []
skip_remote_syncing = True
# 3. for each remote note
@@ -509,44 +570,58 @@ def sync_notes(self, server_sync=True, full_sync=True):
# a new note and key is not in local store
# retrieve note, update note with response
if not skip_remote_syncing:
- for note_index, n in enumerate(nl):
- k = n.get('id')
- c = n.get('category') if n.get('category') is not None \
+ for _, note in enumerate(note_list):
+ key = note.get('id')
+ category = note.get('category') \
+ if note.get('category') is not None \
else ''
- server_keys[k] = True
+ server_keys[key] = True
# this works because in the prior step we rewrite local keys to
# server keys when we get an updated note back from the server
- if k in self.notes:
+ if key in self.notes:
# we already have this note
# if the server note has a newer syncnum we need to get it
- if int(n.get('modified')) > int(self.notes[k].get('modified')):
- gret = self.note.get_note(k)
+ if int(note.get('modified')) > \
+ int(self.notes[key].get('modified')):
+ gret = self.note.get_note(key)
if gret[1] == 0:
- self.notes[k].update(gret[0])
- local_updates[k] = True
- self.notes[k]['syncdate'] = now
- self.notes[k]['localkey'] = k
- self.notes[k]['category'] = c
- self.notes[k]['deleted'] = False
-
- self.log('Synced newer note from server (key={0})'.format(k))
+ self.notes[key].update(gret[0])
+ local_updates[key] = True
+ self.notes[key]['syncdate'] = now
+ self.notes[key]['localkey'] = key
+ self.notes[key]['category'] = category
+ self.notes[key]['deleted'] = False
+
+ self.log(
+ 'Synced newer note from server (key={0})'.
+ format(key)
+ )
else:
- self.log('ERROR: Failed to sync newer note from server (key={0})'.format(k))
+ self.log(
+ 'ERROR: Failed to sync newer note '
+ 'from server (key={0})'.format(key)
+ )
sync_errors += 1
else:
# this is a new note
- gret = self.note.get_note(k)
+ gret = self.note.get_note(key)
if gret[1] == 0:
- self.notes[k] = gret[0]
- local_updates[k] = True
- self.notes[k]['syncdate'] = now
- self.notes[k]['localkey'] = k
- self.notes[k]['category'] = c
- self.notes[k]['deleted'] = False
-
- self.log('Synced new note from server (key={0})'.format(k))
+ self.notes[key] = gret[0]
+ local_updates[key] = True
+ self.notes[key]['syncdate'] = now
+ self.notes[key]['localkey'] = key
+ self.notes[key]['category'] = category
+ self.notes[key]['deleted'] = False
+
+ self.log(
+ 'Synced new note from server (key={0})'.
+ format(key)
+ )
else:
- self.log('ERROR: Failed syncing new note from server (key={0})'.format(k))
+ self.log(
+ 'ERROR: Failed syncing new note from'
+ 'server (key={0})'.format(key)
+ )
sync_errors += 1
# 4. for each local note not in the index
@@ -562,22 +637,22 @@ def sync_notes(self, server_sync=True, full_sync=True):
for k in list(local_updates.keys()):
try:
- self.helper_save_note(k, self.notes[k])
- except WriteError as e:
- raise WriteError (str(e))
- self.log("Saved note to disk (key={0})".format(k))
+ self._helper_save_note(k, self.notes[k])
+ except WriteError as ex:
+ raise WriteError(str(ex))
+ self.log("Saved note to disk (key={0})".format(key))
for k in list(local_deletes.keys()):
- fn = self.helper_key_to_fname(k)
- if os.path.exists(fn):
- os.unlink(fn)
- self.log("Deleted note from disk (key={0})".format(k))
+ fnote = self._helper_key_to_fname(k)
+ if os.path.exists(fnote):
+ os.unlink(fnote)
+ self.log("Deleted note from disk (key={0})".format(key))
if not sync_errors:
self.last_sync = sync_start_time
# if there were any changes then update the current view
- if len(local_updates) > 0 or len(local_deletes) > 0:
+ if local_updates or local_deletes:
self.update_view()
if server_sync and full_sync:
@@ -585,35 +660,41 @@ def sync_notes(self, server_sync=True, full_sync=True):
return sync_errors
- def get_note_status(self, key):
- n = self.notes[key]
- o = utils.KeyValueObject(saved=False, synced=False, modified=False)
- modified = float(n['modified'])
- savedate = float(n['savedate'])
+ def _get_note_status(self, key):
+ """Get the note status"""
+ note = self.notes[key]
+ obj = utils.KeyValueObject(saved=False, synced=False, modified=False)
+ modified = float(note['modified'])
+ savedate = float(note['savedate'])
if savedate > modified:
- o.saved = True
- return o
+ obj.saved = True
+ return obj
- def verify_all_saved(self):
+ def _verify_all_saved(self):
+ """
+ Verify all notes in the local database are saved to the
+ server
+ """
all_saved = True
self.sync_lock.acquire()
for k in list(self.notes.keys()):
- o = self.get_note_status(k)
- if not o.saved:
+ obj = self._get_note_status(k)
+ if not obj.saved:
all_saved = False
break
self.sync_lock.release()
return all_saved
def sync_now(self, do_server_sync=True):
+ """Sync the notes to the server"""
self.sync_lock.acquire()
- self.sync_notes(server_sync=do_server_sync,
- full_sync=True if not self.last_sync else False)
+ self._sync_notes(server_sync=do_server_sync,
+ full_sync=True if not self.last_sync else False)
self.sync_lock.release()
- # sync worker thread...
- def sync_worker(self, do_server_sync):
+ def _sync_worker(self, do_server_sync):
+ """The sync worker thread"""
time.sleep(1) # give some time to wait for GUI initialization
self.log('Sync worker: started')
self.sync_now(do_server_sync)
@@ -623,7 +704,8 @@ def sync_worker(self, do_server_sync):
self.sync_now(do_server_sync)
self.go_cond.release()
- def sync_worker_go(self):
+ def _sync_worker_go(self):
+ """Start the sync worker"""
self.go_cond.acquire()
self.go_cond.notify()
self.go_cond.release()
diff --git a/nncli/temp.py b/nncli/temp.py
@@ -1,42 +1,50 @@
# -*- coding: utf-8 -*-
-
-import os, json, tempfile
+"""temp module"""
+import json
+import os
+import tempfile
def tempfile_create(note, raw=False, tempdir=None):
+ """create a temp file"""
if raw:
# dump the raw json of the note
- tf = tempfile.NamedTemporaryFile(suffix='.json', delete=False, dir=tempdir)
+ tfile = tempfile.NamedTemporaryFile(suffix='.json',
+ delete=False, dir=tempdir)
contents = json.dumps(note, indent=2)
- tf.write(contents.encode('utf-8'))
- tf.flush()
+ tfile.write(contents.encode('utf-8'))
+ tfile.flush()
else:
ext = '.mkd'
- tf = tempfile.NamedTemporaryFile(suffix=ext, delete=False, dir=tempdir)
+ tfile = tempfile.NamedTemporaryFile(suffix=ext, delete=False,
+ dir=tempdir)
if note:
contents = note['content']
- tf.write(contents.encode('utf-8'))
- tf.flush()
- return tf
+ tfile.write(contents.encode('utf-8'))
+ tfile.flush()
+ return tfile
-def tempfile_delete(tf):
- if tf:
- tf.close()
- os.unlink(tf.name)
+def tempfile_delete(tfile):
+ """delete a temp file"""
+ if tfile:
+ tfile.close()
+ os.unlink(tfile.name)
-def tempfile_name(tf):
- if tf:
- return tf.name
+def tempfile_name(tfile):
+ """get the name of a temp file"""
+ if tfile:
+ return tfile.name
return ''
-def tempfile_content(tf):
+def tempfile_content(tfile):
+ """read the contents of the temp file"""
# This 'hack' is needed because some editors use an intermediate temporary
# file, and rename it to that of the correct file, overwriting it. This
# means that the tf file handle won't be updated with the new contents, and
# the tempfile must be re-opened and read
- if not tf:
+ if not tfile:
return None
- with open(tf.name, 'rb') as f:
- updated_tf_contents = f.read()
+ with open(tfile.name, 'rb') as temp:
+ updated_tf_contents = temp.read()
return updated_tf_contents.decode('utf-8')
diff --git a/nncli/user_input.py b/nncli/user_input.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
-
+"""user_input module"""
import urwid
+# pylint: disable=too-many-arguments
class UserInput(urwid.Edit):
-
+ """UserInput class"""
def __init__(self, config, caption, edit_text, callback_func, args):
self.config = config
- self.callback_func = callback_func
+ self.callback_func = callback_func
self.callback_func_args = args
super(UserInput, self).__init__(caption=caption,
edit_text=edit_text,
diff --git a/nncli/utils.py b/nncli/utils.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
-
-import datetime
+"""utils module"""
import random
import re
import shlex
@@ -10,6 +9,7 @@
from . import temp
+# pylint: disable=too-many-arguments,too-few-public-methods
def get_editor(config, logger):
"""Get the editor"""
editor = config.get_config('editor')
@@ -87,16 +87,21 @@ def generate_random_key():
return '%030x' % (random.randrange(256**15),)
def get_note_category(note):
+ """get a note category"""
if 'category' in note:
category = note['category'] if note['category'] is not None else ''
else:
category = ''
return category
-# Returns a fixed length string:
-# 'X' - needs sync
-# '*' - favorite
def get_note_flags(note):
+ """
+ get the note flags
+
+ Returns a fixed length string:
+ 'X' - needs sync
+ '*' - favorite
+ """
flags = ''
flags += 'X' if float(note['modified']) > float(note['syncdate']) else ' '
if 'favorite' in note:
@@ -106,30 +111,40 @@ def get_note_flags(note):
return flags
def get_note_title(note):
+ """get the note title"""
if 'title' in note:
return note['title']
- else:
- return ''
+ return ''
-def note_favorite(n):
- if 'favorite' in n:
- return n['favorite']
- else:
- return False
+def note_favorite(note):
+ """
+ get the status of the note as a favorite
-def sort_by_title_favorite(a):
- return (not note_favorite(a.note), get_note_title(a.note))
+ returns True if the note is marked as a favorite
+ False otherwise
+ """
+ if 'favorite' in note:
+ return note['favorite']
+ return False
+
+def sort_by_title_favorite(left):
+ """sort notes by title, favorites on top"""
+ return (not note_favorite(left.note), get_note_title(left.note))
def sort_notes_by_categories(notes, favorite_ontop=False):
+ """
+ sort notes by category, optionally pushing favorites to the
+ top
+ """
notes.sort(key=lambda i: (favorite_ontop and not note_favorite(i.note),
i.note.get('category'),
get_note_title(i.note)))
-def sort_by_modify_date_favorite(a):
- if note_favorite(a.note):
- return 100.0 * float(a.note.get('modified', 0))
- else:
- return float(a.note.get('modified', 0))
+def sort_by_modify_date_favorite(left):
+ """sort notest by modify date, favorites on top"""
+ if note_favorite(left.note):
+ return 100.0 * float(left.note.get('modified', 0))
+ return float(left.note.get('modified', 0))
class KeyValueObject:
"""Store key=value pairs in this object and retrieve with o.key.
@@ -155,7 +170,8 @@ def build_regex_search(search_string):
}
if search_string:
try:
- search_string, flag_letters = re.match(r'^(.+?)(?:/([a-z]+))?$', search_string).groups()
+ search_string, flag_letters = \
+ re.match(r'^(.+?)(?:/([a-z]+))?$', search_string).groups()
flags = 0
# if flags are given, OR together all the valid flags
# see https://docs.python.org/3/library/re.html#re.compile
diff --git a/nncli/view_help.py b/nncli/view_help.py
@@ -1,19 +1,22 @@
# -*- coding: utf-8 -*-
-
-import re, urwid
+"""view_help module"""
+import re
+import urwid
class ViewHelp(urwid.ListBox):
-
+ """ViewHelp class"""
def __init__(self, config):
self.config = config
- self.descr_width = 26
+ self.descr_width = 26
self.config_width = 29
lines = []
lines.extend(self.create_kb_help_lines('Keybinds Common', 'common'))
lines.extend(self.create_kb_help_lines('Keybinds Note List', 'titles'))
- lines.extend(self.create_kb_help_lines('Keybinds Note Content', 'notes'))
+ lines.extend(
+ self.create_kb_help_lines('Keybinds Note Content', 'notes')
+ )
lines.extend(self.create_config_help_lines())
lines.extend(self.create_color_help_lines())
lines.append(urwid.Text(('help_header', '')))
@@ -21,10 +24,11 @@ def __init__(self, config):
super(ViewHelp, self).__init__(urwid.SimpleFocusListWalker(lines))
def get_status_bar(self):
- cur = -1
+ """get the status bar"""
+ cur = -1
total = 0
- if len(self.body.positions()) > 0:
- cur = self.focus_position
+ if self.body.positions():
+ cur = self.focus_position
total = len(self.body.positions())
status_title = \
@@ -38,87 +42,123 @@ def get_status_bar(self):
str(total)),
'status_bar'))
return \
- urwid.AttrMap(urwid.Columns([ status_title, status_index ]),
+ urwid.AttrMap(urwid.Columns([status_title, status_index]),
'status_bar')
def create_kb_help_lines(self, header, use):
- lines = [ urwid.AttrMap(urwid.Text(''),
- 'help_header',
- 'help_focus') ]
+ """create the help page for the keybindings"""
+ lines = [urwid.AttrMap(urwid.Text(''),
+ 'help_header',
+ 'help_focus')]
lines.append(urwid.AttrMap(urwid.Text(' ' + header),
'help_header',
'help_focus'))
- for c in self.config.keybinds:
- if use not in self.config.get_keybind_use(c):
+ for config in self.config.keybinds:
+ if use not in self.config.get_keybind_use(config):
continue
- lines.append(
- urwid.AttrMap(urwid.AttrMap(
- urwid.Text(
+ keybinds_text = urwid.Text(
[
- ('help_descr', ('{:>' + str(self.descr_width) + '} ').format(self.config.get_keybind_descr(c))),
- ('help_config', ('{:>' + str(self.config_width) + '} ').format('kb_' + c)),
- ('help_value', "'" + self.config.get_keybind(c) + "'")
- ]
- ),
- attr_map = None,
- focus_map = {
- 'help_value' : 'help_focus',
- 'help_config' : 'help_focus',
- 'help_descr' : 'help_focus'
- }
- ), 'default', 'help_focus'))
+ (
+ 'help_descr',
+ (
+ '{:>' + str(self.descr_width) + '} '
+ ).format(
+ self.config.get_keybind_descr(
+ config
+ )
+ )
+ ),
+ (
+ 'help_config',
+ (
+ '{:>' + str(self.config_width) \
+ + '} '
+ ).format('kb_' + config)
+ ),
+ (
+ 'help_value',
+ "'" +
+ self.config.get_keybind(config) + "'"
+ )
+ ])
+ lines.append(
+ urwid.AttrMap(
+ urwid.AttrMap(
+ keybinds_text,
+ attr_map=None,
+ focus_map= \
+ {
+ 'help_value': 'help_focus',
+ 'help_config' : 'help_focus',
+ 'help_descr' : 'help_focus'
+ }),
+ 'default', 'help_focus'))
return lines
def create_config_help_lines(self):
- lines = [ urwid.AttrMap(urwid.Text(''),
- 'help_header',
- 'help_focus') ]
+ """create the help lines for the general config settings"""
+ lines = [urwid.AttrMap(urwid.Text(''),
+ 'help_header',
+ 'help_focus')]
lines.append(urwid.AttrMap(urwid.Text(' Configuration'),
'help_header',
'help_focus'))
- for c in self.config.configs:
- if c in [ 'nn_username', 'nn_password' ]: continue
- lines.append(
- urwid.AttrMap(urwid.AttrMap(
- urwid.Text(
+ for config in self.config.configs:
+ if config in ['nn_username', 'nn_password']:
+ continue
+ config_text = urwid.Text(
[
- ('help_descr', ('{:>' + str(self.descr_width) + '} ').format(self.config.get_config_descr(c))),
- ('help_config', ('{:>' + str(self.config_width) + '} ').format('cfg_' + c)),
- ('help_value', "'" + str(self.config.get_config(c)) + "'")
- ]
- ),
- attr_map = None,
- focus_map = {
- 'help_value' : 'help_focus',
- 'help_config' : 'help_focus',
- 'help_descr' : 'help_focus'
- }
- ), 'default', 'help_focus'))
+ ('help_descr',
+ ('{:>' + str(self.descr_width) + '} ').
+ format(self.config.get_config_descr(config))),
+ ('help_config',
+ ('{:>' + str(self.config_width) + '} ').
+ format('cfg_' + config)),
+ ('help_value',
+ "'" +
+ str(self.config.get_config(config)) + "'")
+ ])
+ lines.append(
+ urwid.AttrMap(urwid.AttrMap(
+ config_text,
+ attr_map=None,
+ focus_map={
+ 'help_value' : 'help_focus',
+ 'help_config' : 'help_focus',
+ 'help_descr' : 'help_focus'
+ }
+ ), 'default', 'help_focus'))
return lines
def create_color_help_lines(self):
- lines = [ urwid.AttrMap(urwid.Text(''),
- 'help_header',
- 'help_focus') ]
+ """create the help lines for the color settings"""
+ lines = [urwid.AttrMap(urwid.Text(''),
+ 'help_header',
+ 'help_focus')]
lines.append(urwid.AttrMap(urwid.Text(' Colors'),
'help_header',
'help_focus'))
fmap = {}
- for c in self.config.colors:
- fmap[re.search('^(.*)(_fg|_bg)$', c).group(1)] = 'help_focus'
- for c in self.config.colors:
- lines.append(
- urwid.AttrMap(urwid.AttrMap(
- urwid.Text(
+ for config in self.config.colors:
+ fmap[re.search('^(.*)(_fg|_bg)$', config).group(1)] = 'help_focus'
+ for color in self.config.colors:
+ colors_text = urwid.Text(
[
- ('help_descr', ('{:>' + str(self.descr_width) + '} ').format(self.config.get_color_descr(c))),
- ('help_config', ('{:>' + str(self.config_width) + '} ').format('clr_' + c)),
- (re.search('^(.*)(_fg|_bg)$', c).group(1), "'" + self.config.get_color(c) + "'")
- ]
- ),
- attr_map = None,
- focus_map = fmap
- ), 'default', 'help_focus'))
+ ('help_descr',
+ ('{:>' + str(self.descr_width) + '} ').
+ format(self.config.get_color_descr(color))),
+ ('help_config',
+ ('{:>' + str(self.config_width) + '} ').
+ format('clr_' + color)),
+ (re.search('^(.*)(_fg|_bg)$', color).group(1),
+ "'" + self.config.get_color(color) + "'")
+ ])
+ lines.append(
+ urwid.AttrMap(urwid.AttrMap(
+ colors_text,
+ attr_map=None,
+ focus_map=fmap
+ ), 'default', 'help_focus'))
return lines
def keypress(self, size, key):
diff --git a/nncli/view_log.py b/nncli/view_log.py
@@ -1,32 +1,38 @@
# -*- coding: utf-8 -*-
-
+"""view_log module"""
import urwid
class ViewLog(urwid.ListBox):
+ """
+ ViewLog class
+ This class defines the urwid view class for the log viewer
+ """
def __init__(self, config):
self.config = config
super(ViewLog, self).__init__(urwid.SimpleFocusListWalker([]))
def update_log(self):
+ """update the log"""
lines = []
- f = open(self.config.logfile)
- for line in f:
- lines.append(
- urwid.AttrMap(urwid.Text(line.rstrip()),
- 'note_content',
- 'note_content_focus'))
- f.close()
+ with open(self.config.logfile) as logfile:
+ for line in logfile:
+ lines.append(
+ urwid.AttrMap(urwid.Text(line.rstrip()),
+ 'note_content',
+ 'note_content_focus')
+ )
if self.config.get_config('log_reversed') == 'yes':
lines.reverse()
self.body[:] = urwid.SimpleFocusListWalker(lines)
self.focus_position = 0
def get_status_bar(self):
- cur = -1
+ """get the log view status bar"""
+ cur = -1
total = 0
- if len(self.body.positions()) > 0:
- cur = self.focus_position
+ if self.body.positions():
+ cur = self.focus_position
total = len(self.body.positions())
status_title = \
@@ -40,7 +46,7 @@ def get_status_bar(self):
str(total)),
'status_bar'))
return \
- urwid.AttrMap(urwid.Columns([ status_title, status_index ]),
+ urwid.AttrMap(urwid.Columns([status_title, status_index]),
'status_bar')
def keypress(self, size, key):
diff --git a/nncli/view_note.py b/nncli/view_note.py
@@ -1,13 +1,18 @@
# -*- coding: utf-8 -*-
-
-import time, urwid
+"""view_note module"""
+import time
+import urwid
from . import utils
-import re
from .clipboard import Clipboard
-import logging
+# pylint: disable=too-many-instance-attributes
class ViewNote(urwid.ListBox):
+ """
+ ViewNote class
+ This class defines the urwid class responsible for displaying an
+ individual note in an internal pager
+ """
def __init__(self, config, args):
self.config = config
self.ndb = args['ndb']
@@ -21,31 +26,35 @@ def __init__(self, config, args):
self.tabstop = int(self.config.get_config('tabstop'))
self.clipboard = Clipboard()
super(ViewNote, self).__init__(
- urwid.SimpleFocusListWalker(self.get_note_content_as_list()))
+ urwid.SimpleFocusListWalker(self.get_note_content_as_list()))
def get_note_content_as_list(self):
+ """return the contents of a note as a list of strings"""
lines = []
if not self.key:
return lines
if self.old_note:
- for l in self.old_note['content'].split('\n'):
+ for line in self.old_note['content'].split('\n'):
lines.append(
- urwid.AttrMap(urwid.Text(l.replace('\t', ' ' * self.tabstop)),
- 'note_content_old',
- 'note_content_old_focus'))
+ urwid.AttrMap(urwid.Text(
+ line.replace('\t', ' ' * self.tabstop)),
+ 'note_content_old',
+ 'note_content_old_focus'))
else:
- for l in self.note['content'].split('\n'):
+ for line in self.note['content'].split('\n'):
lines.append(
- urwid.AttrMap(urwid.Text(l.replace('\t', ' ' * self.tabstop)),
- 'note_content',
- 'note_content_focus'))
+ urwid.AttrMap(urwid.Text(
+ line.replace('\t', ' ' * self.tabstop)),
+ 'note_content',
+ 'note_content_focus'))
lines.append(urwid.AttrMap(urwid.Divider('-'), 'default'))
return lines
- def update_note_view(self, key=None, version=None):
+ def update_note_view(self, key=None):
+ """update the view"""
if key: # setting a new note
- self.key = key
- self.note = self.ndb.get_note(self.key)
+ self.key = key
+ self.note = self.ndb.get_note(self.key)
self.old_note = None
self.body[:] = \
@@ -54,63 +63,80 @@ def update_note_view(self, key=None, version=None):
self.focus_position = 0
def lines_after_current_position(self):
- lines_after_current_position = list(range(self.focus_position + 1, len(self.body.positions()) - 1))
+ """
+ return the number of lines after the currently-focused
+ line
+ """
+ lines_after_current_position = \
+ list(range(self.focus_position + 1,
+ len(self.body.positions()) - 1))
return lines_after_current_position
def lines_before_current_position(self):
+ """
+ return the number of lines before the currently-focused line
+ """
lines_before_current_position = list(range(0, self.focus_position))
lines_before_current_position.reverse()
return lines_before_current_position
def search_note_view_next(self, search_string=None, search_mode=None):
+ """move to the next match in search mode"""
if search_string:
self.search_string = search_string
if search_mode:
self.search_mode = search_mode
- note_range = self.lines_after_current_position() if self.search_direction == 'forward' else self.lines_before_current_position()
+ note_range = self.lines_after_current_position() \
+ if self.search_direction == 'forward' \
+ else self.lines_before_current_position()
self.search_note_range(note_range)
def search_note_view_prev(self, search_string=None, search_mode=None):
+ """move to the previous match in search mode"""
if search_string:
self.search_string = search_string
if search_mode:
self.search_mode = search_mode
- note_range = self.lines_after_current_position() if self.search_direction == 'backward' else self.lines_before_current_position()
+ note_range = self.lines_after_current_position() \
+ if self.search_direction == 'backward' \
+ else self.lines_before_current_position()
self.search_note_range(note_range)
def search_note_range(self, note_range):
+ """search within a range of lines"""
for line in note_range:
line_content = self.note['content'].split('\n')[line]
- if (self.is_match(self.search_string, line_content)):
+ if self.is_match(self.search_string, line_content):
self.focus_position = line
break
self.update_note_view()
def is_match(self, term, full_text):
+ """returns True if there is a match, False otherwise"""
if self.search_mode == 'gstyle':
return term in full_text
- else:
- sspat = utils.build_regex_search(term)
- return sspat and sspat.search(full_text)
+ sspat = utils.build_regex_search(term)
+ return sspat and sspat.search(full_text)
def get_status_bar(self):
+ """get the note view status bar"""
if not self.key:
return \
urwid.AttrMap(urwid.Text('No note...'),
'status_bar')
- cur = -1
+ cur = -1
total = 0
- if len(self.body.positions()) > 0:
- cur = self.focus_position
+ if self.body.positions():
+ cur = self.focus_position
total = len(self.body.positions())
- t = time.localtime(float(self.note['modified']))
- title = utils.get_note_title(self.note)
- flags = utils.get_note_flags(self.note)
+ localtime = time.localtime(float(self.note['modified']))
+ title = utils.get_note_title(self.note)
+ flags = utils.get_note_flags(self.note)
category = utils.get_note_category(self.note)
- mod_time = time.strftime('Date: %a, %d %b %Y %H:%M:%S', t)
+ mod_time = time.strftime('Date: %a, %d %b %Y %H:%M:%S', localtime)
status_title = \
urwid.AttrMap(urwid.Text('Title: ' +
@@ -140,14 +166,15 @@ def get_status_bar(self):
']'),
'status_bar'))
- pile_top = urwid.Columns([ status_title, status_key_index ])
- pile_bottom = urwid.Columns([ status_date, status_category_flags ])
+ pile_top = urwid.Columns([status_title, status_key_index])
+ pile_bottom = urwid.Columns([status_date, status_category_flags])
return \
- urwid.AttrMap(urwid.Pile([ pile_top, pile_bottom ]),
+ urwid.AttrMap(urwid.Pile([pile_top, pile_bottom]),
'status_bar')
def copy_note_text(self):
+ """copy the text of the note to the system clipboard"""
line_content = self.note['content'].split('\n')[self.focus_position]
self.clipboard.copy(line_content)
diff --git a/nncli/view_titles.py b/nncli/view_titles.py
@@ -1,32 +1,48 @@
# -*- coding: utf-8 -*-
-
-import re, time, datetime, urwid, subprocess
-from . import utils, view_note
-
+"""view_titles module"""
+import re
+import time
+import datetime
+import urwid
+from . import utils
+
+# pylint: disable=too-many-instance-attributes, too-many-statements
class ViewTitles(urwid.ListBox):
+ """
+ ViewTitles class
+ Implements the urwid class for the view_titles view
+ """
def __init__(self, config, args):
self.config = config
self.ndb = args['ndb']
self.search_string = args['search_string']
self.log = args['log']
self.note_list, self.match_regex, self.all_notes_cnt = \
- self.ndb.filter_notes(self.search_string, sort_mode=self.config.get_config('sort_mode'))
+ self.ndb.filter_notes(
+ self.search_string,
+ sort_mode=self.config.get_config('sort_mode')
+ )
super(ViewTitles, self).__init__(
- urwid.SimpleFocusListWalker(self.get_note_titles()))
+ urwid.SimpleFocusListWalker(self.get_note_titles()))
- def update_note_list(self, search_string, search_mode='gstyle', sort_mode='date'):
+ def update_note_list(self, search_string,
+ search_mode='gstyle', sort_mode='date'):
+ """update the note list"""
self.search_string = search_string
self.note_list, self.match_regex, self.all_notes_cnt = \
- self.ndb.filter_notes(self.search_string, search_mode, sort_mode=sort_mode)
+ self.ndb.filter_notes(
+ self.search_string, search_mode, sort_mode=sort_mode
+ )
self.body[:] = \
urwid.SimpleFocusListWalker(self.get_note_titles())
- if len(self.note_list) == 0:
+ if self.note_list:
self.log('No notes found!')
else:
self.focus_position = 0
def sort_note_list(self, sort_mode):
+ """sort the note list"""
self.ndb.filtered_notes_sort(self.note_list, sort_mode)
self.body[:] = \
urwid.SimpleFocusListWalker(self.get_note_titles())
@@ -44,21 +60,23 @@ def format_title(self, note):
%N -- note title
"""
- t = time.localtime(float(note['modified']))
- mod_time = time.strftime(self.config.get_config('format_strftime'), t)
+ localtime = time.localtime(float(note['modified']))
+ mod_time = \
+ time.strftime(self.config.get_config('format_strftime'),
+ localtime)
title = utils.get_note_title(note)
flags = utils.get_note_flags(note)
- category = utils.get_note_category(note)
+ category = utils.get_note_category(note)
# get the age of the note
- dt = datetime.datetime.fromtimestamp(time.mktime(t))
- if dt > datetime.datetime.now() - datetime.timedelta(days=1):
+ dtime = datetime.datetime.fromtimestamp(time.mktime(localtime))
+ if dtime > datetime.datetime.now() - datetime.timedelta(days=1):
note_age = 'd' # less than a day old
- elif dt > datetime.datetime.now() - datetime.timedelta(weeks=1):
+ elif dtime > datetime.datetime.now() - datetime.timedelta(weeks=1):
note_age = 'w' # less than a week old
- elif dt > datetime.datetime.now() - datetime.timedelta(weeks=4):
+ elif dtime > datetime.datetime.now() - datetime.timedelta(weeks=4):
note_age = 'm' # less than a month old
- elif dt > datetime.datetime.now() - datetime.timedelta(weeks=52):
+ elif dtime > datetime.datetime.now() - datetime.timedelta(weeks=52):
note_age = 'y' # less than a year old
else:
note_age = 'a' # ancient
@@ -66,86 +84,101 @@ def format_title(self, note):
def recursive_format(title_format):
if not title_format:
return None
- fmt = re.search("^(.*)%([-]*)([0-9]*)([FDTN])(.*)$", title_format)
+ fmt = re.search(r'^(.*)%([-]*)([0-9]*)([FDTN])(.*)$', title_format)
if not fmt:
- m = ('pack', urwid.AttrMap(urwid.Text(title_format),
- 'default'))
+ attr_map = ('pack', urwid.AttrMap(urwid.Text(title_format),
+ 'default'))
l_fmt = None
r_fmt = None
else:
- l = fmt.group(1) if fmt.group(1) else None
- m = None
- r = fmt.group(5) if fmt.group(5) else None
+ left = fmt.group(1) if fmt.group(1) else None
+ attr_map = None
+ right = fmt.group(5) if fmt.group(5) else None
align = 'left' if fmt.group(2) == '-' else 'right'
width = int(fmt.group(3)) if fmt.group(3) else 'pack'
if fmt.group(4) == 'F':
- m = (width, urwid.AttrMap(urwid.Text(flags,
- align=align,
- wrap='clip'),
- 'note_flags'))
+ attr_map = (width, urwid.AttrMap(urwid.Text(flags,
+ align=align,
+ wrap='clip'),
+ 'note_flags'))
elif fmt.group(4) == 'D':
- m = (width, urwid.AttrMap(urwid.Text(mod_time,
- align=align,
- wrap='clip'),
- 'note_date'))
+ attr_map = (width, urwid.AttrMap(urwid.Text(mod_time,
+ align=align,
+ wrap='clip'),
+ 'note_date'))
elif fmt.group(4) == 'T':
- m = (width, urwid.AttrMap(urwid.Text(category,
- align=align,
- wrap='clip'),
- 'note_category'))
+ attr_map = (width, urwid.AttrMap(urwid.Text(category,
+ align=align,
+ wrap='clip'),
+ 'note_category'))
elif fmt.group(4) == 'N':
- if note_age == 'd': attr = 'note_title_day'
- elif note_age == 'w': attr = 'note_title_week'
- elif note_age == 'm': attr = 'note_title_month'
- elif note_age == 'y': attr = 'note_title_year'
- elif note_age == 'a': attr = 'note_title_ancient'
+ if note_age == 'd':
+ attr = 'note_title_day'
+ elif note_age == 'w':
+ attr = 'note_title_week'
+ elif note_age == 'm':
+ attr = 'note_title_month'
+ elif note_age == 'y':
+ attr = 'note_title_year'
+ elif note_age == 'a':
+ attr = 'note_title_ancient'
if width != 'pack':
- m = (width, urwid.AttrMap(urwid.Text(title,
- align=align,
- wrap='clip'),
- attr))
+ attr_map = (width, urwid.AttrMap(
+ urwid.Text(
+ title,
+ align=align,
+ wrap='clip'
+ ),
+ attr))
else:
- m = urwid.AttrMap(urwid.Text(title,
- align=align,
- wrap='clip'),
- attr)
- l_fmt = recursive_format(l)
- r_fmt = recursive_format(r)
+ attr_map = urwid.AttrMap(urwid.Text(title,
+ align=align,
+ wrap='clip'),
+ attr)
+ l_fmt = recursive_format(left)
+ r_fmt = recursive_format(right)
tmp = []
- if l_fmt: tmp.extend(l_fmt)
- tmp.append(m)
- if r_fmt: tmp.extend(r_fmt)
+ if l_fmt:
+ tmp.extend(l_fmt)
+ tmp.append(attr_map)
+ if r_fmt:
+ tmp.extend(r_fmt)
return tmp
# convert the format string into the actual note title line
- title_line = recursive_format(self.config.get_config('format_note_title'))
+ title_line = recursive_format(
+ self.config.get_config('format_note_title')
+ )
return urwid.Columns(title_line)
def get_note_title(self, note):
+ """get the title of a note"""
return urwid.AttrMap(self.format_title(note),
'default',
- { 'default' : 'note_focus',
- 'note_title_day' : 'note_focus',
- 'note_title_week' : 'note_focus',
- 'note_title_month' : 'note_focus',
- 'note_title_year' : 'note_focus',
- 'note_title_ancient' : 'note_focus',
- 'note_date' : 'note_focus',
- 'note_flags' : 'note_focus',
- 'note_categories' : 'note_focus' })
+ {'default' : 'note_focus',
+ 'note_title_day' : 'note_focus',
+ 'note_title_week' : 'note_focus',
+ 'note_title_month' : 'note_focus',
+ 'note_title_year' : 'note_focus',
+ 'note_title_ancient' : 'note_focus',
+ 'note_date' : 'note_focus',
+ 'note_flags' : 'note_focus',
+ 'note_categories' : 'note_focus'})
def get_note_titles(self):
+ """get the titles of all of the notes"""
lines = []
- for n in self.note_list:
- lines.append(self.get_note_title(n.note))
+ for note in self.note_list:
+ lines.append(self.get_note_title(note.note))
return lines
def get_status_bar(self):
- cur = -1
+ """get the status bar"""
+ cur = -1
total = 0
- if len(self.body.positions()) > 0:
- cur = self.focus_position
+ if self.body.positions():
+ cur = self.focus_position
total = len(self.body.positions())
hdr = 'NextCloud Notes'
@@ -153,7 +186,7 @@ def get_status_bar(self):
# include connection status in header
hdr += ' (' + self.ndb.note.status + ')'
- if self.search_string != None:
+ if self.search_string is not None:
hdr += ' - Search: ' + self.search_string
status_title = \
@@ -167,10 +200,11 @@ def get_status_bar(self):
str(total)),
'status_bar'))
return \
- urwid.AttrMap(urwid.Columns([ status_title, status_index ]),
+ urwid.AttrMap(urwid.Columns([status_title, status_index]),
'status_bar')
def update_note_title(self, key=None):
+ """update a note title"""
if not key:
self.body[self.focus_position] = \
self.get_note_title(self.note_list[self.focus_position].note)
@@ -180,6 +214,7 @@ def update_note_title(self, key=None):
self.body[i] = self.get_note_title(self.note_list[i].note)
def focus_note(self, key):
+ """set the focus on a given note"""
for i in range(len(self.note_list)):
if 'localkey' in self.note_list[i].note and \
self.note_list[i].note['localkey'] == key: