mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-20 13:08:31 +02:00
More refactoring of cli.py
break up code from cli.py (now in jrnl.py) up into smaller functions get rid of export mode move --encrypt and --decrypt to commands.py clean up the help screen even more update flag name for import
This commit is contained in:
parent
6f71c98e13
commit
38b78b1d1f
7 changed files with 450 additions and 273 deletions
|
@ -180,7 +180,7 @@ Feature: Basic reading and writing to a journal
|
|||
And the journal should contain "Life is good."
|
||||
But the journal should not contain "I have an @idea"
|
||||
And the journal should not contain "I met with"
|
||||
When we run "jrnl --import -i features/journals/tags.journal"
|
||||
When we run "jrnl --import --file features/journals/tags.journal"
|
||||
Then the journal should contain "My first entry."
|
||||
And the journal should contain "Life is good."
|
||||
And the journal should contain "PROFIT!"
|
||||
|
@ -191,10 +191,11 @@ Feature: Basic reading and writing to a journal
|
|||
And the journal should contain "Life is good."
|
||||
But the journal should not contain "I have an @idea"
|
||||
And the journal should not contain "I met with"
|
||||
When we run "jrnl --import -i features/journals/tags.journal" and pipe
|
||||
When we run "jrnl --import --file features/journals/tags.journal" and pipe
|
||||
"""
|
||||
[2020-07-05 15:00] I should not exist!
|
||||
"""
|
||||
Then the journal should contain "My first entry."
|
||||
And the journal should contain "PROFIT!"
|
||||
But the journal should not contain "I should not exist!"
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ class EncryptedJournal(Journal):
|
|||
print(f"[Directory {dirname} created]", file=sys.stderr)
|
||||
self.create_file(filename)
|
||||
self.password = util.create_password(self.name)
|
||||
|
||||
print(
|
||||
f"Encrypted journal '{self.name}' created at {filename}",
|
||||
file=sys.stderr,
|
||||
|
@ -90,11 +91,16 @@ class EncryptedJournal(Journal):
|
|||
@classmethod
|
||||
def from_journal(cls, other: Journal):
|
||||
new_journal = super().from_journal(other)
|
||||
try:
|
||||
new_journal.password = (
|
||||
other.password
|
||||
if hasattr(other, "password")
|
||||
else util.create_password(other.name)
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("[Interrupted while creating new journal]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
return new_journal
|
||||
|
||||
|
||||
|
|
237
jrnl/cli.py
237
jrnl/cli.py
|
@ -24,75 +24,15 @@ import packaging.version
|
|||
import platform
|
||||
import sys
|
||||
|
||||
from . import install, plugins, util
|
||||
from . import install, util
|
||||
from . import jrnl
|
||||
from .parse_args import parse_args
|
||||
from .Journal import PlainJournal, open_journal
|
||||
from .util import ERROR_COLOR, RESET_COLOR, UserAbort
|
||||
from .Journal import open_journal
|
||||
from .util import UserAbort
|
||||
from .util import get_journal_name
|
||||
from .util import WARNING_COLOR, RESET_COLOR
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
logging.getLogger("keyring.backend").setLevel(logging.ERROR)
|
||||
|
||||
|
||||
def guess_mode(args, config):
|
||||
"""Guesses the mode (compose, read or export) from the given arguments"""
|
||||
compose = True
|
||||
export = False
|
||||
if (
|
||||
args.decrypt is not False
|
||||
or args.encrypt is not False
|
||||
or args.export is not False
|
||||
or any((args.short, args.tags, args.edit, args.delete))
|
||||
):
|
||||
compose = False
|
||||
export = True
|
||||
elif any(
|
||||
(
|
||||
args.start_date,
|
||||
args.end_date,
|
||||
args.on_date,
|
||||
args.limit,
|
||||
args.strict,
|
||||
args.starred,
|
||||
args.contains,
|
||||
)
|
||||
):
|
||||
# Any sign of displaying stuff?
|
||||
compose = False
|
||||
elif args.text and all(
|
||||
word[0] in config["tagsymbols"] for word in " ".join(args.text).split()
|
||||
):
|
||||
# No date and only tags?
|
||||
compose = False
|
||||
|
||||
return compose, export
|
||||
|
||||
|
||||
def encrypt(journal, filename=None):
|
||||
""" Encrypt into new file. If filename is not set, we encrypt the journal file itself. """
|
||||
from .EncryptedJournal import EncryptedJournal
|
||||
|
||||
journal.config["encrypt"] = True
|
||||
|
||||
new_journal = EncryptedJournal.from_journal(journal)
|
||||
new_journal.write(filename)
|
||||
|
||||
print(
|
||||
"Journal encrypted to {}.".format(filename or new_journal.config["journal"]),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
def decrypt(journal, filename=None):
|
||||
""" Decrypts into new file. If filename is not set, we encrypt the journal file itself. """
|
||||
journal.config["encrypt"] = False
|
||||
|
||||
new_journal = PlainJournal.from_journal(journal)
|
||||
new_journal.write(filename)
|
||||
print(
|
||||
"Journal decrypted to {}.".format(filename or new_journal.config["journal"]),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
def update_config(config, new_config, scope, force_local=False):
|
||||
|
@ -113,9 +53,8 @@ def configure_logger(debug=False):
|
|||
level=logging.DEBUG if debug else logging.ERROR,
|
||||
format="%(levelname)-8s %(name)-12s %(message)s",
|
||||
)
|
||||
logging.getLogger("parsedatetime").setLevel(
|
||||
logging.INFO
|
||||
) # disable parsedatetime debug logging
|
||||
logging.getLogger("parsedatetime").setLevel(logging.INFO)
|
||||
logging.getLogger("keyring.backend").setLevel(logging.ERROR)
|
||||
|
||||
|
||||
def run(manual_args=None):
|
||||
|
@ -132,18 +71,29 @@ Please update to Python 3.7 (or higher) in order to use jrnl.
|
|||
)
|
||||
sys.exit(1)
|
||||
|
||||
"""
|
||||
Flow:
|
||||
1. Parse cli arguments
|
||||
2. Run standalone command if it doesn't require config (help, version, etc), then exit
|
||||
3. Load config
|
||||
4. Run standalone command if it does require config (encrypt, decrypt, etc), then exit
|
||||
5. Load specified journal
|
||||
6. Start write mode, or search mode
|
||||
7. Profit
|
||||
"""
|
||||
if manual_args is None:
|
||||
manual_args = sys.argv[1:]
|
||||
|
||||
args = parse_args(manual_args)
|
||||
configure_logger(args.debug)
|
||||
log.debug("Parsed args: %s", args)
|
||||
|
||||
# Run command if possible before config is available
|
||||
if callable(args.preconfig_cmd):
|
||||
args.preconfig_cmd(args)
|
||||
sys.exit(0)
|
||||
|
||||
# Load the config
|
||||
# Load the config, and extract journal name
|
||||
try:
|
||||
config = install.load_or_install_jrnl()
|
||||
original_config = config.copy()
|
||||
|
@ -155,7 +105,7 @@ Please update to Python 3.7 (or higher) in order to use jrnl.
|
|||
|
||||
# Run post-config command now that config is ready
|
||||
if callable(args.postconfig_cmd):
|
||||
args.postconfig_cmd(args=args, config=config)
|
||||
args.postconfig_cmd(args=args, config=config, original_config=original_config)
|
||||
sys.exit(0)
|
||||
|
||||
# --- All the standalone commands are now done --- #
|
||||
|
@ -163,144 +113,15 @@ Please update to Python 3.7 (or higher) in order to use jrnl.
|
|||
# Get the journal we're going to be working with
|
||||
journal = open_journal(args.journal_name, config)
|
||||
|
||||
mode_compose, mode_export = guess_mode(args, config)
|
||||
kwargs = {
|
||||
"args": args,
|
||||
"config": config,
|
||||
"journal": journal,
|
||||
}
|
||||
|
||||
if mode_compose and not args.text:
|
||||
if not sys.stdin.isatty():
|
||||
# Piping data into jrnl
|
||||
raw = sys.stdin.read()
|
||||
elif config["editor"]:
|
||||
template = ""
|
||||
if config["template"]:
|
||||
try:
|
||||
template = open(config["template"]).read()
|
||||
except OSError:
|
||||
print(
|
||||
f"[Could not read template at '{config['template']}']",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
raw = util.get_text_from_editor(config, template)
|
||||
if jrnl._is_write_mode(**kwargs):
|
||||
jrnl.write_mode(**kwargs)
|
||||
else:
|
||||
try:
|
||||
_how_to_quit = (
|
||||
"Ctrl+z and then Enter" if "win32" in sys.platform else "Ctrl+d"
|
||||
)
|
||||
print(
|
||||
f"[Writing Entry; on a blank line, press {_how_to_quit} to finish writing]\n",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raw = sys.stdin.read()
|
||||
except KeyboardInterrupt:
|
||||
print("[Entry NOT saved to journal]", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
if raw:
|
||||
args.text = [raw]
|
||||
else:
|
||||
sys.exit()
|
||||
jrnl.search_mode(**kwargs)
|
||||
|
||||
# Writing mode
|
||||
if mode_compose:
|
||||
raw = " ".join(args.text).strip()
|
||||
log.debug('Appending raw line "%s" to journal "%s"', raw, args.journal_name)
|
||||
journal.new_entry(raw)
|
||||
print(f"[Entry added to {args.journal_name} journal]", file=sys.stderr)
|
||||
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,
|
||||
starred=args.starred,
|
||||
exclude=args.excluded,
|
||||
contains=args.contains,
|
||||
)
|
||||
journal.limit(args.limit)
|
||||
|
||||
# Reading mode
|
||||
if not mode_compose and not mode_export:
|
||||
print(journal.pprint())
|
||||
|
||||
# Various export modes
|
||||
elif args.short:
|
||||
print(journal.pprint(short=True))
|
||||
|
||||
elif args.tags:
|
||||
print(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}, args.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}, args.journal_name, force_local=True
|
||||
)
|
||||
install.save_config(original_config)
|
||||
|
||||
elif args.edit:
|
||||
if not config["editor"]:
|
||||
print(
|
||||
"[{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
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
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(
|
||||
"{} {} deleted".format(
|
||||
num_deleted, "entry" if num_deleted == 1 else "entries"
|
||||
)
|
||||
)
|
||||
if num_edited:
|
||||
prompts.append(
|
||||
"{} {} modified".format(
|
||||
num_edited, "entry" if num_deleted == 1 else "entries"
|
||||
)
|
||||
)
|
||||
if prompts:
|
||||
print("[{}]".format(", ".join(prompts).capitalize()), file=sys.stderr)
|
||||
journal.entries += other_entries
|
||||
journal.sort()
|
||||
journal.write()
|
||||
|
||||
elif args.delete:
|
||||
if journal.entries:
|
||||
entries_to_delete = journal.prompt_delete_entries()
|
||||
|
||||
if entries_to_delete:
|
||||
journal.entries = old_entries
|
||||
journal.delete_entries(entries_to_delete)
|
||||
|
||||
journal.write()
|
||||
else:
|
||||
print(
|
||||
"No entries deleted, because the search returned no results.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
# All done!
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
def preconfig_diagnostic(_):
|
||||
"""
|
||||
Functions in this file are standalone commands. All standalone commands are split into
|
||||
two categories depending on whether they require the config to be loaded to be able to
|
||||
run.
|
||||
|
||||
1. "preconfig" commands don't require the config at all, and can be run before the
|
||||
config has been loaded.
|
||||
2. "postconfig" commands require to config to have already been loaded, parsed, and
|
||||
scoped before they can be run.
|
||||
|
||||
Also, please note that all (non-builtin) imports should be scoped to each function to
|
||||
avoid any possible overhead for these standalone commands.
|
||||
"""
|
||||
import platform
|
||||
import sys
|
||||
import jrnl
|
||||
|
||||
|
||||
def preconfig_diagnostic(_):
|
||||
from jrnl import __version__
|
||||
|
||||
print(
|
||||
f"jrnl: {jrnl.__version__}\n"
|
||||
f"jrnl: {__version__}\n"
|
||||
f"Python: {sys.version}\n"
|
||||
f"OS: {platform.system()} {platform.release()}"
|
||||
)
|
||||
|
||||
|
||||
def preconfig_version(_):
|
||||
import jrnl
|
||||
from jrnl import __title__
|
||||
from jrnl import __version__
|
||||
|
||||
version_str = f"{jrnl.__title__} version {jrnl.__version__}"
|
||||
version_str = f"{__title__} version {__version__}"
|
||||
print(version_str)
|
||||
|
||||
|
||||
|
@ -31,4 +47,59 @@ def postconfig_import(args, config, **kwargs):
|
|||
journal = open_journal(args.journal_name, config)
|
||||
|
||||
format = args.export if args.export else "jrnl"
|
||||
get_importer(format).import_(journal, args.input)
|
||||
get_importer(format).import_(journal, args.filename)
|
||||
|
||||
|
||||
def postconfig_encrypt(args, config, original_config, **kwargs):
|
||||
"""
|
||||
Encrypt a journal in place, or optionally to a new file
|
||||
"""
|
||||
from .EncryptedJournal import EncryptedJournal
|
||||
from .Journal import open_journal
|
||||
from .cli import update_config
|
||||
from .install import save_config
|
||||
|
||||
# Open the journal
|
||||
journal = open_journal(args.journal_name, config)
|
||||
|
||||
journal.config["encrypt"] = True
|
||||
|
||||
new_journal = EncryptedJournal.from_journal(journal)
|
||||
new_journal.write(args.filename)
|
||||
|
||||
print(
|
||||
f"Journal encrypted to {args.filename or new_journal.config['journal']}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
# Update the config, if we encrypted in place
|
||||
if not args.filename:
|
||||
update_config(
|
||||
original_config, {"encrypt": True}, args.journal_name, force_local=True
|
||||
)
|
||||
save_config(original_config)
|
||||
|
||||
|
||||
def postconfig_decrypt(args, config, original_config, **kwargs):
|
||||
""" Decrypts into new file. If filename is not set, we encrypt the journal file itself. """
|
||||
from .Journal import PlainJournal
|
||||
from .Journal import open_journal
|
||||
from .cli import update_config
|
||||
from .install import save_config
|
||||
|
||||
journal = open_journal(args.journal_name, config)
|
||||
journal.config["encrypt"] = False
|
||||
|
||||
new_journal = PlainJournal.from_journal(journal)
|
||||
new_journal.write(args.filename)
|
||||
print(
|
||||
f"Journal decrypted to {args.filename or new_journal.config['journal']}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
# Update the config, if we decrypted in place
|
||||
if not args.filename:
|
||||
update_config(
|
||||
original_config, {"encrypt": False}, args.journal_name, force_local=True
|
||||
)
|
||||
save_config(original_config)
|
||||
|
|
266
jrnl/jrnl.py
Normal file
266
jrnl/jrnl.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
import logging
|
||||
import sys
|
||||
|
||||
from . import util
|
||||
from . import install
|
||||
from . import plugins
|
||||
|
||||
from .util import ERROR_COLOR
|
||||
from .util import RESET_COLOR
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _is_write_mode(args, config, **kwargs):
|
||||
"""Determines if we are in write mode (as opposed to search mode)"""
|
||||
write_mode = True
|
||||
|
||||
# Are any search filters present? If so, then search mode.
|
||||
write_mode = not any(
|
||||
(
|
||||
args.contains,
|
||||
args.delete,
|
||||
args.edit,
|
||||
args.export,
|
||||
args.end_date,
|
||||
args.limit,
|
||||
args.on_date,
|
||||
args.short,
|
||||
args.starred,
|
||||
args.start_date,
|
||||
args.strict,
|
||||
args.tags,
|
||||
)
|
||||
)
|
||||
|
||||
# If the text is entirely tags, then we are also searching (not writing)
|
||||
if (
|
||||
write_mode
|
||||
and args.text
|
||||
and all(word[0] in config["tagsymbols"] for word in " ".join(args.text).split())
|
||||
):
|
||||
write_mode = False
|
||||
|
||||
return write_mode
|
||||
|
||||
|
||||
def write_mode(args, config, journal, **kwargs):
|
||||
"""
|
||||
Gets input from the user to write to the journal
|
||||
1. Check for input from cli
|
||||
2. Check input being piped in
|
||||
3. Open editor if configured (prepopulated with template if available)
|
||||
4. Use stdin.read as last resort
|
||||
6. Write any found text to journal, or exit
|
||||
"""
|
||||
log.debug("Write mode: starting")
|
||||
|
||||
if args.text:
|
||||
log.debug("Write mode: cli text detected: %s", args.text)
|
||||
raw = " ".join(args.text).strip()
|
||||
|
||||
elif not sys.stdin.isatty():
|
||||
log.debug("Write mode: receiving piped text")
|
||||
raw = sys.stdin.read()
|
||||
|
||||
else:
|
||||
raw = _write_in_editor(config)
|
||||
|
||||
if not raw:
|
||||
log.error("Write mode: couldn't get raw text")
|
||||
sys.exit()
|
||||
|
||||
log.debug(
|
||||
'Write mode: appending raw text to journal "%s": %s', args.journal_name, raw
|
||||
)
|
||||
journal.new_entry(raw)
|
||||
print(f"[Entry added to {args.journal_name} journal]", file=sys.stderr)
|
||||
journal.write()
|
||||
log.debug("Write mode: completed journal.write()", args.journal_name, raw)
|
||||
|
||||
|
||||
def search_mode(args, journal, **kwargs):
|
||||
"""
|
||||
Search for entries in a journal, then either:
|
||||
1. Send them to configured editor for user manipulation
|
||||
2. Delete them (with confirmation for each entry)
|
||||
3. Display them (with formatting options)
|
||||
"""
|
||||
kwargs = {
|
||||
**kwargs,
|
||||
"args": args,
|
||||
"journal": journal,
|
||||
"old_entries": journal.entries,
|
||||
}
|
||||
|
||||
# Filters the journal entries in place
|
||||
_search_journal(**kwargs)
|
||||
|
||||
# Where do the search results go?
|
||||
if args.edit:
|
||||
_edit_search_results(**kwargs)
|
||||
|
||||
elif args.delete:
|
||||
_delete_search_results(**kwargs)
|
||||
|
||||
else:
|
||||
_display_search_results(**kwargs)
|
||||
|
||||
|
||||
def _write_in_editor(config):
|
||||
if config["editor"]:
|
||||
log.debug("Write mode: opening editor")
|
||||
template = _get_editor_template(config)
|
||||
raw = util.get_text_from_editor(config, template)
|
||||
|
||||
else:
|
||||
_how_to_quit = "Ctrl+z and then Enter" if "win32" in sys.platform else "Ctrl+d"
|
||||
print(
|
||||
f"[Writing Entry; on a blank line, press {_how_to_quit} to finish writing]\n",
|
||||
file=sys.stderr,
|
||||
)
|
||||
try:
|
||||
raw = sys.stdin.read()
|
||||
except KeyboardInterrupt:
|
||||
log.error("Write mode: keyboard interrupt")
|
||||
print("[Entry NOT saved to journal]", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
return raw
|
||||
|
||||
|
||||
def _get_editor_template(config, **kwargs):
|
||||
log.debug("Write mode: loading template for entry")
|
||||
|
||||
if not config["template"]:
|
||||
log.debug("Write mode: no template configured")
|
||||
return ""
|
||||
|
||||
try:
|
||||
template = open(config["template"]).read()
|
||||
log.debug("Write mode: template loaded: %s", template)
|
||||
except OSError:
|
||||
log.error("Write mode: template not loaded")
|
||||
print(
|
||||
f"[Could not read template at '{config['template']}']", file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def _search_journal(args, journal, **kwargs):
|
||||
""" Search the journal with the given args"""
|
||||
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,
|
||||
starred=args.starred,
|
||||
exclude=args.excluded,
|
||||
contains=args.contains,
|
||||
)
|
||||
journal.limit(args.limit)
|
||||
|
||||
|
||||
def _edit_search_results(config, journal, old_entries, **kwargs):
|
||||
"""
|
||||
1. Send the given journal entries to the user-configured editor
|
||||
2. Print out stats on any modifications to journal
|
||||
3. Write modifications to journal
|
||||
"""
|
||||
if not config["editor"]:
|
||||
print(
|
||||
f"""
|
||||
[{ERROR_COLOR}ERROR{RESET_COLOR}: There is no editor configured.]
|
||||
|
||||
Please specify an editor in config file ({install.CONFIG_FILE_PATH})
|
||||
to use the --edit option.
|
||||
""",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# separate entries we are not editing
|
||||
other_entries = [e for e in old_entries if e not in journal.entries]
|
||||
|
||||
# Get stats now for summary later
|
||||
old_stats = _get_predit_stats(journal)
|
||||
|
||||
# Send user to the editor
|
||||
edited = util.get_text_from_editor(config, journal.editable_str())
|
||||
journal.parse_editable_str(edited)
|
||||
|
||||
# Print summary if available
|
||||
_print_edited_summary(journal, old_stats)
|
||||
|
||||
# Put back entries we separated earlier, sort, and write the journal
|
||||
journal.entries += other_entries
|
||||
journal.sort()
|
||||
journal.write()
|
||||
|
||||
|
||||
def _print_edited_summary(journal, old_stats, **kwargs):
|
||||
stats = {
|
||||
"deleted": old_stats["count"] - len(journal),
|
||||
"modified": len([e for e in journal.entries if e.modified]),
|
||||
}
|
||||
|
||||
prompts = []
|
||||
|
||||
if stats["deleted"]:
|
||||
prompts.append(
|
||||
f"{stats['deleted']} {_pluralize_entry(stats['deleted'])} deleted"
|
||||
)
|
||||
|
||||
if stats["modified"]:
|
||||
prompts.append(
|
||||
f"{stats['modified']} {_pluralize_entry(stats['modified'])} modified"
|
||||
)
|
||||
|
||||
if prompts:
|
||||
print(f"[{', '.join(prompts).capitalize()}]", file=sys.stderr)
|
||||
|
||||
|
||||
def _get_predit_stats(journal):
|
||||
return {"count": len(journal)}
|
||||
|
||||
|
||||
def _pluralize_entry(num):
|
||||
return "entry" if num == 1 else "entries"
|
||||
|
||||
|
||||
def _delete_search_results(journal, old_entries, **kwargs):
|
||||
if not journal.entries:
|
||||
print(
|
||||
"[No entries deleted, because the search returned no results.]",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
entries_to_delete = journal.prompt_delete_entries()
|
||||
|
||||
if entries_to_delete:
|
||||
journal.entries = old_entries
|
||||
journal.delete_entries(entries_to_delete)
|
||||
|
||||
journal.write()
|
||||
|
||||
|
||||
def _display_search_results(args, journal, **kwargs):
|
||||
if args.short:
|
||||
print(journal.pprint(short=True))
|
||||
|
||||
elif args.tags:
|
||||
print(plugins.get_exporter("tags").export(journal))
|
||||
|
||||
elif args.export:
|
||||
exporter = plugins.get_exporter(args.export)
|
||||
print(exporter.export(journal, args.filename))
|
||||
|
||||
else:
|
||||
# Default display mode
|
||||
print(journal.pprint())
|
|
@ -9,13 +9,18 @@ from .commands import preconfig_version
|
|||
from .commands import preconfig_diagnostic
|
||||
from .commands import postconfig_list
|
||||
from .commands import postconfig_import
|
||||
from .commands import postconfig_encrypt
|
||||
from .commands import postconfig_decrypt
|
||||
from .util import deprecated_cmd
|
||||
|
||||
|
||||
class WrappingFormatter(argparse.RawDescriptionHelpFormatter):
|
||||
class WrappingFormatter(argparse.RawTextHelpFormatter):
|
||||
def _split_lines(self, text, width):
|
||||
text = self._whitespace_matcher.sub(" ", text).strip()
|
||||
return textwrap.wrap(text, width=56)
|
||||
text = text.split("\n\n")
|
||||
text = map(lambda t: self._whitespace_matcher.sub(" ", t).strip(), text)
|
||||
text = map(lambda t: textwrap.wrap(t, width=56), text)
|
||||
text = [item for sublist in text for item in sublist]
|
||||
return text
|
||||
|
||||
|
||||
def parse_args(args=[]):
|
||||
|
@ -54,7 +59,7 @@ def parse_args(args=[]):
|
|||
action="store_const",
|
||||
const=preconfig_version,
|
||||
dest="preconfig_cmd",
|
||||
help="prints version information",
|
||||
help="Print version information",
|
||||
)
|
||||
standalone.add_argument(
|
||||
"-v",
|
||||
|
@ -75,7 +80,7 @@ def parse_args(args=[]):
|
|||
action="store_const",
|
||||
const=postconfig_list,
|
||||
dest="postconfig_cmd",
|
||||
help="list all configured journals",
|
||||
help="List all configured journals",
|
||||
)
|
||||
standalone.add_argument(
|
||||
"--ls",
|
||||
|
@ -95,21 +100,19 @@ def parse_args(args=[]):
|
|||
)
|
||||
standalone.add_argument(
|
||||
"--encrypt",
|
||||
metavar="FILENAME",
|
||||
dest="encrypt",
|
||||
help="Encrypts your existing journal with a new password",
|
||||
nargs="?",
|
||||
default=False,
|
||||
const=None,
|
||||
help="Encrypt selected journal with a password",
|
||||
action="store_const",
|
||||
metavar="TYPE",
|
||||
dest="postconfig_cmd",
|
||||
const=postconfig_encrypt,
|
||||
)
|
||||
standalone.add_argument(
|
||||
"--decrypt",
|
||||
metavar="FILENAME",
|
||||
dest="decrypt",
|
||||
help="Decrypts your journal and stores it in plain text",
|
||||
nargs="?",
|
||||
default=False,
|
||||
const=None,
|
||||
help="Decrypt selected journal and store it in plain text",
|
||||
action="store_const",
|
||||
metavar="TYPE",
|
||||
dest="postconfig_cmd",
|
||||
const=postconfig_decrypt,
|
||||
)
|
||||
standalone.add_argument(
|
||||
"--import",
|
||||
|
@ -117,18 +120,27 @@ def parse_args(args=[]):
|
|||
metavar="TYPE",
|
||||
dest="postconfig_cmd",
|
||||
const=postconfig_import,
|
||||
help=f"Import entries into your journal. TYPE can be: {util.oxford_list(IMPORT_FORMATS)} (default: jrnl)",
|
||||
help=f"""
|
||||
Import entries from another journal.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
--file FILENAME (default: uses stdin)
|
||||
|
||||
--format [{util.oxford_list(IMPORT_FORMATS)}] (default: jrnl)
|
||||
""",
|
||||
)
|
||||
standalone.add_argument(
|
||||
"-i",
|
||||
"--file",
|
||||
metavar="FILENAME",
|
||||
dest="input",
|
||||
help="Optionally specifies input file when using --import.",
|
||||
default=False,
|
||||
const=None,
|
||||
dest="filename",
|
||||
help=argparse.SUPPRESS,
|
||||
default=None,
|
||||
)
|
||||
standalone.add_argument("-i", dest="filename", help=argparse.SUPPRESS)
|
||||
|
||||
compose_msg = """ To add a new entry into your journal, simply write it on the command line:
|
||||
compose_msg = """
|
||||
To add a new entry into your journal, simply write it on the command line:
|
||||
|
||||
jrnl yesterday: I was walking and I found this big log.
|
||||
|
||||
|
@ -171,9 +183,7 @@ def parse_args(args=[]):
|
|||
metavar="DATE",
|
||||
help="Show entries before, or on, this date (alias: -until)",
|
||||
)
|
||||
reading.add_argument(
|
||||
"-until", dest="end_date", help=argparse.SUPPRESS,
|
||||
)
|
||||
reading.add_argument("-until", dest="end_date", help=argparse.SUPPRESS)
|
||||
reading.add_argument(
|
||||
"-contains",
|
||||
dest="contains",
|
||||
|
@ -214,7 +224,7 @@ def parse_args(args=[]):
|
|||
search_options_msg = """ These help you do various tasks with the selected entries from your search.
|
||||
If used on their own (with no search), they will act on your entire journal"""
|
||||
exporting = parser.add_argument_group(
|
||||
"Options for Searching", textwrap.dedent(search_options_msg)
|
||||
"Searching Options", textwrap.dedent(search_options_msg)
|
||||
)
|
||||
exporting.add_argument(
|
||||
"--edit",
|
||||
|
@ -233,7 +243,15 @@ def parse_args(args=[]):
|
|||
metavar="TYPE",
|
||||
dest="export",
|
||||
choices=EXPORT_FORMATS,
|
||||
help=f"Display selected entries in an alternate format (other than jrnl). TYPE can be: {util.oxford_list(EXPORT_FORMATS)}.",
|
||||
help=f"""
|
||||
Display selected entries in an alternate format.
|
||||
|
||||
TYPE can be: {util.oxford_list(EXPORT_FORMATS)}.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
--file FILENAME Write output to file instead of stdout
|
||||
""",
|
||||
default=False,
|
||||
)
|
||||
exporting.add_argument(
|
||||
|
@ -259,17 +277,11 @@ def parse_args(args=[]):
|
|||
"-s", dest="short", action="store_true", help=argparse.SUPPRESS,
|
||||
)
|
||||
exporting.add_argument(
|
||||
"-o",
|
||||
metavar="FILENAME",
|
||||
dest="output",
|
||||
help="Optionally specifies output file (or directory) when using --format.",
|
||||
default=False,
|
||||
const=None,
|
||||
"-o", dest="filename", help=argparse.SUPPRESS,
|
||||
)
|
||||
|
||||
# Handle '-123' as a shortcut for '-n 123'
|
||||
num = re.compile(r"^-(\d+)$")
|
||||
args = [num.sub(r"-n \1", arg) for arg in args]
|
||||
|
||||
# return parser.parse_args(args)
|
||||
return parser.parse_intermixed_args(args)
|
||||
|
|
|
@ -14,17 +14,14 @@ def expected_args(**kwargs):
|
|||
default_args = {
|
||||
"contains": None,
|
||||
"debug": False,
|
||||
"decrypt": False,
|
||||
"delete": False,
|
||||
"edit": False,
|
||||
"encrypt": False,
|
||||
"end_date": None,
|
||||
"excluded": [],
|
||||
"export": False,
|
||||
"input": False,
|
||||
"filename": None,
|
||||
"limit": None,
|
||||
"on_date": None,
|
||||
"output": False,
|
||||
"preconfig_cmd": None,
|
||||
"postconfig_cmd": None,
|
||||
"short": False,
|
||||
|
@ -66,7 +63,15 @@ def test_edit_alone():
|
|||
|
||||
|
||||
def test_encrypt_alone():
|
||||
assert cli_as_dict("--encrypt 'test.txt'") == expected_args(encrypt="test.txt")
|
||||
from jrnl.commands import postconfig_encrypt
|
||||
|
||||
assert cli_as_dict("--encrypt") == expected_args(postconfig_cmd=postconfig_encrypt)
|
||||
|
||||
|
||||
def test_decrypt_alone():
|
||||
from jrnl.commands import postconfig_decrypt
|
||||
|
||||
assert cli_as_dict("--decrypt") == expected_args(postconfig_cmd=postconfig_decrypt)
|
||||
|
||||
|
||||
def test_end_date_alone():
|
||||
|
@ -110,15 +115,10 @@ def test_import_alone():
|
|||
assert cli_as_dict("--import") == expected_args(postconfig_cmd=postconfig_import)
|
||||
|
||||
|
||||
def test_input_flag_alone():
|
||||
assert cli_as_dict("-i test.txt") == expected_args(input="test.txt")
|
||||
assert cli_as_dict("-i 'lorem ipsum.txt'") == expected_args(input="lorem ipsum.txt")
|
||||
|
||||
|
||||
def test_output_flag_alone():
|
||||
assert cli_as_dict("-o test.txt") == expected_args(output="test.txt")
|
||||
assert cli_as_dict("-o 'lorem ipsum.txt'") == expected_args(
|
||||
output="lorem ipsum.txt"
|
||||
def test_file_flag_alone():
|
||||
assert cli_as_dict("--file test.txt") == expected_args(filename="test.txt")
|
||||
assert cli_as_dict("--file 'lorem ipsum.txt'") == expected_args(
|
||||
filename="lorem ipsum.txt"
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue