summaryrefslogtreecommitdiffstats
path: root/jrnl/journals/FolderJournal.py
blob: 88fa21e14397870ad870547f7a31d7dcecf19bec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# Copyright © 2012-2023 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

import codecs
import fnmatch
import os
from typing import TYPE_CHECKING

from jrnl import time

from .Journal import Journal

if TYPE_CHECKING:
    from jrnl.journals import Entry


def get_files(journal_config: str) -> list[str]:
    """Searches through sub directories starting with journal_config and find all text files"""
    filenames = []
    for root, dirnames, f in os.walk(journal_config):
        for filename in fnmatch.filter(f, "*.txt"):
            filenames.append(os.path.join(root, filename))
    return filenames


class Folder(Journal):
    """A Journal handling multiple files in a folder"""

    def __init__(self, name: str = "default", **kwargs):
        self.entries = []
        self._diff_entry_dates = []
        self.can_be_encrypted = False
        super().__init__(name, **kwargs)

    def open(self) -> "Folder":
        filenames = []
        self.entries = []
        filenames = get_files(self.config["journal"])
        for filename in filenames:
            with codecs.open(filename, "r", "utf-8") as f:
                journal = f.read()
                self.entries.extend(self._parse(journal))
        self.sort()
        return self

    def write(self) -> None:
        """Writes only the entries that have been modified into proper files."""
        # Create a list of dates of modified entries. Start with diff_entry_dates
        modified_dates = self._diff_entry_dates
        seen_dates = set(self._diff_entry_dates)

        for e in self.entries:
            if e.modified:
                if e.date not in modified_dates:
                    modified_dates.append(e.date)
                if e.date not in seen_dates:
                    seen_dates.add(e.date)

        # For every date that had a modified entry, write to a file
        for d in modified_dates:
            write_entries = []
            filename = os.path.join(
                self.config["journal"],
                d.strftime("%Y"),
                d.strftime("%m"),
                d.strftime("%d") + ".txt",
            )
            dirname = os.path.dirname(filename)
            # create directory if it doesn't exist
            if not os.path.exists(dirname):
                os.makedirs(dirname)
            for e in self.entries:
                if (
                    e.date.year == d.year
                    and e.date.month == d.month
                    and e.date.day == d.day
                ):
                    write_entries.append(e)
            journal = "\n".join([e.__str__() for e in write_entries])
            with codecs.open(filename, "w", "utf-8") as journal_file:
                journal_file.write(journal)
        # look for and delete empty files
        filenames = []
        filenames = get_files(self.config["journal"])
        for filename in filenames:
            if os.stat(filename).st_size <= 0:
                os.remove(filename)

    def delete_entries(self, entries_to_delete: list["Entry"]) -> None:
        """Deletes specific entries from a journal."""
        for entry in entries_to_delete:
            self.entries.remove(entry)
            self._diff_entry_dates.append(entry.date)
            self.deleted_entry_count += 1

    def change_date_entries(self, date: str, entries_to_change: list["Entry"]) -> None:
        """Changes entry dates to given date."""

        date = time.parse(date)

        self._diff_entry_dates.append(date)

        for entry in entries_to_change:
            self._diff_entry_dates.append(entry.date)
            entry.date = date
            entry.modified = True

    def parse_editable_str(self, edited: str) -> None:
        """Parses the output of self.editable_str and updates its entries."""
        mod_entries = self._parse(edited)
        diff_entries = set(self.entries) - set(mod_entries)
        for e in diff_entries:
            self._diff_entry_dates.append(e.date)
        # Match those entries that can be found in self.entries and set
        # these to modified, so we can get a count of how many entries got
        # modified and how many got deleted later.
        for entry in mod_entries:
            entry.modified = not any(entry == old_entry for old_entry in self.entries)

        self.increment_change_counts_by_edit(mod_entries)
        self.entries = mod_entries