diff --git a/CHANGELOG.md b/CHANGELOG.md index 903bdb24..321712ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Changelog #### 1.1.1 +* [New] Export to multiple files +* [New] Feature to export to given output file * [Fixed] Unicode and Python3 issues resolved. ### 1.1.0 diff --git a/README.md b/README.md index 903c3618..3d6492cf 100644 --- a/README.md +++ b/README.md @@ -114,16 +114,44 @@ you'll get a list of all tags you used in your journal, sorted by most frequent. Can do: - jrnl --json + jrnl --export json Why not create a beautiful [timeline](http://timeline.verite.co/) of your journal? ### Markdown export - jrnl --markdown +Use: + + jrnl --export markdown + +or + + jrnl --export md Markdown is a simple markup language that is human readable and can be used to be rendered to other formats (html, pdf). This README for example is formatted in markdown and github makes it look nice. +### Text export + + jrnl --export text + +or + + jrnl --export txt + +Prettyprints your entire journal. + +### Export to files + + jrnl --export md -o journal + +The above command will generate a file named `journal.md`. The extension will be generated on the type of export option. This way a json export will generate a `.json` file and a plain text export will generate a `.txt` file. + +In case you wish to export to multiple files, you can use a glob-able filename. + + jrnl --export markdown -o %C-%m-%d_slug + +It also works with `json` and `text` export types. + Encryption ---------- diff --git a/jrnl/exporters.py b/jrnl/exporters.py index 4a4b0245..85efd43b 100644 --- a/jrnl/exporters.py +++ b/jrnl/exporters.py @@ -1,6 +1,10 @@ #!/usr/bin/env python # encoding: utf-8 +import os +import string +try: from slugify import slugify +except ImportError: import slugify try: import simplejson as json except ImportError: import json @@ -17,7 +21,7 @@ def get_tags_count(journal): return tag_counts def to_tag_list(journal): - """Prints a list of all tags and the number of occurances.""" + """Prints a list of all tags and the number of occurrences.""" tag_counts = get_tags_count(journal) result = "" if not tag_counts: @@ -28,16 +32,24 @@ def to_tag_list(journal): result += "\n".join(u"{0:20} : {1}".format(tag, n) for n, tag in sorted(tag_counts, reverse=False)) return result -def to_json(journal): +def to_json(journal, output): """Returns a JSON representation of the Journal.""" tags = get_tags_count(journal) result = { "tags": dict((tag, count) for count, tag in tags), "entries": [e.to_dict() for e in journal.entries] } - return json.dumps(result, indent=2) + if output is not False: + path = output_path('json', output) + if not is_globable(path): + message = write_file(json.dumps(result, indent=2), path) + else: + message = to_files(journal, path) + return message + else: + return json.dumps(result, indent=2) -def to_md(journal): +def to_md(journal, output): """Returns a markdown representation of the Journal""" out = [] year, month = -1, -1 @@ -51,4 +63,80 @@ def to_md(journal): out.append(e.date.strftime("%B")) out.append('-' * len(e.date.strftime("%B")) + "\n") out.append(e.to_md()) - return "\n".join(out) + result = "\n".join(out) + if output is not False: + path = output_path('md', output) + if not is_globable(path): + message = write_file(result, path) + else: + message = to_files(journal, path) + return message + else: + return result + +def to_txt(journal, output): + """Returns the complete text of the Journal.""" + if output is not False: + path = output_path('txt', output) + if not is_globable(path): + message = write_file(unicode(journal), path) + else: + message = to_files(journal, path) + return message + else: + return unicode(journal) + +def to_files(journal, output): + """Turns your journal into separate files for each entry.""" + path, extension = os.path.splitext(os.path.expanduser(output)) + + for e in journal.entries: + content = "" + date = e.date.strftime('%C-%m-%d') + title = slugify(unicode(e.title)) + + filename = string.replace(path, "%C-%m-%d", date) + filename = string.replace(filename, "slug", title) + + fullpath = filename + extension + + if extension == '.json': + content = json.dumps(e.to_dict(), indent=2) + elif extension == '.md': + content = e.to_md() + elif extension == '.txt': + content = unicode(e) + write_file(content, fullpath) + + return ("Journal exported.") + +def is_globable(output): + path, extension = os.path.splitext(os.path.expanduser(output)) + head, tail = os.path.split(path) + + if tail == "%C-%m-%d_slug": + return True + else: + return False + +def output_path(file_ext, output): + path, extension = os.path.splitext(os.path.expanduser(output)) + + head, tail = os.path.split(path) + if head != '': + if not os.path.exists(head): # if the folder doesn't exist, create it + os.makedirs(head) + fullpath = head + '/' + tail + '.' + file_ext + else: + fullpath = tail + '.' + file_ext + + return fullpath + +def write_file(content, path): + """Writes content to the file provided""" + + f = open(path, 'w+') + f.write(content) + f.close() + + return ("File exported to " + path) diff --git a/jrnl/install.py b/jrnl/install.py index acb519a9..2037e8aa 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -93,5 +93,3 @@ def install_jrnl(config_path='~/.jrnl_config'): if password: config['password'] = password return config - - diff --git a/jrnl/jrnl.py b/jrnl/jrnl.py index 2af9b60c..eafc05e4 100755 --- a/jrnl/jrnl.py +++ b/jrnl/jrnl.py @@ -45,8 +45,8 @@ def parse_args(): exporting = parser.add_argument_group('Export / Import', 'Options for transmogrifying your journal') exporting.add_argument('--tags', dest='tags', action="store_true", help='Returns a list of all tags and number of occurences') - exporting.add_argument('--json', dest='json', action="store_true", help='Returns a JSON-encoded version of the Journal') - exporting.add_argument('--markdown', dest='markdown', action="store_true", help='Returns a Markdown-formated version of the Journal') + exporting.add_argument('--export', metavar='TYPE', dest='export', help='Export your journal to Markdown, JSON or Text', nargs='?', default=False, const=None) + exporting.add_argument('-o', metavar='OUTPUT', dest='output', help='The output of the file can be provided when using with --export', nargs='?', default=False, const=None) exporting.add_argument('--encrypt', metavar='FILENAME', dest='encrypt', help='Encrypts your existing journal with a new password', nargs='?', default=False, const=None) exporting.add_argument('--decrypt', metavar='FILENAME', dest='decrypt', help='Decrypts your journal and stores it in plain text', nargs='?', default=False, const=None) exporting.add_argument('--delete-last', dest='delete_last', help='Deletes the last entry from your journal file.', action="store_true") @@ -57,7 +57,7 @@ def guess_mode(args, config): """Guesses the mode (compose, read or export) from the given arguments""" compose = True export = False - if args.json or args.decrypt is not False or args.encrypt is not False or args.markdown or args.tags or args.delete_last: + if args.decrypt is not False or args.encrypt is not False or args.export is not False or args.tags or args.delete_last: compose = False export = True elif args.start_date or args.end_date or args.limit or args.strict or args.short: @@ -190,11 +190,14 @@ def cli(): elif args.tags: print(exporters.to_tag_list(journal)) - elif args.json: # export to json - print(exporters.to_json(journal)) + elif args.export == 'json': # export to json + print(exporters.to_json(journal, args.output)) - elif args.markdown: # export to json - print(exporters.to_md(journal)) + elif args.export == 'markdown' or args.export == 'md': # export to markdown + print(exporters.to_md(journal, args.output)) + + elif args.export == 'text' or args.export == 'txt': # export to text + print(exporters.to_txt(journal, args.output)) elif (args.encrypt is not False or args.decrypt is not False) and not PYCRYPTO: print("PyCrypto not found. To encrypt or decrypt your journal, install the PyCrypto package from http://www.pycrypto.org.") diff --git a/requirements.txt b/requirements.txt index 0871fab2..999b9b05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ colorama >= 0.2.5 pycrypto >= 2.6 argparse==1.2.1 tzlocal == 1.0 - +slugify==0.0.1