Implement --change-time flag (#1452)

* Implement --change-time flag
* Remove todo from --change-time bdd folder journal tests
* Add warning when --change-time used with no matching entries
* Add a test to make sure running --change-time with nothing prints a warning and doesn't change anything
* Add prompt for --change-time
* Don't prompt for --change-time when used with --edit and only one entry
* When using --edit and --change-time, change the time before editing
* Add test for --change-time used with --edit
* Modify failing --change-time test to conform to text in develop branch
This commit is contained in:
Richard Schneider 2022-05-21 14:03:27 -05:00 committed by GitHub
parent e6ed64ac7a
commit 33c9dce80d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 342 additions and 12 deletions

View file

@ -8,6 +8,7 @@ import fnmatch
import os
from . import Journal
from . import time
def get_files(journal_config):
@ -89,6 +90,17 @@ class Folder(Journal.Journal):
self.entries.remove(entry)
self._diff_entry_dates.append(entry.date)
def change_date_entries(self, date):
"""Changes entry dates to given date."""
date = time.parse(date)
self._diff_entry_dates.append(date)
for entry in self.entries:
self._diff_entry_dates.append(entry.date)
entry.date = date
def parse_editable_str(self, edited):
"""Parses the output of self.editable_str and updates its entries."""
mod_entries = self._parse(edited)

View file

@ -261,23 +261,29 @@ class Journal:
for entry in entries_to_delete:
self.entries.remove(entry)
def prompt_delete_entries(self):
"""Prompts for deletion of each of the entries in a journal.
Returns the entries the user wishes to delete."""
def change_date_entries(self, date):
"""Changes entry dates to given date."""
date = time.parse(date)
to_delete = []
for entry in self.entries:
entry.date = date
def ask_delete(entry):
def prompt_action_entries(self, message):
"""Prompts for action for each entry in a journal, using given message.
Returns the entries the user wishes to apply the action on."""
to_act = []
def ask_action(entry):
return yesno(
f"Delete entry '{entry.pprint(short=True)}'?",
f"{message} '{entry.pprint(short=True)}'?",
default=False,
)
for entry in self.entries:
if ask_delete(entry):
to_delete.append(entry)
if ask_action(entry):
to_act.append(entry)
return to_delete
return to_act
def new_entry(self, raw, date=None, sort=True):
"""Constructs a new entry from some raw text input.

View file

@ -267,6 +267,14 @@ def parse_args(args=[]):
action="store_true",
help="Interactively deletes selected entries",
)
exporting.add_argument(
"--change-time",
dest="change_time",
nargs="?",
metavar="DATE",
const="now",
help="Change timestamp for seleted entries (default: now)",
)
exporting.add_argument(
"--format",
metavar="TYPE",

View file

@ -79,6 +79,7 @@ def _is_write_mode(args, config, **kwargs):
args.contains,
args.delete,
args.edit,
args.change_time,
args.export,
args.end_date,
args.today_in_history,
@ -150,7 +151,9 @@ def write_mode(args, config, journal, **kwargs):
def search_mode(args, journal, **kwargs):
"""
Search for entries in a journal, then either:
1. Send them to configured editor for user manipulation
1. Send them to configured editor for user manipulation (and also
change their timestamps if requested)
2. Change their timestamps
2. Delete them (with confirmation for each entry)
3. Display them (with formatting options)
"""
@ -166,8 +169,27 @@ def search_mode(args, journal, **kwargs):
# Where do the search results go?
if args.edit:
# If we want to both edit and change time in one action
if args.change_time:
# Generate a new list instead of assigning so it won't be
# modified by _change_time_search_results
selected_entries = [e for e in journal.entries]
no_change_time_prompt = len(journal.entries) == 1
_change_time_search_results(no_prompt=no_change_time_prompt, **kwargs)
# Re-filter the journal enties (_change_time_search_results
# puts the filtered entries back); use selected_entries
# instead of running _search_journal again, because times
# have changed since the original search
kwargs["old_entries"] = journal.entries
journal.entries = selected_entries
_edit_search_results(**kwargs)
elif args.change_time:
_change_time_search_results(**kwargs)
elif args.delete:
_delete_search_results(**kwargs)
@ -236,6 +258,11 @@ def _search_journal(args, journal, **kwargs):
journal.limit(args.limit)
def _other_entries(journal, entries):
"""Find entries that are not in journal"""
return [e for e in entries if e not in journal.entries]
def _edit_search_results(config, journal, old_entries, **kwargs):
"""
1. Send the given journal entries to the user-configured editor
@ -252,7 +279,7 @@ def _edit_search_results(config, journal, old_entries, **kwargs):
)
# separate entries we are not editing
other_entries = [e for e in old_entries if e not in journal.entries]
other_entries = _other_entries(journal, old_entries)
# Get stats now for summary later
old_stats = _get_predit_stats(journal)
@ -309,7 +336,7 @@ def _delete_search_results(journal, old_entries, **kwargs):
if not journal.entries:
raise JrnlException(Message(MsgText.NothingToDelete, MsgType.ERROR))
entries_to_delete = journal.prompt_delete_entries()
entries_to_delete = journal.prompt_action_entries("Delete entry")
if entries_to_delete:
journal.entries = old_entries
@ -318,6 +345,30 @@ def _delete_search_results(journal, old_entries, **kwargs):
journal.write()
def _change_time_search_results(args, journal, old_entries, no_prompt=False, **kwargs):
if not journal.entries:
raise JrnlException(Message(MsgText.NothingToModify, MsgType.WARNING))
# separate entries we are not editing
other_entries = _other_entries(journal, old_entries)
if no_prompt:
entries_to_change = journal.entries
else:
entries_to_change = journal.prompt_action_entries("Change time")
if entries_to_change:
other_entries += [e for e in journal.entries if e not in entries_to_change]
journal.entries = entries_to_change
date = time.parse(args.change_time)
journal.change_date_entries(date)
journal.entries += other_entries
journal.sort()
journal.write()
def _display_search_results(args, journal, **kwargs):
if args.short or args.export == "short":
print(journal.pprint(short=True))

View file

@ -130,6 +130,10 @@ class MsgText(Enum):
No entries to delete, because the search returned no results
"""
NothingToModify = """
No entries to modify, because the search returned no results
"""
class Message(NamedTuple):
text: MsgText