Skip to content

Latest commit

 

History

History
316 lines (207 loc) · 10.4 KB

README.md

File metadata and controls

316 lines (207 loc) · 10.4 KB

joppy

Python interface for the Joplin data API.

build lint tests codecov

https://img.shields.io/badge/Joplin-2.14.17-blueviolet Python version

💻 Installation

From pypi:

pip install joppy

From source:

git clone https://github.com/marph91/joppy.git
cd joppy
pip install .

🔧 Usage

General function description

  • add_<type>(): Create a new element.
  • delete_<type>(): Delete an element by ID.
  • get_<type>(): Get an element by ID.
  • get_all_<type>(): Get all elements of a kind.
  • modify_<type>(): Modify an elements property by ID.
  • search_all(): Search elements using joplins search engine.

For details, consult the implementation, joplin documentation or create an issue.

💡 Example snippets

Start joplin and get your API token. Click to expand the examples.

Get all notes
from joppy.api import Api

# Create a new Api instance.
api = Api(token=YOUR_TOKEN)

# Get all notes. Note that this method calls get_notes() multiple times to assemble the unpaginated result.
notes = api.get_all_notes()
Add a tag to a note
from joppy.api import Api

# Create a new Api instance.

api = Api(token=YOUR_TOKEN)

# Add a notebook.

notebook_id = api.add_notebook(title="My first notebook")

# Add a note in the previously created notebook.

note_id = api.add_note(title="My first note", body="With some content", parent_id=notebook_id)

# Add a tag, that is not yet attached to a note.

tag_id = api.add_tag(title="introduction")

# Link the tag to the note.

api.add_tag_to_note(tag_id=tag_id, note_id=note_id)
Add a resource to a note
from joppy.api import Api
from joppy import tools

# Create a new Api instance.
api = Api(token=YOUR_TOKEN)

# Add a notebook.
notebook_id = api.add_notebook(title="My first notebook")

# Option 1: Add a note with an image data URL. This works only for images.
image_data = tools.encode_base64("path/to/image.png")
api.add_note(
    title="My first note",
    image_data_url=f"data:image/png;base64,{image_data}",
)

# Option 2: Create note and resource separately. Link them later. This works for arbitrary attachments.
note_id = api.add_note(title="My second note")
resource_id = api.add_resource(filename="path/to/image.png", title="My first resource")
api.add_resource_to_note(resource_id=resource_id, note_id=note_id)
Bulk remove tags

Inspired by https://discourse.joplinapp.org/t/bulk-tag-delete-python-script/5497/1.

import re

from joppy.api import Api

# Create a new Api instance.
api = Api(token=YOUR_TOKEN)

# Iterate through all tags.
for tag in api.get_all_tags():

    # Delete all tags that match the regex. I. e. start with "!".
    if re.search("^!", tag.title) is not None:
        api.delete_tag(tag.id)
Remove unused tags

Reference: https://discourse.joplinapp.org/t/prune-empty-tags-from-web-clipper/36194

from joppy.api import Api

# Create a new Api instance.
api = Api(token=YOUR_TOKEN)

for tag in api.get_all_tags():
    notes_for_tag = api.get_all_notes(tag_id=tag.id)
    if len(notes_for_tag) == 0:
        print("Deleting tag:", tag.title)
        api.delete_tag(tag.id)
Remove spaces from tags

Reference: https://www.reddit.com/r/joplinapp/comments/pozric/batch_remove_spaces_from_all_tags/

import re

from joppy.api import Api

# Create a new Api instance.
api = Api(token=YOUR_TOKEN)

# Define the conversion function.
def to_camel_case(name: str) -> str:
    name = re.sub(r"(_|-)+", " ", name).title().replace(" ", "")
    return "".join([name[0].lower(), name[1:]])

# Iterate through all tags and apply the conversion.
for tag in api.get_all_tags():
    api.modify_tag(id_=tag.id, title=to_camel_case(tag.title))
Remove orphaned resources

Inspired by https://discourse.joplinapp.org/t/joplin-vacuum-a-python-script-to-remove-orphaned-resources/19742. Note: The note history is not considered. See: https://discourse.joplinapp.org/t/joplin-vacuum-a-python-script-to-remove-orphaned-resources/19742/13.

import re

from joppy.api import Api

# Create a new Api instance.
api = Api(token=YOUR_TOKEN)

# Getting the referenced resource directly doesn't work:
# https://github.com/laurent22/joplin/issues/4535
# So we have to find the referenced resources by regex.

# Iterate through all notes and find the referenced resources.
referenced_resources = set()
for note in api.get_all_notes(fields="id,body"):
    matches = re.findall(r"\[.*\]\(:.*\/([A-Za-z0-9]{32})\)", note.body)
    referenced_resources.update(matches)

assert len(referenced_resources) > 0, "sanity check"

for resource in api.get_all_resources():
    if resource.id not in referenced_resources:
        print("Deleting resource:", resource.title)
        api.delete_resource(resource.id)

For more usage examples, check the example scripts or tests.

📰 Examples

Before using joppy, you should check the Joplin plugins. They are probably more convenient. However, if you need a new feature or just want to code in python, you can use joppy.

Apps

App Description
jimmy A tool to import your notes to Joplin
joplin-sticky-notes Stick your Joplin notes to the desktop
joplin-vieweb A simple web viewer for Joplin

Scripts

Script Description
custom_export.py Export resources next to notes, instead of a separate folder.
note_export.py Export notes to any format supported by pandoc.
note_stats.py Get some simple statistics about your notes, based on nltk.
note_tree_export.py Joplin only supports PDF export of a single note. This script allows to export one, multiple or all notebooks to PDF or TXT.
visualize_note_locations.py Visualize the locations of your notes.
joplin-ui-tests System tests for the joplin desktop app. Based on selenium.

☀️ Tests

To run the tests, some additional system packages and python modules are needed. After installing them, just run:

python -m unittest

It's possible to configure the test run via some environment variables:

  • SLOW_TESTS: Set this variable to run the slow tests. Default not set.
  • API_TOKEN: Set this variable if there is already a joplin instance running. Don't use your default joplin profile! By default, a joplin instance is started inside xvfb. This takes some time, but works for CI.

📖 Changelog

0.2.3

  • Don't use the root logger for logging.
  • Add support for revisions.

0.2.2

  • Fix adding non-image ressources (#24).
  • Cast markup_language to an appropriate enum type.
  • Add changelog.

0.2.1

  • Fix PDF output example (#19).
  • ⚠️ Drop tests for python 3.6, since it's EOL. It may still work.
  • Fix the type of todo_completed and todo_due. They are a unix timestamp, not a bool.

0.1.1

  • Add typing support to the pypi module.

0.1.0

  • Use a requests session for speedup (#15).
  • ⚠️ Convert the API responses to data objects (#17). Main difference is to use note.id instead of note["id"] for example.

0.0.7

  • Fix getting the binary resource file (#13).

0.0.6

  • Add convenience method for deleting all notes.
  • Add example scripts.

0.0.5

  • Fix package publishing workflow.

0.0.4

  • Add support for python 3.6 and 3.7.

0.0.3

  • Fix search with special characters (#5).
  • Remove arbitrary arguments from the internal base requests, since they aren't needed and may cause bugs.

0.0.2

  • CI and test improvements.
  • Move complete setup to setup.cfg.

0.0.1

  • Initial release.