From 2780a0d9df30732fb1ad061a4f4aaa115b69e87c Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 7 Aug 2014 13:25:50 +0200 Subject: [PATCH 01/31] Tags at the beginning of lines --- features/tagging.feature | 29 ++++++++++++++++++++--------- jrnl/Entry.py | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/features/tagging.feature b/features/tagging.feature index 649ff9bf..9e6bca76 100644 --- a/features/tagging.feature +++ b/features/tagging.feature @@ -31,12 +31,23 @@ Feature: Tagging @c++ : 1 @c# : 1 """ - Scenario: An email should not be a tag - Given we use the config "tags-237.json" - When we run "jrnl --tags" - Then we should get no error - and the output should be - """ - @newline : 1 - @email : 1 - """ \ No newline at end of file + Scenario: An email should not be a tag + Given we use the config "tags-237.json" + When we run "jrnl --tags" + Then we should get no error + and the output should be + """ + @newline : 1 + @email : 1 + """ + + Scenario: Entry cans start and end with tags + Scenario: Writing an entry from command line + Given we use the config "basic.json" + When we run "jrnl today: @foo came over, we went to a @bar" + When we run "jrnl --tags" + Then the output should be + """ + @foo : 1 + @bar : 1 + """ diff --git a/jrnl/Entry.py b/jrnl/Entry.py index fb92c3b6..f04d9e0f 100755 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -17,7 +17,7 @@ class Entry: self.modified = False def parse_tags(self): - fulltext = " ".join([self.title, self.body]).lower() + fulltext = " " + " ".join([self.title, self.body]).lower() tags = re.findall(r'(?u)\s([{tags}][-+*#/\w]+)'.format(tags=self.journal.config['tagsymbols']), fulltext, re.UNICODE) self.tags = tags return set(tags) From b851941574a0524149a29562be1b87caebc6ca74 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 7 Aug 2014 13:26:10 +0200 Subject: [PATCH 02/31] Version bump --- CHANGELOG.md | 1 + jrnl/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 699d7c08..0c320414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changelog ### 1.9 (July 21, 2014) +* __1.9.3__ Fixed: Tags at the beginning of lines * __1.9.2__ Fixed: Tag search ignores email-addresses (thanks to @mjhoffman65) * __1.9.1__ Fixed: Dates in the future can be parsed as well. * __1.9.0__ Improved: Greatly improved date parsing. Also added an `-on` option for filtering diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 8d23f05e..c041bbbc 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -8,7 +8,7 @@ jrnl is a simple journal application for your command line. from __future__ import absolute_import __title__ = 'jrnl' -__version__ = '1.9.2' +__version__ = '1.9.3' __author__ = 'Manuel Ebert' __license__ = 'MIT License' __copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' From 0d981241acca386b67c66a8598531c4b3cfa7792 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 7 Aug 2014 13:27:48 +0200 Subject: [PATCH 03/31] typo --- features/tagging.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/features/tagging.feature b/features/tagging.feature index 9e6bca76..4eba8470 100644 --- a/features/tagging.feature +++ b/features/tagging.feature @@ -42,7 +42,6 @@ Feature: Tagging """ Scenario: Entry cans start and end with tags - Scenario: Writing an entry from command line Given we use the config "basic.json" When we run "jrnl today: @foo came over, we went to a @bar" When we run "jrnl --tags" From 4390e1f10c14caa18445250811f528ee6ba82bc3 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 12 Aug 2014 10:10:15 +0200 Subject: [PATCH 04/31] Sort journal entries after editing Fixes #269 --- jrnl/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jrnl/cli.py b/jrnl/cli.py index af60bbe4..47aa09d7 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -263,6 +263,7 @@ def run(manual_args=None): if prompts: util.prompt("[{0}]".format(", ".join(prompts).capitalize())) journal.entries += other_entries + journal.sort() journal.write() if __name__ == "__main__": From a43ebd7b08c327e5d250a58464607c8c87f275c7 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 12 Aug 2014 10:11:18 +0200 Subject: [PATCH 05/31] Version bump --- CHANGELOG.md | 1 + jrnl/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c320414..82180900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changelog ### 1.9 (July 21, 2014) +* __1.9.4__ Fixed: Order of journal entries in file correct after --edit'ing * __1.9.3__ Fixed: Tags at the beginning of lines * __1.9.2__ Fixed: Tag search ignores email-addresses (thanks to @mjhoffman65) * __1.9.1__ Fixed: Dates in the future can be parsed as well. diff --git a/jrnl/__init__.py b/jrnl/__init__.py index c041bbbc..234e175c 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -8,7 +8,7 @@ jrnl is a simple journal application for your command line. from __future__ import absolute_import __title__ = 'jrnl' -__version__ = '1.9.3' +__version__ = '1.9.4' __author__ = 'Manuel Ebert' __license__ = 'MIT License' __copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' From 6d783a80ac601b45f6738587ad0496c1d5aca385 Mon Sep 17 00:00:00 2001 From: Kurt Neufeld Date: Thu, 31 Jul 2014 17:22:49 -0600 Subject: [PATCH 06/31] using a common tag regex for searching and highlighting --- jrnl/Entry.py | 8 +++++++- jrnl/Journal.py | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/jrnl/Entry.py b/jrnl/Entry.py index f04d9e0f..fa2650ca 100755 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -16,9 +16,15 @@ class Entry: self.starred = starred self.modified = False + @staticmethod + def tag_regex(tagsymbols): + pattern = r'(?u)\s([{tags}][-+*#/\w]+)'.format(tags=tagsymbols) + return re.compile( pattern, re.UNICODE ) + def parse_tags(self): fulltext = " " + " ".join([self.title, self.body]).lower() - tags = re.findall(r'(?u)\s([{tags}][-+*#/\w]+)'.format(tags=self.journal.config['tagsymbols']), fulltext, re.UNICODE) + tagsymbols = self.journal.config['tagsymbols'] + tags = re.findall( Entry.tag_regex(tagsymbols), fulltext ) self.tags = tags return set(tags) diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 2dab065a..b94a5d36 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -165,9 +165,9 @@ class Journal(object): lambda match: util.colorize(match.group(0)), pp, re.UNICODE) else: - pp = re.sub(r"(?u)([{tags}]\w+)".format(tags=self.config['tagsymbols']), - lambda match: util.colorize(match.group(0)), - pp) + pp = re.sub( Entry.Entry.tag_regex(self.config['tagsymbols']), + lambda match: util.colorize(match.group(0)), + pp) return pp def __repr__(self): From 182e51b92dfcebe9f4cc0f60c49f768ca33f55bf Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 2 Sep 2014 13:26:50 -0700 Subject: [PATCH 07/31] Use unicode literals Fixes #273 --- jrnl/DayOneJournal.py | 4 ++-- jrnl/Entry.py | 7 ++++--- jrnl/Journal.py | 6 +++--- jrnl/cli.py | 8 ++++---- jrnl/exporters.py | 12 ++++++------ 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py index d5649eb3..75336560 100644 --- a/jrnl/DayOneJournal.py +++ b/jrnl/DayOneJournal.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals from . import Entry from . import Journal import os @@ -75,7 +75,7 @@ class DayOne(Journal.Journal): def editable_str(self): """Turns the journal into a string of entries that can be edited manually and later be parsed with eslf.parse_editable_str.""" - return u"\n".join([u"# {0}\n{1}".format(e.uuid, e.__unicode__()) for e in self.entries]) + return "\n".join(["# {0}\n{1}".format(e.uuid, e.__unicode__()) for e in self.entries]) def parse_editable_str(self, edited): """Parses the output of self.editable_str and updates it's entries.""" diff --git a/jrnl/Entry.py b/jrnl/Entry.py index fa2650ca..66b32f6c 100755 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 +from __future__ import unicode_literals import re import textwrap from datetime import datetime @@ -34,7 +35,7 @@ class Entry: title = date_str + " " + self.title.rstrip("\n ") if self.starred: title += " *" - return u"{title}{sep}{body}\n".format( + return "{title}{sep}{body}\n".format( title=title, sep="\n" if self.body.rstrip("\n ") else "", body=self.body.rstrip("\n ") @@ -64,7 +65,7 @@ class Entry: if short: return title else: - return u"{title}{sep}{body}\n".format( + return "{title}{sep}{body}\n".format( title=title, sep="\n" if has_body else "", body=body if has_body else "", @@ -101,7 +102,7 @@ class Entry: space = "\n" md_head = "###" - return u"{md} {date}, {title} {body} {space}".format( + return "{md} {date}, {title} {body} {space}".format( md=md_head, date=date_str, title=self.title, diff --git a/jrnl/Journal.py b/jrnl/Journal.py index b94a5d36..92a6774f 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals from . import Entry from . import util from . import time @@ -176,7 +176,7 @@ class Journal(object): def write(self, filename=None): """Dumps the journal into the config file, overwriting it""" filename = filename or self.config['journal'] - journal = u"\n".join([e.__unicode__() for e in self.entries]) + journal = "\n".join([e.__unicode__() for e in self.entries]) if self.config['encrypt']: journal = self._encrypt(journal) with open(filename, 'wb') as journal_file: @@ -269,7 +269,7 @@ class Journal(object): def editable_str(self): """Turns the journal into a string of entries that can be edited manually and later be parsed with eslf.parse_editable_str.""" - return u"\n".join([e.__unicode__() for e in self.entries]) + return "\n".join([e.__unicode__() for e in self.entries]) def parse_editable_str(self, edited): """Parses the output of self.editable_str and updates it's entries.""" diff --git a/jrnl/cli.py b/jrnl/cli.py index 47aa09d7..fe105e89 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -7,7 +7,7 @@ license: MIT, see LICENSE for more details. """ -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals from . import Journal from . import DayOneJournal from . import util @@ -61,7 +61,7 @@ def guess_mode(args, config): elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred)): # Any sign of displaying stuff? compose = False - elif args.text and all(word[0] in config['tagsymbols'] for word in u" ".join(args.text).split()): + elif args.text and all(word[0] in config['tagsymbols'] for word in " ".join(args.text).split()): # No date and only tags? compose = False @@ -189,7 +189,7 @@ def run(manual_args=None): "entries" in os.listdir(config['journal']): journal = DayOneJournal.DayOne(**config) else: - util.prompt(u"[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal'])) + util.prompt("[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal'])) sys.exit(1) else: journal = Journal.Journal(journal_name, **config) @@ -246,7 +246,7 @@ def run(manual_args=None): elif args.edit: if not config['editor']: - util.prompt(u"[You need to specify an editor in {0} to use the --edit function.]".format(CONFIG_PATH)) + util.prompt("[You need to specify an editor in {0} to use the --edit function.]".format(CONFIG_PATH)) sys.exit(1) other_entries = [e for e in old_entries if e not in journal.entries] # Edit diff --git a/jrnl/exporters.py b/jrnl/exporters.py index 71919f34..ad2c3186 100644 --- a/jrnl/exporters.py +++ b/jrnl/exporters.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals import os import json from .util import u, slugify @@ -29,7 +29,7 @@ def to_tag_list(journal): elif min(tag_counts)[0] == 0: tag_counts = filter(lambda x: x[0] > 1, tag_counts) result += '[Removed tags that appear only once.]\n' - result += "\n".join(u"{0:20} : {1}".format(tag, n) for n, tag in sorted(tag_counts, reverse=True)) + result += "\n".join("{0:20} : {1}".format(tag, n) for n, tag in sorted(tag_counts, reverse=True)) return result @@ -81,7 +81,7 @@ def export(journal, format, output=None): "markdown": to_md } if format not in maps: - return u"[ERROR: can't export to '{0}'. Valid options are 'md', 'txt', and 'json']".format(format) + return "[ERROR: can't export to '{0}'. Valid options are 'md', 'txt', and 'json']".format(format) if output and os.path.isdir(output): # multiple files return write_files(journal, output, format) else: @@ -90,9 +90,9 @@ def export(journal, format, output=None): try: with codecs.open(output, "w", "utf-8") as f: f.write(content) - return u"[Journal exported to {0}]".format(output) + return "[Journal exported to {0}]".format(output) except IOError as e: - return u"[ERROR: {0} {1}]".format(e.filename, e.strerror) + return "[ERROR: {0} {1}]".format(e.filename, e.strerror) else: return content @@ -111,4 +111,4 @@ def write_files(journal, path, format): content = e.__unicode__() with codecs.open(full_path, "w", "utf-8") as f: f.write(content) - return u"[Journal exported individual files in {0}]".format(path) + return "[Journal exported individual files in {0}]".format(path) From 391269335faa679b036e7e3e9615409018648235 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 2 Sep 2014 13:27:11 -0700 Subject: [PATCH 08/31] Multi-Word tags for DayOne Journals Closes #274 --- jrnl/DayOneJournal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py index 75336560..6d91a9bd 100644 --- a/jrnl/DayOneJournal.py +++ b/jrnl/DayOneJournal.py @@ -65,7 +65,7 @@ class DayOne(Journal.Journal): 'Entry Text': entry.title + "\n" + entry.body, 'Time Zone': str(tzlocal.get_localzone()), 'UUID': entry.uuid, - 'Tags': [tag.strip(self.config['tagsymbols']) for tag in entry.tags] + 'Tags': [tag.strip(self.config['tagsymbols']).replace("_", " ") for tag in entry.tags] } plistlib.writePlist(entry_plist, filename) for entry in self._deleted_entries: From 411b9eb8449f6bfc96b1e466c9b6b8ae64246447 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 2 Sep 2014 13:27:16 -0700 Subject: [PATCH 09/31] Version bump --- CHANGELOG.md | 1 + jrnl/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82180900..402ca773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changelog ### 1.9 (July 21, 2014) +* __1.9.5__ Multi-word tags for DayOne Journals * __1.9.4__ Fixed: Order of journal entries in file correct after --edit'ing * __1.9.3__ Fixed: Tags at the beginning of lines * __1.9.2__ Fixed: Tag search ignores email-addresses (thanks to @mjhoffman65) diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 234e175c..030a9d4b 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -8,7 +8,7 @@ jrnl is a simple journal application for your command line. from __future__ import absolute_import __title__ = 'jrnl' -__version__ = '1.9.4' +__version__ = '1.9.5' __author__ = 'Manuel Ebert' __license__ = 'MIT License' __copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' From 20e4ca2e14c854cbab7c5890e1d9497c5c29143a Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Wed, 17 Sep 2014 08:35:39 -0700 Subject: [PATCH 10/31] Fix docs typo --- docs/encryption.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/encryption.rst b/docs/encryption.rst index 37d341a8..e4a10368 100644 --- a/docs/encryption.rst +++ b/docs/encryption.rst @@ -30,7 +30,7 @@ A note on security While jrnl follows best practises, true security is an illusion. Specifically, jrnl will leave traces in your memory and your shell history -- it's meant to keep journals secure in transit, for example when storing it on an `untrusted `_ services such as Dropbox. If you're concerned about security, disable history logging for journal in your ``.bashrc`` :: - HISTINGNORE="jrnl *" + HISTIGNORE="jrnl *" Manual decryption ----------------- From 00015abf952b35b6d09d166bd6e14b2b365139bb Mon Sep 17 00:00:00 2001 From: Pablo Carranza Date: Mon, 22 Sep 2014 07:42:00 -0400 Subject: [PATCH 11/31] Added how to ignore history appending for zsh --- docs/encryption.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/encryption.rst b/docs/encryption.rst index e4a10368..8ca1faba 100644 --- a/docs/encryption.rst +++ b/docs/encryption.rst @@ -32,6 +32,11 @@ While jrnl follows best practises, true security is an illusion. Specifically, j HISTIGNORE="jrnl *" +If you are using zsh instead of bash, you can get the same behaviour adding this to your ``zshrc`` :: + + setopt HIST_IGNORE_SPACE + alias jrnl=" jrnl" + Manual decryption ----------------- From 3edb28790e8c44a0ddd0c79291c5973f7502b338 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Date: Thu, 25 Sep 2014 10:04:17 -0400 Subject: [PATCH 12/31] Fixed -on today option parsing --- jrnl/time.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jrnl/time.py b/jrnl/time.py index 531293de..378d4c92 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -48,7 +48,10 @@ def parse(date_str, inclusive=False, default_hour=None, default_minute=None): return None if flag is 1: # Date found, but no time. Use the default time. - date = datetime(*date[:3], hour=default_hour or 0, minute=default_minute or 0) + date = datetime(*date[:3], + hour=23 if inclusive else default_hour or 0, + minute=59 if inclusive else default_minute or 0, + second=59 if inclusive else 0) else: date = datetime(*date[:6]) From 0af00be1bac903baf7e323c2b97a73cfe6504542 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 30 Sep 2014 10:16:50 -0700 Subject: [PATCH 13/31] util fixes --- jrnl/util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index b06113c2..946fdb90 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -71,15 +71,18 @@ def py2encode(s): def prompt(msg): """Prints a message to the std err stream defined in util.""" + if not msg: + return if not msg.endswith("\n"): msg += "\n" STDERR.write(u(msg)) def py23_input(msg=""): - STDERR.write(u(msg)) + prompt(msg) return STDIN.readline().strip() def py23_read(msg=""): + prompt(msg) return STDIN.read() def yesno(prompt, default=True): @@ -93,7 +96,7 @@ def load_and_fix_json(json_path): """ with open(json_path) as f: json_str = f.read() - config = fixed = None + config = None try: return json.loads(json_str) except ValueError as e: From 9310b60ed052d2c5deb85bd7ede09d961a2a2dac Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 30 Sep 2014 10:17:07 -0700 Subject: [PATCH 14/31] version bump --- CHANGELOG.md | 1 + jrnl/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 402ca773..d513a596 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changelog ### 1.9 (July 21, 2014) +* __1.9.6__ Fuzzy time parsing improvements (thanks to @pcarranza) * __1.9.5__ Multi-word tags for DayOne Journals * __1.9.4__ Fixed: Order of journal entries in file correct after --edit'ing * __1.9.3__ Fixed: Tags at the beginning of lines diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 030a9d4b..062ca255 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -8,7 +8,7 @@ jrnl is a simple journal application for your command line. from __future__ import absolute_import __title__ = 'jrnl' -__version__ = '1.9.5' +__version__ = '1.9.6' __author__ = 'Manuel Ebert' __license__ = 'MIT License' __copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' From c13029c6682b3d5ec54fcede4e7ebbcab5375a71 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 2 Oct 2014 10:28:39 -0700 Subject: [PATCH 15/31] Installation instructions for homebrew --- docs/installation.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 46df9b3a..9cadd548 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -6,7 +6,11 @@ Getting started Installation ------------ -Install *jrnl* using pip :: +On OS X, the easiest way to install *jrnl* is using `Homebrew `_ :: + + brew install jrnl + +On other platforms, install *jrnl* using pip :: pip install jrnl From 8fd0e2f62dd190ebe646918fcd76c223948f9f85 Mon Sep 17 00:00:00 2001 From: Will Barrett Date: Mon, 6 Oct 2014 16:45:25 -0400 Subject: [PATCH 16/31] Handle situation where a specified jounal is not found. --- .../data/configs/multiple_without_default.json | 16 ++++++++++++++++ features/multiple_journals.feature | 5 +++++ jrnl/cli.py | 5 +++++ 3 files changed, 26 insertions(+) create mode 100644 features/data/configs/multiple_without_default.json diff --git a/features/data/configs/multiple_without_default.json b/features/data/configs/multiple_without_default.json new file mode 100644 index 00000000..042e843a --- /dev/null +++ b/features/data/configs/multiple_without_default.json @@ -0,0 +1,16 @@ +{ + "default_hour": 9, + "timeformat": "%Y-%m-%d %H:%M", + "linewrap": 80, + "encrypt": false, + "editor": "", + "default_minute": 0, + "highlight": true, + "password": "", + "journals": { + "simple": "features/journals/simple.journal", + "work": "features/journals/work.journal", + "ideas": "features/journals/nothing.journal" + }, + "tagsymbols": "@" +} diff --git a/features/multiple_journals.feature b/features/multiple_journals.feature index 0510209b..7c77ff72 100644 --- a/features/multiple_journals.feature +++ b/features/multiple_journals.feature @@ -34,3 +34,8 @@ Feature: Multiple journals Then journal "ideas" should not exist When we run "jrnl ideas 23 july 2012: sell my junk on ebay and make lots of money" Then journal "ideas" should have 1 entry + + Scenario: Gracefully handle a config without a default journal + Given we use the config "multiple_without_default.json" + When we run "jrnl fork this repo and fix something" + Then we should see the message "You have not specified a journal. Either provide a default journal in your config file, or specify one of your journals on the command line." diff --git a/jrnl/cli.py b/jrnl/cli.py index fe105e89..ce54c4a2 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -156,6 +156,11 @@ def run(manual_args=None): config.update(journal_conf) else: # But also just give them a string to point to the journal file config['journal'] = journal_conf + + if config['journal'] is None: + util.prompt("You have not specified a journal. Either provide a default journal in your config file, or specify one of your journals on the command line.") + sys.exit(1) + config['journal'] = os.path.expanduser(os.path.expandvars(config['journal'])) touch_journal(config['journal']) mode_compose, mode_export = guess_mode(args, config) From c64bf402fd840bedee72cb0c2dfb785d192a813c Mon Sep 17 00:00:00 2001 From: jfunction Date: Wed, 15 Oct 2014 14:23:03 +0200 Subject: [PATCH 17/31] Fixed a bug whereby editing a single entry resulted in the plural form being used. Now editing one entry results in "[1 entry modified]" --- jrnl/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index fe105e89..0c2f90bd 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -259,7 +259,7 @@ def run(manual_args=None): if num_deleted: prompts.append("{0} {1} deleted".format(num_deleted, "entry" if num_deleted == 1 else "entries")) if num_edited: - prompts.append("{0} {1} modified".format(num_edited, "entry" if num_deleted == 1 else "entries")) + prompts.append("{0} {1} modified".format(num_edited, "entry" if num_edited == 1 else "entries")) if prompts: util.prompt("[{0}]".format(", ".join(prompts).capitalize())) journal.entries += other_entries From 733009e28232a4b16304719352e1d53136b081df Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 21 Oct 2014 15:33:28 +0200 Subject: [PATCH 18/31] Safer temp file creation --- jrnl/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/util.py b/jrnl/util.py index 946fdb90..49a2b467 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -116,7 +116,7 @@ def load_and_fix_json(json_path): sys.exit(1) def get_text_from_editor(config, template=""): - tmpfile = os.path.join(tempfile.mktemp(prefix="jrnl")) + _, tmpfile = os.path.join(tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt")) with codecs.open(tmpfile, 'w', "utf-8") as f: if template: f.write(template) From 4256052e90fdaf00917addf7ef30c506a4041165 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 21 Oct 2014 15:35:48 +0200 Subject: [PATCH 19/31] Open journal before writing --- jrnl/cli.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index e7f7d83d..b7b980b4 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -165,6 +165,17 @@ def run(manual_args=None): touch_journal(config['journal']) mode_compose, mode_export = guess_mode(args, config) + # open journal file or folder + if os.path.isdir(config['journal']): + if config['journal'].strip("/").endswith(".dayone") or \ + "entries" in os.listdir(config['journal']): + journal = DayOneJournal.DayOne(**config) + else: + util.prompt("[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal'])) + sys.exit(1) + else: + journal = Journal.Journal(journal_name, **config) + # How to quit writing? if "win32" in sys.platform: _exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter" @@ -188,17 +199,6 @@ def run(manual_args=None): else: mode_compose = False - # open journal file or folder - if os.path.isdir(config['journal']): - if config['journal'].strip("/").endswith(".dayone") or \ - "entries" in os.listdir(config['journal']): - journal = DayOneJournal.DayOne(**config) - else: - util.prompt("[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal'])) - sys.exit(1) - else: - journal = Journal.Journal(journal_name, **config) - # Writing mode if mode_compose: raw = " ".join(args.text).strip() From 5b5c7cb6e8e59754b44ba3bea698e3a2af23d6a1 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 21 Oct 2014 18:27:56 +0200 Subject: [PATCH 20/31] Timezone parsing fix --- features/steps/core.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index c4aa2f59..9b0679e0 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -124,10 +124,8 @@ def check_output(context, text=None): def check_output_time_inline(context, text): out = context.stdout_capture.getvalue() local_tz = tzlocal.get_localzone() - utc_time = date_parser.parse(text) - date = utc_time + local_tz._utcoffset - local_date = date.strftime("%Y-%m-%d %H:%M") - assert local_date in out, local_date + local_time = date_parser.parse(text).astimezone(local_tz).strftime("%Y-%m-%d %H:%M") + assert local_time in out, local_time @then('the output should contain "{text}"') def check_output_inline(context, text): From 0f754f97c1d95337d90a2b0bec99e27fdafaf1a4 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 11:30:06 +0100 Subject: [PATCH 21/31] Tests for writing non-unicode entries on prompt --- features/regression.feature | 7 +++++++ features/steps/core.py | 11 +++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/features/regression.feature b/features/regression.feature index 1672afb4..f975a4b1 100644 --- a/features/regression.feature +++ b/features/regression.feature @@ -59,3 +59,10 @@ Feature: Zapped bugs should stay dead. 2014-04-24 09:00 Ran 6.2 miles today in 1:02:03. | I'm feeling sore because I forgot to stretch. """ + + Scenario: Writing an entry at the prompt with non-ascii characters + # https://github.com/maebert/jrnl/issues/295 + Given we use the config "basic.json" + When we run "jrnl" and enter "Crème brûlée & Mötorhead" + Then we should get no error + and the journal should contain "Crème brûlée & Mötorhead" diff --git a/features/steps/core.py b/features/steps/core.py index 9b0679e0..d0ea8460 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -2,9 +2,8 @@ from behave import * from jrnl import cli, Journal, util from dateutil import parser as date_parser import os -import sys +import codecs import json -import pytz import keyring keyring.set_keyring(keyring.backends.file.PlaintextKeyring()) try: @@ -30,7 +29,7 @@ def _parse_args(command): def read_journal(journal_name="default"): with open(cli.CONFIG_PATH) as config_file: config = json.load(config_file) - with open(config['journals'][journal_name]) as journal_file: + with codecs.open(config['journals'][journal_name], 'r', 'utf-8') as journal_file: journal = journal_file.read() return journal @@ -57,7 +56,7 @@ def run_with_input(context, command, inputs=None): buffer = StringIO(text.strip()) util.STDIN = buffer try: - cli.run(args or None) + cli.run(args) context.exit_status = 0 except SystemExit as e: context.exit_status = e.code @@ -66,7 +65,7 @@ def run_with_input(context, command, inputs=None): def run(context, command): args = _parse_args(command) try: - cli.run(args or None) + cli.run(args) context.exit_status = 0 except SystemExit as e: context.exit_status = e.code @@ -184,7 +183,7 @@ def config_var(context, key, value, journal=None): @then('the journal should have {number:d} entry') @then('journal "{journal_name}" should have {number:d} entries') @then('journal "{journal_name}" should have {number:d} entry') -def check_journal_content(context, number, journal_name="default"): +def check_num_entries(context, number, journal_name="default"): journal = open_journal(journal_name) assert len(journal.entries) == number From bbf7f73a3fe228dd6c0a2df4b1d8c31868fbf502 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 11:30:11 +0100 Subject: [PATCH 22/31] Fix for writing non-unicode entries on prompt --- jrnl/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index 49a2b467..db8b0af7 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -79,11 +79,11 @@ def prompt(msg): def py23_input(msg=""): prompt(msg) - return STDIN.readline().strip() + return u(STDIN.readline()).strip() def py23_read(msg=""): prompt(msg) - return STDIN.read() + return u(STDIN.read()) def yesno(prompt, default=True): prompt = prompt.strip() + (" [Y/n]" if default else " [y/N]") From 33cb12d64ff8bc3f518bee883b635598446378fc Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 11:30:15 +0100 Subject: [PATCH 23/31] version bump --- CHANGELOG.md | 1 + jrnl/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d513a596..0f0379f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changelog ### 1.9 (July 21, 2014) +* __1.9.7__ Fixes writing non-ascii entries on the prompt * __1.9.6__ Fuzzy time parsing improvements (thanks to @pcarranza) * __1.9.5__ Multi-word tags for DayOne Journals * __1.9.4__ Fixed: Order of journal entries in file correct after --edit'ing diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 062ca255..29a9cef8 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -8,7 +8,7 @@ jrnl is a simple journal application for your command line. from __future__ import absolute_import __title__ = 'jrnl' -__version__ = '1.9.6' +__version__ = '1.9.7' __author__ = 'Manuel Ebert' __license__ = 'MIT License' __copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' From 1b98b3a3e55eefb202d937a6d0a42baf4af5eb80 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 12:35:31 +0100 Subject: [PATCH 24/31] github_release plug on setup.py --- setup.py | 102 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/setup.py b/setup.py index 1578b452..2abfa6cd 100644 --- a/setup.py +++ b/setup.py @@ -51,13 +51,8 @@ except ImportError: readline_available = False -if sys.argv[-1] == 'publish': - os.system("python setup.py sdist upload") - sys.exit() - base_dir = os.path.dirname(os.path.abspath(__file__)) - def get_version(filename="jrnl/__init__.py"): with open(os.path.join(base_dir, filename)) as initfile: for line in initfile.readlines(): @@ -65,6 +60,71 @@ def get_version(filename="jrnl/__init__.py"): if m: return m.group(1) + +def get_changelog(filename="CHANGELOG.md"): + changelog = {} + current_version = None + with open(os.path.join(base_dir, filename)) as changelog_file: + for line in changelog_file.readlines(): + if line.startswith("* __"): + parts = line.strip("* ").split(" ", 1) + if len(parts) == 2: + current_version, changes = parts[0].strip("_\n"), parts[1] + changelog[current_version] = [changes.strip()] + else: + current_version = parts[0].strip("_\n") + changelog[current_version] = [] + elif line.strip() and current_version and not line.startswith("#"): + changelog[current_version].append(line.strip(" *\n")) + return changelog + +def dist_pypi(): + os.system("python setup.py sdist upload") + sys.exit() + +def dist_github(): + """Creates a release on the maebert/jrnl repository on github""" + import requests + import keyring + import getpass + version = get_version() + version_tuple = version.split(".") + changes_since_last_version = ["* __{}__: {}".format(key, "\n".join(changes)) for key, changes in get_changelog().items() if key.startswith("{}.{}".format(*version_tuple))] + changes_since_last_version = "\n".join(sorted(changes_since_last_version, reverse=True)) + payload = { + "tag_name": version, + "target_commitish": "master", + "name": version, + "body": "Changes in Version {}.{}: \n\n{}".format(version_tuple[0], version_tuple[1], changes_since_last_version) + } + print "Preparing release {}...".format(version) + username = keyring.get_password("github", "__default_user") or raw_input("Github username: ") + password = keyring.get_password("github", username) or getpass.getpass() + otp = raw_input("One Time Token: ") + response = requests.post("https://api.github.com/repos/maebert/jrnl/releases", headers={"X-GitHub-OTP": otp}, json=payload, auth=(username, password)) + if response.status_code in (403, 404): + print "Authentication error." + else: + keyring.set_password("github", "__default_user", username) + keyring.set_password("github", username, password) + if response.status_code > 299: + if "message" in response.json(): + print "Error: {}".format(response.json()['message']) + for error_dict in response.json().get('errors', []): + print "*", error_dict + else: + print "Unkown error" + print response.text + else: + print "Release created." + sys.exit() + +if sys.argv[-1] == 'publish': + dist_pypi() + +if sys.argv[-1] == 'github_release': + dist_github() + conditional_dependencies = { "pyreadline>=2.0": not readline_available and "win32" in sys.platform, "readline>=6.2": not readline_available and "win32" not in sys.platform, @@ -92,24 +152,24 @@ setup( }, long_description=__doc__, entry_points={ - 'console_scripts': [ - 'jrnl = jrnl:run', - ], + "console_scripts": [ + "jrnl = jrnl:run", + ] }, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: End Users/Desktop', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Topic :: Office/Business :: News/Diary', - 'Topic :: Text Processing' + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Topic :: Office/Business :: News/Diary", + "Topic :: Text Processing" ], # metadata for upload to PyPI author = "Manuel Ebert", From fbecc6d062ac80c52722c54634baacf01e3ec299 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 12:38:57 +0100 Subject: [PATCH 25/31] Travis update --- .travis.yml | 4 ++-- setup.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35f34a63..93c1f82b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ python: - "3.3" - "3.4" install: - - "pip install -e . --use-mirrors" - - "pip install pycrypto>=2.6 --use-mirrors" + - "pip install -e ." + - "pip install pycrypto>=2.6" - "pip install -q behave" # command to run tests script: diff --git a/setup.py b/setup.py index 2abfa6cd..0208acdf 100644 --- a/setup.py +++ b/setup.py @@ -97,26 +97,26 @@ def dist_github(): "name": version, "body": "Changes in Version {}.{}: \n\n{}".format(version_tuple[0], version_tuple[1], changes_since_last_version) } - print "Preparing release {}...".format(version) + print("Preparing release {}...".format(version)) username = keyring.get_password("github", "__default_user") or raw_input("Github username: ") password = keyring.get_password("github", username) or getpass.getpass() otp = raw_input("One Time Token: ") response = requests.post("https://api.github.com/repos/maebert/jrnl/releases", headers={"X-GitHub-OTP": otp}, json=payload, auth=(username, password)) if response.status_code in (403, 404): - print "Authentication error." + print("Authentication error.") else: keyring.set_password("github", "__default_user", username) keyring.set_password("github", username, password) if response.status_code > 299: if "message" in response.json(): - print "Error: {}".format(response.json()['message']) + print("Error: {}".format(response.json()['message'])) for error_dict in response.json().get('errors', []): - print "*", error_dict + print("*", error_dict) else: - print "Unkown error" - print response.text + print("Unkown error") + print(response.text) else: - print "Release created." + print("Release created.") sys.exit() if sys.argv[-1] == 'publish': From 155b8e8f723252b2f248e96de3fba6f48d59e003 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Date: Thu, 6 Nov 2014 15:36:03 -0500 Subject: [PATCH 26/31] Added basic logging feature to understand how is configuration loaded --- jrnl/cli.py | 18 ++++++++++++++++++ jrnl/util.py | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/jrnl/cli.py b/jrnl/cli.py index b7b980b4..90feda09 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -17,16 +17,19 @@ import jrnl import os import argparse import sys +import logging xdg_config = os.environ.get('XDG_CONFIG_HOME') CONFIG_PATH = os.path.join(xdg_config, "jrnl") if xdg_config else os.path.expanduser('~/.jrnl_config') PYCRYPTO = install.module_exists("Crypto") +log = logging.getLogger(__name__) def parse_args(args=None): parser = argparse.ArgumentParser() parser.add_argument('-v', '--version', dest='version', action="store_true", help="prints version information and exits") parser.add_argument('-ls', dest='ls', action="store_true", help="displays accessible journals") + parser.add_argument('-d', '--debug', dest='debug', action='store_true', help='execute in debug mode') composing = parser.add_argument_group('Composing', 'To write an entry simply write it on the command line, e.g. "jrnl yesterday at 1pm: Went to the gym."') composing.add_argument('text', metavar='', nargs="*") @@ -90,6 +93,7 @@ def decrypt(journal, filename=None): def touch_journal(filename): """If filename does not exist, touch the file""" if not os.path.exists(filename): + log.debug('Creating journal file %s', filename) util.prompt("[Journal created at {0}]".format(filename)) open(filename, 'a').close() @@ -114,8 +118,15 @@ def update_config(config, new_config, scope, force_local=False): config.update(new_config) +def configure_logger(debug=False): + logging.basicConfig(level=logging.DEBUG if debug else logging.INFO, + format='%(levelname)-8s %(name)-12s %(message)s') + logging.getLogger('parsedatetime').setLevel(logging.INFO) # disable parsedatetime debug logging + + def run(manual_args=None): args = parse_args(manual_args) + configure_logger(args.debug) args.text = [p.decode('utf-8') if util.PY2 and not isinstance(p, unicode) else p for p in args.text] if args.version: version_str = "{0} version {1}".format(jrnl.__title__, jrnl.__version__) @@ -123,8 +134,10 @@ def run(manual_args=None): sys.exit(0) if not os.path.exists(CONFIG_PATH): + log.debug('Configuration file not found, installing jrnl...') config = install.install_jrnl(CONFIG_PATH) else: + log.debug('Reading configuration from file %s', CONFIG_PATH) config = util.load_and_fix_json(CONFIG_PATH) install.upgrade_config(config, config_path=CONFIG_PATH) @@ -132,6 +145,7 @@ def run(manual_args=None): print(util.py2encode(list_journals(config))) sys.exit(0) + log.debug('Using configuration "%s"', config) original_config = config.copy() # check if the configuration is supported by available modules if config['encrypt'] and not PYCRYPTO: @@ -151,8 +165,10 @@ def run(manual_args=None): except: pass + log.debug('Using journal "%s"', journal_name) journal_conf = config['journals'].get(journal_name) if type(journal_conf) is dict: # We can override the default config on a by-journal basis + log.debug('Updating configuration with specific jourlnal overrides %s', journal_conf) config.update(journal_conf) else: # But also just give them a string to point to the journal file config['journal'] = journal_conf @@ -163,6 +179,7 @@ def run(manual_args=None): config['journal'] = os.path.expanduser(os.path.expandvars(config['journal'])) touch_journal(config['journal']) + log.debug('Using journal path %(journal)s', config) mode_compose, mode_export = guess_mode(args, config) # open journal file or folder @@ -204,6 +221,7 @@ def run(manual_args=None): raw = " ".join(args.text).strip() if util.PY2 and type(raw) is not unicode: raw = raw.decode(sys.getfilesystemencoding()) + log.debug('Appending raw line "%s" to journal "%s"', raw, journal_name) journal.new_entry(raw) util.prompt("[Entry added to {0} journal]".format(journal_name)) journal.write() diff --git a/jrnl/util.py b/jrnl/util.py index db8b0af7..47d34314 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -13,6 +13,7 @@ import tempfile import subprocess import codecs import unicodedata +import logging PY3 = sys.version_info[0] == 3 PY2 = sys.version_info[0] == 2 @@ -22,6 +23,8 @@ STDOUT = sys.stdout TEST = False __cached_tz = None +log = logging.getLogger(__name__) + def getpass(prompt="Password: "): if not TEST: @@ -96,18 +99,22 @@ def load_and_fix_json(json_path): """ with open(json_path) as f: json_str = f.read() + log.debug('Configuration file %s read correctly', json_path) config = None try: return json.loads(json_str) except ValueError as e: + log.debug('Could not parse configuration %s: %s', json_str, e) # Attempt to fix extra , json_str = re.sub(r",[ \n]*}", "}", json_str) # Attempt to fix missing , json_str = re.sub(r"([^{,]) *\n *(\")", r"\1,\n \2", json_str) try: + log.debug('Attempting to reload automatically fixed configuration file %s', json_str) config = json.loads(json_str) with open(json_path, 'w') as f: json.dump(config, f, indent=2) + log.debug('Fixed configuration saved in file %s', json_path) prompt("[Some errors in your jrnl config have been fixed for you.]") return config except ValueError as e: From 2511785dd45524d9cecdbd6822d07222cbb2acd6 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Date: Thu, 6 Nov 2014 19:15:11 -0500 Subject: [PATCH 27/31] Added exception info to the output when it cannot be loaded --- jrnl/util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index 47d34314..0a2727cf 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -104,13 +104,15 @@ def load_and_fix_json(json_path): try: return json.loads(json_str) except ValueError as e: - log.debug('Could not parse configuration %s: %s', json_str, e) + log.debug('Could not parse configuration %s: %s', json_str, e, + exc_info=True) # Attempt to fix extra , json_str = re.sub(r",[ \n]*}", "}", json_str) # Attempt to fix missing , json_str = re.sub(r"([^{,]) *\n *(\")", r"\1,\n \2", json_str) try: - log.debug('Attempting to reload automatically fixed configuration file %s', json_str) + log.debug('Attempting to reload automatically fixed configuration file %s', + json_str) config = json.loads(json_str) with open(json_path, 'w') as f: json.dump(config, f, indent=2) @@ -118,6 +120,7 @@ def load_and_fix_json(json_path): prompt("[Some errors in your jrnl config have been fixed for you.]") return config except ValueError as e: + log.debug('Could not load fixed configuration: %s', e, exc_info=True) prompt("[There seems to be something wrong with your jrnl config at {0}: {1}]".format(json_path, e.message)) prompt("[Entry was NOT added to your journal]") sys.exit(1) From e3329716c1bac5085f951dc487da4a2aed6f1d5a Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Mon, 17 Nov 2014 15:36:43 +0100 Subject: [PATCH 28/31] Fixes #308 --- CHANGELOG.md | 1 + jrnl/__init__.py | 2 +- jrnl/util.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0379f6..4dd5e1d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changelog ### 1.9 (July 21, 2014) +* __1.9.8__ Fixes a problem with temporary files on windows * __1.9.7__ Fixes writing non-ascii entries on the prompt * __1.9.6__ Fuzzy time parsing improvements (thanks to @pcarranza) * __1.9.5__ Multi-word tags for DayOne Journals diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 29a9cef8..44bf8eac 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -8,7 +8,7 @@ jrnl is a simple journal application for your command line. from __future__ import absolute_import __title__ = 'jrnl' -__version__ = '1.9.7' +__version__ = '1.9.8' __author__ = 'Manuel Ebert' __license__ = 'MIT License' __copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' diff --git a/jrnl/util.py b/jrnl/util.py index db8b0af7..513ba8d7 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -116,7 +116,7 @@ def load_and_fix_json(json_path): sys.exit(1) def get_text_from_editor(config, template=""): - _, tmpfile = os.path.join(tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt")) + _, tmpfile = tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt") with codecs.open(tmpfile, 'w', "utf-8") as f: if template: f.write(template) From 511952e20308ad9943f141e6d9549677371df0ef Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Sat, 29 Nov 2014 18:36:42 +0700 Subject: [PATCH 29/31] Icon update! --- docs/_themes/jrnl/static/img/favicon-152.png | Bin 3261 -> 12511 bytes docs/_themes/jrnl/static/img/favicon.ico | Bin 5558 -> 5558 bytes docs/_themes/jrnl/static/img/logo.png | Bin 2783 -> 5390 bytes docs/_themes/jrnl/static/img/logo@2x.png | Bin 5598 -> 13865 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/_themes/jrnl/static/img/favicon-152.png b/docs/_themes/jrnl/static/img/favicon-152.png index ac658d9ce73f8bddb02c753987998b52bc7325a2..539d40cc834f1c65e59b4e52eacdea1206ee3a0e 100644 GIT binary patch literal 12511 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 KtqLvc$o~M=2{qmT diff --git a/docs/_themes/jrnl/static/img/favicon.ico b/docs/_themes/jrnl/static/img/favicon.ico index 7c9c2c1e2ed5cbf83ae4302ce72f729bef91ea48..d6197b35b8e02b11fc2980ba30a11bde265f6305 100644 GIT binary patch literal 5558 zcmd^DX;YL}7VepvACUP6ewxb9n2%HLiMZgtv_PYdBq}Z_L@^@tzAu7+3obEZW@3y{ zP*FoB6L+Htri>z}=m;8hoM=&;d7g9M`*sU4RjK6Lbk(VI-*cbyJm=i*>U;0&@%+y7 zd(W6L9{4eyH~;AI{K@0-yzz#4F8hPWGZC>j5d*~YXBcs>`vk_9h#zLW*Y%9~$9wa0 z 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 diff --git a/docs/_themes/jrnl/static/img/logo.png b/docs/_themes/jrnl/static/img/logo.png index 1ea79cf1583927ca8bdbde9f8779fecfc567b72a..900ebac66c05e4e5f2838ee82ca704c0501f19e2 100644 GIT binary patch literal 5390 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 literal 2783 zcmc&$c{tQx7ypiZ$&w~JBZX{*kSvkNOm;#)OD3`uV;g1&W1G-e#t_n&sVpUfD3UFb z2-$ZT!p~OrefNIf|KI=L`#k5K=bn3>^PF?;IiGW%T9_GeaR_q&0KkQS8(4wd9wZkw z7I5#5YH9=-6Ivf}hYeg|Y!BkWGrJ%BA2g^x^k0JLUnB>ELZLvzyMfle?t$3*0d4>m zi@o&F#|!Ox-_PxmZ-57RSz8zYI2;iMH|_*yFHu4Qt!>03>!lwWX9m8`m%TuD$mq4o z7?re8HzY_fx(GQYw?kTjn%$Z#yZmRBIyJ~`LSo-@FTM(X;4hRQ8BO3%cgt%t6OqVA zI8fL`ksAvhZ1*YI-~0`a?9f*$!=+-IbMNZ2oLxlv6~REQb%;zU=}x zff|dqo_~1Pgoq)|Cns~S5BbVO&@(}+csz3&hl-FYsAyHeEP%E7vkKNCNv4#}KLZ^6)5IdG4n3oF(+hdas-rlq{?1IYqwA zVF_hUy4BI-)$F1jmKuOQ`3mGf!rea)+X(0?BmRR)xH|LE(QFkgGkchC7tl)IPw%s$c&TynfL%!&a)Hzj!=Z41ajHNUr%-x##+b!u7Y>Nq?UVM@Y#^(CLXq9Hz z2WEfBc{s9fB=%3kxa95ROl2jNXCnM&jIWTtb~F~vtc+x?V8Y~pm%jVlk)Zs%`u$ZY zLH85n;lHP1^Q8cbz;337XJZP1Hzmt{WkX?d5D6@fJ0^!Ups*;GF*Lz4E71)wA*Uyu z0J;DsyrN4?+m{bW+Okbv)P?AECi*wB3^=#Gc*d>waIVdy*w()zdkql1tit$eZ9qxw zDkqTE6W#Wp96-hsSsE5RbE`jaM-Dfg1Mb`{1PcWP=OtNAM#uI=ECkq$qS!^xtQ%-PtHr+D=_Qf3j4$J-}$jX$zeny2Cfcm<()vv^h8x=l#UmvNa|m|c;l zvo2SqLe{se-}3RF1Q@nzm(yTiyZ_E3ewz0>kzsB39_((iF}SUCM! z$`U57H2FxhQ)*9tx-+jmok--t;g-?ml@+0*paN>UGb{=cbzCsTM;{StH>M-rvT}1U zzACBiN%K}Jx{~XsQj!|`o$8~tfO(PPqL-dB#a|cN zf-`UUOryU`#0q@QywS4{XAxn6_?33g7au#TLtzS#dD3{5-_B`(PYZn@PiX9XAHgJX z-H<4U8nY-rcSDfp<%79x81Ou>SSx64+Z<~>7-HO-MPhHzdLM3Lym1+8OEx{bxEppE zEKimON?Wk`oPjAKi4{3#r|Tm3iC4J9t^f1u0?^bLX2KB|tgFguTepvWE2DxQzu;KH z4Zhj+PqwU88)!i)6%InBZ~cqQIQ$V{Xc_GF-6$|HkT+8mPjXEys~8Rm{cZ&OtoB#g zAHG;9_voLK*|)&hT7x!248xf2*B0|m3ADa2SCDOn&c2G#Hq4`E>Muf!lDM{tpoT1- z{qsmTc=esb)BYzP(FZ)Wn~U~Eriz(9{n2IW6{TS#j|e>JuLcXMmZ()A$gTeDJ^MV7 zAN2FXYKbF=W;0#HFaJgIku)Ra>XkJ2x; zRrZ)vT~ueC2(yGIRHS#8B7MGua)BePAcQ4Lk#>0a| zF>1z-w3FSWxgb+Fovx!bQ#pkJ(A`M`OWCJ%>a>&Gen;YRvVHhI&kH1q_e&jAbtM7r zr18<`=UVOcd)`&0+>hDUa=JWjpO{p0XfgELTn#>YJQuS&L)p>UDOmOPT7r>r`!mJU zl8#LWL!b5V@t7)2ZK<0_n(}HpGM!jqB&nSQ}G&3mCM@Ih#m%}p~ diff --git a/docs/_themes/jrnl/static/img/logo@2x.png b/docs/_themes/jrnl/static/img/logo@2x.png index 9cc3d76b6447c42cf7c19f859d3a7ccb89c01387..2ef28d1a3d9b091eb342870399258796135615da 100644 GIT binary patch literal 13865 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ⅇtFjiCzo&hr@cM^=3a z*d(W+L_~1H_atni#9x;MMPp|;C5`T*+U8&7&&ikvi%b+|zLx>dViz&ND|7N)&F~~& z``D-AfoghJ+LEN$6UK0tUs35V<%5Z07u7@kjGEE$KQrPBL|F`72goa3>JGgI$R}LX zvcPYJ^jL|8b!-2t$)a>VZG#r&wKAI6BN6+$JspP0+>6FX^5wHWS0q-L#1k)U65pfA zDBDQS(l^`nd;X`_4=P;RTsAKl5Jj2>#so%P#xbG59(ip#RS|ndJqFEKRa$|yyF%T~ znWvCVsv+>%4$c2-;6`%koskfG&c!u5}HaUApG zReZ89%GYzT)4`5J@?u(BYKPpocqJu7S$!hx;$4GSkDiO36n+H$gHP$Zsr(YRr5`

kKVg&1=NMS$A)mbasg$Qb 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 literal 5598 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 Og4C2W70VSYg8m0xQL!Tc From 53c9663bb9c8c83b86058ee8cc8f6c9201cb3737 Mon Sep 17 00:00:00 2001 From: seinfield Date: Sun, 7 Dec 2014 02:55:49 -0500 Subject: [PATCH 30/31] corrects issue #316 --- jrnl/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index 90feda09..014c8842 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -256,14 +256,14 @@ def run(manual_args=None): elif args.encrypt is not False: encrypt(journal, filename=args.encrypt) # Not encrypting to a separate file: update config! - if not args.encrypt: + if not args.encrypt or args.encrypt == config['journal]: update_config(original_config, {"encrypt": True}, journal_name, force_local=True) install.save_config(original_config, config_path=CONFIG_PATH) elif args.decrypt is not False: decrypt(journal, filename=args.decrypt) # Not decrypting to a separate file: update config! - if not args.decrypt: + if not args.decrypt or args.decrypt == config['journal']: update_config(original_config, {"encrypt": False}, journal_name, force_local=True) install.save_config(original_config, config_path=CONFIG_PATH) From 5d9cd1c324c5d87cc8f9510449a54ef7c43646c0 Mon Sep 17 00:00:00 2001 From: seinfield Date: Sun, 7 Dec 2014 03:00:41 -0500 Subject: [PATCH 31/31] corrected typo --- jrnl/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index 014c8842..35764734 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -256,7 +256,7 @@ def run(manual_args=None): elif args.encrypt is not False: encrypt(journal, filename=args.encrypt) # Not encrypting to a separate file: update config! - if not args.encrypt or args.encrypt == config['journal]: + if not args.encrypt or args.encrypt == config['journal']: update_config(original_config, {"encrypt": True}, journal_name, force_local=True) install.save_config(original_config, config_path=CONFIG_PATH)