nncli

NextCloud Notes Command Line Interface
git clone git://git.danielmoch.com/nncli.git
Log | Files | Refs | LICENSE

commit e544771db37edc1908b0a60fdcc7ad6b23f7550b
parent 143aa40944eddbb7b70ccb424364205ac5e147da
Author: Eric Davis <edavis@insanum.com>
Date:   Thu, 10 Jul 2014 14:56:13 -0700

cleaned up and simplified the search code
use OrderedDict for config items so help content is ordered

Diffstat:
Mconfig.py | 202+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mnotes_db.py | 268++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mview_help.py | 8++++----
Mview_titles.py | 4++--
4 files changed, 234 insertions(+), 248 deletions(-)

diff --git a/config.py b/config.py @@ -1,5 +1,5 @@ -import os, urwid, ConfigParser +import os, urwid, collections, ConfigParser class Config: @@ -10,9 +10,8 @@ def __init__(self): 'cfg_sn_username' : '', 'cfg_sn_password' : '', 'cfg_db_path' : os.path.join(self.home, '.sncli'), - 'cfg_search_mode' : 'gstyle', # gstyle/regex - 'cfg_search_tags' : 'yes', - 'cfg_sort_mode' : 'date', # alpha/date + 'cfg_search_tags' : 'yes', # with regex searches + 'cfg_sort_mode' : 'date', # 'alpha' or 'date' 'cfg_pinned_ontop' : 'yes', 'cfg_tabstop' : '4', 'cfg_format_strftime' : '%Y/%m/%d', @@ -48,7 +47,9 @@ def __init__(self): 'kb_tabstop4' : '4', 'kb_tabstop8' : '8', 'kb_search' : '/', - 'kb_clear_search' : 'a', + 'kb_clear_search' : 'A', + 'kb_sort_date' : 'd', + 'kb_sort_alpha' : 'a', 'kb_note_pin' : 'p', 'kb_note_unpin' : 'P', 'kb_note_markdown' : 'm', @@ -105,103 +106,100 @@ def __init__(self): if not cp.has_section(cfg_sec): cp.add_section(cfg_sec) - self.configs = \ - { - 'sn_username' : [ cp.get(cfg_sec, 'cfg_sn_username', raw=True), 'Simplenote Username' ], - 'sn_password' : [ cp.get(cfg_sec, 'cfg_sn_password', raw=True), 'Simplenote Password' ], - 'db_path' : [ cp.get(cfg_sec, 'cfg_db_path'), 'Note storage path' ], - 'search_mode' : [ cp.get(cfg_sec, 'cfg_search_mode'), 'Search mode' ], - 'search_tags' : [ cp.get(cfg_sec, 'cfg_search_tags'), 'Search tags as well' ], - 'sort_mode' : [ cp.get(cfg_sec, 'cfg_sort_mode'), 'Sort mode' ], - 'pinned_ontop' : [ cp.get(cfg_sec, 'cfg_pinned_ontop'), 'Pinned at top of list' ], - 'tabstop' : [ cp.get(cfg_sec, 'cfg_tabstop'), 'Tabstop spaces' ], - 'format_strftime' : [ cp.get(cfg_sec, 'cfg_format_strftime', raw=True), 'Date strftime format' ], - 'format_note_title' : [ cp.get(cfg_sec, 'cfg_format_note_title', raw=True), 'Note title format' ], - 'status_bar' : [ cp.get(cfg_sec, 'cfg_status_bar'), 'Status bar location' ], - 'editor' : [ cp.get(cfg_sec, 'cfg_editor'), 'Editor' ], - 'pager' : [ cp.get(cfg_sec, 'cfg_pager'), 'External pager' ], - 'max_logs' : [ cp.get(cfg_sec, 'cfg_max_logs'), 'Max logs in footer' ], - 'log_reversed' : [ cp.get(cfg_sec, 'cfg_log_reversed'), 'Log file reversed' ] - } - - self.keybinds = \ - { - 'help' : [ cp.get(cfg_sec, 'kb_help'), [ 'common' ], 'Help' ], - 'quit' : [ cp.get(cfg_sec, 'kb_quit'), [ 'common' ], 'Quit' ], - 'sync' : [ cp.get(cfg_sec, 'kb_sync'), [ 'common' ], 'Full sync' ], - 'down' : [ cp.get(cfg_sec, 'kb_down'), [ 'common' ], 'Scroll down one line' ], - 'up' : [ cp.get(cfg_sec, 'kb_up'), [ 'common' ], 'Scroll up one line' ], - 'page_down' : [ cp.get(cfg_sec, 'kb_page_down'), [ 'common' ], 'Page down' ], - 'page_up' : [ cp.get(cfg_sec, 'kb_page_up'), [ 'common' ], 'Page up' ], - 'half_page_down' : [ cp.get(cfg_sec, 'kb_half_page_down'), [ 'common' ], 'Half page down' ], - 'half_page_up' : [ cp.get(cfg_sec, 'kb_half_page_up'), [ 'common' ], 'Half page up' ], - 'bottom' : [ cp.get(cfg_sec, 'kb_bottom'), [ 'common' ], 'Goto bottom' ], - 'top' : [ cp.get(cfg_sec, 'kb_top'), [ 'common' ], 'Goto top' ], - 'status' : [ cp.get(cfg_sec, 'kb_status'), [ 'common' ], 'Toggle status bar' ], - 'view_log' : [ cp.get(cfg_sec, 'kb_view_log'), [ 'common' ], 'View log' ], - 'trash_note' : [ cp.get(cfg_sec, 'kb_trash_note'), [ 'titles', 'notes' ], 'Trash a note' ], - 'create_note' : [ cp.get(cfg_sec, 'kb_create_note'), [ 'titles' ], 'Create a new note' ], - 'edit_note' : [ cp.get(cfg_sec, 'kb_edit_note'), [ 'titles', 'notes' ], 'Edit note' ], - 'view_note' : [ cp.get(cfg_sec, 'kb_view_note'), [ 'titles' ], 'View note' ], - 'view_note_ext' : [ cp.get(cfg_sec, 'kb_view_note_ext'), [ 'titles', 'notes' ], 'View note with pager' ], - 'pipe_note' : [ cp.get(cfg_sec, 'kb_pipe_note'), [ 'titles', 'notes' ], 'Pipe note contents' ], - 'view_next_note' : [ cp.get(cfg_sec, 'kb_view_next_note'), [ 'notes' ], 'View next note' ], - 'view_prev_note' : [ cp.get(cfg_sec, 'kb_view_prev_note'), [ 'notes' ], 'View previous note' ], - 'tabstop2' : [ cp.get(cfg_sec, 'kb_tabstop2'), [ 'notes' ], 'View with tabstop=2' ], - 'tabstop4' : [ cp.get(cfg_sec, 'kb_tabstop4'), [ 'notes' ], 'View with tabstop=4' ], - 'tabstop8' : [ cp.get(cfg_sec, 'kb_tabstop8'), [ 'notes' ], 'View with tabstop=8' ], - 'search' : [ cp.get(cfg_sec, 'kb_search'), [ 'titles' ], 'Search notes' ], - 'clear_search' : [ cp.get(cfg_sec, 'kb_clear_search'), [ 'titles' ], 'Show all notes' ], - 'note_pin' : [ cp.get(cfg_sec, 'kb_note_pin'), [ 'titles', 'notes' ], 'Pin note' ], - 'note_unpin' : [ cp.get(cfg_sec, 'kb_note_unpin'), [ 'titles', 'notes' ], 'Unpin note' ], - 'note_markdown' : [ cp.get(cfg_sec, 'kb_note_markdown'), [ 'titles', 'notes' ], 'Flag note as markdown' ], - 'note_unmarkdown' : [ cp.get(cfg_sec, 'kb_note_unmarkdown'), [ 'titles', 'notes' ], 'Unflag note as markdown' ], - 'note_tags' : [ cp.get(cfg_sec, 'kb_note_tags'), [ 'titles', 'notes' ], 'Edit note tags' ] - } - - self.colors = \ - { - 'default_fg' : [ cp.get(cfg_sec, 'clr_default_fg'), 'Default fg' ], - 'default_bg' : [ cp.get(cfg_sec, 'clr_default_bg'), 'Default bg' ], - 'status_bar_fg' : [ cp.get(cfg_sec, 'clr_status_bar_fg'), 'Status bar fg' ], - 'status_bar_bg' : [ cp.get(cfg_sec, 'clr_status_bar_bg'), 'Status bar bg' ], - 'log_fg' : [ cp.get(cfg_sec, 'clr_log_fg'), 'Log message fg' ], - 'log_bg' : [ cp.get(cfg_sec, 'clr_log_bg'), 'Log message bg' ], - 'search_bar_fg' : [ cp.get(cfg_sec, 'clr_search_bar_fg'), 'Search bar fg' ], - 'search_bar_bg' : [ cp.get(cfg_sec, 'clr_search_bar_bg'), 'Search bar bg' ], - 'note_focus_fg' : [ cp.get(cfg_sec, 'clr_note_focus_fg'), 'Note title focus fg' ], - 'note_focus_bg' : [ cp.get(cfg_sec, 'clr_note_focus_bg'), 'Note title focus bg' ], - 'note_title_day_fg' : [ cp.get(cfg_sec, 'clr_note_title_day_fg'), 'Day old note title fg' ], - 'note_title_day_bg' : [ cp.get(cfg_sec, 'clr_note_title_day_bg'), 'Day old note title bg' ], - 'note_title_week_fg' : [ cp.get(cfg_sec, 'clr_note_title_week_fg'), 'Week old note title fg' ], - 'note_title_week_bg' : [ cp.get(cfg_sec, 'clr_note_title_week_bg'), 'Week old note title bg' ], - 'note_title_month_fg' : [ cp.get(cfg_sec, 'clr_note_title_month_fg'), 'Month old note title fg' ], - 'note_title_month_bg' : [ cp.get(cfg_sec, 'clr_note_title_month_bg'), 'Month old note title bg' ], - 'note_title_year_fg' : [ cp.get(cfg_sec, 'clr_note_title_year_fg'), 'Year old note title fg' ], - 'note_title_year_bg' : [ cp.get(cfg_sec, 'clr_note_title_year_bg'), 'Year old note title bg' ], - 'note_title_ancient_fg' : [ cp.get(cfg_sec, 'clr_note_title_ancient_fg'), 'Ancient note title fg' ], - 'note_title_ancient_bg' : [ cp.get(cfg_sec, 'clr_note_title_ancient_bg'), 'Ancient note title bg' ], - 'note_date_fg' : [ cp.get(cfg_sec, 'clr_note_date_fg'), 'Note date fg' ], - 'note_date_bg' : [ cp.get(cfg_sec, 'clr_note_date_bg'), 'Note date bg' ], - 'note_flags_fg' : [ cp.get(cfg_sec, 'clr_note_flags_fg'), 'Note flags fg' ], - 'note_flags_bg' : [ cp.get(cfg_sec, 'clr_note_flags_bg'), 'Note flags bg' ], - 'note_tags_fg' : [ cp.get(cfg_sec, 'clr_note_tags_fg'), 'Note tags fg' ], - 'note_tags_bg' : [ cp.get(cfg_sec, 'clr_note_tags_bg'), 'Note tags bg' ], - 'note_content_fg' : [ cp.get(cfg_sec, 'clr_note_content_fg'), 'Note content fg' ], - 'note_content_bg' : [ cp.get(cfg_sec, 'clr_note_content_bg'), 'Note content bg' ], - 'note_content_focus_fg' : [ cp.get(cfg_sec, 'clr_note_content_focus_fg'), 'Note content focus fg' ], - 'note_content_focus_bg' : [ cp.get(cfg_sec, 'clr_note_content_focus_bg'), 'Note content focus bg' ], - 'help_focus_fg' : [ cp.get(cfg_sec, 'clr_help_focus_fg'), 'Help focus fg' ], - 'help_focus_bg' : [ cp.get(cfg_sec, 'clr_help_focus_bg'), 'Help focus bg' ], - 'help_header_fg' : [ cp.get(cfg_sec, 'clr_help_header_fg'), 'Help header fg' ], - 'help_header_bg' : [ cp.get(cfg_sec, 'clr_help_header_bg'), 'Help header bg' ], - 'help_config_fg' : [ cp.get(cfg_sec, 'clr_help_config_fg'), 'Help config fg' ], - 'help_config_bg' : [ cp.get(cfg_sec, 'clr_help_config_bg'), 'Help config bg' ], - 'help_value_fg' : [ cp.get(cfg_sec, 'clr_help_value_fg'), 'Help value fg' ], - 'help_value_bg' : [ cp.get(cfg_sec, 'clr_help_value_bg'), 'Help value bg' ], - 'help_descr_fg' : [ cp.get(cfg_sec, 'clr_help_descr_fg'), 'Help description fg' ], - 'help_descr_bg' : [ cp.get(cfg_sec, 'clr_help_descr_bg'), 'Help description bg' ] - } + # ordered dicts used to ease help + + self.configs = collections.OrderedDict() + self.configs['sn_username'] = [ cp.get(cfg_sec, 'cfg_sn_username', raw=True), 'Simplenote Username' ] + self.configs['sn_password'] = [ cp.get(cfg_sec, 'cfg_sn_password', raw=True), 'Simplenote Password' ] + self.configs['db_path'] = [ cp.get(cfg_sec, 'cfg_db_path'), 'Note storage path' ] + self.configs['search_tags'] = [ cp.get(cfg_sec, 'cfg_search_tags'), 'Search tags as well' ] + self.configs['sort_mode'] = [ cp.get(cfg_sec, 'cfg_sort_mode'), 'Sort mode' ] + self.configs['pinned_ontop'] = [ cp.get(cfg_sec, 'cfg_pinned_ontop'), 'Pinned at top of list' ] + self.configs['tabstop'] = [ cp.get(cfg_sec, 'cfg_tabstop'), 'Tabstop spaces' ] + self.configs['format_strftime'] = [ cp.get(cfg_sec, 'cfg_format_strftime', raw=True), 'Date strftime format' ] + self.configs['format_note_title'] = [ cp.get(cfg_sec, 'cfg_format_note_title', raw=True), 'Note title format' ] + self.configs['status_bar'] = [ cp.get(cfg_sec, 'cfg_status_bar'), 'Show the status bar' ] + self.configs['editor'] = [ cp.get(cfg_sec, 'cfg_editor'), 'Editor' ] + self.configs['pager'] = [ cp.get(cfg_sec, 'cfg_pager'), 'External pager' ] + self.configs['max_logs'] = [ cp.get(cfg_sec, 'cfg_max_logs'), 'Max logs in footer' ] + self.configs['log_reversed'] = [ cp.get(cfg_sec, 'cfg_log_reversed'), 'Log file reversed' ] + + self.keybinds = collections.OrderedDict() + self.keybinds['help'] = [ cp.get(cfg_sec, 'kb_help'), [ 'common' ], 'Help' ] + self.keybinds['quit'] = [ cp.get(cfg_sec, 'kb_quit'), [ 'common' ], 'Quit' ] + self.keybinds['sync'] = [ cp.get(cfg_sec, 'kb_sync'), [ 'common' ], 'Full sync' ] + self.keybinds['down'] = [ cp.get(cfg_sec, 'kb_down'), [ 'common' ], 'Scroll down one line' ] + self.keybinds['up'] = [ cp.get(cfg_sec, 'kb_up'), [ 'common' ], 'Scroll up one line' ] + self.keybinds['page_down'] = [ cp.get(cfg_sec, 'kb_page_down'), [ 'common' ], 'Page down' ] + self.keybinds['page_up'] = [ cp.get(cfg_sec, 'kb_page_up'), [ 'common' ], 'Page up' ] + self.keybinds['half_page_down'] = [ cp.get(cfg_sec, 'kb_half_page_down'), [ 'common' ], 'Half page down' ] + self.keybinds['half_page_up'] = [ cp.get(cfg_sec, 'kb_half_page_up'), [ 'common' ], 'Half page up' ] + self.keybinds['bottom'] = [ cp.get(cfg_sec, 'kb_bottom'), [ 'common' ], 'Goto bottom' ] + self.keybinds['top'] = [ cp.get(cfg_sec, 'kb_top'), [ 'common' ], 'Goto top' ] + self.keybinds['status'] = [ cp.get(cfg_sec, 'kb_status'), [ 'common' ], 'Toggle status bar' ] + self.keybinds['view_log'] = [ cp.get(cfg_sec, 'kb_view_log'), [ 'common' ], 'View log' ] + self.keybinds['create_note'] = [ cp.get(cfg_sec, 'kb_create_note'), [ 'titles' ], 'Create a new note' ] + self.keybinds['trash_note'] = [ cp.get(cfg_sec, 'kb_trash_note'), [ 'titles', 'notes' ], 'Trash a note' ] + self.keybinds['edit_note'] = [ cp.get(cfg_sec, 'kb_edit_note'), [ 'titles', 'notes' ], 'Edit note' ] + self.keybinds['view_note'] = [ cp.get(cfg_sec, 'kb_view_note'), [ 'titles' ], 'View note' ] + self.keybinds['view_note_ext'] = [ cp.get(cfg_sec, 'kb_view_note_ext'), [ 'titles', 'notes' ], 'View note with pager' ] + self.keybinds['pipe_note'] = [ cp.get(cfg_sec, 'kb_pipe_note'), [ 'titles', 'notes' ], 'Pipe note contents' ] + self.keybinds['view_next_note'] = [ cp.get(cfg_sec, 'kb_view_next_note'), [ 'notes' ], 'View next note' ] + self.keybinds['view_prev_note'] = [ cp.get(cfg_sec, 'kb_view_prev_note'), [ 'notes' ], 'View previous note' ] + self.keybinds['tabstop2'] = [ cp.get(cfg_sec, 'kb_tabstop2'), [ 'notes' ], 'View with tabstop=2' ] + self.keybinds['tabstop4'] = [ cp.get(cfg_sec, 'kb_tabstop4'), [ 'notes' ], 'View with tabstop=4' ] + self.keybinds['tabstop8'] = [ cp.get(cfg_sec, 'kb_tabstop8'), [ 'notes' ], 'View with tabstop=8' ] + self.keybinds['search'] = [ cp.get(cfg_sec, 'kb_search'), [ 'titles' ], 'Search notes' ] + self.keybinds['clear_search'] = [ cp.get(cfg_sec, 'kb_clear_search'), [ 'titles' ], 'Show all notes' ] + self.keybinds['sort_date'] = [ cp.get(cfg_sec, 'kb_sort_date'), [ 'titles' ], 'Sort notes by date' ] + self.keybinds['sort_alpha'] = [ cp.get(cfg_sec, 'kb_sort_alpha'), [ 'titles' ], 'Sort notes by alpha' ] + self.keybinds['note_pin'] = [ cp.get(cfg_sec, 'kb_note_pin'), [ 'titles', 'notes' ], 'Pin note' ] + self.keybinds['note_unpin'] = [ cp.get(cfg_sec, 'kb_note_unpin'), [ 'titles', 'notes' ], 'Unpin note' ] + self.keybinds['note_markdown'] = [ cp.get(cfg_sec, 'kb_note_markdown'), [ 'titles', 'notes' ], 'Flag note as markdown' ] + self.keybinds['note_unmarkdown'] = [ cp.get(cfg_sec, 'kb_note_unmarkdown'), [ 'titles', 'notes' ], 'Unflag note as markdown' ] + self.keybinds['note_tags'] = [ cp.get(cfg_sec, 'kb_note_tags'), [ 'titles', 'notes' ], 'Edit note tags' ] + + self.colors = collections.OrderedDict() + self.colors['default_fg'] = [ cp.get(cfg_sec, 'clr_default_fg'), 'Default fg' ] + self.colors['default_bg'] = [ cp.get(cfg_sec, 'clr_default_bg'), 'Default bg' ] + self.colors['status_bar_fg'] = [ cp.get(cfg_sec, 'clr_status_bar_fg'), 'Status bar fg' ] + self.colors['status_bar_bg'] = [ cp.get(cfg_sec, 'clr_status_bar_bg'), 'Status bar bg' ] + self.colors['log_fg'] = [ cp.get(cfg_sec, 'clr_log_fg'), 'Log message fg' ] + self.colors['log_bg'] = [ cp.get(cfg_sec, 'clr_log_bg'), 'Log message bg' ] + self.colors['search_bar_fg'] = [ cp.get(cfg_sec, 'clr_search_bar_fg'), 'Search bar fg' ] + self.colors['search_bar_bg'] = [ cp.get(cfg_sec, 'clr_search_bar_bg'), 'Search bar bg' ] + self.colors['note_focus_fg'] = [ cp.get(cfg_sec, 'clr_note_focus_fg'), 'Note title focus fg' ] + self.colors['note_focus_bg'] = [ cp.get(cfg_sec, 'clr_note_focus_bg'), 'Note title focus bg' ] + self.colors['note_title_day_fg'] = [ cp.get(cfg_sec, 'clr_note_title_day_fg'), 'Day old note title fg' ] + self.colors['note_title_day_bg'] = [ cp.get(cfg_sec, 'clr_note_title_day_bg'), 'Day old note title bg' ] + self.colors['note_title_week_fg'] = [ cp.get(cfg_sec, 'clr_note_title_week_fg'), 'Week old note title fg' ] + self.colors['note_title_week_bg'] = [ cp.get(cfg_sec, 'clr_note_title_week_bg'), 'Week old note title bg' ] + self.colors['note_title_month_fg'] = [ cp.get(cfg_sec, 'clr_note_title_month_fg'), 'Month old note title fg' ] + self.colors['note_title_month_bg'] = [ cp.get(cfg_sec, 'clr_note_title_month_bg'), 'Month old note title bg' ] + self.colors['note_title_year_fg'] = [ cp.get(cfg_sec, 'clr_note_title_year_fg'), 'Year old note title fg' ] + self.colors['note_title_year_bg'] = [ cp.get(cfg_sec, 'clr_note_title_year_bg'), 'Year old note title bg' ] + self.colors['note_title_ancient_fg'] = [ cp.get(cfg_sec, 'clr_note_title_ancient_fg'), 'Ancient note title fg' ] + self.colors['note_title_ancient_bg'] = [ cp.get(cfg_sec, 'clr_note_title_ancient_bg'), 'Ancient note title bg' ] + self.colors['note_date_fg'] = [ cp.get(cfg_sec, 'clr_note_date_fg'), 'Note date fg' ] + self.colors['note_date_bg'] = [ cp.get(cfg_sec, 'clr_note_date_bg'), 'Note date bg' ] + self.colors['note_flags_fg'] = [ cp.get(cfg_sec, 'clr_note_flags_fg'), 'Note flags fg' ] + self.colors['note_flags_bg'] = [ cp.get(cfg_sec, 'clr_note_flags_bg'), 'Note flags bg' ] + self.colors['note_tags_fg'] = [ cp.get(cfg_sec, 'clr_note_tags_fg'), 'Note tags fg' ] + self.colors['note_tags_bg'] = [ cp.get(cfg_sec, 'clr_note_tags_bg'), 'Note tags bg' ] + self.colors['note_content_fg'] = [ cp.get(cfg_sec, 'clr_note_content_fg'), 'Note content fg' ] + self.colors['note_content_bg'] = [ cp.get(cfg_sec, 'clr_note_content_bg'), 'Note content bg' ] + self.colors['note_content_focus_fg'] = [ cp.get(cfg_sec, 'clr_note_content_focus_fg'), 'Note content focus fg' ] + self.colors['note_content_focus_bg'] = [ cp.get(cfg_sec, 'clr_note_content_focus_bg'), 'Note content focus bg' ] + self.colors['help_focus_fg'] = [ cp.get(cfg_sec, 'clr_help_focus_fg'), 'Help focus fg' ] + self.colors['help_focus_bg'] = [ cp.get(cfg_sec, 'clr_help_focus_bg'), 'Help focus bg' ] + self.colors['help_header_fg'] = [ cp.get(cfg_sec, 'clr_help_header_fg'), 'Help header fg' ] + self.colors['help_header_bg'] = [ cp.get(cfg_sec, 'clr_help_header_bg'), 'Help header bg' ] + self.colors['help_config_fg'] = [ cp.get(cfg_sec, 'clr_help_config_fg'), 'Help config fg' ] + self.colors['help_config_bg'] = [ cp.get(cfg_sec, 'clr_help_config_bg'), 'Help config bg' ] + self.colors['help_value_fg'] = [ cp.get(cfg_sec, 'clr_help_value_fg'), 'Help value fg' ] + self.colors['help_value_bg'] = [ cp.get(cfg_sec, 'clr_help_value_bg'), 'Help value bg' ] + self.colors['help_descr_fg'] = [ cp.get(cfg_sec, 'clr_help_descr_fg'), 'Help description fg' ] + self.colors['help_descr_bg'] = [ cp.get(cfg_sec, 'clr_help_descr_bg'), 'Help description bg' ] def get_config(self, name): return self.configs[name][0] diff --git a/notes_db.py b/notes_db.py @@ -61,114 +61,88 @@ def __init__(self, config, log, update_view): # in progress. This variable is only used by the background thread. self.threaded_syncing_keys = {} - def create_note(self, content): - # 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() - while new_key in self.notes: - new_key = utils.generate_random_key() - - timestamp = time.time() - - # note has no internal key yet. - new_note = { - 'content' : content, - 'modifydate' : timestamp, - 'createdate' : timestamp, - 'savedate' : 0, # never been written to disc - 'syncdate' : 0, # never been synced with server - 'tags' : [] - } - - self.notes[new_key] = new_note - - return new_key + def filtered_notes_sort(self, filtered_notes, sort_mode='date'): + if sort_mode == 'date': + if self.config.get_config('pinned_ontop') == 'yes': + filtered_notes.sort(utils.sort_by_modify_date_pinned, reverse=True) + else: + filtered_notes.sort(key=lambda o: -float(o.note.get('modifydate', 0))) + else: + if self.config.get_config('pinned_ontop') == 'yes': + filtered_notes.sort(utils.sort_by_title_pinned) + else: + filtered_notes.sort(key=lambda o: utils.get_note_title(o.note)) - def filter_notes(self, search_string=None): + def filter_notes(self, search_string=None, search_mode='gstyle'): """Return list of notes filtered with search string. Based on the search mode that has been selected in self.config, this method will call the appropriate helper method to do the actual work of filtering the notes. - @param search_string: String that will be used for searching. - Different meaning depending on the search mode. - @return: notes filtered with selected search mode and sorted according - to configuration. Two more elements in tuple: a regular expression - that can be used for highlighting strings in the text widget; the - total number of notes in memory. + Returns a list of filtered notes with selected search mode and sorted + according to configuration. Two more elements in tuple: a regular + expression that can be used for highlighting strings in the text widget + and the total number of notes in memory. """ - if self.config.get_config('search_mode') == 'regexp': - filtered_notes, match_regexp, active_notes = self.filter_notes_regexp(search_string) + if search_mode == 'gstyle': + filtered_notes, match_regexp, active_notes = \ + self.filter_notes_gstyle(search_string) else: - filtered_notes, match_regexp, active_notes = self.filter_notes_gstyle(search_string) + filtered_notes, match_regexp, active_notes = \ + self.filter_notes_regex(search_string) - if self.config.get_config('sort_mode') == 'alpha': - if self.config.get_config('pinned_ontop') == 'no': - # sort alphabetically on title - filtered_notes.sort(key=lambda o: utils.get_note_title(o.note)) - else: - filtered_notes.sort(utils.sort_by_title_pinned) - - else: - if self.config.get_config('pinned_ontop') == 'no': - # last modified on top - filtered_notes.sort(key=lambda o: -float(o.note.get('modifydate', 0))) - else: - filtered_notes.sort(utils.sort_by_modify_date_pinned, reverse=True) + self.filtered_notes_sort(filtered_notes, + self.config.get_config('sort_mode')) return filtered_notes, match_regexp, active_notes def _helper_gstyle_tagmatch(self, tag_pats, note): - if tag_pats: - tags = note.get('tags') - - # tag: patterns specified, but note has no tags, so no match - if not tags: - return 0 - - # for each tag_pat, we have to find a matching tag - for tp in tag_pats: - # at the first match between tp and a tag: - if next((tag for tag in tags if tag.startswith(tp)), None) is not None: - # we found a tag that matches current tagpat, so we move to the next tagpat - continue + # Returns: + # 2 = match - no tag patterns specified + # 1 = match - all tag patterns match a tag on this note + # 0 = no match - note has no tags or not all tag patterns match - else: - # we found no tag that matches current tagpat, so we break out of for loop - break - - else: - # for loop never broke out due to no match for tagpat, so: - # all tag_pats could be matched, so note is a go. - return 1 + if not tag_pats: + # match because no tag patterns were specified + return 2 + note_tags = note.get('tags') - # break out of for loop will have us end up here - # for one of the tag_pats we found no matching tag + if not note_tags: + # tag patterns specified but note has no tags, so no match return 0 + # for each tag_pat, we have to find a matching tag + tag_pats_matched = 0 + for tp in tag_pats: + for t in note_tags: + if t.startswith(tp): + tag_pats_matched += 1 + break + if tag_pats_matched == len(tag_pats): + # all tag patterns specified matched a tag on this note + return 1 - else: - # match because no tag: patterns were specified - return 2 + # note doesn't match + return 0 - def _helper_gstyle_mswordmatch(self, msword_pats, content): - """If all words / multi-words in msword_pats are found in the content, + def _helper_gstyle_wordmatch(self, word_pats, content): + """If all words / multi-words in word_pats are found in the content, the note goes through, otherwise not. - @param msword_pats: + @param word_pats: @param content: @return: """ # no search patterns, so note goes through - if not msword_pats: + if not word_pats: return True # search for the first p that does NOT occur in content - if next((p for p in msword_pats if p not in content), None) is None: + if next((p for p in word_pats if p not in content), None) is None: # we only found pats that DO occur in content so note goes through return True @@ -179,112 +153,126 @@ def _helper_gstyle_mswordmatch(self, msword_pats, content): def filter_notes_gstyle(self, search_string=None): filtered_notes = [] - # total number of notes, excluding deleted - active_notes = 0 + active_notes = 0 # total number of notes, excluding deleted if not search_string: for k in self.notes: n = self.notes[k] - if not n.get('deleted'): - active_notes += 1 - filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) + if n.get('deleted'): + continue + active_notes += 1 + filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) return filtered_notes, [], active_notes - # group0: ag - not used - # group1: t(ag)?:([^\s]+) - # group2: multiple words in quotes - # group3: single words - # example result for 't:tag1 t:tag2 word1 "word2 word3" tag:tag3' == - # [('', 'tag1', '', ''), ('', 'tag2', '', ''), ('', '', '', 'word1'), ('', '', 'word2 word3', ''), ('ag', 'tag3', '', '')] + # group0: tag:([^\s]+) + # group1: multiple words in quotes + # group2: single words - groups = re.findall('t(ag)?:([^\s]+)|"([^"]+)"|([^\s]+)', search_string) - tms_pats = [[] for _ in range(3)] + # example result for: 't:tag1 t:tag2 word1 "word2 word3" tag:tag3' + # [ ('tag1', '', ''), + # ('tag2', '', ''), + # ('', '', 'word1'), + # ('', 'word2 word3', ''), + # ('tag3', '', '') ] + + groups = re.findall('tag:([^\s]+)|"([^"]+)"|([^\s]+)', search_string) + all_pats = [[] for _ in range(3)] # we end up with [[tag_pats],[multi_word_pats],[single_word_pats]] - for gi in groups: - for mi in range(1,4): - if gi[mi]: - tms_pats[mi-1].append(gi[mi]) + for g in groups: + for i in range(3): + if g[i]: all_pats[i].append(g[i]) for k in self.notes: n = self.notes[k] - if not n.get('deleted'): - active_notes += 1 - c = n.get('content') + if n.get('deleted'): + continue + + active_notes += 1 - tagmatch = self._helper_gstyle_tagmatch(tms_pats[0], n) - msword_pats = tms_pats[1] + tms_pats[2] + tagmatch = self._helper_gstyle_tagmatch(all_pats[0], n) - if tagmatch and self._helper_gstyle_mswordmatch(msword_pats, c): - # we have a note that can go through! + word_pats = all_pats[1] + all_pats[2] - # tagmatch == 1 if a tag was specced and found - # tagmatch == 2 if no tag was specced (so all notes go through) - tagfound = 1 if tagmatch == 1 else 0 - # we have to store our local key also - filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=tagfound)) + if tagmatch and \ + self._helper_gstyle_wordmatch(word_pats, n.get('content')): + # we have a note that can go through! + filtered_notes.append( + utils.KeyValueObject(key=k, + note=n, + tagfound=1 if tagmatch == 1 else 0)) - return filtered_notes, '|'.join(tms_pats[1] + tms_pats[2]), active_notes + return filtered_notes, '|'.join(all_pats[1] + all_pats[2]), active_notes def filter_notes_regexp(self, search_string=None): - """Return list of notes filtered with search_string, - a regular expression, each a tuple with (local_key, note). """ - + Return a list of notes filtered using the regex search_string. + Each element in the list is a tuple (local_key, note). + """ + sspat = None if search_string: try: sspat = re.compile(search_string) except re.error: sspat = None - else: - sspat = None - filtered_notes = [] - # total number of notes, excluding deleted ones - active_notes = 0 + active_notes = 0 # total number of notes, excluding deleted ones + for k in self.notes: n = self.notes[k] - # we don't do anything with deleted notes (yet) + + # we don't do anything with deleted notes if n.get('deleted'): continue active_notes += 1 - c = n.get('content') + if not sspat: + filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) + continue + if self.config.search_tags == 'yes': - t = n.get('tags') - if sspat: - # this used to use a filter(), but that would by definition - # test all elements, whereas we can stop when the first - # matching element is found - # now I'm using this awesome trick by Alex Martelli on - # http://stackoverflow.com/a/2748753/532513 - # first parameter of next is a generator - # next() executes one step, but due to the if, this will - # either be first matching element or None (second param) - if t and next((ti for ti in t if sspat.search(ti)), None) is not None: - # we have to store our local key also + tag_matched = False + for t in n.get('tags'): + if sspat.seatch(t): + tag_matched = True filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=1)) + break + if tag_matched: + continue - elif sspat.search(c): - # we have to store our local key also - filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) - - else: - # we have to store our local key also - filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) - else: - if (not sspat or sspat.search(c)): - # we have to store our local key also - filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) + if sspat.search(n.get('content')): + filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) match_regexp = search_string if sspat else '' - return filtered_notes, match_regexp, active_notes + def create_note(self, content): + # 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() + while new_key in self.notes: + new_key = utils.generate_random_key() + + timestamp = time.time() + + # note has no internal key yet. + new_note = { + 'content' : content, + 'modifydate' : timestamp, + 'createdate' : timestamp, + 'savedate' : 0, # never been written to disc + 'syncdate' : 0, # never been synced with server + 'tags' : [] + } + + self.notes[new_key] = new_note + + return new_key + def get_note(self, key): return self.notes[key] diff --git a/view_help.py b/view_help.py @@ -44,7 +44,7 @@ def create_kb_help_lines(self, header, use): lines.append(urwid.AttrMap(urwid.Text(u' ' + header), 'help_header', 'help_focus')) - for c in self.config.keybinds.keys(): + for c in self.config.keybinds: if use not in self.config.get_keybind_use(c): continue lines.append( @@ -72,7 +72,7 @@ def create_config_help_lines(self): lines.append(urwid.AttrMap(urwid.Text(u' Configuration'), 'help_header', 'help_focus')) - for c in sorted(self.config.configs): + for c in self.config.configs: if c in [ 'sn_username', 'sn_password' ]: continue lines.append( urwid.AttrMap(urwid.AttrMap( @@ -100,9 +100,9 @@ def create_color_help_lines(self): 'help_header', 'help_focus')) fmap = {} - for c in sorted(self.config.colors): + for c in self.config.colors: fmap[re.search('^(.*)(_fg|_bg)$', c).group(1)] = 'help_focus' - for c in sorted(self.config.colors): + for c in self.config.colors: lines.append( urwid.AttrMap(urwid.AttrMap( urwid.Text( diff --git a/view_titles.py b/view_titles.py @@ -9,14 +9,14 @@ def __init__(self, config, args): self.ndb = args['ndb'] self.search_string = args['search_string'] self.log = args['log'] - self.note_list, match_regex, self.all_notes_cnt = \ + self.note_list, self.match_regex, self.all_notes_cnt = \ self.ndb.filter_notes(self.search_string) super(ViewTitles, self).__init__( urwid.SimpleFocusListWalker(self.get_note_titles())) def update_note_list(self, search_string): self.search_string = search_string - self.note_list, match_regex, self.all_notes_cnt = \ + self.note_list, self.match_regex, self.all_notes_cnt = \ self.ndb.filter_notes(self.search_string) self.body[:] = \ urwid.SimpleFocusListWalker(self.get_note_titles())