def filter_notes(self, search_string=None): """Return list of notes filtered with search_string, a regular expression, each a tuple with (local_key, note). """ if search_string: try: sspat = re.compile(search_string) except re.error: sspat = None else: sspat = None filtered_notes = [] for k in self.notes: n = self.notes[k] c = n.get('content') if not n.get('deleted') and (not sspat or sspat.search(c)): # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n)) if self.config.sort_mode == 0: # sort alphabetically on title filtered_notes.sort(key=lambda o: utils.get_note_title(o.note)) else: # last modified on top filtered_notes.sort(key=lambda o: -float(o.note.get('modifydate', 0))) return filtered_notes
def filter_notes(self, search_string=None): """Return list of notes filtered with search_string, a regular expression, each a tuple with (local_key, note). """ if search_string: try: sspat = re.compile(search_string) except re.error: sspat = None else: sspat = None filtered_notes = [] for k in self.notes: n = self.notes[k] c = n.get('content') if not n.get('deleted') and (not sspat or sspat.search(c)): # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n)) if self.config.sort_mode == 0: # sort alphabetically on title filtered_notes.sort(key=lambda o: utils.get_note_title(o.note)) else: # last modified on top filtered_notes.sort( key=lambda o: -float(o.note.get('modifydate', 0))) return filtered_notes
def append(self, note, config): """ @param note: The complete note dictionary. """ title = utils.get_note_title(note) tags = note.get('tags') modifydate = float(note.get('modifydate')) pinned = utils.note_pinned(note) self.note_headers.append((title, tags, modifydate, pinned)) self.enable_text() self.text.insert(tk.END, title, ("title,")) if pinned: self.text.insert(tk.END, ' *', ("pinned",)) self.text.insert(tk.END, ' ' + utils.human_date(modifydate), ("modifydate",)) # tags can be None (newly created note) or [] or ['tag1', 'tag2'] if tags > 0: if config.tagfound: self.text.insert(tk.END, ' ' + ','.join(tags), ("found",)) else: self.text.insert(tk.END, ' ' + ','.join(tags), ("tags",)) self.text.insert(tk.END, '\n') self.disable_text()
def cli_note_dump(self, key): note = self.ndb.get_note(key) if not note: self.log(u'ERROR: Key does not exist') return w = 60 sep = u'+' + u'-' * (w + 2) + u'+' t = time.localtime(float(note['modifydate'])) mod_time = time.strftime('%a, %d %b %Y %H:%M:%S', t) title = utils.get_note_title(note) flags = utils.get_note_flags(note) tags = utils.get_note_tags(note) print sep print(u'| {:<' + str(w) + u'} |').format((u' Title: ' + title)[:w]) print(u'| {:<' + str(w) + u'} |').format( (u' Key: ' + note['key'])[:w]) print(u'| {:<' + str(w) + u'} |').format( (u' Date: ' + mod_time)[:w]) print(u'| {:<' + str(w) + u'} |').format((u' Tags: ' + tags)[:w]) print(u'| {:<' + str(w) + u'} |').format( (u' Version: v' + str(note['version']))[:w]) print(u'| {:<' + str(w) + u'} |').format( (u' Flags: [' + flags + u']')[:w]) if utils.note_published(note) and 'publishkey' in note: print(u'| {:<' + str(w) + u'} |').format( (u'Published: http://simp.ly/publish/' + note['publishkey'])[:w]) else: print(u'| {:<' + str(w) + u'} |').format((u'Published: n/a')[:w]) print sep print note['content']
def filter_notes(self, search_string=None): """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. """ if self.config.search_mode == 'regexp': filtered_notes, match_regexp = self.filter_notes_regexp(search_string) else: filtered_notes, match_regexp = self.filter_notes_gstyle(search_string) if self.config.sort_mode == 0: if self.config.pinned_ontop == 0: # 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.pinned_ontop == 0: # 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) return filtered_notes, match_regexp
def append(self, note, config): """ @param note: The complete note dictionary. """ title = utils.get_note_title(note) tags = note.get('tags') modifydate = float(note.get('modifydate')) pinned = utils.note_pinned(note) self.note_headers.append((title, tags, modifydate, pinned)) self.enable_text() self.text.insert(tk.END, title, ("title,")) if pinned: self.text.insert(tk.END, ' *', ("pinned", )) self.text.insert(tk.END, ' ' + utils.human_date(modifydate), ("modifydate", )) # tags can be None (newly created note) or [] or ['tag1', 'tag2'] if tags > 0: if config.tagfound: self.text.insert(tk.END, ' ' + ','.join(tags), ("found", )) else: self.text.insert(tk.END, ' ' + ','.join(tags), ("tags", )) self.text.insert(tk.END, '\n') self.disable_text()
def cli_note_dump(self, key): note = self.ndb.get_note(key) if not note: self.log(u'ERROR: Key does not exist') return w = 60 sep = u'+' + u'-'*(w+2) + u'+' t = time.localtime(float(note['modifydate'])) mod_time = time.strftime('%a, %d %b %Y %H:%M:%S', t) title = utils.get_note_title(note) flags = utils.get_note_flags(note) tags = utils.get_note_tags(note) print sep print (u'| {:<' + str(w) + u'} |').format((u' Title: ' + title)[:w]) print (u'| {:<' + str(w) + u'} |').format((u' Key: ' + note['key'])[:w]) print (u'| {:<' + str(w) + u'} |').format((u' Date: ' + mod_time)[:w]) print (u'| {:<' + str(w) + u'} |').format((u' Tags: ' + tags)[:w]) print (u'| {:<' + str(w) + u'} |').format((u' Version: v' + str(note['version']))[:w]) print (u'| {:<' + str(w) + u'} |').format((u' Flags: [' + flags + u']')[:w]) if utils.note_published(note) and 'publishkey' in note: print (u'| {:<' + str(w) + u'} |').format((u'Published: http://simp.ly/publish/' + note['publishkey'])[:w]) else: print (u'| {:<' + str(w) + u'} |').format((u'Published: n/a')[:w]) print sep print note['content']
def filter_notes(self, search_string=None): """Return list of notes filtered with search_string, a regular expression, each a tuple with (local_key, note). """ if search_string: try: if self.config.case_sensitive == 0: sspat = re.compile(search_string, re.I) else: sspat = re.compile(search_string) except re.error: sspat = None else: sspat = None filtered_notes = [] for k in self.notes: n = self.notes[k] c = n.get('content') if self.config.search_tags == 1: t = n.get('tags') if not n.get('deleted') and sspat: if filter(sspat.search, t): # we have to store our local key also filtered_notes.append( utils.KeyValueObject(key=k, note=n, tagfound=1)) elif sspat.search(c): # we have to store our local key also filtered_notes.append( utils.KeyValueObject(key=k, note=n, tagfound=0)) elif not n.get('deleted') and not sspat: # we have to store our local key also filtered_notes.append( utils.KeyValueObject(key=k, note=n, tagfound=0)) else: if not n.get('deleted') and (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 self.config.sort_mode == 0: if self.config.pinned_ontop == 0: # 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.pinned_ontop == 0: # 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) return filtered_notes
def cli_list_notes(self, regex, search_string): note_list, match_regex, all_notes_cnt = \ self.ndb.filter_notes( search_string, search_mode='regex' if regex else 'gstyle') for n in note_list: flags = utils.get_note_flags(n.note) print n.key + \ u' [' + flags + u'] ' + \ utils.get_note_title(n.note)
def filter_notes(self, search_string=None): """Return list of notes filtered with search_string, a regular expression, each a tuple with (local_key, note). """ if search_string: try: if self.config.case_sensitive == 0: sspat = re.compile(search_string, re.I) else: sspat = re.compile(search_string) except re.error: sspat = None else: sspat = None filtered_notes = [] for k in self.notes: n = self.notes[k] c = n.get('content') if self.config.search_tags == 1: t = n.get('tags') if not n.get('deleted') and sspat: if filter(sspat.search, t): # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=1)) elif sspat.search(c): # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) elif not n.get('deleted') and not sspat: # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) else: if not n.get('deleted') and (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 self.config.sort_mode == 0: if self.config.pinned_ontop == 0: # 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.pinned_ontop == 0: # 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) return filtered_notes
def filter_notes(self, search_string=None): """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. """ if self.config.search_mode == 'regexp': filtered_notes, match_regexp, active_notes = self.filter_notes_regexp( search_string) else: filtered_notes, match_regexp, active_notes = self.filter_notes_gstyle( search_string) if self.config.sort_mode == 0: if self.config.pinned_ontop == 0: # sort alphabetically on title filtered_notes.sort(key=lambda o: utils.get_note_title(o.note)) else: filtered_notes.sort(key=utils.sort_key_by_title_pinned) elif self.config.sort_mode == 2: if self.config.pinned_ontop == 0: # last modified on top filtered_notes.sort( key=lambda o: -float(o.note.get('createdate', 0))) else: filtered_notes.sort(key=utils.sort_key_by_create_date_pinned, reverse=True) else: if self.config.pinned_ontop == 0: # last modified on top filtered_notes.sort( key=lambda o: -float(o.note.get('modifydate', 0))) else: filtered_notes.sort(key=utils.sort_key_by_modify_date_pinned, reverse=True) return filtered_notes, match_regexp, active_notes
def handler_housekeeper(self): # nvPY will do saving and syncing! self.notify_observers('keep:house', None) # check if titles need refreshing refresh_notes_list = False prev_title = None prev_modifydate = None for i,o in enumerate(self.notes_list_model.list): # order should be the same as our listbox nt = utils.get_note_title(o.note) ot = self.lb_notes.get(i) # if we strike a note with an out-of-date title, redo. if nt != ot: logging.debug('title "%s" resync' % (nt,)) refresh_notes_list = True continue if self.config.sort_mode == 0: # alpha if prev_title is not None and prev_title > nt: logging.debug("alpha resort triggered") refresh_notes_list = True continue prev_title = nt else: md = float(o.note.get('modifydate', 0)) if prev_modifydate is not None and prev_modifydate < md: logging.debug("modifydate resort triggered") refresh_notes_list = True continue prev_modifydate = md if refresh_notes_list: self.refresh_notes_list() self.root.after(self.config.housekeeping_interval_ms, self.handler_housekeeper)
def handler_housekeeper(self): # nvPY will do saving and syncing! self.notify_observers('keep:house', None) # check if titles need refreshing refresh_notes_list = False prev_title = None prev_modifydate = None for i,o in enumerate(self.notes_list_model.list): # order should be the same as our listbox nt = utils.get_note_title(o.note) ot = self.lb_notes.get(i) # if we strike a note with an out-of-date title, redo. if nt != ot: print "title out of date" refresh_notes_list = True continue if self.config.sort_mode == 0: # alpha if prev_title is not None and prev_title > nt: print "alpha resort" refresh_notes_list = True continue prev_title = nt else: md = float(o.note.get('modifydate', 0)) if prev_modifydate is not None and prev_modifydate < md: print "modifydate resort" refresh_notes_list = True continue prev_modifydate = md if refresh_notes_list: self.refresh_notes_list() self.root.after(self.config.housekeeping_interval_ms, self.handler_housekeeper)
def filter_notes(self, search_string=None): """Return list of notes filtered with search_string, a regular expression, each a tuple with (local_key, note). """ if search_string: try: if self.config.case_sensitive == 0: sspat = re.compile(search_string, re.I) else: sspat = re.compile(search_string) except re.error: sspat = None else: sspat = None filtered_notes = [] for k in self.notes: n = self.notes[k] c = n.get('content') if self.config.search_tags == 1: t = n.get('tags') if not n.get('deleted') and 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 next((ti for ti in t if sspat.search(ti)), None) is not None: # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=1)) elif sspat.search(c): # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) elif not n.get('deleted') and not sspat: # we have to store our local key also filtered_notes.append(utils.KeyValueObject(key=k, note=n, tagfound=0)) else: if not n.get('deleted') and (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 self.config.sort_mode == 0: if self.config.pinned_ontop == 0: # 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.pinned_ontop == 0: # 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) return filtered_notes
def handler_housekeeper(self): # nvPY will do saving and syncing! self.notify_observers('keep:house', None) # check if titles need refreshing refresh_notes_list = False prev_title = None prev_modifydate = None prev_pinned = 0 for i, o in enumerate(self.notes_list_model.list): # order should be the same as our listbox nt = utils.get_note_title(o.note) ot = self.notes_list.get_title(i) # if we strike a note with an out-of-date title, redo. if nt != ot: logging.debug('title "%s" resync' % (nt, )) refresh_notes_list = True break # compare modifydate timestamp in our notes_list_model to what's displayed # if these are more than 60 seconds apart, we want to update our # mod-date display. md = float(o.note.get('modifydate', 0)) omd = self.notes_list.get_modifydate(i) if abs(md - omd) > 60: # we log the title logging.debug('modifydate "%s" resync' % (nt, )) refresh_notes_list = True break pinned = utils.note_pinned(o.note) old_pinned = self.notes_list.get_pinned(i) if pinned != old_pinned: # we log the title logging.debug('pinned "%s" resync' % (nt, )) refresh_notes_list = True break tags = o.note.get('tags', 0) old_tags = self.notes_list.get_tags(i) if tags != old_tags: # we log the title logging.debug('tags "%s" resync' % (nt, )) refresh_notes_list = True break if self.config.sort_mode == 0: # alpha if prev_title is not None and prev_title > nt: logging.debug("alpha resort triggered") refresh_notes_list = True break prev_title = nt else: # we go from top to bottom, newest to oldest # this means that prev_modifydate (above) needs to be larger # than md (below). if it's not, re-sort. if prev_modifydate is not None and prev_modifydate < md and \ not prev_pinned: logging.debug("modifydate resort triggered") refresh_notes_list = True break prev_modifydate = md if self.config.pinned_ontop: prev_pinned = utils.note_pinned(o.note) if refresh_notes_list: self.refresh_notes_list() self.root.after(self.config.housekeeping_interval_ms, self.handler_housekeeper)
def get_status_bar(self): if not self.key: return \ urwid.AttrMap(urwid.Text(u'No note...'), 'status_bar') cur = -1 total = 0 if len(self.body.positions()) > 0: cur = self.focus_position total = len(self.body.positions()) if self.old_note: t = time.localtime(float(self.old_note['versiondate'])) title = utils.get_note_title(self.old_note) version = self.old_note['version'] else: t = time.localtime(float(self.note['modifydate'])) title = utils.get_note_title(self.note) flags = utils.get_note_flags(self.note) tags = utils.get_note_tags(self.note) version = self.note['version'] mod_time = time.strftime(u'Date: %a, %d %b %Y %H:%M:%S', t) status_title = \ urwid.AttrMap(urwid.Text(u'Title: ' + title, wrap='clip'), 'status_bar') status_key_index = \ ('pack', urwid.AttrMap(urwid.Text(u' [' + self.key + u'] ' + str(cur + 1) + u'/' + str(total)), 'status_bar')) status_date = \ urwid.AttrMap(urwid.Text(mod_time, wrap='clip'), 'status_bar') if self.old_note: status_tags_flags = \ ('pack', urwid.AttrMap(urwid.Text(u'[OLD:v' + str(version) + u']'), 'status_bar')) else: status_tags_flags = \ ('pack', urwid.AttrMap(urwid.Text(u'[' + tags + u'] [v' + str(version) + u'] [' + flags + u']'), 'status_bar')) pile_top = urwid.Columns([ status_title, status_key_index ]) pile_bottom = urwid.Columns([ status_date, status_tags_flags ]) if self.old_note or \ not (utils.note_published(self.note) and 'publishkey' in self.note): return urwid.AttrMap(urwid.Pile([ pile_top, pile_bottom ]), 'status_bar') pile_publish = \ urwid.AttrMap(urwid.Text(u'Published: http://simp.ly/publish/' + self.note['publishkey']), 'status_bar') return \ urwid.AttrMap(urwid.Pile([ pile_top, pile_bottom, pile_publish ]), 'status_bar')
def set_notes(self, notes): # clear the listbox self.lb_notes.delete(0, tk.END) for o in notes: self.lb_notes.insert(tk.END, utils.get_note_title(o.note))
def handler_housekeeper(self): # nvPY will do saving and syncing! self.notify_observers('keep:house', None) # check if titles need refreshing refresh_notes_list = False prev_title = None prev_modifydate = None prev_pinned = 0 for i,o in enumerate(self.notes_list_model.list): # order should be the same as our listbox nt = utils.get_note_title(o.note) ot = self.notes_list.get_title(i) # if we strike a note with an out-of-date title, redo. if nt != ot: logging.debug('title "%s" resync' % (nt,)) refresh_notes_list = True break # compare modifydate timestamp in our notes_list_model to what's displayed # if these are more than 60 seconds apart, we want to update our # mod-date display. md = float(o.note.get('modifydate', 0)) omd = self.notes_list.get_modifydate(i) if abs(md - omd) > 60: # we log the title logging.debug('modifydate "%s" resync' % (nt,)) refresh_notes_list = True break pinned = utils.note_pinned(o.note) old_pinned = self.notes_list.get_pinned(i) if pinned != old_pinned: # we log the title logging.debug('pinned "%s" resync' % (nt,)) refresh_notes_list = True break tags = o.note.get('tags', 0) old_tags = self.notes_list.get_tags(i) if tags != old_tags: # we log the title logging.debug('tags "%s" resync' % (nt,)) refresh_notes_list = True break if self.config.sort_mode == 0: # alpha if prev_title is not None and prev_title > nt: logging.debug("alpha resort triggered") refresh_notes_list = True break prev_title = nt else: # we go from top to bottom, newest to oldest # this means that prev_modifydate (above) needs to be larger # than md (below). if it's not, re-sort. if prev_modifydate is not None and prev_modifydate < md and \ not prev_pinned: logging.debug("modifydate resort triggered") refresh_notes_list = True break prev_modifydate = md if self.config.pinned_ontop: prev_pinned = utils.note_pinned(o.note) if refresh_notes_list: self.refresh_notes_list() self.root.after(self.config.housekeeping_interval_ms, self.handler_housekeeper)
def filter_notes(self, search_string=None): """Return list of notes filtered with search_string, a regular expression, each a tuple with (local_key, note). """ if search_string: try: if self.config.case_sensitive == 0: sspat = re.compile(search_string, re.I) else: sspat = re.compile(search_string) except re.error: sspat = None else: sspat = None filtered_notes = [] for k in self.notes: n = self.notes[k] c = n.get('content') if self.config.search_tags == 1: t = n.get('tags') if not n.get('deleted') and 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 next((ti for ti in t if sspat.search(ti)), None) is not None: # we have to store our local key also filtered_notes.append( utils.KeyValueObject(key=k, note=n, tagfound=1)) elif sspat.search(c): # we have to store our local key also filtered_notes.append( utils.KeyValueObject(key=k, note=n, tagfound=0)) elif not n.get('deleted') and not sspat: # we have to store our local key also filtered_notes.append( utils.KeyValueObject(key=k, note=n, tagfound=0)) else: if not n.get('deleted') and (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 self.config.sort_mode == 0: if self.config.pinned_ontop == 0: # 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.pinned_ontop == 0: # 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) return filtered_notes
def format_title(self, note): """ Various formatting tags are supported for dynamically building the title string. Each of these formatting tags supports a width specifier (decimal) and a left justification (-) like that supported by printf. %F -- flags %T -- tags %D -- date %N -- note title """ t = time.localtime(float(note['modifydate'])) mod_time = time.strftime(self.config.get_config('format_strftime'), t) title = utils.get_note_title(note) flags = utils.get_note_flags(note) tags = utils.get_note_tags(note) # get the age of the note dt = datetime.datetime.fromtimestamp(time.mktime(t)) if dt > datetime.datetime.now() - datetime.timedelta(days=1): note_age = 'd' # less than a day old elif dt > datetime.datetime.now() - datetime.timedelta(weeks=1): note_age = 'w' # less than a week old elif dt > datetime.datetime.now() - datetime.timedelta(weeks=4): note_age = 'm' # less than a month old elif dt > datetime.datetime.now() - datetime.timedelta(weeks=52): note_age = 'y' # less than a year old else: note_age = 'a' # ancient def recursive_format(title_format): if not title_format: return None fmt = re.search("^(.*)%([-]*)([0-9]*)([FDTN])(.*)$", title_format) if not fmt: m = ('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 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')) elif fmt.group(4) == 'D': m = (width, urwid.AttrMap( urwid.Text(mod_time, align=align, wrap='clip'), 'note_date')) elif fmt.group(4) == 'T': m = (width, urwid.AttrMap( urwid.Text(tags, align=align, wrap='clip'), 'note_tags')) 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 width != 'pack': m = (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) tmp = [] if l_fmt: tmp.extend(l_fmt) tmp.append(m) 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')) return urwid.Columns(title_line)
class NotesDB(utils.SubjectMixin): """NotesDB will take care of the local notes database and syncing with SN. """ def __init__(self, config): utils.SubjectMixin.__init__(self) self.config = config # create db dir if it does not exist if not os.path.exists(config.db_path): os.mkdir(config.db_path) self.db_path = config.db_path # create txt Notes dir if it does not exist if self.config.notes_as_txt and not os.path.exists(config.txt_path): os.mkdir(config.txt_path) now = time.time() # now read all .json files from disk fnlist = glob.glob(self.helper_key_to_fname('*')) txtlist = glob.glob(self.config.txt_path + '/*.txt') txtlist += glob.glob(self.config.txt_path + '/*.mkdn') # removing json files and force full full sync if using text files # and none exists and json files are there if self.config.notes_as_txt and not txtlist and fnlist: logging.debug('Forcing resync: using text notes, first usage') for fn in fnlist: os.unlink(fn) fnlist = [] self.notes = {} if self.config.notes_as_txt: self.titlelist = {} for fn in fnlist: try: n = json.load(open(fn, 'rb')) if self.config.notes_as_txt: nt = utils.get_note_title_file(n) tfn = os.path.join(self.config.txt_path, nt) if os.path.isfile(tfn): self.titlelist[n.get('key')] = nt txtlist.remove(tfn) if os.path.getmtime(tfn) > os.path.getmtime(fn): logging.debug('Text note was changed: %s' % (fn, )) #with open(tfn, mode='r') as f: with codecs.open(tfn, mode='rb', encoding='utf-8') as f: c = f.read() n['content'] = c n['modifydate'] = os.path.getmtime(tfn) else: logging.debug('Deleting note : %s' % (fn, )) if not self.config.simplenote_sync: os.unlink(fn) continue else: n['deleted'] = 1 n['modifydate'] = now except ValueError, e: logging.error('Error parsing %s: %s' % (fn, str(e))) else: # we always have a localkey, also when we don't have a note['key'] yet (no sync) localkey = os.path.splitext(os.path.basename(fn))[0] self.notes[localkey] = n # 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 if self.config.notes_as_txt: for fn in txtlist: logging.debug('New text note found : %s' % (fn), ) tfn = os.path.join(self.config.txt_path, fn) #with open(tfn, mode='r') as f: with codecs.open(tfn, mode='rb', encoding='utf-8') as f: c = f.read() nk = self.create_note(c) nn = os.path.splitext(os.path.basename(fn))[0] if nn != utils.get_note_title(self.notes[nk]): self.notes[nk]['content'] = nn + "\n\n" + c os.unlink(tfn) # save and sync queue self.q_save = Queue() self.q_save_res = Queue() thread_save = Thread(target=self.worker_save) thread_save.setDaemon(True) thread_save.start() # initialise the simplenote instance we're going to use # this does not yet need network access if self.config.simplenote_sync: self.simplenote = Simplenote(config.sn_username, config.sn_password) # we'll use this to store which notes are currently being synced by # the background thread, so we don't add them anew if they're still # in progress. This variable is only used by the background thread. self.threaded_syncing_keys = {} # reading a variable or setting this variable is atomic # so sync thread will write to it, main thread will only # check it sometimes. self.waiting_for_simplenote = False self.q_sync = Queue() self.q_sync_res = Queue() thread_sync = Thread(target=self.worker_sync) thread_sync.setDaemon(True) thread_sync.start()
def get_status_bar(self): if not self.key: return \ urwid.AttrMap(urwid.Text(u'No note...'), 'status_bar') cur = -1 total = 0 if len(self.body.positions()) > 0: cur = self.focus_position total = len(self.body.positions()) if self.old_note: t = time.localtime(float(self.old_note['versiondate'])) title = utils.get_note_title(self.old_note) version = self.old_note['version'] else: t = time.localtime(float(self.note['modifydate'])) title = utils.get_note_title(self.note) flags = utils.get_note_flags(self.note) tags = utils.get_note_tags(self.note) version = self.note['version'] mod_time = time.strftime(u'Date: %a, %d %b %Y %H:%M:%S', t) status_title = \ urwid.AttrMap(urwid.Text(u'Title: ' + title, wrap='clip'), 'status_bar') status_key_index = \ ('pack', urwid.AttrMap(urwid.Text(u' [' + self.key + u'] ' + str(cur + 1) + u'/' + str(total)), 'status_bar')) status_date = \ urwid.AttrMap(urwid.Text(mod_time, wrap='clip'), 'status_bar') if self.old_note: status_tags_flags = \ ('pack', urwid.AttrMap(urwid.Text(u'[OLD:v' + str(version) + u']'), 'status_bar')) else: status_tags_flags = \ ('pack', urwid.AttrMap(urwid.Text(u'[' + tags + u'] [v' + str(version) + u'] [' + flags + u']'), 'status_bar')) pile_top = urwid.Columns([status_title, status_key_index]) pile_bottom = urwid.Columns([status_date, status_tags_flags]) if self.old_note or \ not (utils.note_published(self.note) and 'publishkey' in self.note): return urwid.AttrMap(urwid.Pile([pile_top, pile_bottom]), 'status_bar') pile_publish = \ urwid.AttrMap(urwid.Text(u'Published: http://simp.ly/publish/' + self.note['publishkey']), 'status_bar') return \ urwid.AttrMap(urwid.Pile([ pile_top, pile_bottom, pile_publish ]), 'status_bar')
def format_title(self, note): """ Various formatting tags are supported for dynamically building the title string. Each of these formatting tags supports a width specifier (decimal) and a left justification (-) like that supported by printf. %F -- flags %T -- tags %D -- date %N -- note title """ t = time.localtime(float(note['modifydate'])) mod_time = time.strftime(self.config.get_config('format_strftime'), t) title = utils.get_note_title(note) flags = utils.get_note_flags(note) tags = utils.get_note_tags(note) # get the age of the note dt = datetime.datetime.fromtimestamp(time.mktime(t)) if dt > datetime.datetime.now() - datetime.timedelta(days=1): note_age = 'd' # less than a day old elif dt > datetime.datetime.now() - datetime.timedelta(weeks=1): note_age = 'w' # less than a week old elif dt > datetime.datetime.now() - datetime.timedelta(weeks=4): note_age = 'm' # less than a month old elif dt > datetime.datetime.now() - datetime.timedelta(weeks=52): note_age = 'y' # less than a year old else: note_age = 'a' # ancient def recursive_format(title_format): if not title_format: return None fmt = re.search("^(.*)%([-]*)([0-9]*)([FDTN])(.*)$", title_format) if not fmt: m = ('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 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')) elif fmt.group(4) == 'D': m = (width, urwid.AttrMap(urwid.Text(mod_time, align=align, wrap='clip'), 'note_date')) elif fmt.group(4) == 'T': m = (width, urwid.AttrMap(urwid.Text(tags, align=align, wrap='clip'), 'note_tags')) 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 width != 'pack': m = (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) tmp = [] if l_fmt: tmp.extend(l_fmt) tmp.append(m) 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')) return urwid.Columns(title_line)