mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-18 03:58:32 +02:00
Improve cli.py readability and reduce complexity.
Refactored implicit run modes into explicit, individual commands.
This commit is contained in:
parent
8a6563d958
commit
36430c5e8b
1 changed files with 201 additions and 123 deletions
324
jrnl/cli.py
324
jrnl/cli.py
|
@ -18,6 +18,7 @@ import jrnl
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -53,26 +54,183 @@ def parse_args(args=None):
|
||||||
return parser.parse_args(args)
|
return parser.parse_args(args)
|
||||||
|
|
||||||
|
|
||||||
def guess_mode(args, config):
|
class CommandEnum:
|
||||||
"""Guesses the mode (compose, read or export) from the given arguments"""
|
READ = 1
|
||||||
compose = True
|
COMPOSE_NEW_ENTRY = 2
|
||||||
export = False
|
EDIT_EXISTING_ENTRY = 3 # Existing entries with external editor
|
||||||
import_ = False
|
EXPORT = 4
|
||||||
if args.import_ is not False:
|
IMPORT = 5
|
||||||
compose = False
|
LS = 6
|
||||||
export = False
|
VERSION = 7
|
||||||
import_ = True
|
LIST_TAGS = 8
|
||||||
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)):
|
ENCRYPT = 9
|
||||||
compose = False
|
DECRYPT = 10
|
||||||
export = True
|
|
||||||
elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred)):
|
|
||||||
|
def guess_command(args, config):
|
||||||
|
"""Guess what the user wants to do from the given arguments"""
|
||||||
|
|
||||||
|
if args.version is not False:
|
||||||
|
return CommandEnum.VERSION
|
||||||
|
elif args.ls is not False:
|
||||||
|
return CommandEnum.LS
|
||||||
|
elif args.import_ is not False:
|
||||||
|
return CommandEnum.IMPORT
|
||||||
|
elif args.tags is not False:
|
||||||
|
return CommandEnum.LIST_TAGS
|
||||||
|
elif args.edit is not False:
|
||||||
|
return CommandEnum.EDIT_EXISTING_ENTRY
|
||||||
|
elif args.export is not False:
|
||||||
|
return CommandEnum.EXPORT
|
||||||
|
elif args.decrypt is not False:
|
||||||
|
return CommandEnum.DECRYPT
|
||||||
|
elif args.encrypt is not False:
|
||||||
|
return CommandEnum.ENCRYPT
|
||||||
|
elif any((args.start_date, args.end_date, args.on_date, args.limit, args.strict, args.starred, args.short)):
|
||||||
# Any sign of displaying stuff?
|
# Any sign of displaying stuff?
|
||||||
compose = False
|
return CommandEnum.READ
|
||||||
elif args.text and all(word[0] in config['tagsymbols'] for word in " ".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?
|
# No date and only tags?
|
||||||
compose = False
|
return CommandEnum.READ
|
||||||
|
|
||||||
return compose, export, import_
|
return CommandEnum.COMPOSE_NEW_ENTRY
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_list_tags(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'list_tags'")
|
||||||
|
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
filter_journal(journal, args)
|
||||||
|
|
||||||
|
print(util.py2encode(plugins.get_exporter("tags").export(journal)))
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_read(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'read'")
|
||||||
|
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
filter_journal(journal, args)
|
||||||
|
|
||||||
|
print(util.py2encode(journal.pprint(short=args.short)))
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_new_entry(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'write'")
|
||||||
|
|
||||||
|
if args.text:
|
||||||
|
raw = " ".join(args.text).strip()
|
||||||
|
else:
|
||||||
|
if "win32" in sys.platform:
|
||||||
|
_exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter"
|
||||||
|
else:
|
||||||
|
_exit_multiline_code = "press Ctrl+D"
|
||||||
|
|
||||||
|
if not sys.stdin.closed and not sys.stdin.isatty():
|
||||||
|
# Piping data into jrnl
|
||||||
|
raw = util.py23_read()
|
||||||
|
elif config['editor']:
|
||||||
|
raw = util.get_text_from_editor(config)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
raw = util.py23_read("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n")
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
util.prompt("[Entry NOT saved to journal.]")
|
||||||
|
sys.exit(0)
|
||||||
|
if raw:
|
||||||
|
args.text = [raw]
|
||||||
|
else:
|
||||||
|
print("No text exit...")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
if util.PY2 and type(raw) is not unicode:
|
||||||
|
raw = raw.decode(sys.getfilesystemencoding())
|
||||||
|
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
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()
|
||||||
|
|
||||||
|
def do_command_edit_existing_entry(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'edit existing entry'")
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
|
||||||
|
if not config['editor']:
|
||||||
|
util.prompt("[{1}ERROR{2}: You need to specify an editor in {0} to use the --edit function.]".format(install.CONFIG_FILE_PATH, ERROR_COLOR, RESET_COLOR))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
old_entries = journal.entries
|
||||||
|
filter_journal(journal, args)
|
||||||
|
other_entries = [e for e in old_entries if e not in journal.entries]
|
||||||
|
# Edit
|
||||||
|
old_num_entries = len(journal)
|
||||||
|
edited = util.get_text_from_editor(config, journal.editable_str())
|
||||||
|
journal.parse_editable_str(edited)
|
||||||
|
num_deleted = old_num_entries - len(journal)
|
||||||
|
num_edited = len([e for e in journal.entries if e.modified])
|
||||||
|
prompts = []
|
||||||
|
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"))
|
||||||
|
if prompts:
|
||||||
|
util.prompt("[{0}]".format(", ".join(prompts).capitalize()))
|
||||||
|
journal.entries += other_entries
|
||||||
|
journal.sort()
|
||||||
|
journal.write()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_import(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'import'")
|
||||||
|
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
|
||||||
|
plugins.get_importer(args.import_).import_(journal, args.input)
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_export(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'export'")
|
||||||
|
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
filter_journal(journal, args)
|
||||||
|
|
||||||
|
exporter = plugins.get_exporter(args.export)
|
||||||
|
print(exporter.export(journal, args.output))
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_encrypt(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'encrypt'")
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
|
||||||
|
encrypt(journal, filename=args.encrypt)
|
||||||
|
# Not encrypting to a separate file: update config!
|
||||||
|
if not args.encrypt:
|
||||||
|
update_config(original_config, {"encrypt": True}, journal_name, force_local=True)
|
||||||
|
install.save_config(original_config)
|
||||||
|
|
||||||
|
|
||||||
|
def do_command_decrypt(journal_name, config, original_config, args):
|
||||||
|
log.debug("Running command 'decrypt'")
|
||||||
|
journal = Journal.open_journal(journal_name, config)
|
||||||
|
|
||||||
|
decrypt(journal, filename=args.decrypt)
|
||||||
|
# Not decrypting to a separate file: update config!
|
||||||
|
if not args.decrypt:
|
||||||
|
update_config(original_config, {"encrypt": False}, journal_name, force_local=True)
|
||||||
|
install.save_config(original_config)
|
||||||
|
|
||||||
|
|
||||||
|
action_for = {
|
||||||
|
CommandEnum.LIST_TAGS: do_command_list_tags,
|
||||||
|
CommandEnum.READ: do_command_read,
|
||||||
|
CommandEnum.COMPOSE_NEW_ENTRY: do_command_new_entry,
|
||||||
|
CommandEnum.EDIT_EXISTING_ENTRY: do_command_edit_existing_entry,
|
||||||
|
CommandEnum.IMPORT: do_command_import,
|
||||||
|
CommandEnum.EXPORT: do_command_export,
|
||||||
|
CommandEnum.ENCRYPT: do_command_encrypt,
|
||||||
|
CommandEnum.DECRYPT: do_command_decrypt,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def encrypt(journal, filename=None):
|
def encrypt(journal, filename=None):
|
||||||
|
@ -133,25 +291,38 @@ def configure_logger(debug=False):
|
||||||
logging.getLogger('parsedatetime').setLevel(logging.INFO) # disable parsedatetime debug logging
|
logging.getLogger('parsedatetime').setLevel(logging.INFO) # disable parsedatetime debug logging
|
||||||
|
|
||||||
|
|
||||||
|
def filter_journal(journal, args):
|
||||||
|
log.debug("Performing filtering...")
|
||||||
|
|
||||||
|
if args.on_date:
|
||||||
|
args.start_date = args.end_date = args.on_date
|
||||||
|
journal.filter(tags=args.text,
|
||||||
|
start_date=args.start_date, end_date=args.end_date,
|
||||||
|
strict=args.strict,
|
||||||
|
short=args.short,
|
||||||
|
starred=args.starred)
|
||||||
|
journal.limit(args.limit)
|
||||||
|
|
||||||
def run(manual_args=None):
|
def run(manual_args=None):
|
||||||
args = parse_args(manual_args)
|
args = parse_args(manual_args)
|
||||||
configure_logger(args.debug)
|
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]
|
args.text = [p.decode('utf-8') if util.PY2 and not isinstance(p, unicode) else p for p in args.text]
|
||||||
|
|
||||||
if args.version:
|
if args.version:
|
||||||
version_str = "{0} version {1}".format(jrnl.__title__, jrnl.__version__)
|
version_str = "{0} version {1}".format(jrnl.__title__, jrnl.__version__)
|
||||||
print(util.py2encode(version_str))
|
print(util.py2encode(version_str))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
config = install.load_or_install_jrnl()
|
config = install.load_or_install_jrnl()
|
||||||
|
log.debug('Using configuration "%s"', config)
|
||||||
|
original_config = config.copy()
|
||||||
|
|
||||||
if args.ls:
|
if args.ls:
|
||||||
util.prnt(list_journals(config))
|
util.prnt(list_journals(config))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
log.debug('Using configuration "%s"', config)
|
# Guess journal name
|
||||||
original_config = config.copy()
|
# If the first textual argument points to a journal file, use this!
|
||||||
|
|
||||||
# If the first textual argument points to a journal file,
|
|
||||||
# use this!
|
|
||||||
journal_name = args.text[0] if (args.text and args.text[0] in config['journals']) else 'default'
|
journal_name = args.text[0] if (args.text and args.text[0] in config['journals']) else 'default'
|
||||||
if journal_name is not 'default':
|
if journal_name is not 'default':
|
||||||
args.text = args.text[1:]
|
args.text = args.text[1:]
|
||||||
|
@ -159,7 +330,9 @@ def run(manual_args=None):
|
||||||
util.prompt("No default journal configured.")
|
util.prompt("No default journal configured.")
|
||||||
util.prompt(list_journals(config))
|
util.prompt(list_journals(config))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
log.debug('Using journal "%s"', journal_name)
|
||||||
|
|
||||||
|
# Find limiter
|
||||||
# If the first remaining argument looks like e.g. '-3', interpret that as a limiter
|
# If the first remaining argument looks like e.g. '-3', interpret that as a limiter
|
||||||
if not args.limit and args.text and args.text[0].startswith("-"):
|
if not args.limit and args.text and args.text[0].startswith("-"):
|
||||||
try:
|
try:
|
||||||
|
@ -168,107 +341,12 @@ def run(manual_args=None):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
log.debug('Using journal "%s"', journal_name)
|
# Guess and execute command
|
||||||
mode_compose, mode_export, mode_import = guess_mode(args, config)
|
command = guess_command(args, config)
|
||||||
|
if command in action_for:
|
||||||
# How to quit writing?
|
action_for[command](journal_name, config, original_config, args)
|
||||||
if "win32" in sys.platform:
|
|
||||||
_exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter"
|
|
||||||
else:
|
else:
|
||||||
_exit_multiline_code = "press Ctrl+D"
|
print("Unknown command number: " + command)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
if mode_compose and not args.text:
|
exit(0)
|
||||||
if not sys.stdin.isatty():
|
|
||||||
# Piping data into jrnl
|
|
||||||
raw = util.py23_read()
|
|
||||||
elif config['editor']:
|
|
||||||
raw = util.get_text_from_editor(config)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
raw = util.py23_read("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n")
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
util.prompt("[Entry NOT saved to journal.]")
|
|
||||||
sys.exit(0)
|
|
||||||
if raw:
|
|
||||||
args.text = [raw]
|
|
||||||
else:
|
|
||||||
mode_compose = False
|
|
||||||
|
|
||||||
# This is where we finally open the journal!
|
|
||||||
journal = Journal.open_journal(journal_name, config)
|
|
||||||
|
|
||||||
# Import mode
|
|
||||||
if mode_import:
|
|
||||||
plugins.get_importer(args.import_).import_(journal, args.input)
|
|
||||||
|
|
||||||
# Writing mode
|
|
||||||
elif mode_compose:
|
|
||||||
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()
|
|
||||||
|
|
||||||
if not mode_compose:
|
|
||||||
old_entries = journal.entries
|
|
||||||
if args.on_date:
|
|
||||||
args.start_date = args.end_date = args.on_date
|
|
||||||
journal.filter(tags=args.text,
|
|
||||||
start_date=args.start_date, end_date=args.end_date,
|
|
||||||
strict=args.strict,
|
|
||||||
short=args.short,
|
|
||||||
starred=args.starred)
|
|
||||||
journal.limit(args.limit)
|
|
||||||
|
|
||||||
# Reading mode
|
|
||||||
if not mode_compose and not mode_export and not mode_import:
|
|
||||||
print(util.py2encode(journal.pprint()))
|
|
||||||
|
|
||||||
# Various export modes
|
|
||||||
elif args.short:
|
|
||||||
print(util.py2encode(journal.pprint(short=True)))
|
|
||||||
|
|
||||||
elif args.tags:
|
|
||||||
print(util.py2encode(plugins.get_exporter("tags").export(journal)))
|
|
||||||
|
|
||||||
elif args.export is not False:
|
|
||||||
exporter = plugins.get_exporter(args.export)
|
|
||||||
print(exporter.export(journal, args.output))
|
|
||||||
|
|
||||||
elif args.encrypt is not False:
|
|
||||||
encrypt(journal, filename=args.encrypt)
|
|
||||||
# Not encrypting to a separate file: update config!
|
|
||||||
if not args.encrypt:
|
|
||||||
update_config(original_config, {"encrypt": True}, journal_name, force_local=True)
|
|
||||||
install.save_config(original_config)
|
|
||||||
|
|
||||||
elif args.decrypt is not False:
|
|
||||||
decrypt(journal, filename=args.decrypt)
|
|
||||||
# Not decrypting to a separate file: update config!
|
|
||||||
if not args.decrypt:
|
|
||||||
update_config(original_config, {"encrypt": False}, journal_name, force_local=True)
|
|
||||||
install.save_config(original_config)
|
|
||||||
|
|
||||||
elif args.edit:
|
|
||||||
if not config['editor']:
|
|
||||||
util.prompt("[{1}ERROR{2}: You need to specify an editor in {0} to use the --edit function.]".format(install.CONFIG_FILE_PATH, ERROR_COLOR, RESET_COLOR))
|
|
||||||
sys.exit(1)
|
|
||||||
other_entries = [e for e in old_entries if e not in journal.entries]
|
|
||||||
# Edit
|
|
||||||
old_num_entries = len(journal)
|
|
||||||
edited = util.get_text_from_editor(config, journal.editable_str())
|
|
||||||
journal.parse_editable_str(edited)
|
|
||||||
num_deleted = old_num_entries - len(journal)
|
|
||||||
num_edited = len([e for e in journal.entries if e.modified])
|
|
||||||
prompts = []
|
|
||||||
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"))
|
|
||||||
if prompts:
|
|
||||||
util.prompt("[{0}]".format(", ".join(prompts).capitalize()))
|
|
||||||
journal.entries += other_entries
|
|
||||||
journal.sort()
|
|
||||||
journal.write()
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue