Merge pull request #349 from MinchinWeb/2.0-yaml-export-fixes

2.0 -- yaml export fixes, dayone fixes
This commit is contained in:
Manuel Ebert 2015-05-06 08:56:03 +02:00
commit 43bb1f1cb2
7 changed files with 57 additions and 31 deletions

View file

@ -4,6 +4,7 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from . import Entry from . import Entry
from . import Journal from . import Journal
from . import time as jrnl_time
import os import os
import re import re
from datetime import datetime from datetime import datetime
@ -53,6 +54,7 @@ class DayOne(Journal.Journal):
entry = Entry.Entry(self, date, title, body, starred=dict_entry["Starred"]) entry = Entry.Entry(self, date, title, body, starred=dict_entry["Starred"])
entry.uuid = dict_entry["UUID"] entry.uuid = dict_entry["UUID"]
entry.tags = [self.config['tagsymbols'][0] + tag for tag in dict_entry.get("Tags", [])] entry.tags = [self.config['tagsymbols'][0] + tag for tag in dict_entry.get("Tags", [])]
self.entries.append(entry) self.entries.append(entry)
self.sort() self.sort()
return self return self
@ -61,16 +63,19 @@ class DayOne(Journal.Journal):
"""Writes only the entries that have been modified into plist files.""" """Writes only the entries that have been modified into plist files."""
for entry in self.entries: for entry in self.entries:
if entry.modified: if entry.modified:
utc_time = datetime.utcfromtimestamp(time.mktime(entry.date.timetuple()))
if not hasattr(entry, "uuid"): if not hasattr(entry, "uuid"):
entry.uuid = uuid.uuid1().hex entry.uuid = uuid.uuid1().hex
utc_time = datetime.utcfromtimestamp(time.mktime(entry.date.timetuple()))
filename = os.path.join(self.config['journal'], "entries", entry.uuid + ".doentry") filename = os.path.join(self.config['journal'], "entries", entry.uuid.upper() + ".doentry")
entry_plist = { entry_plist = {
'Creation Date': utc_time, 'Creation Date': utc_time,
'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': str(tzlocal.get_localzone()), 'Time Zone': str(tzlocal.get_localzone()),
'UUID': entry.uuid, 'UUID': entry.uuid.upper(),
'Tags': [tag.strip(self.config['tagsymbols']).replace("_", " ") for tag in entry.tags] 'Tags': [tag.strip(self.config['tagsymbols']).replace("_", " ") for tag in entry.tags]
} }
plistlib.writePlist(entry_plist, filename) plistlib.writePlist(entry_plist, filename)
@ -84,12 +89,11 @@ class DayOne(Journal.Journal):
return "\n".join(["# {0}\n{1}".format(e.uuid, e.__unicode__()) for e in self.entries]) return "\n".join(["# {0}\n{1}".format(e.uuid, e.__unicode__()) for e in self.entries])
def parse_editable_str(self, edited): def parse_editable_str(self, edited):
"""Parses the output of self.editable_str and updates it's entries.""" """Parses the output of self.editable_str and updates its entries."""
# Method: create a new list of entries from the edited text, then match # Method: create a new list of entries from the edited text, then match
# UUIDs of the new entries against self.entries, updating the entries # UUIDs of the new entries against self.entries, updating the entries
# if the edited entries differ, and deleting entries from self.entries # if the edited entries differ, and deleting entries from self.entries
# if they don't show up in the edited entries anymore. # if they don't show up in the edited entries anymore.
date_length = len(datetime.today().strftime(self.config['timeformat']))
# Initialise our current entry # Initialise our current entry
entries = [] entries = []
@ -106,16 +110,18 @@ class DayOne(Journal.Journal):
current_entry.modified = False current_entry.modified = False
current_entry.uuid = m.group(1).lower() current_entry.uuid = m.group(1).lower()
else: else:
try: date_blob_re = re.compile("^\[[^\\]]+\] ")
new_date = datetime.strptime(line[:date_length], self.config['timeformat']) date_blob = date_blob_re.findall(line)
if date_blob:
date_blob = date_blob[0]
new_date = jrnl_time.parse(date_blob.strip(" []"))
if line.endswith("*"): if line.endswith("*"):
current_entry.starred = True current_entry.starred = True
line = line[:-1] line = line[:-1]
current_entry.title = line[date_length + 1:] current_entry.title = line[len(date_blob) - 1:]
current_entry.date = new_date current_entry.date = new_date
except ValueError: elif current_entry:
if current_entry: current_entry.body += line + "\n"
current_entry.body += line + "\n"
# Append last entry # Append last entry
if current_entry: if current_entry:

View file

@ -11,4 +11,4 @@ __title__ = 'jrnl'
__version__ = '2.0.0-rc1' __version__ = '2.0.0-rc1'
__author__ = 'Manuel Ebert' __author__ = 'Manuel Ebert'
__license__ = 'MIT License' __license__ = 'MIT License'
__copyright__ = 'Copyright 2013 - 2014 Manuel Ebert' __copyright__ = 'Copyright 2013 - 2015 Manuel Ebert'

View file

@ -13,6 +13,7 @@ from . import Journal
from . import util from . import util
from . import install from . import install
from . import plugins from . import plugins
from .util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR
import jrnl import jrnl
import argparse import argparse
import sys import sys
@ -252,7 +253,7 @@ def run(manual_args=None):
elif args.edit: elif args.edit:
if not config['editor']: if not config['editor']:
util.prompt("[You need to specify an editor in {0} to use the --edit function.]".format(install.CONFIG_FILE_PATH)) util.prompt("[{1}ERROR{2}: You need to specify an editor in {0} to use the --edit function.]".format(install.CONFIG_FILE_PATH, ERROR_COLOR, RESET_COLOR))
sys.exit(1) sys.exit(1)
other_entries = [e for e in old_entries if e not in journal.entries] other_entries = [e for e in old_entries if e not in journal.entries]
# Edit # Edit

View file

@ -5,6 +5,7 @@ from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter from .text_exporter import TextExporter
import re import re
import sys import sys
from ..util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR
class MarkdownExporter(TextExporter): class MarkdownExporter(TextExporter):
@ -49,7 +50,7 @@ class MarkdownExporter(TextExporter):
newbody = newbody + previous_line # add very last line newbody = newbody + previous_line # add very last line
if warn_on_heading_level is True: if warn_on_heading_level is True:
print("{}WARNING{}: Headings increased past H6 on export - {} {}".format("\033[33m", "\033[0m", date_str, entry.title), file=sys.stderr) print("{}WARNING{}: Headings increased past H6 on export - {} {}".format(WARNING_COLOR, RESET_COLOR, date_str, entry.title), file=sys.stderr)
return "{md} {date} {title} {body} {space}".format( return "{md} {date} {title} {body} {space}".format(
md=heading, md=heading,

View file

@ -6,6 +6,7 @@ import codecs
from . import BaseExporter from . import BaseExporter
from ..util import u, slugify from ..util import u, slugify
import os import os
from ..util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR
class TextExporter(BaseExporter): class TextExporter(BaseExporter):
@ -31,7 +32,7 @@ class TextExporter(BaseExporter):
f.write(cls.export_journal(journal)) f.write(cls.export_journal(journal))
return "[Journal exported to {0}]".format(path) return "[Journal exported to {0}]".format(path)
except IOError as e: except IOError as e:
return "[ERROR: {0} {1}]".format(e.filename, e.strerror) return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR)
@classmethod @classmethod
def make_filename(cls, entry): def make_filename(cls, entry):
@ -46,7 +47,7 @@ class TextExporter(BaseExporter):
with codecs.open(full_path, "w", "utf-8") as f: with codecs.open(full_path, "w", "utf-8") as f:
f.write(cls.export_entry(entry)) f.write(cls.export_entry(entry))
except IOError as e: except IOError as e:
return "[ERROR: {0} {1}]".format(e.filename, e.strerror) return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR)
return "[Journal exported to {0}]".format(path) return "[Journal exported to {0}]".format(path)
@classmethod @classmethod

View file

@ -5,11 +5,11 @@ from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter from .text_exporter import TextExporter
import re import re
import sys import sys
import yaml from ..util import WARNING_COLOR, ERROR_COLOR, RESET_COLOR
class MarkdownExporter(TextExporter): class YAMLExporter(TextExporter):
"""This Exporter can convert entries and journals into Markdown with YAML front matter.""" """This Exporter can convert entries and journals into Markdown formatted text with YAML front matter."""
names = ["yaml"] names = ["yaml"]
extension = "md" extension = "md"
@ -24,9 +24,13 @@ class MarkdownExporter(TextExporter):
body_wrapper = "\n" if entry.body else "" body_wrapper = "\n" if entry.body else ""
body = body_wrapper + entry.body body = body_wrapper + entry.body
tagsymbols = entry.journal.config['tagsymbols']
# see also Entry.Entry.rag_regex
multi_tag_regex = re.compile(r'(?u)^\s*([{tags}][-+*#/\w]+\s*)+$'.format(tags=tagsymbols), re.UNICODE)
'''Increase heading levels in body text''' '''Increase heading levels in body text'''
newbody = '' newbody = ''
heading = '###' heading = '#'
previous_line = '' previous_line = ''
warn_on_heading_level = False warn_on_heading_level = False
for line in entry.body.splitlines(True): for line in entry.body.splitlines(True):
@ -44,27 +48,36 @@ class MarkdownExporter(TextExporter):
"""Setext style H2""" """Setext style H2"""
newbody = newbody + heading + "## " + previous_line newbody = newbody + heading + "## " + previous_line
line = '' line = ''
elif multi_tag_regex.match(line):
"""Tag only lines"""
line = ''
else: else:
newbody = newbody + previous_line newbody = newbody + previous_line
previous_line = line previous_line = line
newbody = newbody + previous_line # add very last line newbody = newbody + previous_line # add very last line
if warn_on_heading_level is True: if warn_on_heading_level is True:
print("{}WARNING{}: Headings increased past H6 on export - {} {}".format("\033[33m", "\033[0m", date_str, entry.title), file=sys.stderr) print("{}WARNING{}: Headings increased past H6 on export - {} {}".format(WARNING_COLOR, RESET_COLOR, date_str, entry.title), file=sys.stderr)
# top = yaml.dump(entry) dayone_attributes = ''
if hasattr(entry, "uuid"):
dayone_attributes += 'uuid: ' + entry.uuid + '\n'
# TODO: copy over pictures, if present
# source directory is entry.journal.config['journal']
# output directory is...?
return "title: {title}\ndate: {date}\nstared: {stared}\ntags: {tags}\n{body} {space}".format( return "title: {title}\ndate: {date}\nstared: {stared}\ntags: {tags}\n{dayone} {body} {space}".format(
date=date_str, date = date_str,
title=entry.title, title = entry.title,
stared=entry.starred, stared = entry.starred,
tags=', '.join([tag[1:] for tag in entry.tags]), tags = ', '.join([tag[1:] for tag in entry.tags]),
body=newbody, dayone = dayone_attributes,
body = newbody,
space="" space=""
) )
@classmethod @classmethod
def export_journal(cls, journal): def export_journal(cls, journal):
"""Returns an error, as YAML export requires a directory as a target.""" """Returns an error, as YAML export requires a directory as a target."""
print("{}ERROR{}: YAML export must be to individual files. Please specify a directory to export to.".format("\033[31m", "\033[0m", file=sys.stderr)) print("{}ERROR{}: YAML export must be to individual files. Please specify a directory to export to.".format(ERROR_COLOR, RESET_COLOR), file=sys.stderr)
return return

View file

@ -23,6 +23,10 @@ STDOUT = sys.stdout
TEST = False TEST = False
__cached_tz = None __cached_tz = None
WARNING_COLOR = "\033[33m"
ERROR_COLOR = "\033[31m"
RESET_COLOR = "\033[0m"
def getpass(prompt="Password: "): def getpass(prompt="Password: "):
if not TEST: if not TEST:
@ -101,6 +105,7 @@ def py23_input(msg=""):
def py23_read(msg=""): def py23_read(msg=""):
print(msg)
return STDIN.read() return STDIN.read()
@ -118,12 +123,11 @@ def load_config(config_path):
def get_text_from_editor(config, template=""): def get_text_from_editor(config, template=""):
tmpfile = os.path.join(tempfile.mktemp(prefix="jrnl", suffix=".txt"))
filehandle, tmpfile = tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt") filehandle, tmpfile = tempfile.mkstemp(prefix="jrnl", text=True, suffix=".txt")
with codecs.open(tmpfile, 'w', "utf-8") as f: with codecs.open(tmpfile, 'w', "utf-8") as f:
if template: if template:
f.write(template) f.write(template)
subprocess.call(shlex.split(config['editor']) + [tmpfile]) subprocess.call(shlex.split(config['editor'], posix="win" not in sys.platform) + [tmpfile])
with codecs.open(tmpfile, "r", "utf-8") as f: with codecs.open(tmpfile, "r", "utf-8") as f:
raw = f.read() raw = f.read()
os.close(filehandle) os.close(filehandle)