From 3aae43e138ce7ef9f4787fb5fccbccf16086a5e1 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 20:32:40 -0800 Subject: [PATCH 1/5] [GH-738] preventing readline usage on Windows - removes autocomplete on the install step for Windows users --- jrnl/install.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/jrnl/install.py b/jrnl/install.py index c104b46b..8eb73c76 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import readline import glob import getpass import os @@ -14,6 +13,9 @@ from .util import UserAbort import yaml import logging import sys +if "win32" not in sys.platform: + # readline is not included in Windows Active Python + import readline DEFAULT_CONFIG_NAME = 'jrnl.yaml' DEFAULT_JOURNAL_NAME = 'journal.txt' @@ -108,14 +110,10 @@ def load_or_install_jrnl(): def install(): - def autocomplete(text, state): - expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + '*') - expansions = [e + "/" if os.path.isdir(e) else e for e in expansions] - expansions.append(None) - return expansions[state] - readline.set_completer_delims(' \t\n;') - readline.parse_and_bind("tab: complete") - readline.set_completer(autocomplete) + if "win32" not in sys.platform: + readline.set_completer_delims(' \t\n;') + readline.parse_and_bind("tab: complete") + readline.set_completer(autocomplete) # Where to create the journal? path_query = f'Path to your journal file (leave blank for {JOURNAL_FILE_PATH}): ' @@ -146,3 +144,9 @@ def install(): if password: config['password'] = password return config + +def autocomplete(text, state): + expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + '*') + expansions = [e + "/" if os.path.isdir(e) else e for e in expansions] + expansions.append(None) + return expansions[state] From d947b19bcd34027132514a17c5b461dcd0211c44 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 21:39:28 -0800 Subject: [PATCH 2/5] [GH-693] work so far on updating developer documentation --- CONTRIBUTING.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0de5982e..8b20e4bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,28 @@ -Contributing -============ +# Contributing If you use jrnl, you can totally make our day by just saying "thanks for the code." It's your chance to make a programmer happy today! If you have a moment, let us know what you use jrnl for and how; it'll help us to make it even better! -Docs & Typos ------------- +# Table of Contents + * [Docs and Typos](#docs-and-typos) + * [Bugs](#bugs) + * [Feature requests and ideas](#feature-requests-and-ideas) + * [New programmers and programmers new to python](#new-programmers-and-programmers-new-to-python) + * [Developing jrnl](#developing-jrnl) + + +## Docs and Typos If you find a typo or a mistake in the docs, please fix it right away and send a pull request. The Right Way™ to fix the docs is to edit the `docs/*.md` files on the **master** branch. You can see the result if you run `make html` inside the project's root directory, which will open a browser that hot-reloads as you change the docs. This requires [mkdocs](https://www.mkdocs.org) to be installed. The `gh-pages` branch is automatically maintained and updates from `master`; you should never have to edit that. -Bugs ----- +## Bugs -Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request! We'll review as quickly as we can. +Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request that references the issue! We'll review as quickly as we can. -Feature requests and ideas --------------------------- +## Feature requests and ideas -So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. +So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. When discussing new features, please keep in mind our design goals. jrnl strives to do one thing well. To us, that means: @@ -27,9 +31,16 @@ When discussing new features, please keep in mind our design goals. jrnl strives * avoid duplicating functionality -A short note for new programmers and programmers new to python --------------------------------------------------------------- +## New programmers and programmers new to python -Although jrnl has grown quite a bit since its inception. The overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. +Although jrnl has grown quite a bit since its inception, the overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. If you have a question, please don't hesitate to ask! Python is known for its welcoming community and openness to novice programmers, so feel free to fork the code and play around with it! If you create something you want to share with us, please create a pull request. We never expect pull requests to be perfect, idiomatic, instantly mergeable code. We can work through it together! + +## Developing jrnl + +The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. + + * To run the source: `python3 -m jrnl` + * To run tests: `make test` or `poetry run behave` if on Windows + * To build and install: `poetry install` then `poetry shell` From 29545c3a81792422a3860f5d2d03898bc89ae30d Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 25 Nov 2019 19:39:58 -0800 Subject: [PATCH 3/5] [GH-741] adding test for bug that displays all entries -on today --- features/regression.feature | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/features/regression.feature b/features/regression.feature index 3e644b19..4534761b 100644 --- a/features/regression.feature +++ b/features/regression.feature @@ -62,3 +62,10 @@ 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: Viewing today's entries does not print the entire journal + # https://github.com/jrnl-org/jrnl/issues/741 + Given we use the config "basic.yaml" + When we run "jrnl -on today" + Then the output should not contain "Life is good" + Then the output should not contain "But I'm better." + From 13bdd94dfb2bc4c4cedc2614212d833784f4d5a1 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 25 Nov 2019 20:22:44 -0800 Subject: [PATCH 4/5] [GH-693] cleaning up "Developing" section and adding a note about behave --- CONTRIBUTING.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b20e4bd..414a7091 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,6 @@ If you find a typo or a mistake in the docs, please fix it right away and send a Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request that references the issue! We'll review as quickly as we can. - ## Feature requests and ideas So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. @@ -30,7 +29,6 @@ When discussing new features, please keep in mind our design goals. jrnl strives * have a simple interface * avoid duplicating functionality - ## New programmers and programmers new to python Although jrnl has grown quite a bit since its inception, the overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. @@ -39,8 +37,9 @@ If you have a question, please don't hesitate to ask! Python is known for its we ## Developing jrnl -The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. +The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. You will need to install it to develop journal. - * To run the source: `python3 -m jrnl` - * To run tests: `make test` or `poetry run behave` if on Windows - * To build and install: `poetry install` then `poetry shell` + * To run tests: `make test` (or `poetry run behave` if on Windows) + * To run the source: `poetry install` then `poetry shell` then run `jrnl` with or without arguments as necessary + +For testing, jrnl uses [behave](https://behave.readthedocs.io/). From a529b70614c54d8270252ca1d3f3b70c61718c87 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Mon, 25 Nov 2019 19:59:53 -0800 Subject: [PATCH 5/5] Squashed commit of the following: commit 75113187432939a51486422c3f70b3a9e2bcf0aa Merge: 74d1854 47e10fb Author: Jonathan Wren <9453067+wren@users.noreply.github.com> Date: Thu Oct 24 17:02:10 2019 -0700 Merge pull request #665 from notbalanced/issue_662 Fixes Issue #662 - Day names not treated consistently for new entry commit 74d1854a4bba468221b4eee254bdee2bb40f5d5a Merge: 1bbf074 6a5726a Author: Jonathan Wren <9453067+wren@users.noreply.github.com> Date: Sat Oct 5 15:30:57 2019 -0700 Merge pull request #418 from philipsd6/2.0-fancy_exporter Add exporter to output entries inside unicode box character boxes commit 47e10fbee746dba14f2550610b06301e7925a24f Author: Craig Moyer Date: Sun Sep 29 19:06:53 2019 -0400 Fix issue #662 to properly handle day names as new entry dates and command line (-on, -from, -to). commit 9588913100979b2a33ab87f60b5233f79c5bdf7d Author: Craig Moyer Date: Sun Sep 29 08:27:27 2019 -0400 Syncing with jrnl-org/master commit 4c68eb193d10db13fe9ec1321f75c89e6baf76f9 Merge: 81dfebb 1bbf074 Author: Craig Moyer Date: Sun Sep 29 07:52:02 2019 -0400 Merge remote-tracking branch 'upstream/master' into 2.0-rc1-maebert commit 81dfebb2c0ef4d4ebf16e8c29fdec5f4a9d9afb3 Author: Manuel Ebert Date: Mon Apr 29 20:34:18 2019 +0200 export changes commit 6a5726acd2102a715c2b1ec92190982ce66eaf07 Author: Philip Douglass Date: Fri Dec 22 20:56:36 2017 -0500 Enable FancyExporter plugin commit 3d1b22687131f5adf8c69769e4b6c79232337be8 Author: Philip Douglass Date: Fri Jan 29 11:17:41 2016 -0500 Add exporter to output entries inside unicode box character boxes --- features/regression.feature | 47 ++++++++++++++++++++++++++++ features/steps/core.py | 13 ++++++-- jrnl/Journal.py | 3 +- jrnl/plugins/__init__.py | 3 +- jrnl/plugins/fancy_exporter.py | 56 ++++++++++++++++++++++++++++++++++ jrnl/time.py | 14 ++++++--- 6 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 jrnl/plugins/fancy_exporter.py 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])