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)
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_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_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
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_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_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_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_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
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_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
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)
class ServiceJoplin(ServicesMgr): def __init__(self, token=None, **kwargs): super(ServiceJoplin, self).__init__(token, **kwargs) self.token = settings.TH_JOPLIN_TOKEN self.user = kwargs.get('user') self.joplin = JoplinApi(token=self.token) def read_data(self, **kwargs): """ get the data from the service as the pocket service does not have any date in its API linked to the note, add the triggered date to the dict data thus the service will be triggered when data will be found :param kwargs: contain keyword args : trigger_id at least :type kwargs: dict :rtype: list """ trigger_id = kwargs.get('trigger_id') data = list() cache.set('th_joplin_' + str(trigger_id), data) def save_data(self, trigger_id, **data): """ let's save the data :param trigger_id: trigger ID from which to save data :param data: the data to check to be used and save :type trigger_id: int :type data: dict :return: the status of the save statement :rtype: boolean """ from th_joplin.models import Joplin status = False data['output_format'] = 'markdown_github' title, content = super(ServiceJoplin, self).save_data(trigger_id, **data) # get the data of this trigger trigger = Joplin.objects.get(trigger_id=trigger_id) status = self.joplin.create_note(title=title, body=content, parent_id=trigger.folder).status_code if status == 200: status = True return status
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_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)
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)
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)
def __init__(self, token=None, **kwargs): super(ServiceJoplin, self).__init__(token, **kwargs) self.token = settings.TH_JOPLIN_TOKEN self.user = kwargs.get('user') self.joplin = JoplinApi(token=self.token)
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)
async def test_get_resources(get_token): joplin = JoplinApi(token=get_token) res = await joplin.get_resources() assert res.status_code == 200