def print_note_listing(self, notepaths): """ Print a list of note in a nicely formatted way. Parameter --------- notepaths : list A list of filepaths of notes. """ # fmt is of the form: # <index> YYYY-MM-DD aaa <title> fmt = '{:>4} {:<14} {}' N = len(notepaths) # If note_list is empty, do not print anything if N == 0: return None else: print() # print(fmt.format('index', 'date', 'title')) for i, notepath in enumerate(notepaths): note = parse_note(notepath) print(fmt.format(str(N - 1 - i), note.creation_date, note.title)) # Flush stdout after each print statement so that the listing # appears smooth to the user: sys.stdout.flush() self.history.append(notepaths) infobar = '\nlisting: {}\ttotal: {}'.format(len(self.history), N) print(infobar)
def build_index(self): self.index = defaultdict(set) for notepath in glob.glob(self.notepath_pattern): _, _, _, note_id = extract_filename_info(notepath) note = parse_note(notepath) for tag in note.tags: self.index[tag].add(note_id) self.save_index()
def test_parse_note(): notepath = os.path.join(root, 'notes', '2015-08-02-2-2015-01-12.txt') note = parse_note(notepath) assert note.filepath == notepath assert note.creation_date == '2015-08-02 Sun' assert note.modification_date == '2015-01-12 Mon' assert note.title == 'Title' assert note.body == 'Body' assert note.tags == ['tag1', 'tag2']
def delete_note(args): """ Delete a given note. """ notepath = parse_index(args) if notepath is None: return None note = parse_note(notepath) print(note.creation_date, ' ', note.title) ans = confirm('delete? (y/n) ') if ans: nm.delete_note(notepath)
def describe_note(self, notepath): note = parse_note(notepath) tokens = [] tokens.append((Token.Attribute, '{:>20}'.format('title: '))) tokens.append((Token.Value, note.title + '\n')) tokens.append((Token.Attribute, '{:>20}'.format('date: '))) tokens.append((Token.Value, note.creation_date + '\n')) tokens.append((Token.Attribute, '{:>20}'.format('last modified: '))) tokens.append((Token.Value, note.modification_date + '\n')) tokens.append((Token.Attribute, '{:>20}'.format('tags: '))) tokens.append((Token.Value, ', '.join(note.tags) + '\n')) tokens.append((Token.Attribute, '{:>20}'.format('number of links: '))) tokens.append((Token.Value, str(len(note.links)) + '\n')) tokens.append((Token.Attribute, '{:>20}'.format('filepath: '))) tokens.append((Token.Value, note.filepath + '\n')) tokens.append((Token.Attribute, '{:>20}'.format('identifier: '))) tokens.append((Token.Value, note.identifier + '\n')) print_tokens(tokens, style=self.color_style)
def update_index(self): """ Parameter --------- date : str Of form YYYY-MM-DD. Only notes whose modification is greater than or equal to this date will be considered when the index is updated. """ # Get the last update date: t = os.path.getmtime(self.filepath) d = datetime.datetime.fromtimestamp(t) date = d.strftime('%Y-%m-%d') for notepath in glob.glob(self.notepath_pattern): _, m_date, _, note_id = extract_filename_info(notepath) m_date = m_date.strftime('%Y-%m-%d') if m_date >= date: note = parse_note(notepath) for tag in note.tags: self.index[tag].add(note_id) # Save the updated index: self.save_index()
def filter_notes(self, notepaths=[], criteria={}): """ Given a list of filepaths of notes and a dictionary of criteria, onlu keep those notes that meet the criteria. NOTE: Filter notes based on days with filter_notes_by_date() because that is a lot faster. Parameters ---------- notepaths : list A list of filepaths representing notes. criteria : dict A dictionary representing various criteria. Returns ------- result : list A list of note that meet the criteria. """ result = [] N = len(notepaths) # NOTE: by default, the case of letters is ignored in title, body, and # tags. But this can be switched off as option: if criteria.get('ignore-case', True): if 'words' in criteria: criteria['words'] = [word.lower() for word in criteria['words']] if 'no-words' in criteria: criteria['no-words'] = [word.lower() for word in criteria['no-words']] if 'tags' in criteria: criteria['tags'] = [tag.lower() for tag in criteria['tags']] for i, notepath in enumerate(notepaths): self._print_progress_bar(i, N) note = parse_note(notepath) # most pattern matching is done to the whole content of a note, # regardless of whether it is title, body, or tags. So let's define # a variable for that: content = note.title + '\n' + note.body + '\n' + '#: ' + ' '.join(note.tags) tags = note.tags if criteria.get('ignore-case', True): content = content.lower() tags = [t.lower() for t in tags] # tags: if tags are given, at least one tag must appear if criteria.get('tags', []): if not set.intersection(set(criteria['tags']), set(tags)): continue # words: all words must appear as a SUBSTRING # NOTE: this includes cases like 'string' in 'substring' # NOTE: matching is done in lower case if criteria.get('words', []): for word in criteria['words']: if word not in content: not_all_words_appear = True break else: not_all_words_appear = False if not_all_words_appear: continue # no_words: none of these words must appear # NOTE: here only full words are considered. For example, 'substring' # does not count as an occurrence of 'string'. # NOTE: matching is done preserving the case of letters if criteria.get('no-words', []): if set.intersection(set(criteria['no-words']), set(content.split())): continue result.append(notepath) return result
def display_note2(self, notepath): """ Display the content of a note on the terminal. Using a bit of syntax here for highlighting: - if a line begins with "$:" or ends with ":$", the whole line is highlighted - if a line begins with "@:", the line is interpreted as a link to a reference - if a line begins with "http://" or "https://", the whole line is interpreted as a url - if a line begins with "#:", it is interpreted as a tag line - if a line contains text between two occurrences of ":::" (or whatever is defined a highlight_marker), that part is highlighted. If the line has an odd number of ":::", then the last remaining part of the line is highlighted. """ with open(notepath, 'rt') as f: self.link_listing = {} note = parse_note(notepath) # title has been stripped, so we have to add a newline character # below. print_tokens([(Token.Title, note.title + '\n')], style=self.color_style) # # Make the body ends with a newline, if the body is nonempty: # if note.body and note.body[-1] != '\n': # note.body += '\n' lines = note.body.split('\n') line_num = 0 while line_num < len(lines): line = lines[line_num] # Is the line a link to a reference? if line.startswith('@:'): ref = line[2:].strip() index = len(self.link_listing) + 1 # NOTE: an extra newline is inserted below because it was # stripped above print_tokens([(Token.Link, str(index) + ' ' + ref + '\n')], style=self.color_style) self.link_listing.append(os.path.join(self.refs, ref)) line_num += 1 continue # Is the line a link to a webpage? if line.startswith('http://') or line.startswith('https://'): url = line.strip() index = len(self.link_listing) + 1 # NOTE: an extra newline is inserted below because it was # stripped above print_tokens([(Token.Link, str(index) + ' ' + url + '\n')], style=self.color_style) self.link_listing.append(url) line_num += 1 continue # Does the line indicate a highlighted block? for i, marker in enumerate(self.highlight_markers): if line == marker: if i == 0: token_type = Token.HL1 elif i == 1: token_type = Token.HL2 else: # i == 2 token_type = Token.HL3 line_num += 1 while line_num < len(lines) and lines[line_num] != marker: print_tokens([(token_type, lines[line_num] + '\n')], style=self.color_style) line_num += 1 # line_num += 1 continue # If neither of the above hold, the line is a regular line. i = 0 marker_0_width = len(self.highlight_markers[0]) marker_1_width = len(self.highlight_markers[1]) while i < len(line): if line[i:].startswith(self.highlight_markers[0]): j = line[i + marker_0_width:].find(self.highlight_markers[0]) if j > -1: print_tokens([(Token.Blue, line[i + marker_0_width: i + marker_0_width + j])], style=self.color_style) i = i + marker_0_width + j + marker_0_width else: print_tokens([(Token.Blue, line[i + marker_0_width:])], style=self.color_style) break elif line[i:].startswith(self.highlight_markers[1]): j = line[i + marker_1_width:].find(self.highlight_markers[1]) if j > -1: print_tokens([(Token.Yellow, line[i + marker_1_width: i + marker_1_width + j])], style=self.color_style) i = i + marker_1_width + j + marker_1_width else: print_tokens([(Token.Yellow, line[i + marker_1_width:])], style=self.color_style) break else: print_tokens([(Token, line[i])], style=self.color_style) i += 1 # Add the newline that was lost when splitting the body on # \n. line_num += 1 print() if note.tags: print_tokens([(Token.Tag, ', '.join(note.tags) + '\n')], style=self.color_style)
def tokenize(self, notepath): """ Display the content of a note on the terminal. Using a bit of syntax here for highlighting: - if a line begins with "$:" or ends with ":$", the whole line is highlighted - if a line begins with "@:", the line is interpreted as a link to a reference - if a line begins with "http://" or "https://", the whole line is interpreted as a url - if a line begins with "#:", it is interpreted as a tag line - if a line contains text between two occurrences of ":::" (or whatever is defined a highlight_marker), that part is highlighted. If the line has an odd number of ":::", then the last remaining part of the line is highlighted. """ with open(notepath, 'rt') as f: self.link_listing = {} note = parse_note(notepath) tokens = [] tokens.append((Token.Tag, note.identifier)) if note.title: tokens.append((Token.Punct, '\n')) tokens.append((Token.Title, note.title)) if note.body: tokens.append((Token.Punct, '\n\n')) token_type = Token.Text i = 0 while i < len(note.body): if note.body[i:].startswith(self.highlight_markers[0]): # toggle token_type on or off token_type = Token.HL1 if token_type != Token.HL1 else Token.Text t = len(self.highlight_markers[0]) i += t elif note.body[i:].startswith(self.highlight_markers[1]): token_type = Token.HL2 if token_type != Token.HL2 else Token.Text t = len(self.highlight_markers[1]) i += t elif note.body[i:].startswith(self.highlight_markers[2]): token_type = Token.HL3 if token_type != Token.HL3 else Token.Text t = len(self.highlight_markers[2]) i += t else: tokens.append((token_type, note.body[i])) i += 1 if note.links: tokens.append((Token.Punct, '\n\n')) for k, v in note.links: tokens.append((Token.LinkID, '[{}] '.format(k))) tokens.append((Token.Link, '{}'.format(v))) tokens.append((Token.Punct, '\n')) self.link_listing[k] = v # Delete the last newline character: del tokens[-1] if note.tags: if note.links: tokens.append((Token.Punct, '\n')) else: tokens.append((Token.Punct, '\n\n')) tokens.append((Token.Tag, ', '.join(note.tags))) tokens.append((Token.Punct, '\n')) return tokens