def rebuild_fts_index(): """ Rebuild virtual table for FTS (fulltext search) populating with all existing notes :return: """ # Create empty FTS table from scratch try: # Remove table in case it existed NoteIndex.drop_table() except Exception: pass NoteIndex.create_table() # Add all entries into virtual FTS table notes = Note.select() with db.atomic(): for note in notes: NoteIndex.insert( { NoteIndex.uid: note.id, NoteIndex.title: note.title, NoteIndex.body: note.body, } ).execute() if config.DO_NOTIFY: notification.show("Rebuilt index", message="FTS index populated from scratch")
def rename_conflicting_notes(): notes = Note.select().where(Note.is_conflict == 1) with db.atomic(): for note in notes: if not note.title.endswith("(CONFLICT)"): note.title = note.title + " (CONFLICT)" num_saved_notes = note.save() if num_saved_notes != 1: notification.show_error("Renaming conflicting note", note.title)
def list_conflicts(): """ List conflicting notes :return: """ notes = Note.select().where(Note.is_conflict == 1) print("List of conflicting notes:") with db.atomic(): for note in notes: print("-----------------------------") print("%s %s" % (note.id, note.title))
def get_notes_by_id(ids, ordered=False): # Find notes with id in uids # Peewee: `x << y` stands for `x in y` # NOTE: Order in query is unrelated to order in uids query = Note.select().where(Note.id << ids).dicts() # Debug: [note['id'] for note in query] if ordered: # Order query list in same order as uids dict_query = {note["id"]: note for note in query} # Grab note only if in the query ordered_notes = [dict_query[id] for id in ids if id in dict_query] query = ordered_notes # Debug: [note['id'] for note in query] return query
def find_empty_notes(delete=False): """ Find and report empty notes Useful e.g. to find if some note was left empty due to synchronization issues :return: """ notes = Note.select().order_by(Note.title) print("Listing empty notes:") with db.atomic(): for note in notes: if not note.body: print("%s %s" % (note.id, note.title)) if delete: note.delete_instance() print("Deleted")
def main(): """Main program. Parse command line, then iterate over files and directories under rootdir and upload all files. Skips some temporary files and directories, and avoids duplicate uploads by comparing size and mtime with the server. """ # Get list of notes in Facebook notebook # These will be skipped in synchronization notebook = Folder.get(Folder.title == "fb") fb_notebook_id = notebook.id # NOTE: If fb folder does not exist, will throw FolderDoesNotExist # TODO: Catch it query = Note.select().where(Note.parent == fb_notebook_id) with db.atomic(): fb_note_ids = [note.id for note in query] args = parser.parse_args() if sum([bool(b) for b in (args.yes, args.no, args.default)]) > 1: print("At most one of --yes, --no, --default is allowed") sys.exit(2) folder = args.folder rootdir = os.path.expanduser(args.rootdir) print("Dropbox folder name:", folder) print("Local directory:", rootdir) if not os.path.exists(rootdir): print(rootdir, "does not exist on your filesystem") sys.exit(1) elif not os.path.isdir(rootdir): print(rootdir, "is not a folder on your filesystem") sys.exit(1) for dn, dirs, files in os.walk(rootdir): subfolder = dn[len(rootdir):].strip(os.path.sep) listing = list_folder(dbx, folder, subfolder) print("Descending into", subfolder, "...") # First do all the files. for name in files: fullname = os.path.join(dn, name) if not isinstance(name, six.text_type): name = name.decode("utf-8") nname = unicodedata.normalize("NFC", name) if name.startswith("."): print("Skipping dot file:", name) elif name.startswith("@") or name.endswith("~"): print("Skipping temporary file:", name) elif name.endswith(".pyc") or name.endswith(".pyo"): print("Skipping generated file:", name) elif nname in listing: md = listing[nname] mtime = os.path.getmtime(fullname) mtime_dt = datetime.datetime(*time.gmtime(mtime)[:6]) size = os.path.getsize(fullname) if (isinstance(md, dropbox.files.FileMetadata) and mtime_dt == md.client_modified and size == md.size): print(name, "is already synced [stats match]") else: print(name, "exists with different stats, downloading") res = download(dbx, folder, subfolder, name) with open(fullname) as f: data = f.read() if res == data: print(name, "is already synced [content match]") else: print(name, "has changed since last sync") if yesno("Refresh %s" % name, False, args): upload(dbx, fullname, folder, subfolder, name, overwrite=True) elif yesno("Upload %s" % name, True, args): upload(dbx, fullname, folder, subfolder, name) # Then choose which subdirectories to traverse. keep = [] for name in dirs: if name.startswith("."): print("Skipping dot directory:", name) elif name.startswith("@") or name.endswith("~"): print("Skipping temporary directory:", name) elif name == "__pycache__": print("Skipping generated directory:", name) elif yesno("Descend into %s" % name, True, args): print("Keeping directory:", name) keep.append(name) else: print("OK, skipping directory:", name) dirs[:] = keep
from __future__ import print_function from peewee import fn, Entity from pathlib2 import Path import subprocess from termcolor import colored from pyjoplin.models import Note, NoteIndex, database as db from pyjoplin.configuration import config from pyjoplin import notification path_repo = Path.home() / 'Backup/joplin' printc = lambda x: print(colored(x, 'cyan')) notes = Note.select().order_by(Note.title) print("Listing empty notes:") for note in notes: if not note.body: printc('Empty: %s %s' % (note.id, note.title)) notefile = '%s.md' % note.id cmd = 'git rev-list HEAD -- %s ' % notefile out = subprocess.check_output(cmd, shell=True, cwd=str(path_repo)) for sha in out.strip().split('\n'): cmd = 'git show %s:%s' % (sha, notefile) note_content = subprocess.check_output(cmd, shell=True, cwd=str(path_repo)) filtered_lines = list() for idx, line in enumerate(note_content.split('\n')): if idx in [0, 1]: continue