Merge pull request #843 from jrnl-org/v2.5

Add support to save journals to multiple files in a directory
This commit is contained in:
Jonathan Wren 2020-02-29 15:13:28 -08:00 committed by GitHub
commit 28a4bd8f18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 152 additions and 14 deletions

View file

@ -23,9 +23,3 @@ Feature: Zapped Dayone bugs stay dead!
2014-04-24 09:00 Ran 6.2 miles today in 1:02:03.
| I'm feeling sore because I forgot to stretch.
"""
Scenario: Opening an folder that's not a DayOne folder gives a nice error message
Given we use the config "empty_folder.yaml"
When we run "jrnl Herro"
Then we should get an error
Then we should see the message "is a directory, but doesn't seem to be a DayOne journal either"

42
features/folder.feature Normal file
View file

@ -0,0 +1,42 @@
Feature: Testing a journal with a root directory and multiple files in the format of yyyy/mm/dd.txt
Scenario: Opening an folder that's not a DayOne folder should treat as folder journal
Given we use the config "empty_folder.yaml"
When we run "jrnl 23 july 2013: Testing folder journal."
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should be
"""
2013-07-23 09:00 Testing folder journal.
"""
Scenario: Adding entries to a Folder journal should generate date files
Given we use the config "empty_folder.yaml"
When we run "jrnl 23 July 2013: Testing folder journal."
Then we should see the message "Entry added"
When the journal directory is listed
Then the output should contain "2013/07/23.txt" or "2013\07\23.txt"
Scenario: Adding multiple entries to a Folder journal should generate multiple date files
Given we use the config "empty_folder.yaml"
When we run "jrnl 23 July 2013: Testing folder journal."
And we run "jrnl 3/7/2014: Second entry of journal."
Then we should see the message "Entry added"
When the journal directory is listed
Then the output should contain "2013/07/23.txt" or "2013\07\23.txt"
Then the output should contain "2014/03/07.txt" or "2014\03\07.txt"
Scenario: Out of order entries to a Folder journal should be listed in date order
Given we use the config "empty_folder.yaml"
When we run "jrnl 3/7/2014 4:37pm: Second entry of journal."
Then we should see the message "Entry added"
When we run "jrnl 23 July 2013: Testing folder journal."
Then we should see the message "Entry added"
When we run "jrnl -2"
Then the output should be
"""
2013-07-23 09:00 Testing folder journal.
2014-03-07 16:37 Second entry of journal.
"""

View file

@ -210,10 +210,11 @@ def check_output_time_inline(context, text):
@then("the output should contain")
@then('the output should contain "{text}"')
def check_output_inline(context, text=None):
@then('the output should contain "{text}" or "{text2}"')
def check_output_inline(context, text=None, text2=None):
text = text or context.text
out = context.stdout_capture.getvalue()
assert text in out, text
assert text in out or text2 in out, text or text2
@then('the output should not contain "{text}"')
@ -270,6 +271,17 @@ def check_journal_entries(context, number, journal_name="default"):
assert len(journal.entries) == number
@when("the journal directory is listed")
def list_journal_directory(context, journal="default"):
files = []
with open(install.CONFIG_FILE_PATH) as config_file:
config = yaml.load(config_file, Loader=yaml.FullLoader)
journal_path = config["journals"][journal]
for root, dirnames, f in os.walk(journal_path):
for file in f:
print(os.path.join(root, file))
@then("fail")
def debug_fail(context):
assert False

View file

@ -23,7 +23,7 @@ def make_key(password):
length=32,
# Salt is hard-coded
salt=b"\xf2\xd5q\x0e\xc1\x8d.\xde\xdc\x8e6t\x89\x04\xce\xf8",
iterations=100000,
iterations=100_000,
backend=default_backend(),
)
key = kdf.derive(password)

93
jrnl/FolderJournal.py Normal file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from . import Entry
from . import Journal
import codecs
import os
import fnmatch
def get_files(journal_config):
"""Searches through sub directories starting with journal_config and find all text files"""
filenames = []
for root, dirnames, f in os.walk(journal_config):
for filename in fnmatch.filter(f, "*.txt"):
filenames.append(os.path.join(root, filename))
return filenames
class Folder(Journal.Journal):
"""A Journal handling multiple files in a folder"""
def __init__(self, **kwargs):
self.entries = []
self._diff_entry_dates = []
super(Folder, self).__init__(**kwargs)
def open(self):
filenames = []
self.entries = []
filenames = get_files(self.config["journal"])
for filename in filenames:
with codecs.open(filename, "r", "utf-8") as f:
journal = f.read()
self.entries.extend(self._parse(journal))
self.sort()
return self
def write(self):
"""Writes only the entries that have been modified into proper files."""
# Create a list of dates of modified entries. Start with diff_entry_dates
modified_dates = self._diff_entry_dates
seen_dates = set(self._diff_entry_dates)
for e in self.entries:
if e.modified:
if e.date not in seen_dates:
modified_dates.append(e.date)
seen_dates.add(e.date)
# For every date that had a modified entry, write to a file
for d in modified_dates:
write_entries = []
filename = os.path.join(
self.config["journal"],
d.strftime("%Y"),
d.strftime("%m"),
d.strftime("%d") + ".txt",
)
dirname = os.path.dirname(filename)
# create directory if it doesn't exist
if not os.path.exists(dirname):
os.makedirs(dirname)
for e in self.entries:
if (
e.date.year == d.year
and e.date.month == d.month
and e.date.day == d.day
):
write_entries.append(e)
journal = "\n".join([e.__str__() for e in write_entries])
with codecs.open(filename, "w", "utf-8") as journal_file:
journal_file.write(journal)
# look for and delete empty files
filenames = []
filenames = get_files(self.config["journal"])
for filename in filenames:
if os.stat(filename).st_size <= 0:
# print("empty file: {}".format(filename))
os.remove(filename)
def parse_editable_str(self, edited):
"""Parses the output of self.editable_str and updates it's entries."""
mod_entries = self._parse(edited)
diff_entries = set(self.entries) - set(mod_entries)
for e in diff_entries:
self._diff_entry_dates.append(e.date)
# Match those entries that can be found in self.entries and set
# these to modified, so we can get a count of how many entries got
# modified and how many got deleted later.
for entry in mod_entries:
entry.modified = not any(entry == old_entry for old_entry in self.entries)
self.entries = mod_entries

View file

@ -378,12 +378,9 @@ def open_journal(name, config, legacy=False):
return DayOneJournal.DayOne(**config).open()
else:
print(
f"[Error: {config['journal']} is a directory, but doesn't seem to be a DayOne journal either.",
file=sys.stderr,
)
from . import FolderJournal
sys.exit(1)
return FolderJournal.Folder(**config).open()
if not config["encrypt"]:
if legacy: