From f0e8fa20606688e3c3cdc1ca9aedd9997875ac1a Mon Sep 17 00:00:00 2001 From: Karim Rahal Date: Sun, 17 Jan 2021 00:55:27 +0200 Subject: [PATCH] Add new `-today-in-history`, `-month`, `-day`, and `-year` search filters (#1145) * Introduce -reminisce, -month, -day, and -year * Update expected_args in parse_args tests * Add check before creating compare_d * Misc changes * Implement testing for -month, -day, -year, and -reminisce * Compress tests into one Scenario Outline * Fix failing tests by updating dates_similar journal * Create 'we set current date and time to' step * Use time.parse in reminisce * Update dates_similar journal * Make 'Searching in a journal' test shorter * Lint * Implement reminiscing test * Add combination tests * Finalize tests * Finalize pytests * Simplify reminisce tests * Change reminsice help (since it also shows today's entries) * Re-do tests; use various tests * Remove old test data * Better scenario description * Standardize format for compare_d * Rename -reminisce to -today-in-history --- features/search.feature | 90 ++++++++++++++++++++++++++++++++++++++++ features/steps/core.py | 21 ++++++++++ jrnl/Journal.py | 11 +++++ jrnl/args.py | 24 +++++++++++ jrnl/jrnl.py | 13 ++++++ tests/test_parse_args.py | 25 +++++++++++ 6 files changed, 184 insertions(+) diff --git a/features/search.feature b/features/search.feature index 9d31bb06..22351b7e 100644 --- a/features/search.feature +++ b/features/search.feature @@ -214,6 +214,96 @@ Feature: Searching in a journal | But I'm better. """ + Scenario Outline: Searching by month + Given we use the config ".yaml" + And we use the password "test" if prompted + When we run "jrnl -month 9 --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + And we flush the output + When we run "jrnl -month Sept --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + And we flush the output + When we run "jrnl -month September --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + + Examples: configs + | config | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + + Scenario Outline: Searching by day + Given we use the config ".yaml" + And we use the password "test" if prompted + When we run "jrnl -day 31 --short" + Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series." + + Examples: configs + | config | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + + Scenario Outline: Searching by year + Given we use the config ".yaml" + And we use the password "test" if prompted + When we run "jrnl 2019-01-01 01:01: I like this year." + And we run "jrnl -year 2019 --short" + Then the output should be "2019-01-01 01:01 I like this year." + And we flush the output + When we run "jrnl -year 19 --short" + Then the output should be "2019-01-01 01:01 I like this year." + + Examples: configs + | config | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + + Scenario Outline: Combining month, day, and year search terms + Given we use the config ".yaml" + And we use the password "test" if prompted + When we run "jrnl -month 08 -day 29 --short" + Then the output should be "2020-08-29 11:11 Entry the first." + And we flush the output + When we run "jrnl -day 29 -year 2020 --short" + Then the output should be "2020-08-29 11:11 Entry the first." + And we flush the output + When we run "jrnl -month 09 -year 2020 --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + And we flush the output + When we run "jrnl -month 08 -day 29 -year 2020 --short" + Then the output should be "2020-08-29 11:11 Entry the first." + + Examples: configs + | config | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + + Scenario Outline: Searching today in history + Given we use the config ".yaml" + And we use the password "test" if prompted + And we set current date and time to "2020-08-31 14:32" + When we run "jrnl 2019-08-31 01:01: Hi, from last year." + And we run "jrnl -today-in-history --short" + Then the output should be + """ + 2019-08-31 01:01 Hi, from last year. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + """ + + Examples: configs + | config | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + Scenario: Loading a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl -from 'feb 2013'" diff --git a/features/steps/core.py b/features/steps/core.py index e3af8243..f5215d58 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -17,6 +17,7 @@ import keyring import toml import yaml +import jrnl.time from jrnl import Journal from jrnl import __version__ from jrnl import plugins @@ -159,6 +160,11 @@ def disable_keyring(context): keyring.core.set_keyring(NoKeyring()) +@given('we set current date and time to "{dt}"') +def set_datetime(context, dt): + context.now = dt + + @when('we change directory to "{path}"') def move_up_dir(context, path): os.chdir(path) @@ -197,6 +203,7 @@ def open_editor_and_enter(context, method, text=""): patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \ patch("getpass.getpass", side_effect=_mock_getpass(password)) as mock_getpass, \ patch("sys.stdin.isatty", return_value=True), \ + patch("jrnl.time.parse", side_effect=_mock_time_parse(context)), \ patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \ patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \ : @@ -289,6 +296,18 @@ def _mock_input(inputs): return prompt_return +def _mock_time_parse(context): + original_parse = jrnl.time.parse + if "now" not in context: + return original_parse + + def wrapper(input, *args, **kwargs): + input = context.now if input == "now" else input + return original_parse(input, *args, **kwargs) + + return wrapper + + @when('we run "{command}" and enter') @when('we run "{command}" and enter nothing') @when('we run "{command}" and enter "{inputs}"') @@ -322,6 +341,7 @@ def run_with_input(context, command, inputs=""): patch("getpass.getpass", side_effect=_mock_getpass(password)) as mock_getpass, \ patch("sys.stdin.read", side_effect=text) as mock_read, \ patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \ + patch("jrnl.time.parse", side_effect=_mock_time_parse(context)), \ patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \ patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \ : @@ -406,6 +426,7 @@ def run(context, command, text=""): patch("getpass.getpass", side_effect=_mock_getpass(password)) as mock_getpass, \ patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \ patch("sys.stdin.read", side_effect=lambda: text), \ + patch("jrnl.time.parse", side_effect=_mock_time_parse(context)), \ patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \ patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \ : diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 1871e3c2..4196571d 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -189,6 +189,9 @@ class Journal: def filter( self, tags=[], + month=None, + day=None, + year=None, start_date=None, end_date=None, starred=False, @@ -220,11 +223,19 @@ class Journal: if contains: contains_lower = contains.casefold() + # Create datetime object for comparison below + # this approach allows various formats + if month or day or year: + compare_d = time.parse(f"{month or 1}.{day or 1}.{year or 1}") + result = [ entry for entry in self.entries if (not tags or tagged(entry.tags)) and (not starred or entry.starred) + and (not month or entry.date.month == compare_d.month) + and (not day or entry.date.day == compare_d.day) + and (not year or entry.date.year == compare_d.year) and (not start_date or entry.date >= start_date) and (not end_date or entry.date <= end_date) and (not exclude or not excluded(entry.tags)) diff --git a/jrnl/args.py b/jrnl/args.py index 5efb00ba..f934ca16 100644 --- a/jrnl/args.py +++ b/jrnl/args.py @@ -176,6 +176,30 @@ def parse_args(args=[]): reading.add_argument( "-on", dest="on_date", metavar="DATE", help="Show entries on this date" ) + reading.add_argument( + "-today-in-history", + dest="today_in_history", + action="store_true", + help="Show entries of today over the years", + ) + reading.add_argument( + "-month", + dest="month", + metavar="DATE", + help="Show entries on this month of any year", + ) + reading.add_argument( + "-day", + dest="day", + metavar="DATE", + help="Show entries on this day of any month", + ) + reading.add_argument( + "-year", + dest="year", + metavar="DATE", + help="Show entries of a specific year", + ) reading.add_argument( "-from", dest="start_date", diff --git a/jrnl/jrnl.py b/jrnl/jrnl.py index 415200fa..257358c4 100644 --- a/jrnl/jrnl.py +++ b/jrnl/jrnl.py @@ -15,6 +15,7 @@ from .config import get_config_path from .editor import get_text_from_editor from .editor import get_text_from_stdin from .exception import UserAbort +from . import time def run(args): @@ -77,6 +78,10 @@ def _is_write_mode(args, config, **kwargs): args.edit, args.export, args.end_date, + args.today_in_history, + args.month, + args.day, + args.year, args.limit, args.on_date, args.short, @@ -206,8 +211,16 @@ def _search_journal(args, journal, **kwargs): if args.on_date: args.start_date = args.end_date = args.on_date + if args.today_in_history: + now = time.parse("now") + args.day = now.day + args.month = now.month + journal.filter( tags=args.text, + month=args.month, + day=args.day, + year=args.year, start_date=args.start_date, end_date=args.end_date, strict=args.strict, diff --git a/tests/test_parse_args.py b/tests/test_parse_args.py index b9147ac0..252638c9 100644 --- a/tests/test_parse_args.py +++ b/tests/test_parse_args.py @@ -18,6 +18,10 @@ def expected_args(**kwargs): "delete": False, "edit": False, "end_date": None, + "today_in_history": False, + "month": None, + "day": None, + "year": None, "excluded": [], "export": False, "filename": None, @@ -147,6 +151,27 @@ def test_on_date_alone(): assert cli_as_dict("-on 'saturday'") == expected_args(on_date="saturday") +def test_month_alone(): + assert cli_as_dict("-month 1") == expected_args(month="1") + assert cli_as_dict("-month 01") == expected_args(month="01") + assert cli_as_dict("-month January") == expected_args(month="January") + assert cli_as_dict("-month Jan") == expected_args(month="Jan") + + +def test_day_alone(): + assert cli_as_dict("-day 1") == expected_args(day="1") + assert cli_as_dict("-day 01") == expected_args(day="01") + + +def test_year_alone(): + assert cli_as_dict("-year 2021") == expected_args(year="2021") + assert cli_as_dict("-year 21") == expected_args(year="21") + + +def test_today_in_history_alone(): + assert cli_as_dict("-today-in-history") == expected_args(today_in_history=True) + + def test_short_alone(): assert cli_as_dict("--short") == expected_args(short=True)