jrnl.jrnl._get_editor_template()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 15
nop 2
dl 0
loc 19
rs 9.65
c 0
b 0
f 0
1
import logging
2
import sys
3
4
from . import install
5
from . import plugins
6
from .Journal import open_journal
7
from .color import ERROR_COLOR
8
from .color import RESET_COLOR
9
from .config import get_journal_name
10
from .config import scope_config
11
from .editor import get_text_from_editor
12
from .editor import get_text_from_stdin
13
from .exception import UserAbort
14
15
16
def run(args):
17
    """
18
    Flow:
19
    1. Run standalone command if it doesn't require config (help, version, etc), then exit
20
    2. Load config
21
    3. Run standalone command if it does require config (encrypt, decrypt, etc), then exit
22
    4. Load specified journal
23
    5. Start write mode, or search mode
24
    6. Profit
25
    """
26
27
    # Run command if possible before config is available
28
    if callable(args.preconfig_cmd):
29
        return args.preconfig_cmd(args)
30
31
    # Load the config, and extract journal name
32
    try:
33
        config = install.load_or_install_jrnl()
34
        original_config = config.copy()
35
        args = get_journal_name(args, config)
36
        config = scope_config(config, args.journal_name)
37
    except UserAbort as err:
38
        print(f"\n{err}", file=sys.stderr)
39
        sys.exit(1)
40
41
    # Run post-config command now that config is ready
42
    if callable(args.postconfig_cmd):
43
        return args.postconfig_cmd(
44
            args=args, config=config, original_config=original_config
0 ignored issues
show
introduced by
The variable original_config does not seem to be defined for all execution paths.
Loading history...
45
        )
46
47
    # --- All the standalone commands are now done --- #
48
49
    # Get the journal we're going to be working with
50
    journal = open_journal(args.journal_name, config)
51
52
    kwargs = {
53
        "args": args,
54
        "config": config,
55
        "journal": journal,
56
    }
57
58
    if _is_write_mode(**kwargs):
59
        write_mode(**kwargs)
60
    else:
61
        search_mode(**kwargs)
62
63
64
def _is_write_mode(args, config, **kwargs):
65
    """Determines if we are in write mode (as opposed to search mode)"""
66
    write_mode = True
67
68
    # Are any search filters present? If so, then search mode.
69
    write_mode = not any(
70
        (
71
            args.contains,
72
            args.delete,
73
            args.edit,
74
            args.export,
75
            args.end_date,
76
            args.limit,
77
            args.on_date,
78
            args.short,
79
            args.starred,
80
            args.start_date,
81
            args.strict,
82
            args.tags,
83
        )
84
    )
85
86
    # Might be writing and want to move to editor part of the way through
87
    if args.edit and args.text:
88
        write_mode = True
89
90
    # If the text is entirely tags, then we are also searching (not writing)
91
    if (
92
        write_mode
93
        and args.text
94
        and all(word[0] in config["tagsymbols"] for word in " ".join(args.text).split())
95
    ):
96
        write_mode = False
97
98
    return write_mode
99
100
101
def write_mode(args, config, journal, **kwargs):
102
    """
103
    Gets input from the user to write to the journal
104
    1. Check for input from cli
105
    2. Check input being piped in
106
    3. Open editor if configured (prepopulated with template if available)
107
    4. Use stdin.read as last resort
108
    6. Write any found text to journal, or exit
109
    """
110
    logging.debug("Write mode: starting")
111
112
    if args.text:
113
        logging.debug("Write mode: cli text detected: %s", args.text)
114
        raw = " ".join(args.text).strip()
115
        if args.edit:
116
            raw = _write_in_editor(config, raw)
117
118
    elif not sys.stdin.isatty():
119
        logging.debug("Write mode: receiving piped text")
120
        raw = sys.stdin.read()
121
122
    else:
123
        raw = _write_in_editor(config)
124
125
    if not raw:
126
        logging.error("Write mode: couldn't get raw text")
127
        sys.exit()
128
129
    logging.debug(
130
        'Write mode: appending raw text to journal "%s": %s', args.journal_name, raw
131
    )
132
    journal.new_entry(raw)
133
    print(f"[Entry added to {args.journal_name} journal]", file=sys.stderr)
134
    journal.write()
135
    logging.debug("Write mode: completed journal.write()", args.journal_name, raw)
136
137
138
def search_mode(args, journal, **kwargs):
139
    """
140
    Search for entries in a journal, then either:
141
    1. Send them to configured editor for user manipulation
142
    2. Delete them (with confirmation for each entry)
143
    3. Display them (with formatting options)
144
    """
145
    kwargs = {
146
        **kwargs,
147
        "args": args,
148
        "journal": journal,
149
        "old_entries": journal.entries,
150
    }
151
152
    # Filters the journal entries in place
153
    _search_journal(**kwargs)
154
155
    # Where do the search results go?
156
    if args.edit:
157
        _edit_search_results(**kwargs)
158
159
    elif args.delete:
160
        _delete_search_results(**kwargs)
161
162
    else:
163
        _display_search_results(**kwargs)
164
165
166
def _write_in_editor(config, template=None):
167
    if config["editor"]:
168
        logging.debug("Write mode: opening editor")
169
        if not template:
170
            template = _get_editor_template(config)
171
        raw = get_text_from_editor(config, template)
172
173
    else:
174
        raw = get_text_from_stdin()
175
176
    return raw
177
178
179
def _get_editor_template(config, **kwargs):
180
    logging.debug("Write mode: loading template for entry")
181
182
    if not config["template"]:
183
        logging.debug("Write mode: no template configured")
184
        return ""
185
186
    try:
187
        template = open(config["template"]).read()
188
        logging.debug("Write mode: template loaded: %s", template)
189
    except OSError:
190
        logging.error("Write mode: template not loaded")
191
        print(
192
            f"[Could not read template at '{config['template']}']",
193
            file=sys.stderr,
194
        )
195
        sys.exit(1)
196
197
    return template
198
199
200
def _search_journal(args, journal, **kwargs):
201
    """ Search the journal with the given args"""
202
    if args.on_date:
203
        args.start_date = args.end_date = args.on_date
204
205
    journal.filter(
206
        tags=args.text,
207
        start_date=args.start_date,
208
        end_date=args.end_date,
209
        strict=args.strict,
210
        starred=args.starred,
211
        exclude=args.excluded,
212
        contains=args.contains,
213
    )
214
    journal.limit(args.limit)
215
216
217
def _edit_search_results(config, journal, old_entries, **kwargs):
218
    """
219
    1. Send the given journal entries to the user-configured editor
220
    2. Print out stats on any modifications to journal
221
    3. Write modifications to journal
222
    """
223
    if not config["editor"]:
224
        print(
225
            f"""
226
            [{ERROR_COLOR}ERROR{RESET_COLOR}: There is no editor configured.]
227
228
            Please specify an editor in config file ({install.CONFIG_FILE_PATH})
229
            to use the --edit option.
230
            """,
231
            file=sys.stderr,
232
        )
233
        sys.exit(1)
234
235
    # separate entries we are not editing
236
    other_entries = [e for e in old_entries if e not in journal.entries]
237
238
    # Get stats now for summary later
239
    old_stats = _get_predit_stats(journal)
240
241
    # Send user to the editor
242
    edited = get_text_from_editor(config, journal.editable_str())
243
    journal.parse_editable_str(edited)
244
245
    # Print summary if available
246
    _print_edited_summary(journal, old_stats)
247
248
    # Put back entries we separated earlier, sort, and write the journal
249
    journal.entries += other_entries
250
    journal.sort()
251
    journal.write()
252
253
254
def _print_edited_summary(journal, old_stats, **kwargs):
255
    stats = {
256
        "deleted": old_stats["count"] - len(journal),
257
        "modified": len([e for e in journal.entries if e.modified]),
258
    }
259
260
    prompts = []
261
262
    if stats["deleted"]:
263
        prompts.append(
264
            f"{stats['deleted']} {_pluralize_entry(stats['deleted'])} deleted"
265
        )
266
267
    if stats["modified"]:
268
        prompts.append(
269
            f"{stats['modified']} {_pluralize_entry(stats['modified'])} modified"
270
        )
271
272
    if prompts:
273
        print(f"[{', '.join(prompts).capitalize()}]", file=sys.stderr)
274
275
276
def _get_predit_stats(journal):
277
    return {"count": len(journal)}
278
279
280
def _pluralize_entry(num):
281
    return "entry" if num == 1 else "entries"
282
283
284
def _delete_search_results(journal, old_entries, **kwargs):
285
    if not journal.entries:
286
        print(
287
            "[No entries deleted, because the search returned no results.]",
288
            file=sys.stderr,
289
        )
290
        sys.exit(1)
291
292
    entries_to_delete = journal.prompt_delete_entries()
293
294
    if entries_to_delete:
295
        journal.entries = old_entries
296
        journal.delete_entries(entries_to_delete)
297
298
        journal.write()
299
300
301
def _display_search_results(args, journal, **kwargs):
302
    if args.short:
303
        print(journal.pprint(short=True))
304
305
    elif args.tags:
306
        print(plugins.get_exporter("tags").export(journal))
307
308
    elif args.export:
309
        exporter = plugins.get_exporter(args.export)
310
        print(exporter.export(journal, args.filename))
311
    elif kwargs["config"].get("display_format"):
312
        exporter = plugins.get_exporter(kwargs["config"]["display_format"])
313
        print(exporter.export(journal, args.filename))
314
    else:
315
        print(journal.pprint())
316