mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Merge pull request #347 from MinchinWeb/2.0-rc1-markdown-export-fix
2.0 rc1 markdown export fix
This commit is contained in:
commit
0d1b381bd1
12 changed files with 225 additions and 16 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -50,3 +50,6 @@ docs/_themes/jrnl/static/less/3L.less
|
||||||
|
|
||||||
# export testing director
|
# export testing director
|
||||||
exp/
|
exp/
|
||||||
|
|
||||||
|
_extras/
|
||||||
|
*.sublime-*
|
||||||
|
|
11
features/data/configs/markdown-headings-335.yaml
Normal file
11
features/data/configs/markdown-headings-335.yaml
Normal 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'
|
42
features/data/journals/markdown-headings-335.journal
Normal file
42
features/data/journals/markdown-headings-335.journal
Normal 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
|
|
@ -26,3 +26,52 @@ Feature: Exporting a Journal
|
||||||
Then we should get no error
|
Then we should get no error
|
||||||
and the output should be parsable as json
|
and the output should be parsable as json
|
||||||
and the json output should contain entries.0.uuid = "4BB1F46946AD439996C9B59DE7C4DDC1"
|
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
|
||||||
|
"""
|
||||||
|
|
|
@ -271,6 +271,3 @@ def run(manual_args=None):
|
||||||
journal.entries += other_entries
|
journal.entries += other_entries
|
||||||
journal.sort()
|
journal.sort()
|
||||||
journal.write()
|
journal.write()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run()
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import importlib
|
||||||
|
|
||||||
|
|
||||||
class PluginMeta(type):
|
class PluginMeta(type):
|
||||||
|
|
||||||
def __init__(cls, name, bases, attrs):
|
def __init__(cls, name, bases, attrs):
|
||||||
"""Called when a Plugin derived class is imported"""
|
"""Called when a Plugin derived class is imported"""
|
||||||
if not hasattr(cls, 'PLUGINS'):
|
if not hasattr(cls, 'PLUGINS'):
|
||||||
|
|
|
@ -1,33 +1,67 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals, print_function
|
||||||
from .text_exporter import TextExporter
|
from .text_exporter import TextExporter
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class MarkdownExporter(TextExporter):
|
class MarkdownExporter(TextExporter):
|
||||||
"""This Exporter can convert entries and journals into json."""
|
"""This Exporter can convert entries and journals into Markdown."""
|
||||||
names = ["md", "markdown"]
|
names = ["md", "markdown"]
|
||||||
extension = "md"
|
extension = "md"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry, to_multifile=True):
|
||||||
"""Returns a markdown representation of a single entry."""
|
"""Returns a markdown representation of a single entry."""
|
||||||
date_str = entry.date.strftime(entry.journal.config['timeformat'])
|
date_str = entry.date.strftime(entry.journal.config['timeformat'])
|
||||||
body_wrapper = "\n" if entry.body else ""
|
body_wrapper = "\n" if entry.body else ""
|
||||||
body = body_wrapper + entry.body
|
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(
|
return "{md} {date} {title} {body} {space}".format(
|
||||||
md="###",
|
md=heading,
|
||||||
date=date_str,
|
date=date_str,
|
||||||
title=entry.title,
|
title=entry.title,
|
||||||
body=body,
|
body=newbody,
|
||||||
space=""
|
space=""
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal):
|
||||||
"""Returns a json representation of an entire journal."""
|
"""Returns a Markdown representation of an entire journal."""
|
||||||
out = []
|
out = []
|
||||||
year, month = -1, -1
|
year, month = -1, -1
|
||||||
for e in journal.entries:
|
for e in journal.entries:
|
||||||
|
@ -39,6 +73,6 @@ class MarkdownExporter(TextExporter):
|
||||||
month = e.date.month
|
month = e.date.month
|
||||||
out.append(e.date.strftime("%B"))
|
out.append(e.date.strftime("%B"))
|
||||||
out.append('-' * len(e.date.strftime("%B")) + "\n")
|
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)
|
result = "\n".join(out)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -7,18 +7,18 @@ from .util import get_tags_count
|
||||||
|
|
||||||
|
|
||||||
class TagExporter(TextExporter):
|
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"]
|
names = ["tags"]
|
||||||
extension = "tags"
|
extension = "tags"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
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)
|
return ", ".join(entry.tags)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
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)
|
tag_counts = get_tags_count(journal)
|
||||||
result = ""
|
result = ""
|
||||||
if not tag_counts:
|
if not tag_counts:
|
||||||
|
|
|
@ -56,7 +56,7 @@ class TextExporter(BaseExporter):
|
||||||
representation as unicode if output is None."""
|
representation as unicode if output is None."""
|
||||||
if output and os.path.isdir(output): # multiple files
|
if output and os.path.isdir(output): # multiple files
|
||||||
return cls.write_files(journal, output)
|
return cls.write_files(journal, output)
|
||||||
elif output:
|
elif output: # single file
|
||||||
return cls.write_file(journal, output)
|
return cls.write_file(journal, output)
|
||||||
else:
|
else:
|
||||||
return cls.export_journal(journal)
|
return cls.export_journal(journal)
|
||||||
|
|
70
jrnl/plugins/yaml_exporter.py
Normal file
70
jrnl/plugins/yaml_exporter.py
Normal 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
|
|
@ -146,6 +146,8 @@ def slugify(string):
|
||||||
"""
|
"""
|
||||||
string = u(string)
|
string = u(string)
|
||||||
ascii_string = str(unicodedata.normalize('NFKD', string).encode('ascii', 'ignore'))
|
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()
|
no_punctuation = re.sub(r'[^\w\s-]', '', ascii_string).strip().lower()
|
||||||
slug = re.sub(r'[-\s]+', '-', no_punctuation)
|
slug = re.sub(r'[-\s]+', '-', no_punctuation)
|
||||||
return u(slug)
|
return u(slug)
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -78,12 +78,12 @@ setup(
|
||||||
name="jrnl",
|
name="jrnl",
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
description="A command line journal application that stores your journal in a plain text file",
|
description="A command line journal application that stores your journal in a plain text file",
|
||||||
packages=['jrnl'],
|
packages=['jrnl', 'jrnl.plugins'],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"parsedatetime>=1.2",
|
"parsedatetime>=1.2",
|
||||||
"pytz>=2013b",
|
"pytz>=2013b",
|
||||||
"six>=1.7.4",
|
"six>=1.7.4",
|
||||||
"cryptography==0.8.1",
|
"cryptography>=0.8.1",
|
||||||
"tzlocal>=1.1",
|
"tzlocal>=1.1",
|
||||||
"pyyaml>=3.11",
|
"pyyaml>=3.11",
|
||||||
"keyring>=3.3",
|
"keyring>=3.3",
|
||||||
|
|
Loading…
Add table
Reference in a new issue