async def test_create_get_update_delete_download_resource(get_token): joplin = JoplinApi(token=get_token) properties = {'title': 'test resource'} assert 'title' in properties file_name = 'tests/cactus.png' res = await joplin.create_resource(file_name, **properties) resource_id = json.loads(res.text)['id'] assert res.status_code == 200 res = await joplin.get_resource(resource_id) assert res.status_code == 200 properties = {'title': 'test update resource'} file_name = 'tests/update_cactus.png' res = await joplin.update_resources(resource_id, **properties) assert res.status_code == 200 res = await joplin.download_resources(resource_id) assert res.status_code == 200 res = await joplin.delete_resources(resource_id) assert res.status_code == 200
async def test_get_folders(get_token): joplin = JoplinApi(token=get_token) res = await joplin.get_folders() print(res.json()) assert type(res.json()) is list assert res.status_code == 200
async def test_create_folder(get_token): joplin = JoplinApi(token=get_token) folder = 'TEST FOLDER1' assert type(folder) is str res = await joplin.create_folder(folder=folder) assert type(res.text) is str assert res.status_code == 200
async def test_get_folder(get_token): joplin = JoplinApi(token=get_token) res = await joplin.create_folder(folder='MY FOLDER2') data = res.json() parent_id = data['id'] res = await joplin.get_folder(parent_id) assert type(res.json()) is dict assert res.status_code == 200
async def test_get_folder(get_token): joplin = JoplinApi(token=get_token) res = await joplin.create_folder(folder='MY FOLDER2') res = await joplin.get_folder(res.json()['id']) assert type(res.json()) is dict assert res.status_code == 200 res = await joplin.delete_folder(res.json()['id']) assert res.status_code == 200
async def test_delete_folder(get_token): joplin = JoplinApi(token=get_token) folder = 'TEST FOLDER1' assert type(folder) == str res = await joplin.create_folder(folder=folder) assert res.status_code == 200 res = await joplin.delete_folder(res.json()['id']) assert res.status_code == 200
async def test_search(get_token): """ grab receipt in all note :param get_token: :return: """ joplin = JoplinApi(token=get_token) query = "recette" search = await joplin.search(query) assert type(search.text) is str assert search.status_code == 200
def update(self, request, *args, **kwargs): serializer = FoldersSerializer(data=request.data) if serializer.is_valid(): joplin = JoplinApi(api_type=settings.JOPLIN_API_TYPE, token=settings.JOPLIN_TOKEN) res = joplin.update_folder(request.data['id'], request.data['title']) if res.status_code == 200: return Response({'status folder updated'}) return Response({'status': 'folder not updated {}'.format(res.status_code)}) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
async def test_search_tag(get_token): """ checkout a tag named 'git*' :param get_token: :return: """ joplin = JoplinApi(token=get_token) query = "git*" item_type = 'tag' search = await joplin.search(query, item_type) assert type(search.text) is str assert search.status_code == 200
async def test_search_folder(get_token): """ checkout a folder named 'database' :param get_token: :return: """ joplin = JoplinApi(token=get_token) query = "database" item_type = 'folder' search = await joplin.search(query, item_type) assert type(search.text) is str assert search.status_code == 200
def create(self, request, *args, **kwargs): request.data['nb_notes'] = 0 serializer = TagsSerializer(data=request.data) if serializer.is_valid(): # call the joplin wrapper to create a tag joplin = JoplinApi(api_type=settings.JOPLIN_API_TYPE, token=settings.JOPLIN_TOKEN) res = joplin.create_tag(title=request.data['title']) if res.status_code == 200: return Response({'status': 'tag created'}) return Response({'status': 'tag not created {}'.format(res.status_code)}) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
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
async def test_search_limited_returned_field(get_token): """ grab receipt in all note :param get_token: :return: """ joplin = JoplinApi(token=get_token) query = "recette" search = await joplin.search(query, item_type='note', field_restrictions='title') assert type(search.text) is str assert search.status_code == 200
async def test_create_note(get_token): joplin = JoplinApi(token=get_token) # 1 - create a folder res = await joplin.create_folder(folder='MY FOLDER3') data = res.json() parent_id = data['id'] assert type(parent_id) is str # 2 - create a note with tag body = '# title 1\n ## subtitle \n ```python\npython --version\n```' assert type(body) is str kwargs = {'tags': 'tag1, tag2', 'id': '00a87474082744c1a8515da6aa5792d2'} assert 'id' in kwargs assert re.match('[a-z0-9]{32}', kwargs['id']) # let's set a user created time timestamp = 1545730073 kwargs['user_created_time'] = timestamp res = await joplin.create_note(title="NOTE TEST", body=body, parent_id=parent_id, **kwargs) assert res.status_code == 200 note_id = res.json()['id'] note_user_created_time = res.json()['user_created_time'] assert note_user_created_time == timestamp """ # 3 - update a note with tag body = '# title 1\n ## subtitle \n ```python\npython --version\n```' assert type(body) is str # BUG : joplin does not update TAG yet # @TOFIX once fixed by Joplin kwargs = {'tags': 'tag1, tag2, tag11'} res = await joplin.update_note(note_id=note_id, body=body, title="NOTE TEST", parent_id=parent_id, **kwargs) assert res.status_code == 200 """ # drop the testing data # 4 - get the tag of that note tags_to_delete = await joplin.get_notes_tags(note_id) # 5 - delete the tags for line in tags_to_delete.json(): await joplin.delete_tags_notes(line['id'], note_id) # delete note await joplin.delete_note(note_id) # delete folder await joplin.delete_folder(parent_id)
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 create(self, request, *args, **kwargs): serializer = NotesSerializer(data=request.data) if serializer.is_valid(): data = {'is_todo': request.data.get('is_todo', 0), 'tags': request.data.get('tag', '')} joplin = JoplinApi(api_type=settings.JOPLIN_API_TYPE, token=settings.JOPLIN_TOKEN) res = joplin.create_note(title=request.data['title'], body=request.data['body'], parent_id=request.data['parent_id'], **data) if res.status_code == 200: return Response({'status': 'note created'}) return Response({'status': 'note not created {}'.format(res.status_code)}) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
async def test_create_tag(get_token): """ Create and delete a new tag :param get_token: :return: """ joplin = JoplinApi(token=get_token) title = 'TEST TAG1' res = await joplin.create_tag(title=title) assert type(res.text) is str assert res.status_code == 200 res = await joplin.delete_tag(res.json()['id']) assert res.status_code == 200
async def mailer(): todo_due = [] joplin = JoplinApi( token=config['JOPLIN_CONFIG']['JOPLIN_WEBCLIPPER_TOKEN']) # get all the notes res = await joplin.get_notes() if res.status_code != 200: raise ConnectionError(res.raise_for_status()) # read all the note for note in res.json(): # just get the todo_due ones if note['todo_due'] > 0: todo_due.append({ 'title': note['title'], 'body': note['body'], 'todo_due': note['todo_due'] }) if len(todo_due) > 0: # sort all of them by todo_due date sorted_x = sorted(todo_due, key=lambda i: i['todo_due']) # for each of them, calculate if the date will be due soon for data in sorted_x: """ as Timer() only handles 'secondes', have to convert twice, to calculate current date and diff between now and todo_due """ # 1 - todo_due is in millisecond, so have to calendar timegm in milliseconds now = calendar.timegm(time.gmtime()) * 1000 # 2 - diff in milliseconds, converted in second in_sec = math.ceil((data['todo_due'] - now) / 1000) # tododate already due if in_sec < 0: continue else: # due date found logger.debug("note to send to %s : title %s" % (config['MAIL']['EMAIL_TO'], data['title'])) # let's mail t = Timer(in_sec, _send_msg, args=[data['title'], data['body']]) t.start() t.join()
async def test_create_note(get_token): joplin = JoplinApi(token=get_token) # 1 - create a folder res = await joplin.create_folder(folder='MY FOLDER3') data = res.json() parent_id = data['id'] assert type(parent_id) is str # 2 - create a note with tag body = '# title 1\n ## subtitle \n ```python\npython --version\n```' assert type(body) is str kwargs = {'tags': 'tag1, tag2'} res = await joplin.create_note(title="NOTE TEST", body=body, parent_id=parent_id, **kwargs) assert res.status_code == 200 note_id = res.json()['id'] """ # 3 - update a note with tag body = '# title 1\n ## subtitle \n ```python\npython --version\n```' assert type(body) is str # BUG : joplin does not update TAG yet # @TOFIX once fixed by Joplin kwargs = {'tags': 'tag1, tag2, tag11'} res = await joplin.update_note(note_id=note_id, body=body, title="NOTE TEST", parent_id=parent_id, **kwargs) assert res.status_code == 200 """ # drop the testing data # 4 - get the tag of that note tags_to_delete = await joplin.get_notes_tags(note_id) # 5 - delete the tags for line in tags_to_delete.json(): await joplin.delete_tags_notes(line['id'], note_id) # delete note await joplin.delete_note(note_id) # delete folder await joplin.delete_folder(parent_id)
async def test_add_tag(get_token): """ Add tag to a note and remove it :param get_token: :return: """ joplin = JoplinApi(token=get_token) # 1 - create a folder res = await joplin.create_folder(folder='MY FOLDER') assert res.status_code == 200 data = res.json() parent_id = data['id'] # 2 - create a note body = '# title 1\n ## subtitle \n ```python\npython --version\n```' res = await joplin.create_note(title="NOTE TEST", body=body, parent_id=parent_id) assert res.status_code == 200 note_id = res.json()['id'] # 3 - create a tag title = 'TEST TAG2' res = await joplin.create_tag(title=title) assert res.status_code == 200 tag_id = res.json()['id'] # 4 - add tag to Note res = await joplin.create_tags_notes(note_id=note_id, tag=tag_id) assert res.status_code == 200 # drop the testing data # delete tag await joplin.delete_tag(tag_id) # delete note await joplin.delete_note(note_id) # delete folder await joplin.delete_folder(parent_id)
async def test_get_note(get_token): joplin = JoplinApi(token=get_token) res = await joplin.create_folder(folder='MY FOLDER4') data = res.json() parent_id = data['id'] body = '# title 1\n ## subtitle \n ```python\npython --version\n```' res = await joplin.create_note(title="NOTE TEST2", body=body, parent_id=parent_id) data = res.json() note_id = data['id'] assert type(note_id) is str res = await joplin.get_note(note_id) assert type(res.json()) is dict assert res.status_code == 200 # drop the testing data await joplin.delete_note(note_id) await joplin.delete_folder(parent_id)
async def test_create_note(get_token): joplin = JoplinApi(token=get_token) res = await joplin.create_folder(folder='MY FOLDER3') data = res.json() parent_id = data['id'] assert type(parent_id) is str body = '# title 1\n ## subtitle \n ```python\npython --version\n```' assert type(body) is str kwargs = {'tags': 'tag1, tag2'} res = await joplin.create_note(title="NOTE TEST", body=body, parent_id=parent_id, **kwargs) assert res.status_code == 200 note_id = res.json()['id'] body = '# title 1\n ## subtitle \n ```python\npython --version\n```' assert type(body) is str kwargs = {'tags': 'tag1, tag2, tag11'} res = await joplin.update_note(note_id=note_id, body=body, title="NOTE TEST", parent_id=parent_id, **kwargs) assert res.status_code == 200 # drop the testing data tags_to_delete = await joplin.get_notes_tags(note_id) for line in tags_to_delete.json(): await joplin.delete_tags_notes(line['id'], note_id) await joplin.delete_note(note_id) await joplin.delete_folder(parent_id)
async def test_create_note_no_body(get_token): joplin = JoplinApi(token=get_token) # 1 - create a folder res = await joplin.create_folder(folder='MY FOLDER3') data = res.json() parent_id = data['id'] assert type(parent_id) is str # 2 - create a note with tag body = '' assert type(body) is str kwargs = {'tags': 'tag1, tag2', 'id': '00a87474082744c1a8515da6aa5792d2'} assert 'id' in kwargs assert re.match('[a-z0-9]{32}', kwargs['id']) # let's set a user created time timestamp = 1545730073 kwargs['user_created_time'] = timestamp res = await joplin.create_note(title="NOTE TEST", body='', parent_id=parent_id, **kwargs) assert res.status_code == 200 note_id = res.json()['id'] note_user_created_time = res.json()['user_created_time'] assert note_user_created_time == timestamp # drop the testing data # 4 - get the tag of that note tags_to_delete = await joplin.get_notes_tags(note_id) # 5 - delete the tags for line in tags_to_delete.json(): await joplin.delete_tags_notes(line['id'], note_id) # delete note await joplin.delete_note(note_id) # delete folder await joplin.delete_folder(parent_id)
def setUp(self): token = '5fa5a79fdd9feb428297b32b8ebfb92160b95678d6a05b7823df19212046106e89c71e3674df7b8c60ee47c53469cfe3cb4c8b9a174cde5960fabc9186503ae4' self.joplin = JoplinApi(token=token)
def export_to_joplin(self, token: str, nsx_content: Dict) -> None: """Exports notes to Joplin Parameters ---------- token : str Authorization token. Within the Joplin app, go to Tools/Options/Web Clipper to find token. nsx_content : Dict Python dictionary with keys 'notebooks' and 'notes'. It will contain all relevant extracted information that will be necessary to use for an import into another note taking app. Returns ------- None """ joplin = JoplinApi(token=token) async def create_folder(notebook_title): res = await joplin.create_folder(folder=notebook_title) data = res.json() parent_id = data["id"] assert type(parent_id) is str return parent_id async def create_resource(attachments, content, attachment_path): for index, attachment in enumerate(attachments, start=0): attachment_type = attachment["type"] name = attachment["name"] res = await joplin.create_resource( attachment_path.joinpath(name), **{"title": name} ) resource_id = res.json()["id"] attachments[index]["joplin_resource_id"] = resource_id assert res.status_code == 200 if attachment_type == "image": content = re.sub( r"!\[\]\(.*\)", f"![{name}](:/{resource_id})", content, 1, ) elif attachment_type == "binary": content = f"[{name}](:/{resource_id})\n\n" + content return content async def create_note( joplin_id, note_title, note_content, note_tags, user_created_time, user_updated_time, ): body = note_content assert type(body) is str kwargs = { "tags": note_tags, "user_created_time": user_created_time, "user_updated_time": user_updated_time, } res = await joplin.create_note( title=note_title, body=body, parent_id=joplin_id, **kwargs ) assert res.status_code == 200 for notebook in nsx_content["notebooks"]: joplin_id = asyncio.run(create_folder(notebook["title"])) # filter notes that belong to current notebook filtered_notes = [ note for note in nsx_content["notes"] if note["parent_nb_id"] == notebook["id"] ] num_filtered_notes = len(filtered_notes) for idx, note in enumerate(filtered_notes, start=1): print( f"Writing note {idx}/{num_filtered_notes} in {notebook['title']}: " f"{note['title']}" ) # transform list of tags to string tag = None if note["tag"]: tag = ",".join(note["tag"]) # Create resource, if necessary if note["attachment"]: note["content"] = asyncio.run( create_resource( attachments=note["attachment"], content=note["content"], attachment_path=notebook["media_path"], ) ) asyncio.run( create_note( joplin_id=joplin_id, note_title=note["title"], note_content=note["content"], note_tags=tag, user_created_time=note["ctime"] * 1000, user_updated_time=note["mtime"] * 1000, ) ) if idx % 1000 == 0: seconds = 300 print( f"Sleep for {seconds} seconds in order to not crash Joplin web " "clipper" ) time.sleep(seconds) elif idx % 500 == 0: seconds = 120 print( f"Sleep for {seconds} seconds in order to not crash Joplin web " "clipper" ) time.sleep(seconds)
async def test_get_resources(get_token): joplin = JoplinApi(token=get_token) res = await joplin.get_resources() assert res.status_code == 200
from starlette.templating import Jinja2Templates # uvicorn import uvicorn from joplin_api import JoplinApi templates = Jinja2Templates(directory="templates") # load configuration settings = Config('.env') main_app = Starlette() main_app.debug = settings('JW_DEBUG') joplin = JoplinApi(token=settings('JOPLIN_WEBCLIPPER_TOKEN')) async def tag_for_notes(data): """ alter the original data to add the tag related to the note :param data: :return: """ payload = [] for note in data.json(): tag = await joplin.get_notes_tags(note['id']) new_note = note new_note['tag'] = tag.json() if tag else '' payload.append(new_note) return payload
import asyncio from bs4 import BeautifulSoup from joplin_api import JoplinApi import json import pypandoc import settings import requests joplin = JoplinApi(settings.TOKEN) def subfolder(folder): """ :param folder: subfolder :return: line of the founded children """ for line in folder['children']: if settings.FOLDER == line['title']: return line['id'] async def myfolder(): """ :return: the folder id """ res = await joplin.get_folders() for line in res.json(): if settings.FOLDER == line['title']: return line['id']
import subprocess from urllib.request import urlopen # external lib from joplin_api import JoplinApi from bs4 import BeautifulSoup import pypandoc current_folder = os.path.dirname(__file__) config = configparser.ConfigParser() config.read(os.path.join(current_folder, 'settings.ini')) if not config['JOPLIN_CONFIG']['JOPLIN_WEBCLIPPER_TOKEN']: raise ValueError('Token not provided, edit the settings.ini and set JOPLIN_WEBCLIPPER_TOKEN accordingly') joplin = JoplinApi(token=config['JOPLIN_CONFIG']['JOPLIN_WEBCLIPPER_TOKEN']) logging.basicConfig(filename='jong_toolkit.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') logger = getLogger("joplin_api.api") def grab_note( note): """ grab the webpage of that note :note :dict: dict of the shared note :return title of the grabed page and its body """ # the body contains the URL all alone body = urlopen(note['title']) page = BeautifulSoup(body, 'html.parser') title = page.title.string if page.title else 'no title found'
async def main(options): joplin = JoplinApi(token=options.joplin_token) # Get all notes, folders and resources info. all_folders = (await joplin.get_folders()).json() all_notes = (await joplin.get_notes()).json() folder_id_paths = {} note_to_folder_ids_dict = {} # Set default locations for generated files notes_folder = "notes" resource_folder = "resources" # Give the user an option to parse all the notebooks or a selection. # Rasul TODO: allow to choose multiple notebooks. response = input("Do you want to parse all notebooks? [y/n]") if response == "n": all_folders = choose_folders_to_parse(all_folders) elif response == "y": pass else: print("Incorrect response") # Generate folders for chosen notebooks for folder in all_folders: create_folders(folder, notes_folder, folder_id_paths) # Generate a dictionary with notes ids as keys and folder ids as values for k in folder_id_paths: notes = (await joplin.get_folders_notes(k)).json() for note in notes: note_to_folder_ids_dict[note["id"]] = k # Create a new list of notes that appear in selected folders only filtered_all_notes = [] for note in all_notes: if note["id"] in note_to_folder_ids_dict: filtered_all_notes.append(note) # Get a dictionary with note titles and ids notes_dict = generate_dict_with_all_notes_and_ids(filtered_all_notes) for note in filtered_all_notes: all_resources = [] resources = (await joplin.get_notes_resources(note["id"])).json() for resource in resources: all_resources.append(resource) response = await download_resource( joplin, resource, os.path.join(notes_folder, resource_folder)) resources_types_dict = generate_dict_with_all_resources(all_resources) resources_names_dict = generate_dict_with_all_resources_filenames( all_resources) for note in filtered_all_notes: folder_path = folder_id_paths[note_to_folder_ids_dict[note["id"]]] file_path = os.path.join(folder_path, remove_spaces(note["title"])) try: note["body"] = search_and_replace_joplin_note_links( note["body"], notes_dict) except KeyError: note["body"] = search_and_replace_joplin_resource_links( note["body"], resources_types_dict, resources_names_dict, resource_folder, ) note["category"] = os.path.basename(os.path.normpath(folder_path)) generate_note(file_path, note)