mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
commit
d50e55480a
13 changed files with 245 additions and 16 deletions
|
@ -5,7 +5,7 @@ python:
|
||||||
- "3.3"
|
- "3.3"
|
||||||
install:
|
install:
|
||||||
- "pip install -q -r requirements.txt --use-mirrors"
|
- "pip install -q -r requirements.txt --use-mirrors"
|
||||||
- "pip install -q behave"
|
- "pip install -q behave python-dateutil"
|
||||||
# command to run tests
|
# command to run tests
|
||||||
script:
|
script:
|
||||||
- python --version
|
- python --version
|
||||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,11 +1,19 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
### 1.5.2
|
#### 1.5.4
|
||||||
|
|
||||||
|
* [New] DayOne journals can now handle tags
|
||||||
|
|
||||||
|
#### 1.5.3
|
||||||
|
|
||||||
|
* [Fixed] DayOne integration with older DayOne Journals
|
||||||
|
|
||||||
|
#### 1.5.2
|
||||||
|
|
||||||
* [Improved] Soft-deprecated `-to` for filtering by time and introduces `-until` instead.
|
* [Improved] Soft-deprecated `-to` for filtering by time and introduces `-until` instead.
|
||||||
|
|
||||||
### 1.5.1
|
#### 1.5.1
|
||||||
|
|
||||||
* [Fixed] Fixed a bug introduced in 1.5.0 that caused the entire journal to be printed after composing an entry
|
* [Fixed] Fixed a bug introduced in 1.5.0 that caused the entire journal to be printed after composing an entry
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ The configuration file is a simple JSON file with the following options.
|
||||||
|
|
||||||
### DayOne Integration
|
### DayOne Integration
|
||||||
|
|
||||||
Using your DayOne journal instead of a flat text file is dead simple - instead of pointing to a text file, set the `"journal"` key in your `.jrnl_conf` to point to your DayOne journal. This is a folder ending with `.dayone`, and it's located at
|
Using your DayOne journal instead of a flat text file is dead simple - instead of pointing to a text file, change your `.jrnl_conf` to point to your DayOne journal. This is a folder ending with `.dayone`, and it's located at
|
||||||
|
|
||||||
* `~/Library/Application Support/Day One/` by default
|
* `~/Library/Application Support/Day One/` by default
|
||||||
* `~/Dropbox/Apps/Day One/` if you're syncing with Dropbox and
|
* `~/Dropbox/Apps/Day One/` if you're syncing with Dropbox and
|
||||||
|
|
14
features/data/configs/dayone.json
Normal file
14
features/data/configs/dayone.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"default_hour": 9,
|
||||||
|
"timeformat": "%Y-%m-%d %H:%M",
|
||||||
|
"linewrap": 80,
|
||||||
|
"encrypt": false,
|
||||||
|
"editor": "",
|
||||||
|
"default_minute": 0,
|
||||||
|
"highlight": true,
|
||||||
|
"password": "",
|
||||||
|
"journals": {
|
||||||
|
"default": "features/journals/dayone.dayone"
|
||||||
|
},
|
||||||
|
"tagsymbols": "@"
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Creation Date</key>
|
||||||
|
<date>2013-05-17T18:39:20Z</date>
|
||||||
|
<key>Creator</key>
|
||||||
|
<dict>
|
||||||
|
<key>Device Agent</key>
|
||||||
|
<string>Macintosh/MacBookAir5,2</string>
|
||||||
|
<key>Generation Date</key>
|
||||||
|
<date>2013-08-17T18:39:20Z</date>
|
||||||
|
<key>Host Name</key>
|
||||||
|
<string>Egeria</string>
|
||||||
|
<key>OS Agent</key>
|
||||||
|
<string>Mac OS X/10.8.4</string>
|
||||||
|
<key>Software Agent</key>
|
||||||
|
<string>Day One (Mac)/1.8</string>
|
||||||
|
</dict>
|
||||||
|
<key>Entry Text</key>
|
||||||
|
<string>This entry has tags!</string>
|
||||||
|
<key>Starred</key>
|
||||||
|
<false/>
|
||||||
|
<key>Tags</key>
|
||||||
|
<array>
|
||||||
|
<string>work</string>
|
||||||
|
<string>play</string>
|
||||||
|
</array>
|
||||||
|
<key>Time Zone</key>
|
||||||
|
<string>America/Los_Angeles</string>
|
||||||
|
<key>UUID</key>
|
||||||
|
<string>044F3747A38546168B572C2E3F217FA2</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Creation Date</key>
|
||||||
|
<date>2013-06-17T18:38:29Z</date>
|
||||||
|
<key>Creator</key>
|
||||||
|
<dict>
|
||||||
|
<key>Device Agent</key>
|
||||||
|
<string>Macintosh/MacBookAir5,2</string>
|
||||||
|
<key>Generation Date</key>
|
||||||
|
<date>2013-08-17T18:38:29Z</date>
|
||||||
|
<key>Host Name</key>
|
||||||
|
<string>Egeria</string>
|
||||||
|
<key>OS Agent</key>
|
||||||
|
<string>Mac OS X/10.8.4</string>
|
||||||
|
<key>Software Agent</key>
|
||||||
|
<string>Day One (Mac)/1.8</string>
|
||||||
|
</dict>
|
||||||
|
<key>Entry Text</key>
|
||||||
|
<string>This entry has a location.</string>
|
||||||
|
<key>Location</key>
|
||||||
|
<dict>
|
||||||
|
<key>Administrative Area</key>
|
||||||
|
<string>California</string>
|
||||||
|
<key>Country</key>
|
||||||
|
<string>Germany</string>
|
||||||
|
<key>Latitude</key>
|
||||||
|
<real>52.4979764</real>
|
||||||
|
<key>Locality</key>
|
||||||
|
<string>Berlin</string>
|
||||||
|
<key>Longitude</key>
|
||||||
|
<real>13.2404758</real>
|
||||||
|
<key>Place Name</key>
|
||||||
|
<string>Abandoned Spy Tower</string>
|
||||||
|
</dict>
|
||||||
|
<key>Starred</key>
|
||||||
|
<false/>
|
||||||
|
<key>Tags</key>
|
||||||
|
<array/>
|
||||||
|
<key>Time Zone</key>
|
||||||
|
<string>Europe/Berlin</string>
|
||||||
|
<key>UUID</key>
|
||||||
|
<string>0BDDD6CDA43C4A9AA2681517CC35AD9D</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Creation Date</key>
|
||||||
|
<date>2013-07-17T18:38:08Z</date>
|
||||||
|
<key>Creator</key>
|
||||||
|
<dict>
|
||||||
|
<key>Device Agent</key>
|
||||||
|
<string>Macintosh/MacBookAir5,2</string>
|
||||||
|
<key>Generation Date</key>
|
||||||
|
<date>2013-08-17T18:38:08Z</date>
|
||||||
|
<key>Host Name</key>
|
||||||
|
<string>Egeria</string>
|
||||||
|
<key>OS Agent</key>
|
||||||
|
<string>Mac OS X/10.8.4</string>
|
||||||
|
<key>Software Agent</key>
|
||||||
|
<string>Day One (Mac)/1.8</string>
|
||||||
|
</dict>
|
||||||
|
<key>Entry Text</key>
|
||||||
|
<string>This entry is starred!</string>
|
||||||
|
<key>Starred</key>
|
||||||
|
<true/>
|
||||||
|
<key>Tags</key>
|
||||||
|
<array/>
|
||||||
|
<key>Time Zone</key>
|
||||||
|
<string>America/Los_Angeles</string>
|
||||||
|
<key>UUID</key>
|
||||||
|
<string>422BC895507944A291E6FC44FC6B8BFC</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Creation Date</key>
|
||||||
|
<date>2013-01-17T18:37:50Z</date>
|
||||||
|
<key>Creator</key>
|
||||||
|
<dict>
|
||||||
|
<key>Device Agent</key>
|
||||||
|
<string>Macintosh/MacBookAir5,2</string>
|
||||||
|
<key>Generation Date</key>
|
||||||
|
<date>2013-08-17T18:37:50Z</date>
|
||||||
|
<key>Host Name</key>
|
||||||
|
<string>Egeria</string>
|
||||||
|
<key>OS Agent</key>
|
||||||
|
<string>Mac OS X/10.8.4</string>
|
||||||
|
<key>Software Agent</key>
|
||||||
|
<string>Day One (Mac)/1.8</string>
|
||||||
|
</dict>
|
||||||
|
<key>Entry Text</key>
|
||||||
|
<string>This is a DayOne entry without Timezone.</string>
|
||||||
|
<key>Starred</key>
|
||||||
|
<false/>
|
||||||
|
<key>Tags</key>
|
||||||
|
<array/>
|
||||||
|
<key>UUID</key>
|
||||||
|
<string>4BB1F46946AD439996C9B59DE7C4DDC1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
48
features/dayone.feature
Normal file
48
features/dayone.feature
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
Feature: DayOne Ingetration
|
||||||
|
|
||||||
|
Scenario: Loading a DayOne Journal
|
||||||
|
Given we use the config "dayone.json"
|
||||||
|
When we run "jrnl -from 'feb 2013'"
|
||||||
|
Then we should get no error
|
||||||
|
and the output should be
|
||||||
|
"""
|
||||||
|
2013-05-17 11:39 This entry has tags!
|
||||||
|
|
||||||
|
2013-06-17 20:38 This entry has a location.
|
||||||
|
|
||||||
|
2013-07-17 11:38 This entry is starred!
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Entries without timezone information will be intepreted in the current timezone
|
||||||
|
Given we use the config "dayone.json"
|
||||||
|
When we run "jrnl -until 'feb 2013'"
|
||||||
|
Then we should get no error
|
||||||
|
and the output should contain "2013-01-17T18:37Z" in the local time
|
||||||
|
|
||||||
|
Scenario: Writing into Dayone
|
||||||
|
Given we use the config "dayone.json"
|
||||||
|
When we run "jrnl 01 may 1979: Being born hurts."
|
||||||
|
and we run "jrnl -until 1980"
|
||||||
|
Then the output should be
|
||||||
|
"""
|
||||||
|
1979-05-01 09:00 Being born hurts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Loading tags from a DayOne Journal
|
||||||
|
Given we use the config "dayone.json"
|
||||||
|
When we run "jrnl --tags"
|
||||||
|
Then the output should be
|
||||||
|
"""
|
||||||
|
work : 1
|
||||||
|
play : 1
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Saving tags from a DayOne Journal
|
||||||
|
Given we use the config "dayone.json"
|
||||||
|
When we run "jrnl A hard day at @work"
|
||||||
|
and we run "jrnl --tags"
|
||||||
|
Then the output should be
|
||||||
|
"""
|
||||||
|
work : 2
|
||||||
|
play : 1
|
||||||
|
"""
|
|
@ -18,7 +18,11 @@ def before_scenario(context, scenario):
|
||||||
if not os.path.exists(working_dir):
|
if not os.path.exists(working_dir):
|
||||||
os.mkdir(working_dir)
|
os.mkdir(working_dir)
|
||||||
for filename in os.listdir(original):
|
for filename in os.listdir(original):
|
||||||
shutil.copy2(os.path.join(original, filename), working_dir)
|
source = os.path.join(original, filename)
|
||||||
|
if os.path.isdir(source):
|
||||||
|
shutil.copytree(source, os.path.join(working_dir, filename))
|
||||||
|
else:
|
||||||
|
shutil.copy2(source, working_dir)
|
||||||
|
|
||||||
def after_scenario(context, scenario):
|
def after_scenario(context, scenario):
|
||||||
"""After each scenario, restore all test data and remove working_dirs."""
|
"""After each scenario, restore all test data and remove working_dirs."""
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
from behave import *
|
from behave import *
|
||||||
from jrnl import jrnl, Journal
|
from jrnl import jrnl, Journal, util
|
||||||
|
from dateutil import parser as date_parser
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
import pytz
|
||||||
try:
|
try:
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -66,15 +68,15 @@ def no_error(context):
|
||||||
@then('the output should be parsable as json')
|
@then('the output should be parsable as json')
|
||||||
def check_output_json(context):
|
def check_output_json(context):
|
||||||
out = context.stdout_capture.getvalue()
|
out = context.stdout_capture.getvalue()
|
||||||
assert json.loads(out)
|
assert json.loads(out), out
|
||||||
|
|
||||||
@then('"{field}" in the json output should have {number:d} elements')
|
@then('"{field}" in the json output should have {number:d} elements')
|
||||||
@then('"{field}" in the json output should have 1 element')
|
@then('"{field}" in the json output should have 1 element')
|
||||||
def check_output_field(context, field, number=1):
|
def check_output_field(context, field, number=1):
|
||||||
out = context.stdout_capture.getvalue()
|
out = context.stdout_capture.getvalue()
|
||||||
out_json = json.loads(out)
|
out_json = json.loads(out)
|
||||||
assert field in out_json
|
assert field in out_json, [field, out_json]
|
||||||
assert len(out_json[field]) == number
|
assert len(out_json[field]) == number, len(out_json[field])
|
||||||
|
|
||||||
@then('"{field}" in the json output should not contain "{key}"')
|
@then('"{field}" in the json output should not contain "{key}"')
|
||||||
def check_output_field_not_key(context, field, key):
|
def check_output_field_not_key(context, field, key):
|
||||||
|
@ -95,7 +97,16 @@ def check_output(context):
|
||||||
text = context.text.strip().splitlines()
|
text = context.text.strip().splitlines()
|
||||||
out = context.stdout_capture.getvalue().strip().splitlines()
|
out = context.stdout_capture.getvalue().strip().splitlines()
|
||||||
for line_text, line_out in zip(text, out):
|
for line_text, line_out in zip(text, out):
|
||||||
assert line_text.strip() == line_out.strip()
|
assert line_text.strip() == line_out.strip(), [line_text.strip(), line_out.strip()]
|
||||||
|
|
||||||
|
@then('the output should contain "{text}" in the local time')
|
||||||
|
def check_output_time_inline(context, text):
|
||||||
|
out = context.stdout_capture.getvalue()
|
||||||
|
local_tz = pytz.timezone(util.get_local_timezone())
|
||||||
|
utc_time = date_parser.parse(text)
|
||||||
|
date = utc_time + local_tz._utcoffset
|
||||||
|
local_date = date.strftime("%Y-%m-%d %H:%M")
|
||||||
|
assert local_date in out, local_date
|
||||||
|
|
||||||
@then('the output should contain "{text}"')
|
@then('the output should contain "{text}"')
|
||||||
def check_output_inline(context, text):
|
def check_output_inline(context, text):
|
||||||
|
|
|
@ -315,13 +315,14 @@ class DayOne(Journal):
|
||||||
dict_entry = plistlib.readPlist(plist_entry)
|
dict_entry = plistlib.readPlist(plist_entry)
|
||||||
try:
|
try:
|
||||||
timezone = pytz.timezone(dict_entry['Time Zone'])
|
timezone = pytz.timezone(dict_entry['Time Zone'])
|
||||||
except pytz.exceptions.UnknownTimeZoneError:
|
except (KeyError, pytz.exceptions.UnknownTimeZoneError):
|
||||||
timezone = pytz.timezone(util.get_local_timezone())
|
timezone = pytz.timezone(util.get_local_timezone())
|
||||||
date = dict_entry['Creation Date']
|
date = dict_entry['Creation Date']
|
||||||
date = date + timezone.utcoffset(date)
|
date = date + timezone.utcoffset(date)
|
||||||
entry = self.new_entry(raw=dict_entry['Entry Text'], date=date, sort=False)
|
entry = self.new_entry(raw=dict_entry['Entry Text'], date=date, sort=False)
|
||||||
entry.starred = dict_entry["Starred"]
|
entry.starred = dict_entry["Starred"]
|
||||||
entry.uuid = dict_entry["UUID"]
|
entry.uuid = dict_entry["UUID"]
|
||||||
|
entry.tags = dict_entry.get("Tags", [])
|
||||||
# We're using new_entry to create the Entry object, which adds the entry
|
# We're using new_entry to create the Entry object, which adds the entry
|
||||||
# to self.entries already. However, in the original Journal.__init__, this
|
# to self.entries already. However, in the original Journal.__init__, this
|
||||||
# method is expected to return a list of newly created entries, which is why
|
# method is expected to return a list of newly created entries, which is why
|
||||||
|
@ -343,6 +344,9 @@ class DayOne(Journal):
|
||||||
'Starred': entry.starred if hasattr(entry, 'starred') else False,
|
'Starred': entry.starred if hasattr(entry, 'starred') else False,
|
||||||
'Entry Text': entry.title+"\n"+entry.body,
|
'Entry Text': entry.title+"\n"+entry.body,
|
||||||
'Time Zone': util.get_local_timezone(),
|
'Time Zone': util.get_local_timezone(),
|
||||||
'UUID': new_uuid
|
'UUID': new_uuid,
|
||||||
|
'Tags': [tag.strip(self.config['tagsymbols']) for tag in entry.tags]
|
||||||
}
|
}
|
||||||
|
# print entry_plist
|
||||||
|
|
||||||
plistlib.writePlist(entry_plist, filename)
|
plistlib.writePlist(entry_plist, filename)
|
||||||
|
|
|
@ -7,7 +7,7 @@ jrnl is a simple journal application for your command line.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'jrnl'
|
__title__ = 'jrnl'
|
||||||
__version__ = '1.5.2'
|
__version__ = '1.5.4'
|
||||||
__author__ = 'Manuel Ebert'
|
__author__ = 'Manuel Ebert'
|
||||||
__license__ = 'MIT License'
|
__license__ = 'MIT License'
|
||||||
__copyright__ = 'Copyright 2013 Manuel Ebert'
|
__copyright__ = 'Copyright 2013 Manuel Ebert'
|
||||||
|
|
Loading…
Add table
Reference in a new issue