|
1
|
|
|
#!/usr/bin/env python |
|
2
|
|
|
# encoding: utf-8 |
|
3
|
|
|
|
|
4
|
|
|
import os |
|
5
|
|
|
import re |
|
6
|
|
|
import unicodedata |
|
7
|
|
|
|
|
8
|
|
|
from jrnl.color import ERROR_COLOR |
|
9
|
|
|
from jrnl.color import RESET_COLOR |
|
10
|
|
|
|
|
11
|
|
|
|
|
12
|
|
|
class TextExporter: |
|
13
|
|
|
"""This Exporter can convert entries and journals into text files.""" |
|
14
|
|
|
|
|
15
|
|
|
names = ["text", "txt"] |
|
16
|
|
|
extension = "txt" |
|
17
|
|
|
|
|
18
|
|
|
@classmethod |
|
19
|
|
|
def export_entry(cls, entry): |
|
20
|
|
|
"""Returns a string representation of a single entry.""" |
|
21
|
|
|
return str(entry) |
|
22
|
|
|
|
|
23
|
|
|
@classmethod |
|
24
|
|
|
def export_journal(cls, journal): |
|
25
|
|
|
"""Returns a string representation of an entire journal.""" |
|
26
|
|
|
return "\n".join(cls.export_entry(entry) for entry in journal) |
|
27
|
|
|
|
|
28
|
|
|
@classmethod |
|
29
|
|
|
def write_file(cls, journal, path): |
|
30
|
|
|
"""Exports a journal into a single file.""" |
|
31
|
|
|
try: |
|
32
|
|
|
with open(path, "w", encoding="utf-8") as f: |
|
33
|
|
|
f.write(cls.export_journal(journal)) |
|
34
|
|
|
return f"[Journal exported to {path}]" |
|
35
|
|
|
except IOError as e: |
|
36
|
|
|
return f"[{ERROR_COLOR}ERROR{RESET_COLOR}: {e.filename} {e.strerror}]" |
|
37
|
|
|
|
|
38
|
|
|
@classmethod |
|
39
|
|
|
def make_filename(cls, entry): |
|
40
|
|
|
return entry.date.strftime("%Y-%m-%d") + "_{}.{}".format( |
|
41
|
|
|
cls._slugify(str(entry.title)), cls.extension |
|
42
|
|
|
) |
|
43
|
|
|
|
|
44
|
|
|
@classmethod |
|
45
|
|
|
def write_files(cls, journal, path): |
|
46
|
|
|
"""Exports a journal into individual files for each entry.""" |
|
47
|
|
|
for entry in journal.entries: |
|
48
|
|
|
try: |
|
49
|
|
|
full_path = os.path.join(path, cls.make_filename(entry)) |
|
50
|
|
|
with open(full_path, "w", encoding="utf-8") as f: |
|
51
|
|
|
f.write(cls.export_entry(entry)) |
|
52
|
|
|
except IOError as e: |
|
53
|
|
|
return "[{2}ERROR{3}: {0} {1}]".format( |
|
54
|
|
|
e.filename, e.strerror, ERROR_COLOR, RESET_COLOR |
|
55
|
|
|
) |
|
56
|
|
|
return "[Journal exported to {}]".format(path) |
|
57
|
|
|
|
|
58
|
|
|
def _slugify(string): |
|
59
|
|
|
"""Slugifies a string. |
|
60
|
|
|
Based on public domain code from https://github.com/zacharyvoase/slugify |
|
61
|
|
|
""" |
|
62
|
|
|
normalized_string = str(unicodedata.normalize("NFKD", string)) |
|
63
|
|
|
no_punctuation = re.sub(r"[^\w\s-]", "", normalized_string).strip().lower() |
|
64
|
|
|
slug = re.sub(r"[-\s]+", "-", no_punctuation) |
|
65
|
|
|
return slug |
|
66
|
|
|
|
|
67
|
|
|
@classmethod |
|
68
|
|
|
def export(cls, journal, output=None): |
|
69
|
|
|
"""Exports to individual files if output is an existing path, or into |
|
70
|
|
|
a single file if output is a file name, or returns the exporter's |
|
71
|
|
|
representation as string if output is None.""" |
|
72
|
|
|
if output and os.path.isdir(output): # multiple files |
|
73
|
|
|
return cls.write_files(journal, output) |
|
74
|
|
|
elif output: # single file |
|
75
|
|
|
return cls.write_file(journal, output) |
|
76
|
|
|
else: |
|
77
|
|
|
return cls.export_journal(journal) |
|
78
|
|
|
|