Merge pull request #347 from MinchinWeb/2.0-rc1-markdown-export-fix

2.0 rc1 markdown export fix
This commit is contained in:
Manuel Ebert 2015-04-20 14:29:17 +02:00
commit 0d1b381bd1
12 changed files with 225 additions and 16 deletions

3
.gitignore vendored
View file

@ -50,3 +50,6 @@ docs/_themes/jrnl/static/less/3L.less
# export testing director
exp/
_extras/
*.sublime-*

View file

@ -0,0 +1,11 @@
default_hour: 9
default_minute: 0
editor: ''
encrypt: false
highlight: true
journals:
default: features/journals/markdown-headings-335.journal
linewrap: 80
password: ''
tagsymbols: '@'
timeformat: '%Y-%m-%d %H:%M'

View file

@ -0,0 +1,42 @@
[2015-04-14 13:23] Heading Test
H1-1
=
H1-2
===
H1-3
============================
H2-1
-
H2-2
---
H2-3
----------------------------------
Horizontal Rules (ignore)
---
===
# ATX H1
## ATX H2
### ATX H3
#### ATX H4
##### ATX H5
###### ATX H6
Stuff
More stuff
more stuff again

View file

@ -26,3 +26,52 @@ Feature: Exporting a Journal
Then we should get no error
and the output should be parsable as json
and the json output should contain entries.0.uuid = "4BB1F46946AD439996C9B59DE7C4DDC1"
Scenario: Increasing Headings on Markdown export
Given we use the config "markdown-headings-335.yaml"
When we run "jrnl --export markdown"
Then the output should be
"""
2015
====
April
-----
### 2015-04-14 13:23 Heading Test
#### H1-1
#### H1-2
#### H1-3
##### H2-1
##### H2-2
##### H2-3
Horizontal Rules (ignore)
---
===
#### ATX H1
##### ATX H2
###### ATX H3
####### ATX H4
######## ATX H5
######### ATX H6
Stuff
More stuff
more stuff again
"""

View file

@ -271,6 +271,3 @@ def run(manual_args=None):
journal.entries += other_entries
journal.sort()
journal.write()
if __name__ == "__main__":
run()

View file

@ -8,6 +8,7 @@ import importlib
class PluginMeta(type):
def __init__(cls, name, bases, attrs):
"""Called when a Plugin derived class is imported"""
if not hasattr(cls, 'PLUGINS'):

View file

@ -1,33 +1,67 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter
import re
import sys
class MarkdownExporter(TextExporter):
"""This Exporter can convert entries and journals into json."""
"""This Exporter can convert entries and journals into Markdown."""
names = ["md", "markdown"]
extension = "md"
@classmethod
def export_entry(cls, entry):
def export_entry(cls, entry, to_multifile=True):
"""Returns a markdown representation of a single entry."""
date_str = entry.date.strftime(entry.journal.config['timeformat'])
body_wrapper = "\n" if entry.body else ""
body = body_wrapper + entry.body
if to_multifile is True:
heading = '#'
else:
heading = '###'
'''Increase heading levels in body text'''
newbody = ''
previous_line = ''
warn_on_heading_level = False
for line in body.splitlines(True):
if re.match(r"#+ ", line):
"""ATX style headings"""
newbody = newbody + previous_line + heading + line
if re.match(r"#######+ ", heading + line):
warn_on_heading_level = True
line = ''
elif re.match(r"=+$", line) and not re.match(r"^$", previous_line):
"""Setext style H1"""
newbody = newbody + heading + "# " + previous_line
line = ''
elif re.match(r"-+$", line) and not re.match(r"^$", previous_line):
"""Setext style H2"""
newbody = newbody + heading + "## " + previous_line
line = ''
else:
newbody = newbody + previous_line
previous_line = line
newbody = newbody + previous_line # add very last line
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)
return "{md} {date} {title} {body} {space}".format(
md="###",
md=heading,
date=date_str,
title=entry.title,
body=body,
body=newbody,
space=""
)
@classmethod
def export_journal(cls, journal):
"""Returns a json representation of an entire journal."""
"""Returns a Markdown representation of an entire journal."""
out = []
year, month = -1, -1
for e in journal.entries:
@ -39,6 +73,6 @@ class MarkdownExporter(TextExporter):
month = e.date.month
out.append(e.date.strftime("%B"))
out.append('-' * len(e.date.strftime("%B")) + "\n")
out.append(cls.export_entry(e))
out.append(cls.export_entry(e, False))
result = "\n".join(out)
return result

View file

@ -7,18 +7,18 @@ from .util import get_tags_count
class TagExporter(TextExporter):
"""This Exporter can convert entries and journals into json."""
"""This Exporter can lists the tags for entries and journals, exported as a plain text file."""
names = ["tags"]
extension = "tags"
@classmethod
def export_entry(cls, entry):
"""Returns a markdown representation of a single entry."""
"""Returns a list of tags for a single entry."""
return ", ".join(entry.tags)
@classmethod
def export_journal(cls, journal):
"""Returns a json representation of an entire journal."""
"""Returns a list of tags and their frequency for an entire journal."""
tag_counts = get_tags_count(journal)
result = ""
if not tag_counts:

View file

@ -56,7 +56,7 @@ class TextExporter(BaseExporter):
representation as unicode if output is None."""
if output and os.path.isdir(output): # multiple files
return cls.write_files(journal, output)
elif output:
elif output: # single file
return cls.write_file(journal, output)
else:
return cls.export_journal(journal)

View file

@ -0,0 +1,70 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter
import re
import sys
import yaml
class MarkdownExporter(TextExporter):
"""This Exporter can convert entries and journals into Markdown with YAML front matter."""
names = ["yaml"]
extension = "md"
@classmethod
def export_entry(cls, entry, to_multifile=True):
"""Returns a markdown representation of a single entry, with YAML front matter."""
if to_multifile is False:
print("{}ERROR{}: YAML export must be to individual files. Please specify a directory to export to.".format("\033[31m", "\033[0m", file=sys.stderr))
return
date_str = entry.date.strftime(entry.journal.config['timeformat'])
body_wrapper = "\n" if entry.body else ""
body = body_wrapper + entry.body
'''Increase heading levels in body text'''
newbody = ''
heading = '###'
previous_line = ''
warn_on_heading_level = False
for line in entry.body.splitlines(True):
if re.match(r"#+ ", line):
"""ATX style headings"""
newbody = newbody + previous_line + heading + line
if re.match(r"#######+ ", heading + line):
warn_on_heading_level = True
line = ''
elif re.match(r"=+$", line) and not re.match(r"^$", previous_line):
"""Setext style H1"""
newbody = newbody + heading + "# " + previous_line
line = ''
elif re.match(r"-+$", line) and not re.match(r"^$", previous_line):
"""Setext style H2"""
newbody = newbody + heading + "## " + previous_line
line = ''
else:
newbody = newbody + previous_line
previous_line = line
newbody = newbody + previous_line # add very last line
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)
# top = yaml.dump(entry)
return "title: {title}\ndate: {date}\nstared: {stared}\ntags: {tags}\n{body} {space}".format(
date=date_str,
title=entry.title,
stared=entry.starred,
tags=', '.join([tag[1:] for tag in entry.tags]),
body=newbody,
space=""
)
@classmethod
def export_journal(cls, journal):
"""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))
return

View file

@ -146,6 +146,8 @@ def slugify(string):
"""
string = u(string)
ascii_string = str(unicodedata.normalize('NFKD', string).encode('ascii', 'ignore'))
if PY3:
ascii_string = ascii_string[1:] # removed the leading 'b'
no_punctuation = re.sub(r'[^\w\s-]', '', ascii_string).strip().lower()
slug = re.sub(r'[-\s]+', '-', no_punctuation)
return u(slug)

View file

@ -78,12 +78,12 @@ setup(
name="jrnl",
version=get_version(),
description="A command line journal application that stores your journal in a plain text file",
packages=['jrnl'],
packages=['jrnl', 'jrnl.plugins'],
install_requires=[
"parsedatetime>=1.2",
"pytz>=2013b",
"six>=1.7.4",
"cryptography==0.8.1",
"cryptography>=0.8.1",
"tzlocal>=1.1",
"pyyaml>=3.11",
"keyring>=3.3",