diff --git a/features/tagging.feature b/features/tagging.feature index cc686673..2cbf7ce1 100644 --- a/features/tagging.feature +++ b/features/tagging.feature @@ -50,3 +50,35 @@ Feature: Tagging @foo : 1 @bar : 1 """ + + Scenario: Excluding a tag should filter it + Given we use the config "basic.yaml" + When we run "jrnl today: @foo came over, we went to a bar" + When we run "jrnl I have decided I did not enjoy that @bar" + When we run "jrnl --tags -not @bar" + Then the output should be + """ + @foo : 1 + """ + + Scenario: Excluding a tag should filter an entry, even if an unfiltered tag is in that entry + Given we use the config "basic.yaml" + When we run "jrnl today: I do @not think this will show up @thought" + When we run "jrnl today: I think this will show up @thought" + When we run "jrnl --tags -not @not" + Then the output should be + """ + @thought : 1 + """ + + Scenario: Excluding multiple tags should filter them + Given we use the config "basic.yaml" + When we run "jrnl today: I do @not think this will show up @thought" + When we run "jrnl today: I think this will show up @thought" + When we run "jrnl today: This should @never show up @thought" + When we run "jrnl today: What a nice day for filtering @thought" + When we run "jrnl --tags -not @not @never" + Then the output should be + """ + @thought : 2 + """ diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 7feb49a6..72fe94b1 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -186,7 +186,7 @@ class Journal(object): tag_counts = set([(tags.count(tag), tag) for tag in tags]) return [Tag(tag, count=count) for count, tag in sorted(tag_counts)] - def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False): + def filter(self, tags=[], start_date=None, end_date=None, starred=False, strict=False, short=False, exclude=[]): """Removes all entries from the journal that don't match the filter. tags is a list of tags, each being a string that starts with one of the @@ -197,19 +197,24 @@ class Journal(object): starred limits journal to starred entries If strict is True, all tags must be present in an entry. If false, the - entry is kept if any tag is present.""" + + exclude is a list of the tags which should not appear in the results. + entry is kept if any tag is present, unless they appear in exclude.""" self.search_tags = set([tag.lower() for tag in tags]) + excluded_tags = set([tag.lower() for tag in exclude]) end_date = time.parse(end_date, inclusive=True) start_date = time.parse(start_date) # If strict mode is on, all tags have to be present in entry tagged = self.search_tags.issubset if strict else self.search_tags.intersection + excluded = lambda tags: len([tag for tag in tags if tag in excluded_tags]) > 0 result = [ entry for entry in self.entries if (not tags or tagged(entry.tags)) and (not starred or entry.starred) 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)) ] self.entries = result diff --git a/jrnl/cli.py b/jrnl/cli.py index 6d88685a..65a53516 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -39,6 +39,7 @@ def parse_args(args=None): reading.add_argument('-and', dest='strict', action="store_true", help='Filter by tags using AND (default: OR)') reading.add_argument('-starred', dest='starred', action="store_true", help='Show only starred entries') reading.add_argument('-n', dest='limit', default=None, metavar="N", help="Shows the last n entries matching the filter. '-n 3' and '-3' have the same effect.", nargs="?", type=int) + reading.add_argument('-not', dest='excluded', nargs='+', default=[], metavar="E", help="Exclude entries with these tags") exporting = parser.add_argument_group('Export / Import', 'Options for transmogrifying your journal') exporting.add_argument('-s', '--short', dest='short', action="store_true", help='Show only titles or line containing the search tags') @@ -239,7 +240,8 @@ def run(manual_args=None): start_date=args.start_date, end_date=args.end_date, strict=args.strict, short=args.short, - starred=args.starred) + starred=args.starred, + exclude=args.excluded) journal.limit(args.limit) # Reading mode