This repository has been archived by the owner on Jan 26, 2024. It is now read-only.
forked from arnaudlimbourg/dayone-to-obsidian
-
Notifications
You must be signed in to change notification settings - Fork 8
/
import.py
165 lines (148 loc) · 5.67 KB
/
import.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# pylint: disable=too-many-arguments,line-too-long,no-value-for-parameter
"""import.py"""
import pathlib
import click
import yaml as Yaml
from rich_utils import info_msg, progress, verbose_msg, warn_msg
from utils import process_journal
@click.command()
@click.argument(
"folder",
type=click.Path(exists=True, file_okay=False),
)
@click.option(
"--config",
"-c",
"config_file",
type=click.Path(dir_okay=False, file_okay=True, path_type=pathlib.Path),
help="A YAML configuration file. Check the README for its syntax/options",
)
@click.option(
"--vault-directory",
type=click.Path(exists=True, file_okay=False, dir_okay=True),
help="Obsidian vault directory where you will copy the exported Markdown files. "
"If passed, the script will skip exporting already existent files. "
"PLEASE NOTE: the script IS NOT performing any copy operation, which must be done manually",
)
@click.option(
"--force",
"-f",
is_flag=True,
default=False,
help="Overwrite output files, even if they are present in the vault directory. "
"Makes sense only with --vault-directory",
)
@click.option("-v", "--verbose", count=True, help="Turn on verbose logging")
@click.option("--yaml/--no-yaml", help="Add a YAML frontmatter", default=False)
@click.option(
"--convert-links",
help="Replace DayOne internal links with Obsidian [[links]]. Default is 'False'",
default=False,
is_flag=True,
)
@click.option(
"--tag-prefix",
help="Prefix to add as part of the tag name for sub-tags. Default is '#on'",
default=None,
)
@click.option(
"--merge-entries",
help="Combine entries with the same date in a single file. Default is 'False'",
is_flag=True,
default=False,
)
@click.option(
"--entries-sep",
help="String to use to separate merged entries. Default is a double '---'",
default=None,
)
@click.option(
"--ignore-tags",
"-i",
multiple=True,
help="Ignore this tag. Can be used multiple times, e.g., -i 'Tag1' -i 'Tag2'",
default=[None],
)
@click.option(
"--status-tags",
"-s",
multiple=True,
default=[None],
help="Add this tag as '#status/' tag, e.g., '-s Draft' becomes '#status/draft'. Can be used multiple times",
)
def convert(
folder: click.Path,
config_file: click.Path,
verbose: int,
tag_prefix: str,
vault_directory: click.Path,
force: bool,
convert_links: bool,
yaml: bool,
merge_entries: bool,
entries_sep: str,
ignore_tags: tuple,
status_tags: tuple,
):
"""Converts DayOne entries into markdown files suitable to use as an Obsidian vault.
Each journal will end up in a sub-folder named after the file (e.g.: Admin.json -> admin/). All JSON files
in the FOLDER will be processed, remove those you don't want processed. The FOLDER will also be the destination
for converted markdown files. After conversion you can open this folder as a vault in Obsidian. This is done
to prevent accidental modification of an existing vault.
FOLDER is where your DayOne exports reside and where the converted markdown files will be written.
"""
if verbose != 0:
verbose_msg(f"Verbose mode enabled. Verbosity level: [blue]{verbose}[/blue]")
if yaml:
info_msg("Each entry will have a YAML frontmatter")
else:
info_msg("No YAML frontmatter will be added")
if convert_links:
info_msg(
":arrows_clockwise: Converting Day One internal links to Obsidian (when possible)"
)
# Read the config file
config = {}
if config_file is not None and config_file.exists():
with config_file.open(encoding="utf-8") as file:
config: dict = Yaml.safe_load(file)
# Build the list of tags to ignore
if (tags_to_ignore := config.get("ignore_tags")) is not None and tags_to_ignore:
ignore_tags += tuple(tags_to_ignore)
# Convert a tuple to a set to discard duplicate tags, if any
# `filter` discards the `None` tag that's added by default to `ignore_tags` when the option is not passed
ignore_tags = set(filter(bool, ignore_tags))
if verbose > 1 and ignore_tags:
verbose_msg(f"Ignoring tags: {', '.join(ignore_tags)}")
# Status tags, if any
if (tags_as_status := config.get("status_tags")) is not None and tags_as_status:
status_tags += tuple(tags_as_status)
status_tags = set(filter(bool, status_tags))
if verbose > 1 and status_tags:
info_msg(f"Status tags: {', '.join(status_tags)}")
# Process each JSON journal file in the input folder
info_msg("[bold green]Processing journals...")
if verbose > 0:
warn_msg("Journal filenames with a leading number will be ignored!")
with progress:
for filename in pathlib.Path(folder).glob("[!0-9]*.json"):
process_journal(
progress=progress,
journal=filename,
vault_directory=vault_directory or config.get("vault_directory", None),
force=force,
verbose=verbose,
ignore_tags=ignore_tags,
status_tags=status_tags,
tag_prefix=tag_prefix or config.get("tag_prefix", "#on/"),
convert_links=convert_links or config.get("convert_links", False),
yaml=yaml or config.get("yaml", False),
merge_entries=merge_entries or config.get("merge_entries", False),
entries_sep=entries_sep or config.get("entries_sep", "---\n---"),
metadata_ext=config.get("metadata", None),
)
info_msg(
"[bold green]:white_check_mark: All JSON journals (if any) have been processed!"
)
if __name__ == "__main__":
convert()