Merge branch 'develop' into maebert-fast-import

This commit is contained in:
Jonathan Wren 2019-12-10 20:47:39 -08:00 committed by GitHub
commit 6a8f3edec1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 1102 additions and 699 deletions

View file

@ -1,8 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from .text_exporter import TextExporter
from .jrnl_importer import JRNLImporter
from .json_exporter import JSONExporter
@ -11,12 +9,13 @@ from .tag_exporter import TagExporter
from .xml_exporter import XMLExporter
from .yaml_exporter import YAMLExporter
from .template_exporter import __all__ as template_exporters
from .fancy_exporter import FancyExporter
__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter] + template_exporters
__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter, FancyExporter] + template_exporters
__importers =[JRNLImporter]
__exporter_types = dict([(name, plugin) for plugin in __exporters for name in plugin.names])
__importer_types = dict([(name, plugin) for plugin in __importers for name in plugin.names])
__exporter_types = {name: plugin for plugin in __exporters for name in plugin.names}
__importer_types = {name: plugin for plugin in __importers for name in plugin.names}
EXPORT_FORMATS = sorted(__exporter_types.keys())
IMPORT_FORMATS = sorted(__importer_types.keys())

View file

@ -0,0 +1,56 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter
from textwrap import TextWrapper
class FancyExporter(TextExporter):
"""This Exporter can convert entries and journals into text with unicode box drawing characters."""
names = ["fancy", "boxed"]
extension = "txt"
border_a=""
border_b=""
border_c=""
border_d=""
border_e=""
border_f=""
border_g=""
border_h=""
border_i=""
border_j=""
border_k=""
border_l=""
border_m=""
@classmethod
def export_entry(cls, entry):
"""Returns a fancy unicode representation of a single entry."""
date_str = entry.date.strftime(entry.journal.config['timeformat'])
linewrap = entry.journal.config['linewrap'] or 78
initial_linewrap = linewrap - len(date_str) - 2
body_linewrap = linewrap - 2
card = [cls.border_a + cls.border_b*(initial_linewrap) + cls.border_c + date_str]
w = TextWrapper(width=initial_linewrap, initial_indent=cls.border_g+' ', subsequent_indent=cls.border_g+' ')
title_lines = w.wrap(entry.title)
card.append(title_lines[0].ljust(initial_linewrap+1) + cls.border_d + cls.border_e*(len(date_str)-1) + cls.border_f)
w.width = body_linewrap
if len(title_lines) > 1:
for line in w.wrap(' '.join([title_line[len(w.subsequent_indent):]
for title_line in title_lines[1:]])):
card.append(line.ljust(body_linewrap+1) + cls.border_h)
if entry.body:
card.append(cls.border_i + cls.border_j*body_linewrap + cls.border_k)
for line in entry.body.splitlines():
body_lines = w.wrap(line) or [cls.border_g]
for body_line in body_lines:
card.append(body_line.ljust(body_linewrap+1) + cls.border_h)
card.append(cls.border_l + cls.border_b*body_linewrap + cls.border_m)
return "\n".join(card)
@classmethod
def export_journal(cls, journal):
"""Returns a unicode representation of an entire journal."""
return "\n".join(cls.export_entry(entry) for entry in journal)

View file

@ -1,12 +1,10 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
import codecs
import sys
from .. import util
class JRNLImporter(object):
class JRNLImporter:
"""This plugin imports entries from other jrnl files."""
names = ["jrnl"]
@ -17,15 +15,15 @@ class JRNLImporter(object):
old_cnt = len(journal.entries)
old_entries = journal.entries
if input:
with codecs.open(input, "r", "utf-8") as f:
with open(input, "r", encoding="utf-8") as f:
other_journal_txt = f.read()
else:
try:
other_journal_txt = util.py23_read()
other_journal_txt = sys.stdin.read()
except KeyboardInterrupt:
util.prompt("[Entries NOT imported into journal.]")
print("[Entries NOT imported into journal.]", file=sys.stderr)
sys.exit(0)
journal.import_(other_journal_txt)
new_cnt = len(journal.entries)
util.prompt("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name))
print("[{} imported to {} journal]".format(new_cnt - old_cnt, journal.name), file=sys.stderr)
journal.write()

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from .text_exporter import TextExporter
import json
from .util import get_tags_count
@ -35,7 +34,7 @@ class JSONExporter(TextExporter):
"""Returns a json representation of an entire journal."""
tags = get_tags_count(journal)
result = {
"tags": dict((tag, count) for count, tag in tags),
"tags": {tag: count for count, tag in tags},
"entries": [cls.entry_to_dict(e) for e in journal.entries]
}
return json.dumps(result, indent=2)

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter
import os
import re
@ -51,15 +50,11 @@ class MarkdownExporter(TextExporter):
newbody = newbody + previous_line # add very last line
if warn_on_heading_level is True:
print("{}WARNING{}: Headings increased past H6 on export - {} {}".format(WARNING_COLOR, RESET_COLOR, date_str, entry.title), file=sys.stderr)
print(f"{WARNING_COLOR}WARNING{RESET_COLOR}: "
f"Headings increased past H6 on export - {date_str} {entry.title}",
file=sys.stderr)
return "{md} {date} {title}\n{body} {space}".format(
md=heading,
date=date_str,
title=entry.title,
body=newbody,
space=""
)
return f"{heading} {date_str} {entry.title}\n{newbody} "
@classmethod
def export_journal(cls, journal):

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from .text_exporter import TextExporter
from .util import get_tags_count
@ -26,5 +25,5 @@ class TagExporter(TextExporter):
elif min(tag_counts)[0] == 0:
tag_counts = filter(lambda x: x[0] > 1, tag_counts)
result += '[Removed tags that appear only once.]\n'
result += "\n".join("{0:20} : {1}".format(tag, n) for n, tag in sorted(tag_counts, reverse=True))
result += "\n".join("{:20} : {}".format(tag, n) for n, tag in sorted(tag_counts, reverse=True))
return result

View file

@ -12,7 +12,7 @@ BLOCK_RE = r"{% *block +(.+?) *%}((?:.|\n)+?){% *endblock *%}"
INCLUDE_RE = r"{% *include +(.+?) *%}"
class Template(object):
class Template:
def __init__(self, template):
self.template = template
self.clean_template = None

View file

@ -1,8 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from .text_exporter import TextExporter
from .template import Template
import os
@ -14,7 +12,7 @@ class GenericTemplateExporter(TextExporter):
@classmethod
def export_entry(cls, entry):
"""Returns a unicode representation of a single entry."""
"""Returns a string representation of a single entry."""
vars = {
'entry': entry,
'tags': entry.tags
@ -23,7 +21,7 @@ class GenericTemplateExporter(TextExporter):
@classmethod
def export_journal(cls, journal):
"""Returns a unicode representation of an entire journal."""
"""Returns a string representation of an entire journal."""
vars = {
'journal': journal,
'entries': journal.entries,
@ -36,7 +34,7 @@ def __exporter_from_file(template_file):
"""Create a template class from a file"""
name = os.path.basename(template_file).replace(".template", "")
template = Template.from_file(template_file)
return type(str("{}Exporter".format(name.title())), (GenericTemplateExporter, ), {
return type(str(f"{name.title()}Exporter"), (GenericTemplateExporter, ), {
"names": [name],
"extension": template.extension,
"template": template

View file

@ -1,41 +1,39 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
import codecs
from ..util import u, slugify
from ..util import slugify
import os
from ..util import ERROR_COLOR, RESET_COLOR
class TextExporter(object):
class TextExporter:
"""This Exporter can convert entries and journals into text files."""
names = ["text", "txt"]
extension = "txt"
@classmethod
def export_entry(cls, entry):
"""Returns a unicode representation of a single entry."""
return entry.__unicode__()
"""Returns a string representation of a single entry."""
return str(entry)
@classmethod
def export_journal(cls, journal):
"""Returns a unicode representation of an entire journal."""
"""Returns a string representation of an entire journal."""
return "\n".join(cls.export_entry(entry) for entry in journal)
@classmethod
def write_file(cls, journal, path):
"""Exports a journal into a single file."""
try:
with codecs.open(path, "w", "utf-8") as f:
with open(path, "w", encoding="utf-8") as f:
f.write(cls.export_journal(journal))
return "[Journal exported to {0}]".format(path)
return f"[Journal exported to {path}]"
except IOError as e:
return "[{2}ERROR{3}: {0} {1}]".format(e.filename, e.strerror, ERROR_COLOR, RESET_COLOR)
return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]"
@classmethod
def make_filename(cls, entry):
return entry.date.strftime("%Y-%m-%d_{0}.{1}".format(slugify(u(entry.title)), cls.extension))
return entry.date.strftime("%Y-%m-%d_{}.{}".format(slugify(str(entry.title)), cls.extension))
@classmethod
def write_files(cls, journal, path):
@ -43,17 +41,17 @@ class TextExporter(object):
for entry in journal.entries:
try:
full_path = os.path.join(path, cls.make_filename(entry))
with codecs.open(full_path, "w", "utf-8") as f:
with open(full_path, "w", encoding="utf-8") as f:
f.write(cls.export_entry(entry))
except IOError as e:
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 {}]".format(path)
@classmethod
def export(cls, journal, output=None):
"""Exports to individual files if output is an existing path, or into
a single file if output is a file name, or returns the exporter's
representation as unicode if output is None."""
representation as string if output is None."""
if output and os.path.isdir(output): # multiple files
return cls.write_files(journal, output)
elif output: # single file

View file

@ -10,7 +10,7 @@ def get_tags_count(journal):
for entry in journal.entries
for tag in set(entry.tags)]
# To be read: [for entry in journal.entries: for tag in set(entry.tags): tag]
tag_counts = set([(tags.count(tag), tag) for tag in tags])
tag_counts = {(tags.count(tag), tag) for tag in tags}
return tag_counts

View file

@ -1,10 +1,8 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from .json_exporter import JSONExporter
from .util import get_tags_count
from ..util import u
from xml.dom import minidom
@ -20,7 +18,7 @@ class XMLExporter(JSONExporter):
entry_el = doc_el.createElement('entry')
for key, value in cls.entry_to_dict(entry).items():
elem = doc_el.createElement(key)
elem.appendChild(doc_el.createTextNode(u(value)))
elem.appendChild(doc_el.createTextNode(value))
entry_el.appendChild(elem)
if not doc:
doc_el.appendChild(entry_el)
@ -33,8 +31,8 @@ class XMLExporter(JSONExporter):
entry_el = doc.createElement('entry')
entry_el.setAttribute('date', entry.date.isoformat())
if hasattr(entry, "uuid"):
entry_el.setAttribute('uuid', u(entry.uuid))
entry_el.setAttribute('starred', u(entry.starred))
entry_el.setAttribute('uuid', entry.uuid)
entry_el.setAttribute('starred', entry.starred)
entry_el.appendChild(doc.createTextNode(entry.fulltext))
return entry_el
@ -49,7 +47,7 @@ class XMLExporter(JSONExporter):
for count, tag in tags:
tag_el = doc.createElement('tag')
tag_el.setAttribute('name', tag)
count_node = doc.createTextNode(u(count))
count_node = doc.createTextNode(str(count))
tag_el.appendChild(count_node)
tags_el.appendChild(tag_el)
for entry in journal.entries:

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals, print_function
from .text_exporter import TextExporter
import os
import re
@ -18,7 +17,8 @@ class YAMLExporter(TextExporter):
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))
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'])
@ -27,7 +27,7 @@ class YAMLExporter(TextExporter):
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)
multi_tag_regex = re.compile(r'(?u)^\s*([{tags}][-+*#/\w]+\s*)+$'.format(tags=tagsymbols))
'''Increase heading levels in body text'''
newbody = ''