mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
|
b45e52f743 | ||
|
474bf0a71a | ||
|
799ff762b2 | ||
|
43f1166ecf | ||
|
acf41a153a | ||
|
19c57ecf70 | ||
|
94b53b9247 | ||
|
9603f7d3ad | ||
|
cde55ad540 | ||
|
1013d77173 | ||
|
76881f66e9 | ||
|
e6cfeabc17 | ||
|
17b439eba4 | ||
|
c8e5d1ff34 | ||
|
bd6edffc81 | ||
|
e9f691e399 | ||
|
05e63dc76a | ||
|
c81f0e0c1d | ||
|
afcccb78c1 |
11 changed files with 78 additions and 31 deletions
|
@ -4,6 +4,8 @@ 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
|
||||
* __1.9.3__ Fixed: Tags at the beginning of lines
|
||||
|
|
|
@ -30,7 +30,12 @@ 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 <http://techcrunch.com/2014/04/09/condoleezza-rice-joins-dropboxs-board/>`_ services such as Dropbox. If you're concerned about security, disable history logging for journal in your ``.bashrc`` ::
|
||||
|
||||
HISTINGNORE="jrnl *"
|
||||
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
|
||||
-----------------
|
||||
|
|
|
@ -6,7 +6,11 @@ Getting started
|
|||
Installation
|
||||
------------
|
||||
|
||||
Install *jrnl* using pip ::
|
||||
On OS X, the easiest way to install *jrnl* is using `Homebrew <http://brew.sh/>`_ ::
|
||||
|
||||
brew install jrnl
|
||||
|
||||
On other platforms, install *jrnl* using pip ::
|
||||
|
||||
pip install jrnl
|
||||
|
||||
|
|
16
features/data/configs/multiple_without_default.json
Normal file
16
features/data/configs/multiple_without_default.json
Normal file
|
@ -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": "@"
|
||||
}
|
|
@ -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."
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
@ -124,10 +123,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):
|
||||
|
@ -186,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
|
||||
|
||||
|
|
|
@ -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.7'
|
||||
__author__ = 'Manuel Ebert'
|
||||
__license__ = 'MIT License'
|
||||
__copyright__ = 'Copyright 2013 - 2014 Manuel Ebert'
|
||||
|
|
29
jrnl/cli.py
29
jrnl/cli.py
|
@ -156,10 +156,26 @@ 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)
|
||||
|
||||
# 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"
|
||||
|
@ -183,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()
|
||||
|
@ -259,7 +264,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
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
13
jrnl/util.py
13
jrnl/util.py
|
@ -71,16 +71,19 @@ 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))
|
||||
return STDIN.readline().strip()
|
||||
prompt(msg)
|
||||
return u(STDIN.readline()).strip()
|
||||
|
||||
def py23_read(msg=""):
|
||||
return STDIN.read()
|
||||
prompt(msg)
|
||||
return u(STDIN.read())
|
||||
|
||||
def yesno(prompt, default=True):
|
||||
prompt = prompt.strip() + (" [Y/n]" if default else " [y/N]")
|
||||
|
@ -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:
|
||||
|
@ -113,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)
|
||||
|
|
Loading…
Add table
Reference in a new issue