diff --git a/features/regression.feature b/features/regression.feature index 3e644b19..fb4f0060 100644 --- a/features/regression.feature +++ b/features/regression.feature @@ -62,3 +62,50 @@ Feature: Zapped bugs should stay dead. Then the output should contain "I'm going to activate the machine." Then the output should contain "I've crossed so many timelines. Is there any going back?" + Scenario: Create entry using day of the week as entry date. + Given we use the config "basic.yaml" + When we run "jrnl monday: This is an entry on a Monday." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "monday at 9am" in the local time + Then the output should contain "This is an entry on a Monday." + + Scenario: Create entry using day of the week abbreviations as entry date. + Given we use the config "basic.yaml" + When we run "jrnl fri: This is an entry on a Friday." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "friday at 9am" in the local time + + Scenario: Displaying entries using -on today should display entries created today. + Given we use the config "basic.yaml" + When we run "jrnl today: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl -on today" + Then the output should contain "Adding an entry right now." + + Scenario: Displaying entries using -from day should display correct entries + Given we use the config "basic.yaml" + When we run "jrnl yesterday: This thing happened yesterday" + Then we should see the message "Entry added" + When we run "jrnl today at 11:59pm: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl tomorrow: A future entry." + Then we should see the message "Entry added" + When we run "jrnl -from today" + Then the output should contain "Adding an entry right now." + Then the output should contain "A future entry." + Then the output should not contain "This thing happened yesterday" + + Scenario: Displaying entries using -from and -to day should display correct entries + Given we use the config "basic.yaml" + When we run "jrnl yesterday: This thing happened yesterday" + Then we should see the message "Entry added" + When we run "jrnl today at 11:59pm: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl tomorrow: A future entry." + Then we should see the message "Entry added" + When we run "jrnl -from yesterday -to today" + Then the output should contain "This thing happened yesterday" + Then the output should contain "Adding an entry right now." + Then the output should not contain "A future entry." diff --git a/features/steps/core.py b/features/steps/core.py index af4d19a3..314a5167 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -5,6 +5,9 @@ from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict +try: import parsedatetime.parsedatetime_consts as pdt +except ImportError: import parsedatetime as pdt +import time import os import json import yaml @@ -13,6 +16,10 @@ import tzlocal import shlex import sys +consts = pdt.Constants(usePyICU=False) +consts.DOWParseStyle = -1 # Prefers past weekdays +CALENDAR = pdt.Calendar(consts) + class TestKeyring(keyring.backend.KeyringBackend): """A test keyring that just stores its values in a hash""" @@ -221,9 +228,9 @@ def check_output(context, text=None): def check_output_time_inline(context, text): out = context.stdout_capture.getvalue() local_tz = tzlocal.get_localzone() - utc_time = date_parser.parse(text) - local_date = utc_time.astimezone(local_tz).strftime("%Y-%m-%d %H:%M") - assert local_date in out, local_date + date, flag = CALENDAR.parse(text) + output_date = time.strftime("%Y-%m-%d %H:%M",date) + assert output_date in out, output_date @then('the output should contain') diff --git a/jrnl/Journal.py b/jrnl/Journal.py index f6813222..0fd41c22 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -122,7 +122,8 @@ class Journal: try: new_date = datetime.strptime(date_blob, self.config["timeformat"]) except ValueError: - new_date = time.parse(date_blob) + # Passing in a date that had brackets around it + new_date = time.parse(date_blob, bracketed=True) if new_date: if entries: diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index 8d59b556..53b595e3 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -9,8 +9,9 @@ from .tag_exporter import TagExporter from .xml_exporter import XMLExporter from .yaml_exporter import YAMLExporter from .template_exporter import __all__ as template_exporters +from .fancy_exporter import FancyExporter -__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter] + template_exporters +__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter, FancyExporter] + template_exporters __importers =[JRNLImporter] __exporter_types = {name: plugin for plugin in __exporters for name in plugin.names} diff --git a/jrnl/plugins/fancy_exporter.py b/jrnl/plugins/fancy_exporter.py new file mode 100644 index 00000000..74d4555f --- /dev/null +++ b/jrnl/plugins/fancy_exporter.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from __future__ import absolute_import, unicode_literals, print_function +from .text_exporter import TextExporter +from textwrap import TextWrapper + + +class FancyExporter(TextExporter): + """This Exporter can convert entries and journals into text with unicode box drawing characters.""" + names = ["fancy", "boxed"] + extension = "txt" + + border_a="┎" + border_b="─" + border_c="╮" + border_d="╘" + border_e="═" + border_f="╕" + border_g="┃" + border_h="│" + border_i="┠" + border_j="╌" + border_k="┤" + border_l="┖" + border_m="┘" + + @classmethod + def export_entry(cls, entry): + """Returns a fancy unicode representation of a single entry.""" + date_str = entry.date.strftime(entry.journal.config['timeformat']) + linewrap = entry.journal.config['linewrap'] or 78 + initial_linewrap = linewrap - len(date_str) - 2 + body_linewrap = linewrap - 2 + card = [cls.border_a + cls.border_b*(initial_linewrap) + cls.border_c + date_str] + w = TextWrapper(width=initial_linewrap, initial_indent=cls.border_g+' ', subsequent_indent=cls.border_g+' ') + title_lines = w.wrap(entry.title) + card.append(title_lines[0].ljust(initial_linewrap+1) + cls.border_d + cls.border_e*(len(date_str)-1) + cls.border_f) + w.width = body_linewrap + if len(title_lines) > 1: + for line in w.wrap(' '.join([title_line[len(w.subsequent_indent):] + for title_line in title_lines[1:]])): + card.append(line.ljust(body_linewrap+1) + cls.border_h) + if entry.body: + card.append(cls.border_i + cls.border_j*body_linewrap + cls.border_k) + for line in entry.body.splitlines(): + body_lines = w.wrap(line) or [cls.border_g] + for body_line in body_lines: + card.append(body_line.ljust(body_linewrap+1) + cls.border_h) + card.append(cls.border_l + cls.border_b*body_linewrap + cls.border_m) + return "\n".join(card) + + @classmethod + def export_journal(cls, journal): + """Returns a unicode representation of an entire journal.""" + return "\n".join(cls.export_entry(entry) for entry in journal) diff --git a/jrnl/time.py b/jrnl/time.py index eee3fc20..66d3f4f8 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -12,15 +12,16 @@ consts.DOWParseStyle = -1 # "Monday" will be either today or the last Monday CALENDAR = pdt.Calendar(consts) -def parse(date_str, inclusive=False, default_hour=None, default_minute=None): +def parse(date_str, inclusive=False, default_hour=None, default_minute=None, bracketed=False): """Parses a string containing a fuzzy date and returns a datetime.datetime object""" if not date_str: return None elif isinstance(date_str, datetime): return date_str - # Don't try to parse anything with 6 or less characters. It's probably a markdown footnote - if len(date_str) <= 6: + # Don't try to parse anything with 6 or less characters and was parsed from the existing journal. + # It's probably a markdown footnote + if len(date_str) <= 6 and bracketed: return None default_date = DEFAULT_FUTURE if inclusive else DEFAULT_PAST @@ -51,8 +52,11 @@ def parse(date_str, inclusive=False, default_hour=None, default_minute=None): except TypeError: return None - if flag == 1: # Date found, but no time. Use the default time. - date = datetime(*date[:3], hour=default_hour or 0, minute=default_minute or 0) + if flag is 1: # Date found, but no time. Use the default time. + date = datetime(*date[:3], + hour=23 if inclusive else default_hour or 0, + minute=59 if inclusive else default_minute or 0, + second=59 if inclusive else 0) else: date = datetime(*date[:6])