Skip to content

Commit c4cb1eb

Browse files
committed
feat: add --add-guid option in export mode to include GUID meta for each note
fix #66
1 parent d697bc6 commit c4cb1eb

File tree

6 files changed

+77
-5
lines changed

6 files changed

+77
-5
lines changed

evernote_backup/cli.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ def sync(
255255
" (e.g. to prevent backup chunking with zero changes)"
256256
),
257257
)
258+
@click.option(
259+
"--add-guid",
260+
is_flag=True,
261+
help=(
262+
"Add GUID meta field to every exported note."
263+
" (GUID is a unique note identifier used internally by Evernote)"
264+
),
265+
)
258266
@click.option(
259267
"--overwrite",
260268
is_flag=True,
@@ -271,6 +279,7 @@ def export(
271279
single_notes: bool,
272280
include_trash: bool,
273281
no_export_date: bool,
282+
add_guid: bool,
274283
overwrite: bool,
275284
output_path: Path,
276285
) -> None:
@@ -281,6 +290,7 @@ def export(
281290
single_notes=single_notes,
282291
include_trash=include_trash,
283292
no_export_date=no_export_date,
293+
add_guid=add_guid,
284294
overwrite=overwrite,
285295
output_path=output_path,
286296
)

evernote_backup/cli_app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def export(
136136
single_notes: bool,
137137
include_trash: bool,
138138
no_export_date: bool,
139+
add_guid: bool,
139140
overwrite: bool,
140141
output_path: Path,
141142
) -> None:
@@ -149,6 +150,7 @@ def export(
149150
single_notes=single_notes,
150151
export_trash=include_trash,
151152
no_export_date=no_export_date,
153+
add_guid=add_guid,
152154
overwrite=overwrite,
153155
)
154156

evernote_backup/note_exporter.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def __init__(
3434
single_notes: bool,
3535
export_trash: bool,
3636
no_export_date: bool,
37+
add_guid: bool,
3738
overwrite: bool,
3839
) -> None:
3940
self.storage = storage
@@ -42,6 +43,7 @@ def __init__(
4243
self.single_notes = single_notes
4344
self.export_trash = export_trash
4445
self.no_export_date = no_export_date
46+
self.add_guid = add_guid
4547

4648
def export_notebooks(self) -> None:
4749
count_notes = self.storage.notes.get_notes_count()
@@ -112,18 +114,23 @@ def _output_single_notes(
112114
for note in notes_source:
113115
note_path = self.safe_paths.get_file(*parent_dir, f"{note.title}.enex")
114116

115-
_write_export_file(note_path, note, self.no_export_date)
117+
_write_export_file(note_path, note, self.no_export_date, self.add_guid)
116118

117119
def _output_notebook(
118120
self, parent_dir: List[str], notebook_name: str, notes_source: Iterable[Note]
119121
) -> None:
120122
notebook_path = self.safe_paths.get_file(*parent_dir, f"{notebook_name}.enex")
121123

122-
_write_export_file(notebook_path, notes_source, self.no_export_date)
124+
_write_export_file(
125+
notebook_path, notes_source, self.no_export_date, self.add_guid
126+
)
123127

124128

125129
def _write_export_file(
126-
file_path: Path, note_source: Union[Iterable[Note], Note], no_export_date: bool
130+
file_path: Path,
131+
note_source: Union[Iterable[Note], Note],
132+
no_export_date: bool,
133+
add_guid: bool,
127134
) -> None:
128135
with open(file_path, "w", encoding="utf-8") as f:
129136
logger.debug(f"Writing file {file_path}")
@@ -139,7 +146,7 @@ def _write_export_file(
139146
f' application="Evernote" version="10.10.5">\n'
140147
)
141148

142-
note_formatter = NoteFormatter()
149+
note_formatter = NoteFormatter(add_guid=add_guid)
143150

144151
if isinstance(note_source, Note):
145152
if logger.getEffectiveLevel() == logging.DEBUG: # pragma: no cover

evernote_backup/note_formatter.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
class NoteFormatter(object):
1414
"""https://xml.evernote.com/pub/evernote-export3.dtd"""
1515

16-
def __init__(self) -> None:
16+
def __init__(self, add_guid: bool = False) -> None:
1717
self._raw_elements: dict = {}
18+
self.add_guid = add_guid
1819

1920
def format_note(self, note: Note) -> str:
2021
self._raw_elements = {}
@@ -31,6 +32,9 @@ def format_note(self, note: Note) -> str:
3132
}
3233
}
3334

35+
if self.add_guid:
36+
note_skeleton["note"]["guid"] = note.guid
37+
3438
if note.attributes:
3539
note_skeleton["note"]["note-attributes"] = {
3640
"subject-date": fmt_time(note.attributes.subjectDate),

tests/test_note_formatter.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,16 @@ def test_note_from_past(mocker):
211211

212212
assert "<created>00010101T000000Z</created>" in formatted_note
213213
assert "<updated>03850725T070640Z</updated>" in formatted_note
214+
215+
216+
def test_formatter_add_guid(mocker):
217+
formatter = NoteFormatter(add_guid=True)
218+
219+
note_from_future = Note(
220+
guid="test-guid",
221+
title="test",
222+
)
223+
224+
formatted_note = formatter.format_note(note_from_future)
225+
226+
assert "<guid>test-guid</guid>" in formatted_note

tests/test_op_export.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,39 @@ def test_export_no_export_date(cli_invoker, fake_storage, tmp_path):
493493
book1_xml = f.read()
494494

495495
assert "export-date" not in book1_xml
496+
497+
498+
@pytest.mark.usefixtures("fake_init_db")
499+
def test_export_add_guid(cli_invoker, fake_storage, tmp_path):
500+
test_out_path = tmp_path / "test_out"
501+
502+
test_notebooks = [
503+
Notebook(guid="nbid1", name="name1", stack=None),
504+
]
505+
506+
fake_storage.notebooks.add_notebooks(test_notebooks)
507+
508+
fake_storage.notes.add_note(
509+
Note(
510+
guid="id1",
511+
title="title1",
512+
content="test",
513+
notebookGuid="nbid1",
514+
active=True,
515+
)
516+
)
517+
518+
cli_invoker(
519+
"export",
520+
"--add-guid",
521+
"--database",
522+
"fake_db",
523+
str(test_out_path),
524+
)
525+
526+
book1_path = test_out_path / "name1.enex"
527+
528+
with open(book1_path, "r") as f:
529+
book1_xml = f.read()
530+
531+
assert "<guid>id1</guid>" in book1_xml

0 commit comments

Comments
 (0)