mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-19 04:28:31 +02:00
Use Base classes for importer and exporters.
This commit is contained in:
parent
51fc091335
commit
2706314306
10 changed files with 185 additions and 105 deletions
119
jrnl/plugins/base.py
Normal file
119
jrnl/plugins/base.py
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# Copyright (C) 2012-2021 jrnl contributors
|
||||||
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
"""
|
||||||
|
Base class for Importers and Exporters.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
from jrnl import __version__
|
||||||
|
from jrnl.color import ERROR_COLOR
|
||||||
|
from jrnl.color import RESET_COLOR
|
||||||
|
|
||||||
|
|
||||||
|
class BaseImporter:
|
||||||
|
"""Base Importer class (to sub-class)"""
|
||||||
|
|
||||||
|
# names = ["jrnl"]
|
||||||
|
# version = __version__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def class_path(cls):
|
||||||
|
return cls.__module__
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def import_(journal, input=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class BaseExporter:
|
||||||
|
"""Base Exporter class (to sub-class)"""
|
||||||
|
|
||||||
|
# names = ["text", "txt"]
|
||||||
|
# extension = "txt"
|
||||||
|
# version = __version__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def class_path(cls):
|
||||||
|
return cls.__module__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_entry(cls, entry):
|
||||||
|
"""Returns a string representation of a single entry."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_journal(cls, 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 open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(cls.export_journal(journal))
|
||||||
|
return (
|
||||||
|
f"[Journal '{journal.name}' exported (as a single file) to {path}]"
|
||||||
|
)
|
||||||
|
except IOError as e:
|
||||||
|
return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]"
|
||||||
|
except NotImplementedError:
|
||||||
|
return (
|
||||||
|
f"[{ERROR_COLOR}ERROR{RESET_COLOR}: This exporter doesn't support "
|
||||||
|
"exporting as a single file.]"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def make_filename(cls, entry):
|
||||||
|
return entry.date.strftime("%Y-%m-%d") + "_{}.{}".format(
|
||||||
|
cls._slugify(str(entry.title)), cls.extension
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def write_files(cls, journal, path):
|
||||||
|
"""Exports a journal into individual files for each entry."""
|
||||||
|
try:
|
||||||
|
for entry in journal.entries:
|
||||||
|
try:
|
||||||
|
full_path = os.path.join(path, cls.make_filename(entry))
|
||||||
|
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
|
||||||
|
)
|
||||||
|
except NotImplementedError:
|
||||||
|
return (
|
||||||
|
f"[{ERROR_COLOR}ERROR{RESET_COLOR}: This exporter doesn't support "
|
||||||
|
"exporting as individual files.]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return f"[Journal '{journal.name}' exported (as multiple files) to {path}]"
|
||||||
|
|
||||||
|
def _slugify(string):
|
||||||
|
"""Slugifies a string.
|
||||||
|
Based on public domain code from https://github.com/zacharyvoase/slugify
|
||||||
|
"""
|
||||||
|
normalized_string = str(unicodedata.normalize("NFKD", string))
|
||||||
|
no_punctuation = re.sub(r"[^\w\s-]", "", normalized_string).strip().lower()
|
||||||
|
slug = re.sub(r"[-\s]+", "-", no_punctuation)
|
||||||
|
return slug
|
||||||
|
|
||||||
|
@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 string if output is None."""
|
||||||
|
if output and os.path.isdir(output): # multiple files
|
||||||
|
return cls.write_files(journal, output)
|
||||||
|
elif output: # single file
|
||||||
|
return cls.write_file(journal, output)
|
||||||
|
else:
|
||||||
|
return cls.export_journal(journal)
|
|
@ -2,13 +2,15 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
# Copyright (C) 2012-2021 jrnl contributors
|
# Copyright (C) 2012-2021 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
from jrnl import __version__
|
from jrnl.plugins.base import BaseExporter
|
||||||
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
class Exporter(TextExporter):
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter lists dates and their respective counts, for heatingmapping etc."""
|
"""This Exporter lists dates and their respective counts, for heatingmapping etc."""
|
||||||
|
|
||||||
names = ["dates"]
|
names = ["dates"]
|
||||||
|
|
|
@ -5,14 +5,17 @@
|
||||||
|
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
|
|
||||||
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
from jrnl.plugins.base import BaseExporter
|
||||||
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
class Exporter(TextExporter):
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter can convert entries and journals into text with unicode box drawing characters."""
|
"""This Exporter can convert entries and journals into text with unicode box drawing characters."""
|
||||||
|
|
||||||
names = ["fancy", "boxed"]
|
names = ["fancy", "boxed"]
|
||||||
extension = "txt"
|
extension = "txt"
|
||||||
|
version = __version__
|
||||||
|
|
||||||
border_a = "┎"
|
border_a = "┎"
|
||||||
border_b = "─"
|
border_b = "─"
|
||||||
|
|
|
@ -5,15 +5,18 @@
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
from jrnl.plugins.base import BaseExporter
|
||||||
from jrnl.plugins.util import get_tags_count
|
from jrnl.plugins.util import get_tags_count
|
||||||
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
class Exporter(TextExporter):
|
|
||||||
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter can convert entries and journals into json."""
|
"""This Exporter can convert entries and journals into json."""
|
||||||
|
|
||||||
names = ["json"]
|
names = ["json"]
|
||||||
extension = "json"
|
extension = "json"
|
||||||
|
version = __version__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def entry_to_dict(cls, entry):
|
def entry_to_dict(cls, entry):
|
||||||
|
|
|
@ -9,14 +9,17 @@ import sys
|
||||||
|
|
||||||
from jrnl.color import RESET_COLOR
|
from jrnl.color import RESET_COLOR
|
||||||
from jrnl.color import WARNING_COLOR
|
from jrnl.color import WARNING_COLOR
|
||||||
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
from jrnl.plugins.base import BaseExporter
|
||||||
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
class Exporter(TextExporter):
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter can convert entries and journals into Markdown."""
|
"""This Exporter can convert entries and journals into Markdown."""
|
||||||
|
|
||||||
names = ["md", "markdown"]
|
names = ["md", "markdown"]
|
||||||
extension = "md"
|
extension = "md"
|
||||||
|
version = __version__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry, to_multifile=True):
|
def export_entry(cls, entry, to_multifile=True):
|
||||||
|
|
|
@ -6,12 +6,15 @@
|
||||||
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
||||||
from jrnl.plugins.util import get_tags_count
|
from jrnl.plugins.util import get_tags_count
|
||||||
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
class Exporter(TextExporter):
|
class Exporter(TextExporter):
|
||||||
"""This Exporter can lists the tags for entries and journals, exported as a plain text file."""
|
"""This Exporter can lists the tags for entries and journals, exported as a plain text file."""
|
||||||
|
|
||||||
names = ["tags"]
|
names = ["tags"]
|
||||||
extension = "tags"
|
extension = "tags"
|
||||||
|
version = __version__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry):
|
||||||
|
|
|
@ -3,83 +3,20 @@
|
||||||
# Copyright (C) 2012-2021 jrnl contributors
|
# Copyright (C) 2012-2021 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import unicodedata
|
|
||||||
|
|
||||||
from jrnl import __version__
|
from jrnl import __version__
|
||||||
from jrnl.color import ERROR_COLOR
|
from jrnl.plugins.base import BaseExporter
|
||||||
from jrnl.color import RESET_COLOR
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
class Exporter:
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter can convert entries and journals into text files."""
|
"""This Exporter can convert entries and journals into text files."""
|
||||||
|
|
||||||
names = ["text", "txt"]
|
names = ["text", "txt"]
|
||||||
extension = "txt"
|
extension = "txt"
|
||||||
version = __version__
|
version = __version__
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def class_path(cls):
|
|
||||||
return cls.__module__
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry):
|
||||||
"""Returns a string representation of a single entry."""
|
"""Returns a string representation of a single entry."""
|
||||||
return str(entry)
|
return str(entry)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def export_journal(cls, 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 open(path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(cls.export_journal(journal))
|
|
||||||
return f"[Journal exported to {path}]"
|
|
||||||
except IOError as e:
|
|
||||||
return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def make_filename(cls, entry):
|
|
||||||
return entry.date.strftime("%Y-%m-%d") + "_{}.{}".format(
|
|
||||||
cls._slugify(str(entry.title)), cls.extension
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def write_files(cls, journal, path):
|
|
||||||
"""Exports a journal into individual files for each entry."""
|
|
||||||
for entry in journal.entries:
|
|
||||||
try:
|
|
||||||
full_path = os.path.join(path, cls.make_filename(entry))
|
|
||||||
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 {}]".format(path)
|
|
||||||
|
|
||||||
def _slugify(string):
|
|
||||||
"""Slugifies a string.
|
|
||||||
Based on public domain code from https://github.com/zacharyvoase/slugify
|
|
||||||
"""
|
|
||||||
normalized_string = str(unicodedata.normalize("NFKD", string))
|
|
||||||
no_punctuation = re.sub(r"[^\w\s-]", "", normalized_string).strip().lower()
|
|
||||||
slug = re.sub(r"[-\s]+", "-", no_punctuation)
|
|
||||||
return slug
|
|
||||||
|
|
||||||
@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 string if output is None."""
|
|
||||||
if output and os.path.isdir(output): # multiple files
|
|
||||||
return cls.write_files(journal, output)
|
|
||||||
elif output: # single file
|
|
||||||
return cls.write_file(journal, output)
|
|
||||||
else:
|
|
||||||
return cls.export_journal(journal)
|
|
||||||
|
|
|
@ -5,30 +5,20 @@
|
||||||
|
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
|
|
||||||
from jrnl.plugins.exporter.json_exporter import Exporter as JSONExporter
|
# from jrnl.plugins.exporter.json_exporter import Exporter as JSONExporter
|
||||||
|
from jrnl.plugins.base import BaseExporter
|
||||||
from jrnl.plugins.util import get_tags_count
|
from jrnl.plugins.util import get_tags_count
|
||||||
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
class Exporter(JSONExporter):
|
|
||||||
|
# class Exporter(JSONExporter):
|
||||||
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter can convert entries and journals into XML."""
|
"""This Exporter can convert entries and journals into XML."""
|
||||||
|
|
||||||
names = ["xml"]
|
names = ["xml"]
|
||||||
extension = "xml"
|
extension = "xml"
|
||||||
|
version = __version__
|
||||||
@classmethod
|
|
||||||
def export_entry(cls, entry, doc=None):
|
|
||||||
"""Returns an XML representation of a single entry."""
|
|
||||||
doc_el = doc or minidom.Document()
|
|
||||||
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(value))
|
|
||||||
entry_el.appendChild(elem)
|
|
||||||
if not doc:
|
|
||||||
doc_el.appendChild(entry_el)
|
|
||||||
return doc_el.toprettyxml()
|
|
||||||
else:
|
|
||||||
return entry_el
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def entry_to_xml(cls, entry, doc):
|
def entry_to_xml(cls, entry, doc):
|
||||||
|
@ -45,6 +35,21 @@ class Exporter(JSONExporter):
|
||||||
entry_el.appendChild(doc.createTextNode(entry.fulltext))
|
entry_el.appendChild(doc.createTextNode(entry.fulltext))
|
||||||
return entry_el
|
return entry_el
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_entry(cls, entry, doc=None):
|
||||||
|
"""Returns an XML representation of a single entry."""
|
||||||
|
doc_el = doc or minidom.Document()
|
||||||
|
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(value))
|
||||||
|
entry_el.appendChild(elem)
|
||||||
|
if not doc:
|
||||||
|
doc_el.appendChild(entry_el)
|
||||||
|
return doc_el.toprettyxml()
|
||||||
|
else:
|
||||||
|
return entry_el
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal):
|
||||||
"""Returns an XML representation of an entire journal."""
|
"""Returns an XML representation of an entire journal."""
|
||||||
|
|
|
@ -10,14 +10,17 @@ import sys
|
||||||
from jrnl.color import ERROR_COLOR
|
from jrnl.color import ERROR_COLOR
|
||||||
from jrnl.color import RESET_COLOR
|
from jrnl.color import RESET_COLOR
|
||||||
from jrnl.color import WARNING_COLOR
|
from jrnl.color import WARNING_COLOR
|
||||||
from jrnl.plugins.exporter.text_exporter import Exporter as TextExporter
|
from jrnl.plugins.base import BaseExporter
|
||||||
|
|
||||||
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
class Exporter(TextExporter):
|
class Exporter(BaseExporter):
|
||||||
"""This Exporter can convert entries and journals into Markdown formatted text 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"
|
||||||
|
version = __version__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry, to_multifile=True):
|
def export_entry(cls, entry, to_multifile=True):
|
||||||
|
@ -132,9 +135,10 @@ class Exporter(TextExporter):
|
||||||
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(
|
print(
|
||||||
"{}ERROR{}: YAML export must be to individual files. Please specify a directory to export to.".format(
|
(
|
||||||
ERROR_COLOR, RESET_COLOR
|
f"[{ERROR_COLOR}ERROR{RESET_COLOR}: YAML export must be to "
|
||||||
|
"individual files. Please specify a directory to export to.]"
|
||||||
),
|
),
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
return
|
raise NotImplementedError
|
||||||
|
|
|
@ -5,18 +5,17 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from jrnl import __version__
|
from jrnl.plugins.base import BaseImporter
|
||||||
|
|
||||||
class Importer:
|
from ... import __version__
|
||||||
|
|
||||||
|
|
||||||
|
class Importer(BaseImporter):
|
||||||
"""This plugin imports entries from other jrnl files."""
|
"""This plugin imports entries from other jrnl files."""
|
||||||
|
|
||||||
names = ["jrnl"]
|
names = ["jrnl"]
|
||||||
version = __version__
|
version = __version__
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def class_path(cls):
|
|
||||||
return cls.__module__
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def import_(journal, input=None):
|
def import_(journal, input=None):
|
||||||
"""Imports from an existing file if input is specified, and
|
"""Imports from an existing file if input is specified, and
|
||||||
|
@ -34,7 +33,9 @@ class Importer:
|
||||||
journal.import_(other_journal_txt)
|
journal.import_(other_journal_txt)
|
||||||
new_cnt = len(journal.entries)
|
new_cnt = len(journal.entries)
|
||||||
print(
|
print(
|
||||||
"[{} imported to {} journal]".format(new_cnt - old_cnt, journal.name),
|
"[{} entries imported to '{}' journal]".format(
|
||||||
|
new_cnt - old_cnt, journal.name
|
||||||
|
),
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
journal.write()
|
journal.write()
|
||||||
|
|
Loading…
Add table
Reference in a new issue