def get_folders(self): """ :return: """ joplin = JoplinApi(token=settings.TH_JOPLIN_TOKEN) folders = joplin.get_folders().json() choices = [] for folder in folders: line = (folder['id'], folder['title']) choices.append(line) return choices
class TestJoplinApi(unittest.TestCase): def setUp(self): token = '5fa5a79fdd9feb428297b32b8ebfb92160b95678d6a05b7823df19212046106e89c71e3674df7b8c60ee47c53469cfe3cb4c8b9a174cde5960fabc9186503ae4' self.joplin = JoplinApi(token=token) def test_create_folder(self): folder = 'TEST FOLDER1' self.assertIs(type(folder), str) res = self.joplin.create_folder(folder=folder) self.assertTrue(res.status_code == 200) def test_get_folders(self): res = self.joplin.get_folders() self.assertTrue(res.status_code == 200) self.assertIsInstance(res.json(), list) def test_get_folder(self): res = self.joplin.create_folder(folder='MY FOLDER2') data = res.json() parent_id = data['id'] res = self.joplin.get_folder(parent_id) self.assertTrue(res.status_code == 200) self.assertIsInstance(res.json(), dict) def test_create_note(self): res = self.joplin.create_folder(folder='MY FOLDER3') data = res.json() parent_id = data['id'] self.assertIs(type(parent_id), str) body = '# title 1\n ## subtitle \n ```python\npython --version\n```' self.assertIs(type(body), str) kwargs = {'tags': 'tag1, tag2'} res = self.joplin.create_note(title="NOTE TEST", body=body, parent_id=parent_id, **kwargs) self.assertTrue(res.status_code == 200) note_id = res.json()['id'] body = '# title 1\n ## subtitle \n ```python\npython --version\n```' self.assertIs(type(body), str) kwargs = {'tags': 'tag1, tag2, tag11'} res = self.joplin.update_note(note_id=note_id, body=body, title="NOTE TEST", parent_id=parent_id, **kwargs) self.assertTrue(res.status_code == 200) def test_get_notes(self): res = self.joplin.get_notes() self.assertTrue(res.status_code == 200) self.assertIsInstance(res.json(), list) def test_get_note(self): res = self.joplin.create_folder(folder='MY FOLDER4') data = res.json() parent_id = data['id'] body = '# title 1\n ## subtitle \n ```python\npython --version\n```' res = self.joplin.create_note(title="NOTE TEST2", body=body, parent_id=parent_id) data = res.json() note_id = data['id'] self.assertIs(type(note_id), str) res = self.joplin.get_note(note_id) self.assertTrue(res.status_code == 200) self.assertIsInstance(res.json(), dict) def test_get_tags(self): res = self.joplin.get_tags() self.assertTrue(res.status_code == 200) self.assertIsInstance(res.json(), list)
class JoplinHelper(): def __init__(self): try: self.orphanes = [] conf_file_abspath = os.path.join(os.path.dirname(__file__), 'joplintool.conf') config = configparser.ConfigParser() if config.read(conf_file_abspath) == []: with open(conf_file_abspath, 'w') as f: f.writelines([ '[paths]\n', 'path_joplin = c:\_office_\Joplin\n', 'path_Dropbox = f:\_Archive_\Dropbox\Apps\Joplin\n', '\n[misc]\n', 'token = xxxxxxxxxxxxxxxxxxxxxxxxxxxx #insert token here\n' ]) raise BaseException( 'could not find joplintool.conf\ncreating new config file\nplease edit paths etc. in config file' ) self.path_joplin = config['paths']['path_joplin'].strip( '\'\"') # strip '' and "" from strings self.path_Dropbox = config['paths']['path_dropbox'].strip('\'\"') self.path_db = os.path.join(self.path_joplin, 'JoplinProfile', 'database.sqlite') self.path_resources = os.path.join(self.path_joplin, 'JoplinProfile', 'resources') if not os.path.exists(self.path_db): raise BaseException('wrongs paths ... check joplintool.conf') self.joplin = JoplinApi(token=config['misc']['token']) asyncio.run(self.joplin.ping( )) # raises exception if it cannot connect to web clipper self.cursor = sqlite3.connect(self.path_db).cursor() except ConnectionRefusedError: print( 'cannot connect to web clipper....\nis joplin running ?\nalso check token in conf file' ) exit() except BaseException as err: print(err) print('exiting...') exit() def recurse_folders(self, folderlist=None, recursion_level=0): def show_notes(d, recursion_level, folder_id): res = asyncio.run(self.joplin.get_folders_notes(folder_id)) for d in res.json(): print('{} {} \t({})'.format(' ' * 4 * recursion_level, d['title'], d['id'])) if folderlist == None: res = asyncio.run(self.joplin.get_folders()) folderlist = res.json() for d in folderlist: print('\n{} {} \t({})'.format('-' * 4 * recursion_level, d['title'], d['id'])) show_notes(d, recursion_level, d['id']) if 'children' in d: recursion_level += 1 self.recurse_folders(d['children'], recursion_level=recursion_level) def sql_get_orphanes(self, verbose=False): orphanes = [] cmd = ''' SELECT resource_id FROM note_resources WHERE is_associated = 0 and resource_id not in (SELECT resource_id FROM note_resources WHERE is_associated = 1) ORDER BY resource_id ''' #group by resource_id having max(last_seen_time) < strftime('%s','now','-${KEEP} days')*1000" >$TMPFILE # time not considered... #not used if verbose: print('orphanes from sql database:') for l in self.cursor.execute(cmd): id = l[0] orphanes.append(id) if verbose: print(id) return orphanes def info(self): notes = len(self.sql_get_notes()) folders = len(self.sql_get_folders()) images = len(self.sql_get_resources()) tags = len(self.sql_get_tags()) files_resource = [ f for f in os.listdir(self.path_resources) if os.path.isfile(os.path.join(self.path_resources, f)) ] len_files_in_resourcedir = sum([ os.path.getsize(os.path.join(self.path_resources, f)) for f in files_resource ]) # get sum of all filesizes print('\ninfo (data in database):') print('-----------------------') print('notes: ', notes) print('folders: ', folders) print('images: {} ({:,} bytes)'.format(images, len_files_in_resourcedir, len(files_resource))) print('tags: ', tags) print('total: ', notes + folders + images + tags) #print('\ntotal size of images in resourcedir: {:,} bytes in {} images'.format(len_files_in_resourcedir, len(files_resource))) def check_resources(self, do_delete=False): # delete orphaned files from resource self.orphanes = self.sql_get_orphanes() print( '\nchecking for orphanes (files in resourcedir not assocciated with any note):' ) print( '-------------------------------------------------------------------------' ) if self.orphanes == []: print('found none ... good') else: for fname in self.orphanes: print('not_associated: ', (fname)) if do_delete: self.del_orphane(fname) print( '\nchecking for additional files in resourcedir which are not in the database:' ) # not sure if is of concern ... files_resource = [ f for f in os.listdir(self.path_resources) if os.path.isfile(os.path.join(self.path_resources, f)) ] files_db = self.sql_get_resources() files_additional = [f for f in files_resource if f not in files_db] if files_additional == []: print('found none ... good') else: for f in files_additional: print(f) if do_delete: os.remove(os.path.join(self.path_resources, f)) print('deleted...') def del_orphane(self, fname): id = os.path.splitext(fname)[0] res = asyncio.run(self.joplin.delete_resources(id)) print('deleting: {} {}'.format(fname, res)) if str(res) == '<Response [403 Forbidden]>': print('cannot delete ... check token in joplintool.conf') def sql_align_db(self): cmd = 'VACUUM;' self.cursor.execute(cmd) print('database has been aligned...') def sql_get_notes(self, verbose=False): cmd = 'SELECT id, title FROM notes' if verbose: print('\nnotes:') notes = [] for l in self.cursor.execute(cmd): id, title = l[0], l[1] notes.append(id) if verbose: print(id, '\t', title) if verbose and notes == []: print('no notes found ...') return notes def sql_get_folders(self, verbose=False): cmd = ' SELECT id FROM folders' if verbose: print('\nfolders:') folders = [] for l in self.cursor.execute(cmd): id = l[0] folders.append(id) if verbose: print(id) return folders def sql_get_tags(self, verbose=False): cmd = ' SELECT id, title FROM tags' if verbose: print('\ntags:') tags = [] for l in self.cursor.execute(cmd): id, title = (l) tags.append(id) if verbose: print(id, title) return tags def sql_get_tagtitle(self, id): title = '' cmd = ' SELECT id, title FROM tags WHERE id = "{}"'.format(id) for l in self.cursor.execute(cmd): id, title = (l) return title def sql_get_resource_title(self, id): size, title = '', '' cmd = 'SELECT id, title FROM resources WHERE id = "{}"'.format(id) for l in self.cursor.execute(cmd): id, title = (l) return title def sql_get_resources(self, verbose=False): cmd = 'SELECT id, file_extension from resources ORDER BY id' if verbose: print('\nresources in sql database:') resources = [] for l in self.cursor.execute(cmd): id, ext = l[0], l[1] fname = id + '.' + ext resources.append(fname) if verbose: print(fname) return resources def sql_get_foldertitle(self, id): title = '' cmd = 'SELECT id, title FROM folders WHERE id = "{}"'.format(id) for l in self.cursor.execute(cmd): id, title = (l) return title def sql_get_notetitle(self, id): title = '' cmd = 'SELECT id, title FROM notes WHERE id = "{}"'.format(id) for l in self.cursor.execute(cmd): id, title = (l) return title def check_dropbox(self, do_delete=False): # extract filetype fom md files ''' checks if md files exist in database and resource dir''' def fmtprint(str1='', str2='', str3=''): # formatted output to 80 width console print('{:<15.13}{:<35.33}{:.28}'.format(str1, str2, str3)) def get_type(fname): typ = None with open(fname, encoding='utf-8') as f: while True: l = f.readline() if l == '': break if l.startswith('type_:'): typ = int(l.lstrip('type_:')) break return typ notes = self.sql_get_notes() folders = self.sql_get_folders() tags = self.sql_get_tags() notfound = 0 print( '\nchecking dropbox(if notes, folder, images exist in database / resourcedir):' ) print( '---------------------------------------------------------------------------' ) n, special = 0, 0 notecnt, foldercnt, imgcnt, tagcnt = 0, 0, 0, 0 for f in os.listdir(self.path_Dropbox): id, e = os.path.splitext(f) if e == '.md': n += 1 typ = get_type(os.path.join(self.path_Dropbox, f)) if typ == 1: if id in notes: notecnt += 1 fmtprint('valid note', id, self.sql_get_notetitle(id)) else: fmtprint('unlinked note', id, self.sql_get_notetitle(id)) notfound += 1 elif typ == 2: if id in folders: foldercnt += 1 fmtprint('valid folder', id, self.sql_get_foldertitle(id)) else: fmtprint('unlinked folder', id, self.sql_get_foldertitle(id)) notfound += 1 elif typ == 4: if self.sql_get_resource_title(id) != '': imgcnt += 1 fmtprint('valid image', id, self.sql_get_resource_title(id)) else: fmtprint('unlinked image', id, self.sql_get_resource_title(id)) notfound += 1 elif typ == 5: if id in tags: tagcnt += 1 fmtprint('valid tag', id, self.sql_get_tagtitle(id)) else: fmtprint('unlinked tag', id, self.sql_get_tagtitle(id)) notfound += 1 elif typ in (6, 7, 8, 9, 10, 11, 12, 13, 14): n -= 1 special += 1 fmtprint('type_' + str(typ), id) else: fmtprint('# not valid #', id) special += 1 notfound += 1 if do_delete: os.remove(os.path.join(self.path_Dropbox, f)) print('removed: ', f) notfound -= 1 n -= notfound print('\nnotes: ', notecnt) print('folders: ', foldercnt) print('images: ', imgcnt) print('tags: ', tagcnt) print('total: ', n) print('\nnotfound: ', notfound) print('special: ', special) if notfound == 0: print('\nfiles in dropbox seem to be OK ...') else: print('\nunlinked files in Dropbox ... please remove')