-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
This page describes how to import notes from QOwnNotes to Joplin. | ||
|
||
## General Information | ||
|
||
- [Website](https://www.qownnotes.org/) | ||
- Typical extension: Folder with `.md` files (notes), a `media` subfolder (containing resources) and a `notes.sqlite` file (containing tags) | ||
|
||
## Instructions | ||
|
||
1. [Install jimmy](../index.md#installation) | ||
2. Import to Joplin. Example: `jimmy-cli-linux qownnotes_folder/ --format qownnotes` | ||
|
||
## Import Structure | ||
|
||
- Markdown style links (`[Link to Markdown Cheatsheet](Markdown Cheatsheet.md)`) and QOwnNotes style links (`<Markdown Cheatsheet.md>`) as described [here](https://www.qownnotes.org/getting-started/markdown.html#links) are converted. | ||
|
||
## Known Limitations | ||
|
||
- Encrypted notes aren't converted. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
"""Convert QOwnNotes notes to the intermediate format.""" | ||
|
||
from collections import defaultdict | ||
from pathlib import Path | ||
import re | ||
import sqlite3 | ||
from urllib.parse import unquote | ||
|
||
import common | ||
import converter | ||
import intermediate_format as imf | ||
|
||
|
||
class Converter(converter.BaseConverter): | ||
accept_folder = True | ||
|
||
qownnote_link_re = re.compile(r"<(.*?.md)>") | ||
|
||
def handle_markdown_links(self, body: str) -> tuple[list, list]: | ||
# markdown style links | ||
note_links = [] | ||
resources = [] | ||
for link in common.get_markdown_links(body): | ||
if link.is_web_link or link.is_mail_link: | ||
continue # keep the original links | ||
if link.url.endswith(".md"): | ||
# internal link | ||
note_links.append( | ||
imf.NoteLink(str(link), Path(unquote(link.url)).stem, link.text) | ||
) | ||
else: | ||
# resource | ||
resources.append( | ||
imf.Resource(self.root_path / link.url, str(link), link.text) | ||
) | ||
|
||
# qownnote style links | ||
# https://www.qownnotes.org/getting-started/markdown.html#internal-links | ||
for link in self.qownnote_link_re.findall(body): | ||
note_links.append(imf.NoteLink(f"<{link}>", Path(unquote(link)).stem, link)) | ||
|
||
return resources, note_links | ||
|
||
def parse_tags(self): | ||
"""Parse tags from the sqlite DB.""" | ||
assert self.root_path is not None | ||
db_file = self.root_path / "notes.sqlite" | ||
if not db_file.is_file(): | ||
self.logger.debug(f"Couldn't find {db_file}") | ||
return {} | ||
|
||
tag_id_name_map = {} | ||
note_tag_map = defaultdict(list) | ||
conn = sqlite3.connect(db_file) | ||
try: | ||
cur = conn.cursor() | ||
|
||
# check version | ||
cur.execute("SELECT * FROM appData") | ||
for name, value in cur.fetchall(): | ||
if name == "database_version" and value != "15": | ||
self.logger.warning(f"Untested DB version {value}") | ||
|
||
# get tags | ||
cur.execute("SELECT * FROM tag") | ||
for tag_id, tag_name, *_ in cur.fetchall(): | ||
tag_id_name_map[tag_id] = tag_name | ||
|
||
# get related notes and assign the tags | ||
cur.execute("SELECT * FROM noteTagLink") | ||
for _, tag_id, note_id, *_ in cur.fetchall(): | ||
note_tag_map[note_id].append( | ||
imf.Tag({"title": tag_id_name_map[tag_id]}, tag_id) | ||
) | ||
except sqlite3.OperationalError as exc: | ||
self.logger.warning("Parsing the tag DB failed.") | ||
self.logger.debug(exc, exc_info=True) | ||
return {} | ||
finally: | ||
conn.close() | ||
return note_tag_map | ||
|
||
def convert(self, file_or_folder: Path): | ||
self.root_path = file_or_folder | ||
|
||
note_tag_map = self.parse_tags() | ||
|
||
for note_qownnotes in file_or_folder.glob("*.md"): | ||
note_body = note_qownnotes.read_text() | ||
|
||
resources, note_links = self.handle_markdown_links(note_body) | ||
note_joplin = imf.Note( | ||
{ | ||
"title": note_qownnotes.stem, | ||
"body": "\n".join(note_body.split("\n")[3:]), # TODO: make robust | ||
**common.get_ctime_mtime_ms(note_qownnotes), | ||
"source_application": self.format, | ||
}, | ||
tags=note_tag_map.get(note_qownnotes.stem, []), | ||
resources=resources, | ||
note_links=note_links, | ||
) | ||
self.root_notebook.child_notes.append(note_joplin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters