Skip to content

Commit 03d551c

Browse files
committed
improve qownnotes compatibility
see pbek/QOwnNotes#3133 - allow for separate folders for images and other attachments - allow generating front matter with space separated tags
1 parent 8000330 commit 03d551c

File tree

5 files changed

+76
-28
lines changed

5 files changed

+76
-28
lines changed

docs/import_instructions.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ Markdown files can be imported to Obsidian as described [on the website](https:/
3131

3232
## QOwnNotes
3333

34-
1. Use the argument `--local-resource-folder media`. This is not required, but aligns with the internal structure of QOwnNotes.
35-
2. Copy the root folder to you note folder or open it as a new note folder.
36-
3. Enable subfolders by `Note -> Settings -> Use note subfolders`.
34+
1. Use the arguments `--local-resource-folder attachments --local-image-folder media`. This is not required, but aligns with the internal structure of QOwnNotes.
35+
2. Copy the root folder to your note folder or open it as a new note folder.
36+
3. If you have subfolders, enable `Note -> Settings -> Use note subfolders`.
37+
4. If you want to convert tags:
38+
1. Use the argument `--frontmatter qownnotes`
39+
2. Detect the front matter tags by installing the [epsilon-notes-tags](https://github.com/qownnotes/scripts/tree/master/epsilon-notes-tags) or [`yaml-nested-tags`](https://github.com/qownnotes/scripts/tree/master/yaml-nested-tags) plugin.
40+
41+
A complete command could look like `jimmy-cli-linux takeout.zip --format google_keep --frontmatter qownnotes --local-resource-folder attachments --local-image-folder media`.
3742

3843
## Notion
3944

src/importer.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,18 @@ def __init__(self, progress_bars, config):
140140
if config.local_resource_folder == Path(".")
141141
else Path(safe_path(config.local_resource_folder))
142142
)
143+
self.local_image_folder = (
144+
None
145+
if config.local_image_folder is None
146+
else Path(safe_path(config.local_image_folder))
147+
)
143148
# reference id - path (new id)
144149
self.note_id_map: dict[str, Path] = {}
145150
self.progress_bars = progress_bars
146151

147152
def import_resources(self, note: imf.Note):
153+
# pylint: disable=too-many-branches
154+
# TODO
148155
assert note.path is not None
149156
assert self.root_path is not None
150157
for resource in note.resources:
@@ -153,12 +160,16 @@ def import_resources(self, note: imf.Note):
153160

154161
# determine new resource path
155162
if self.global_resource_folder is None:
163+
if self.local_image_folder is not None and resource.is_image:
164+
target_folder = self.local_image_folder
165+
else:
166+
target_folder = self.local_resource_folder
156167
# local resources (next to the markdown files)
157-
resource_folder = note.path.parent / self.local_resource_folder
168+
resource_folder = note.path.parent / target_folder
158169
# TODO: done for each resource in each note
159-
if self.local_resource_folder != Path("."):
170+
if target_folder != Path("."):
160171
resource_folder.mkdir(
161-
exist_ok=True, parents=len(self.local_resource_folder.parts) > 1
172+
exist_ok=True, parents=len(target_folder.parts) > 1
162173
)
163174
resource.path = resource_folder / safe_path(resource.filename.name)
164175
else:
@@ -255,6 +266,17 @@ def write_note(self, note: imf.Note):
255266
frontmatter.dump(post, note.path)
256267
else:
257268
note.path.write_text(note.body, encoding="utf-8")
269+
case "qownnotes":
270+
# space separated tags, as supported by:
271+
# - https://github.com/qownnotes/scripts/tree/master/epsilon-notes-tags
272+
# - https://github.com/qownnotes/scripts/tree/master/yaml-nested-tags
273+
if note.tags:
274+
post = frontmatter.Post(
275+
note.body, tags=" ".join([tag.title for tag in note.tags])
276+
)
277+
frontmatter.dump(post, note.path)
278+
else:
279+
note.path.write_text(note.body, encoding="utf-8")
258280
case _:
259281
note.path.write_text(note.body, encoding="utf-8")
260282

src/jimmy_cli.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
LOGGER = logging.getLogger("jimmy")
1313

1414

15-
def relative_path(path: str | Path) -> Path:
15+
def relative_path(path: str | Path | None) -> Path | None:
1616
"""
1717
Checks if a path is relative.
1818
@@ -31,6 +31,8 @@ def relative_path(path: str | Path) -> Path:
3131
>>> str(relative_path("./a"))
3232
'a'
3333
"""
34+
if path is None:
35+
return None
3436
# https://stackoverflow.com/a/37472037
3537
path_to_check = Path(path)
3638
if path_to_check.is_absolute():
@@ -59,7 +61,7 @@ def main():
5961
parser.add_argument(
6062
"--frontmatter",
6163
default=None,
62-
choices=(None, "all", "joplin", "obsidian"),
64+
choices=(None, "all", "joplin", "obsidian", "qownnotes"),
6365
help="Frontmatter type.",
6466
)
6567
parser.add_argument(
@@ -80,6 +82,13 @@ def main():
8082
"Relative to the location of the corresponding note.",
8183
default=Path("."), # next to the note
8284
)
85+
parser.add_argument(
86+
"--local-image-folder",
87+
type=relative_path,
88+
help="The folder for images. Works only together with "
89+
"--local-resource-folder. "
90+
"Relative to the location of the corresponding note.",
91+
)
8392
parser.add_argument(
8493
"--print-tree",
8594
action="store_true",

test/data

Submodule data updated 76 files

test/test_convert.py

+31-19
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def setUp(self):
3030
frontmatter=None,
3131
global_resource_folder=None,
3232
local_resource_folder=Path("."),
33+
local_image_folder=None,
3334
print_tree=False,
3435
exclude_notes=None,
3536
exclude_notes_with_tags=None,
@@ -222,37 +223,48 @@ def test_frontmatter(self, frontmatter):
222223

223224
@parameterized.expand(
224225
[
225-
["global_resource_folder", Path("images")],
226-
["global_resource_folder", Path("ima:ges")], # forbidden char
227-
["global_resource_folder", Path("../images")], # outside root dir
228-
["local_resource_folder", Path(".")], # default
229-
["local_resource_folder", Path("images")],
230-
["local_resource_folder", Path("ima:ges")], # forbidden char
226+
["global", {"global_resource_folder": Path("res")}],
227+
[
228+
"global_forbidden",
229+
{"global_resource_folder": Path("r:es")},
230+
],
231+
[
232+
"global_outside",
233+
{"global_resource_folder": Path("../res")},
234+
],
235+
["local_default", {"local_resource_folder": Path(".")}], # default
236+
["local", {"local_resource_folder": Path("res")}],
237+
[
238+
"local_forbidden",
239+
{"local_resource_folder": Path("re:s")},
240+
],
241+
[
242+
"local_splitted",
243+
{
244+
"local_resource_folder": Path("attachments"),
245+
"local_image_folder": Path("media"),
246+
},
247+
],
231248
]
232249
)
233-
def test_attachment_folder(self, folder_option, value):
250+
def test_attachment_folder(self, name, folder_options):
234251
"""Test the attachment folders."""
235252

236-
test_data = [Path("test/data/test_data/joplin/test_1/29_04_2024.jex")]
237-
value_safe = str(value).replace("/", "_").replace(".", "_").replace(":", "_")
238-
test_data_output = (
239-
Path("tmp_output/attachment_folder") / f"{folder_option}_{value_safe}"
240-
)
253+
test_data = [Path("test/data/test_data/obsidian/test_1")]
254+
test_data_output = Path(f"tmp_output/attachment_folder/{name}")
241255
shutil.rmtree(test_data_output, ignore_errors=True)
242256
# separate folder for each input
243-
reference_data = (
244-
Path("test/data/reference_data/attachment_folder")
245-
/ f"{folder_option}_{value_safe}"
246-
)
257+
reference_data = Path(f"test/data/reference_data/attachment_folder/{name}")
247258

248259
self.config.input = test_data
249-
self.config.format = "joplin"
260+
self.config.format = "obsidian"
250261
self.config.output_folder = test_data_output
251-
setattr(self.config, folder_option, value)
262+
for option, value in folder_options.items():
263+
setattr(self.config, option, value)
252264
jimmy.jimmy(self.config)
253265

254266
self.assert_dir_trees_equal(test_data_output, reference_data)
255-
if folder_option == "global_resource_folder" and str(value).startswith(".."):
267+
if name == "global_outside":
256268
# outside root dir -> verify separately
257269
self.assert_dir_trees_equal(
258270
test_data_output / value, reference_data / value

0 commit comments

Comments
 (0)