From afcccb78c13e899f7c87b1dbdbc06bb34409e247 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Wed, 17 Sep 2014 08:35:39 -0700 Subject: [PATCH 01/14] 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 c81f0e0c1dd9f40fe1f286d77a3aba0565713993 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Date: Mon, 22 Sep 2014 07:42:00 -0400 Subject: [PATCH 02/14] 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 e9f691e3997fde872435a101f348990013c82b9c Mon Sep 17 00:00:00 2001 From: Pablo Carranza Date: Thu, 25 Sep 2014 10:04:17 -0400 Subject: [PATCH 03/14] 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 c8e5d1ff3415aaeee9a09b3ef9258cfb38f5c03b Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 30 Sep 2014 10:16:50 -0700 Subject: [PATCH 04/14] 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 17b439eba49ea155b3aff27b8ce205f87c19259b Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 30 Sep 2014 10:17:07 -0700 Subject: [PATCH 05/14] 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 e6cfeabc178a1922af5822fc3e429b28b0bc2e59 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 2 Oct 2014 10:28:39 -0700 Subject: [PATCH 06/14] 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 76881f66e93b715a3db1e744f3ed020338e989fb Mon Sep 17 00:00:00 2001 From: Will Barrett Date: Mon, 6 Oct 2014 16:45:25 -0400 Subject: [PATCH 07/14] 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 1013d77173a9623836dcc1601dca4a735f97839c Mon Sep 17 00:00:00 2001 From: jfunction Date: Wed, 15 Oct 2014 14:23:03 +0200 Subject: [PATCH 08/14] 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 94b53b9247aa5863579976dd1e2d74ba0d55af4d Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 21 Oct 2014 15:33:28 +0200 Subject: [PATCH 09/14] 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 19c57ecf70a48495a1bbe9704cd30805372eef84 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 21 Oct 2014 15:35:48 +0200 Subject: [PATCH 10/14] 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 acf41a153ad01c923f1afa230bde4544eda463ec Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Tue, 21 Oct 2014 18:27:56 +0200 Subject: [PATCH 11/14] 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 43f1166ecfe4ac18089d6ed48ab06f3d51652eed Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 11:30:06 +0100 Subject: [PATCH 12/14] 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 799ff762b2d7ccfffee0cac40b360ab9789a6004 Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 11:30:11 +0100 Subject: [PATCH 13/14] 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 474bf0a71abd72b9c713f41aeb4be5c648f3c30f Mon Sep 17 00:00:00 2001 From: Manuel Ebert Date: Thu, 6 Nov 2014 11:30:15 +0100 Subject: [PATCH 14/14] 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'