mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-18 12:08:31 +02:00
Add support for folder base journal.
Adds feature for issue #170 (and #398) where you configure your journal to be a directory and entries are added as sub-directories and files: yyyy/mm/dd.txt. Multiple entries in a day will go in the same file, but a new entry for a specific day will create a new file (and directory structure).
This commit is contained in:
parent
2031902f1c
commit
cefc2db8b7
6 changed files with 139 additions and 9 deletions
43
features/folder.feature
Normal file
43
features/folder.feature
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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"
|
||||||
|
|
||||||
|
|
||||||
|
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"
|
||||||
|
And the output should contain "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.
|
||||||
|
"""
|
|
@ -8,12 +8,6 @@ Feature: Zapped bugs should stay dead.
|
||||||
When we run "jrnl -n 1"
|
When we run "jrnl -n 1"
|
||||||
Then the output should not contain "Life is good"
|
Then the output should not contain "Life is good"
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
Scenario: Date with time should be parsed correctly
|
Scenario: Date with time should be parsed correctly
|
||||||
# https://github.com/maebert/jrnl/issues/117
|
# https://github.com/maebert/jrnl/issues/117
|
||||||
Given we use the config "basic.yaml"
|
Given we use the config "basic.yaml"
|
||||||
|
|
|
@ -254,6 +254,15 @@ def check_journal_entries(context, number, journal_name="default"):
|
||||||
journal = open_journal(journal_name)
|
journal = open_journal(journal_name)
|
||||||
assert len(journal.entries) == number
|
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)
|
||||||
|
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')
|
@then('fail')
|
||||||
def debug_fail(context):
|
def debug_fail(context):
|
||||||
|
|
84
jrnl/FolderJournal.py
Normal file
84
jrnl/FolderJournal.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#!/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.__unicode__() 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
|
|
@ -314,8 +314,8 @@ def open_journal(name, config, legacy=False):
|
||||||
from . import DayOneJournal
|
from . import DayOneJournal
|
||||||
return DayOneJournal.DayOne(**config).open()
|
return DayOneJournal.DayOne(**config).open()
|
||||||
else:
|
else:
|
||||||
util.prompt(u"[Error: {0} is a directory, but doesn't seem to be a DayOne journal either.".format(config['journal']))
|
from . import FolderJournal
|
||||||
sys.exit(1)
|
return FolderJournal.Folder(**config).open()
|
||||||
|
|
||||||
if not config['encrypt']:
|
if not config['encrypt']:
|
||||||
if legacy:
|
if legacy:
|
||||||
|
|
|
@ -36,7 +36,7 @@ def __exporter_from_file(template_file):
|
||||||
"""Create a template class from a file"""
|
"""Create a template class from a file"""
|
||||||
name = os.path.basename(template_file).replace(".template", "")
|
name = os.path.basename(template_file).replace(".template", "")
|
||||||
template = Template.from_file(template_file)
|
template = Template.from_file(template_file)
|
||||||
return type("{}Exporter".format(name.title()), (GenericTemplateExporter, ), {
|
return type(str("{}Exporter".format(name.title())), (GenericTemplateExporter, ), {
|
||||||
"names": [name],
|
"names": [name],
|
||||||
"extension": template.extension,
|
"extension": template.extension,
|
||||||
"template": template
|
"template": template
|
||||||
|
|
Loading…
Add table
Reference in a new issue