From 6258465f5d163faa8c71b0bb48454327a9ef6df9 Mon Sep 17 00:00:00 2001 From: Gregory Crosswhite Date: Fri, 24 Oct 2014 15:21:10 -0700 Subject: [PATCH 1/3] Added support for importing entries. --- jrnl/Entry.py | 3 +++ jrnl/Journal.py | 8 ++++++++ jrnl/cli.py | 24 ++++++++++++++++++------ jrnl/plugins/__init__.py | 7 +++++++ jrnl/plugins/jrnl_importer.py | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 jrnl/plugins/jrnl_importer.py diff --git a/jrnl/Entry.py b/jrnl/Entry.py index c6c29c6d..34503cd9 100755 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -74,6 +74,9 @@ class Entry: def __repr__(self): return "".format(self.title.strip(), self.date.strftime("%Y-%m-%d %H:%M")) + def __hash__(self): + return hash(self.__repr__()) + def __eq__(self, other): if not isinstance(other, Entry) \ or self.title.strip() != other.title.strip() \ diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 5fc92df4..212cafab 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -33,6 +33,14 @@ class Journal(object): """Returns the number of entries""" return len(self.entries) + def import_(self, other_journal_txt): + other_entries = self._parse(other_journal_txt) + entries_set = frozenset(self.entries) + for other_entry in other_entries: + if other_entry not in entries_set: + self.entries.append(other_entry) + self.sort() + def open(self, filename=None): """Opens the journal file defined in the config and parses it into a list of Entries. Entries have the form (date, title, body).""" diff --git a/jrnl/cli.py b/jrnl/cli.py index 7dd18c9b..ebb36d97 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -40,6 +40,8 @@ def parse_args(args=None): exporting.add_argument('--tags', dest='tags', action="store_true", help='Returns a list of all tags and number of occurences') exporting.add_argument('--export', metavar='TYPE', dest='export', choices=plugins.BaseExporter.PLUGIN_NAMES, help='Export your journal. TYPE can be json, markdown, or text.', default=False, const=None) exporting.add_argument('-o', metavar='OUTPUT', dest='output', help='Optionally specifies output file when using --export. If OUTPUT is a directory, exports each entry into an individual file instead.', default=False, const=None) + exporting.add_argument('--import', metavar='TYPE', dest='import_', choices=plugins.BaseImporter.PLUGIN_NAMES, help='Import entries into your journal. TYPE can be jrnl, and it defaults to jrnl if nothing else is specified.', default=False, const='jrnl', nargs='?') + exporting.add_argument('-i', metavar='INPUT', dest='input', help='Optionally specifies input file when using --import.', 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('--edit', dest='edit', help='Opens your editor to edit the selected entries.', action="store_true") @@ -51,7 +53,12 @@ def guess_mode(args, config): """Guesses the mode (compose, read or export) from the given arguments""" compose = True export = False - if args.decrypt is not False or args.encrypt is not False or args.export is not False or any((args.short, args.tags, args.edit)): + import_ = False + if args.import_ is not False: + compose = False + export = False + import_ = True + elif args.decrypt is not False or args.encrypt is not False or args.export is not False or any((args.short, args.tags, args.edit)): compose = False export = True elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred)): @@ -61,7 +68,7 @@ def guess_mode(args, config): # No date and only tags? compose = False - return compose, export + return compose, export, import_ def encrypt(journal, filename=None): @@ -162,7 +169,7 @@ def run(manual_args=None): config['journal'] = journal_conf config['journal'] = os.path.expanduser(os.path.expandvars(config['journal'])) touch_journal(config['journal']) - mode_compose, mode_export = guess_mode(args, config) + mode_compose, mode_export, mode_import = guess_mode(args, config) # How to quit writing? if "win32" in sys.platform: @@ -189,15 +196,20 @@ def run(manual_args=None): journal = Journal.open_journal(journal_name, config) + # Import mode + if mode_import: + plugins.get_importer(args.import_).import_(journal, args.input) + # Writing mode - if mode_compose: + elif mode_compose: raw = " ".join(args.text).strip() if util.PY2 and type(raw) is not unicode: raw = raw.decode(sys.getfilesystemencoding()) journal.new_entry(raw) util.prompt("[Entry added to {0} journal]".format(journal_name)) journal.write() - else: + + if not mode_compose: old_entries = journal.entries if args.on_date: args.start_date = args.end_date = args.on_date @@ -209,7 +221,7 @@ def run(manual_args=None): journal.limit(args.limit) # Reading mode - if not mode_compose and not mode_export: + if not mode_compose and not mode_export and not mode_import: print(util.py2encode(journal.pprint())) # Various export modes diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index f5129b3a..2ecc6da7 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -42,3 +42,10 @@ def get_exporter(format): if hasattr(exporter, "names") and format in exporter.names: return exporter return None + + +def get_importer(format): + for importer in BaseImporter.PLUGINS: + if hasattr(importer, "names") and format in importer.names: + return importer + return None diff --git a/jrnl/plugins/jrnl_importer.py b/jrnl/plugins/jrnl_importer.py new file mode 100644 index 00000000..9c0a4b47 --- /dev/null +++ b/jrnl/plugins/jrnl_importer.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from __future__ import absolute_import, unicode_literals +import codecs +import sys +from . import BaseImporter +from .. import util + +class JRNLImporter(BaseImporter): + """This plugin imports entries from other jrnl files.""" + names = ["jrnl"] + + @staticmethod + def import_(journal, input=None): + """Imports from an existing file if input is specified, and + standard input otherwise.""" + old_cnt = len(journal.entries) + old_entries = journal.entries + if input: + with codecs.open(input, "r", "utf-8") as f: + other_journal_txt = f.read() + else: + try: + other_journal_txt = sys.stdin.read() + except KeyboardInterrupt: + util.prompt("[Entries NOT imported into journal.]") + sys.exit(0) + journal.import_(other_journal_txt) + new_cnt = len(journal.entries) + util.prompt("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name)) + journal.write() From 93ea6a2a6a455581b16cdddb24cbfaf1d895c036 Mon Sep 17 00:00:00 2001 From: Gregory Crosswhite Date: Wed, 29 Oct 2014 13:02:05 -0700 Subject: [PATCH 2/3] Made Journal.import_ more concise. --- jrnl/Journal.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 212cafab..a7b6521b 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -34,11 +34,7 @@ class Journal(object): return len(self.entries) def import_(self, other_journal_txt): - other_entries = self._parse(other_journal_txt) - entries_set = frozenset(self.entries) - for other_entry in other_entries: - if other_entry not in entries_set: - self.entries.append(other_entry) + self.entries = list(frozenset(self.entries) | frozenset(self._parse(other_journal_txt))) self.sort() def open(self, filename=None): From 324dfbd2db27d8f92f53370b66de27154c1d89fc Mon Sep 17 00:00:00 2001 From: Gregory Crosswhite Date: Wed, 29 Oct 2014 12:56:44 -0700 Subject: [PATCH 3/3] Now the importer reads from standard input using util.py23_read(). --- jrnl/plugins/jrnl_importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/plugins/jrnl_importer.py b/jrnl/plugins/jrnl_importer.py index 9c0a4b47..6563233b 100644 --- a/jrnl/plugins/jrnl_importer.py +++ b/jrnl/plugins/jrnl_importer.py @@ -22,7 +22,7 @@ class JRNLImporter(BaseImporter): other_journal_txt = f.read() else: try: - other_journal_txt = sys.stdin.read() + other_journal_txt = util.py23_read() except KeyboardInterrupt: util.prompt("[Entries NOT imported into journal.]") sys.exit(0)