Skip to content

Commit

Permalink
add commandline option for metadata output
Browse files Browse the repository at this point in the history
  • Loading branch information
hbunke committed Apr 21, 2017
1 parent 8dfe66f commit bc73095
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 53 deletions.
33 changes: 20 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ notes, and that is it.
This package installs a script to help you migrate from Evernote into
Simplenote by exporting the notes as files. The script
will take an Evernote ``enex`` export and turn it into a ``json``, ``csv`` or
directory of ``*.txt`` files.
directory of ``*.md`` files.

The html that is provided by Evernote is processed by the html2text_
library. This transforms the html into Markdown_. The Simplenote web UI
Expand Down Expand Up @@ -58,7 +58,7 @@ to a directory and using ``dir`` as the parameter to ``--format``::

$ ever2simple my_evernote.enex --output simplenote_dir --format dir

That will output each note in a ``*.txt`` file named by a number to the
That will output each note in a ``*.md`` file named by the note's title to the
``simplenote_dir`` directory (creating it if it doesn't exist).

You can now request Simplenote's support to enable Dropbox synchronization
Expand Down Expand Up @@ -87,32 +87,39 @@ Command Line Help

The help given by running ``ever2simple -h``::


usage: ever2simple [-h] [-o OUTPUT] [-f {json,csv,dir}] enex-file
usage: ever2simple [-h] [-o OUTPUT] [-f {json,csv,dir}]
[-m {all,title,date,keywords}]
enex_file

Convert Evernote.enex files to Markdown

positional arguments:
enex-file the path to the Evernote.enex file
enex_file the path to the Evernote.enex file

optional arguments:
-h, --help show this help message and exit
-o OUTPUT, --output OUTPUT
the path to the output file or directory, leave black
to output to the terminal (stdout) (default: None)
-f {json,csv,dir}, --format {json,csv,dir}
-h, --help show this help message and exit
-o OUTPUT, --output OUTPUT
the path to the output file or directory, omit to
output to the terminal (stdout) (default: None)
-f {json,csv,dir}, --format {json,csv,dir}
the output format, json, csv or a directory (default:
json)

-m {all,title,date,keywords}, --metadata {all,title,date,keywords}
For directory output only. Specify the metadata you
would like to add on top of the markdown file. Valid
options are 'all', 'title', 'created', and 'keywords'.
Default is 'all'. You can specify this argument
multiple times, by which you can also control the
order in which metadata lines are printed. Metadata is
printed in MultiMarkdown format. (default: None)

Notes and Caveats
-----------------

- Simplenote no longer supports JSON and CSV imports, only text files via
Dropbox.

- Exporting to a directory will not preserve tags in the notes.

- This does not handle any attachments since simplenote doesn't support
them. This script does not ignore the note that has attachments. This
may make for some strange notes being imported with little to no text.
Expand Down
59 changes: 29 additions & 30 deletions ever2simple/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ class EverConverter(object):
"""Evernote conversion runner
"""

fieldnames = ['createdate', 'modifydate', 'content', 'tags']
fieldnames = ['createdate', 'modifydate', 'content', 'tags', 'title']
date_fmt = '%h %d %Y %H:%M:%S'

def __init__(self, enex_filename, simple_filename=None, fmt='json'):
def __init__(self, enex_filename, simple_filename, fmt, metadata):
self.enex_filename = os.path.expanduser(enex_filename)
self.stdout = False
if simple_filename is None:
Expand All @@ -24,6 +24,7 @@ def __init__(self, enex_filename, simple_filename=None, fmt='json'):
else:
self.simple_filename = os.path.expanduser(simple_filename)
self.fmt = fmt
self.metadata = metadata

def _load_xml(self, enex_file):
try:
Expand Down Expand Up @@ -121,48 +122,46 @@ def _convert_dir(self, notes):
sys.exit(1)
elif not os.path.exists(self.simple_filename):
os.makedirs(self.simple_filename)
for i, note in enumerate(notes):
# Original name with number
#output_file_path = os.path.join(self.simple_filename, str(i) + '.txt')
for note in notes:
# Overwrite duplicates
#output_file_path = os.path.join(self.simple_filename, note['title'] + '.md')
# output_file_path = os.path.join(self.simple_filename, note['title'] + '.md')
# Check for duplicates
filename = self._format_filename(note['title']);
output_file_path_no_ext_original = os.path.join(self.simple_filename, filename);
filename = self._format_filename(note['title'])
output_file_path_no_ext_original = os.path.join(self.simple_filename, filename)
output_file_path_no_ext = output_file_path_no_ext_original
count = 0;
count = 0
while os.path.isfile(output_file_path_no_ext + ".md"):
count = count + 1;
count = count + 1
output_file_path_no_ext = output_file_path_no_ext_original + " (" + str(count) + ")"
output_file_path = output_file_path_no_ext + ".md"
with open(output_file_path, 'w') as output_file:
output_file.write(metadata(note).encode(encoding='utf-8'))
if self.metadata:
output_file.write(self._metadata(note).encode(encoding='utf-8'))
output_file.write(note['content'].encode(encoding='utf-8'))

def _format_filename(self, s):
for c in r'[]/\;,><&*:%=+@!#^()|?^':
s = s.replace(c, '-')
return s


def metadata(note):
"""
get metadata of note. Ideally we would print all metadata as MultiMarkdown
on top of file, but that renders badly with other markdown variants. So for
now we just return creation date on top, since this might be the most
important metadata.
# TODO: could be a commandline option
"""
# Tags is a selectable option when exporting from Evernote, so we can not
# be sure, if it is available
kw = note.get('tags', [])
kws = u", ".join(kw)
def _metadata(self, note):
"""
optionally print metadata of note. Default is 'all', but can be limited
to any combination of 'title', 'date', 'keywords'. Output is in
MultiMarkdown format, but also rendered nicely in standard Markdown.
"""
# Tags is a selectable option when exporting from Evernote, so we can not
# be sure that it is available
keywords = u", ".join(note.get('tags', []))

data = {"Title": note['title'],
"Date": note['createdate'],
"Keywords": kws}

# for now only return creation date
return u"{}\n\n".format(data['Date'])
# XXX two spaces at the end of a metadata line are intentionally set,
# so that regular markdown renderers append a linebreak
md = {'title': u"Title: {} \n".format(note['title']),
'date': u"Date: {} \n".format(note['createdate']),
'keywords': u"Keywords: {} \n".format(keywords)}
if 'all' in self.metadata:
return u"{title}{date}{keywords}\n".format(**md)
md_lines = map(lambda l: md[l], self.metadata)
return u"".join(md_lines) + u"\n"


32 changes: 22 additions & 10 deletions ever2simple/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,31 @@


def main():
parser = argparse.ArgumentParser(prog=None, description="Convert Evernote.enex files to Markdown", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('enex-file', help="the path to the Evernote.enex file")
parser.add_argument('-o', '--output', help="the path to the output file or directory, leave black to output to the terminal (stdout)")
parser.add_argument('-f', '--format', help="the output format, json, csv or a directory", choices=['json', 'csv', 'dir'], default='json')
parser = argparse.ArgumentParser(prog=None,
description="Convert Evernote.enex files to Markdown",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('enex_file', help="the path to the Evernote.enex file")
parser.add_argument('-o', '--output',
help="the path to the output file or directory, omit to output to the terminal (stdout)")
parser.add_argument('-f', '--format',
help="the output format, json, csv or a directory",
choices=['json', 'csv', 'dir'], default='json')
parser.add_argument('-m', '--metadata',
help="For directory output only. Specify the metadata you\
would like to add on top of the markdown file. Valid options are\
'all', 'title', 'created', and 'keywords'. Default is 'all'. You can\
specify this argument multiple times, by which you can also control\
the order in which metadata lines are printed.\
Metadata is printed in MultiMarkdown format.",
choices=['all', 'title', 'date', 'keywords'],
action='append')

args = parser.parse_args()
enex_file = vars(args)['enex-file']
output = args.output
fmt = args.format
filepath = os.path.expanduser(enex_file)
filepath = os.path.expanduser(args.enex_file)
if not os.path.exists(filepath):
print 'File does not exist: %s' % filepath
print 'File does not exist: {}'.format(filepath)
sys.exit(1)
converter = EverConverter(filepath, simple_filename=output, fmt=fmt)
converter = EverConverter(filepath, args.output, args.format, args.metadata)
converter.convert()
sys.exit()

Expand Down

0 comments on commit bc73095

Please sign in to comment.