mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Add type hints (#1614)
* Add type hints * Fix linters * Add remaining type hints * Fix type-checking linter * Update jrnl/DayOneJournal.py Co-authored-by: Jonathan Wren <jonathan@nowandwren.com>
This commit is contained in:
parent
c1eb0c54a3
commit
30b41fdb88
28 changed files with 219 additions and 120 deletions
|
@ -38,7 +38,7 @@ class DayOne(Journal.Journal):
|
||||||
self.can_be_encrypted = False
|
self.can_be_encrypted = False
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def open(self):
|
def open(self) -> "DayOne":
|
||||||
filenames = []
|
filenames = []
|
||||||
for root, dirnames, f in os.walk(self.config["journal"]):
|
for root, dirnames, f in os.walk(self.config["journal"]):
|
||||||
for filename in fnmatch.filter(f, "*.doentry"):
|
for filename in fnmatch.filter(f, "*.doentry"):
|
||||||
|
@ -113,7 +113,7 @@ class DayOne(Journal.Journal):
|
||||||
self.sort()
|
self.sort()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def write(self):
|
def write(self) -> None:
|
||||||
"""Writes only the entries that have been modified into plist files."""
|
"""Writes only the entries that have been modified into plist files."""
|
||||||
for entry in self.entries:
|
for entry in self.entries:
|
||||||
if entry.modified:
|
if entry.modified:
|
||||||
|
@ -177,12 +177,12 @@ class DayOne(Journal.Journal):
|
||||||
)
|
)
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
|
||||||
def editable_str(self):
|
def editable_str(self) -> str:
|
||||||
"""Turns the journal into a string of entries that can be edited
|
"""Turns the journal into a string of entries that can be edited
|
||||||
manually and later be parsed with eslf.parse_editable_str."""
|
manually and later be parsed with eslf.parse_editable_str."""
|
||||||
return "\n".join([f"{str(e)}\n# {e.uuid}\n" for e in self.entries])
|
return "\n".join([f"{str(e)}\n# {e.uuid}\n" for e in self.entries])
|
||||||
|
|
||||||
def _update_old_entry(self, entry, new_entry):
|
def _update_old_entry(self, entry: Entry, new_entry: Entry) -> None:
|
||||||
for attr in ("title", "body", "date"):
|
for attr in ("title", "body", "date"):
|
||||||
old_attr = getattr(entry, attr)
|
old_attr = getattr(entry, attr)
|
||||||
new_attr = getattr(new_entry, attr)
|
new_attr = getattr(new_entry, attr)
|
||||||
|
@ -190,7 +190,7 @@ class DayOne(Journal.Journal):
|
||||||
entry.modified = True
|
entry.modified = True
|
||||||
setattr(entry, attr, new_attr)
|
setattr(entry, attr, new_attr)
|
||||||
|
|
||||||
def _get_and_remove_uuid_from_entry(self, entry):
|
def _get_and_remove_uuid_from_entry(self, entry: Entry) -> Entry:
|
||||||
uuid_regex = "^ *?# ([a-zA-Z0-9]+) *?$"
|
uuid_regex = "^ *?# ([a-zA-Z0-9]+) *?$"
|
||||||
m = re.search(uuid_regex, entry.body, re.MULTILINE)
|
m = re.search(uuid_regex, entry.body, re.MULTILINE)
|
||||||
entry.uuid = m.group(1) if m else None
|
entry.uuid = m.group(1) if m else None
|
||||||
|
@ -201,7 +201,7 @@ class DayOne(Journal.Journal):
|
||||||
|
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def parse_editable_str(self, edited):
|
def parse_editable_str(self, edited: str) -> None:
|
||||||
"""Parses the output of self.editable_str and updates its entries."""
|
"""Parses the output of self.editable_str and updates its entries."""
|
||||||
# Method: create a new list of entries from the edited text, then match
|
# Method: create a new list of entries from the edited text, then match
|
||||||
# UUIDs of the new entries against self.entries, updating the entries
|
# UUIDs of the new entries against self.entries, updating the entries
|
||||||
|
|
|
@ -5,15 +5,25 @@ import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import ansiwrap
|
import ansiwrap
|
||||||
|
|
||||||
from .color import colorize
|
from .color import colorize
|
||||||
from .color import highlight_tags_with_background_color
|
from .color import highlight_tags_with_background_color
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class Entry:
|
class Entry:
|
||||||
def __init__(self, journal, date=None, text="", starred=False):
|
def __init__(
|
||||||
|
self,
|
||||||
|
journal: "Journal",
|
||||||
|
date: datetime.datetime | None = None,
|
||||||
|
text: str = "",
|
||||||
|
starred: bool = False,
|
||||||
|
):
|
||||||
self.journal = journal # Reference to journal mainly to access its config
|
self.journal = journal # Reference to journal mainly to access its config
|
||||||
self.date = date or datetime.datetime.now()
|
self.date = date or datetime.datetime.now()
|
||||||
self.text = text
|
self.text = text
|
||||||
|
@ -24,7 +34,7 @@ class Entry:
|
||||||
self.modified = False
|
self.modified = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fulltext(self):
|
def fulltext(self) -> str:
|
||||||
return self.title + " " + self.body
|
return self.title + " " + self.body
|
||||||
|
|
||||||
def _parse_text(self):
|
def _parse_text(self):
|
||||||
|
@ -68,11 +78,11 @@ class Entry:
|
||||||
self._tags = x
|
self._tags = x
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_regex(tagsymbols):
|
def tag_regex(tagsymbols: str) -> re.Pattern:
|
||||||
pattern = rf"(?<!\S)([{tagsymbols}][-+*#/\w]+)"
|
pattern = rf"(?<!\S)([{tagsymbols}][-+*#/\w]+)"
|
||||||
return re.compile(pattern)
|
return re.compile(pattern)
|
||||||
|
|
||||||
def _parse_tags(self):
|
def _parse_tags(self) -> set[str]:
|
||||||
tagsymbols = self.journal.config["tagsymbols"]
|
tagsymbols = self.journal.config["tagsymbols"]
|
||||||
return {
|
return {
|
||||||
tag.lower() for tag in re.findall(Entry.tag_regex(tagsymbols), self.text)
|
tag.lower() for tag in re.findall(Entry.tag_regex(tagsymbols), self.text)
|
||||||
|
@ -90,7 +100,7 @@ class Entry:
|
||||||
body=self.body.rstrip("\n "),
|
body=self.body.rstrip("\n "),
|
||||||
)
|
)
|
||||||
|
|
||||||
def pprint(self, short=False):
|
def pprint(self, short: bool = False) -> str:
|
||||||
"""Returns a pretty-printed version of the entry.
|
"""Returns a pretty-printed version of the entry.
|
||||||
If short is true, only print the title."""
|
If short is true, only print the title."""
|
||||||
# Handle indentation
|
# Handle indentation
|
||||||
|
@ -197,7 +207,7 @@ class Entry:
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.__repr__())
|
return hash(self.__repr__())
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other: "Entry"):
|
||||||
if (
|
if (
|
||||||
not isinstance(other, Entry)
|
not isinstance(other, Entry)
|
||||||
or self.title.strip() != other.title.strip()
|
or self.title.strip() != other.title.strip()
|
||||||
|
@ -230,7 +240,7 @@ SENTENCE_SPLITTER = re.compile(
|
||||||
SENTENCE_SPLITTER_ONLY_NEWLINE = re.compile("\n")
|
SENTENCE_SPLITTER_ONLY_NEWLINE = re.compile("\n")
|
||||||
|
|
||||||
|
|
||||||
def split_title(text):
|
def split_title(text: str) -> tuple[str, str]:
|
||||||
"""Splits the first sentence off from a text."""
|
"""Splits the first sentence off from a text."""
|
||||||
sep = SENTENCE_SPLITTER_ONLY_NEWLINE.search(text.lstrip())
|
sep = SENTENCE_SPLITTER_ONLY_NEWLINE.search(text.lstrip())
|
||||||
if not sep:
|
if not sep:
|
||||||
|
|
|
@ -4,12 +4,16 @@
|
||||||
import codecs
|
import codecs
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import os
|
import os
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl import Journal
|
from jrnl import Journal
|
||||||
from jrnl import time
|
from jrnl import time
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
|
||||||
def get_files(journal_config):
|
|
||||||
|
def get_files(journal_config: str) -> list[str]:
|
||||||
"""Searches through sub directories starting with journal_config and find all text files"""
|
"""Searches through sub directories starting with journal_config and find all text files"""
|
||||||
filenames = []
|
filenames = []
|
||||||
for root, dirnames, f in os.walk(journal_config):
|
for root, dirnames, f in os.walk(journal_config):
|
||||||
|
@ -21,13 +25,13 @@ def get_files(journal_config):
|
||||||
class Folder(Journal.Journal):
|
class Folder(Journal.Journal):
|
||||||
"""A Journal handling multiple files in a folder"""
|
"""A Journal handling multiple files in a folder"""
|
||||||
|
|
||||||
def __init__(self, name="default", **kwargs):
|
def __init__(self, name: str = "default", **kwargs):
|
||||||
self.entries = []
|
self.entries = []
|
||||||
self._diff_entry_dates = []
|
self._diff_entry_dates = []
|
||||||
self.can_be_encrypted = False
|
self.can_be_encrypted = False
|
||||||
super().__init__(name, **kwargs)
|
super().__init__(name, **kwargs)
|
||||||
|
|
||||||
def open(self):
|
def open(self) -> "Folder":
|
||||||
filenames = []
|
filenames = []
|
||||||
self.entries = []
|
self.entries = []
|
||||||
filenames = get_files(self.config["journal"])
|
filenames = get_files(self.config["journal"])
|
||||||
|
@ -38,7 +42,7 @@ class Folder(Journal.Journal):
|
||||||
self.sort()
|
self.sort()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def write(self):
|
def write(self) -> None:
|
||||||
"""Writes only the entries that have been modified into proper files."""
|
"""Writes only the entries that have been modified into proper files."""
|
||||||
# Create a list of dates of modified entries. Start with diff_entry_dates
|
# Create a list of dates of modified entries. Start with diff_entry_dates
|
||||||
modified_dates = self._diff_entry_dates
|
modified_dates = self._diff_entry_dates
|
||||||
|
@ -81,13 +85,13 @@ class Folder(Journal.Journal):
|
||||||
if os.stat(filename).st_size <= 0:
|
if os.stat(filename).st_size <= 0:
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
|
||||||
def delete_entries(self, entries_to_delete):
|
def delete_entries(self, entries_to_delete: list["Entry"]) -> None:
|
||||||
"""Deletes specific entries from a journal."""
|
"""Deletes specific entries from a journal."""
|
||||||
for entry in entries_to_delete:
|
for entry in entries_to_delete:
|
||||||
self.entries.remove(entry)
|
self.entries.remove(entry)
|
||||||
self._diff_entry_dates.append(entry.date)
|
self._diff_entry_dates.append(entry.date)
|
||||||
|
|
||||||
def change_date_entries(self, date):
|
def change_date_entries(self, date: str) -> None:
|
||||||
"""Changes entry dates to given date."""
|
"""Changes entry dates to given date."""
|
||||||
|
|
||||||
date = time.parse(date)
|
date = time.parse(date)
|
||||||
|
@ -98,7 +102,7 @@ class Folder(Journal.Journal):
|
||||||
self._diff_entry_dates.append(entry.date)
|
self._diff_entry_dates.append(entry.date)
|
||||||
entry.date = date
|
entry.date = date
|
||||||
|
|
||||||
def parse_editable_str(self, edited):
|
def parse_editable_str(self, edited: str) -> None:
|
||||||
"""Parses the output of self.editable_str and updates its entries."""
|
"""Parses the output of self.editable_str and updates its entries."""
|
||||||
mod_entries = self._parse(edited)
|
mod_entries = self._parse(edited)
|
||||||
diff_entries = set(self.entries) - set(mod_entries)
|
diff_entries = set(self.entries) - set(mod_entries)
|
||||||
|
|
|
@ -20,7 +20,7 @@ from jrnl.plugins import util
|
||||||
class WrappingFormatter(argparse.RawTextHelpFormatter):
|
class WrappingFormatter(argparse.RawTextHelpFormatter):
|
||||||
"""Used in help screen"""
|
"""Used in help screen"""
|
||||||
|
|
||||||
def _split_lines(self, text, width):
|
def _split_lines(self, text: str, width: int) -> list[str]:
|
||||||
text = text.split("\n\n")
|
text = text.split("\n\n")
|
||||||
text = map(lambda t: self._whitespace_matcher.sub(" ", t).strip(), text)
|
text = map(lambda t: self._whitespace_matcher.sub(" ", t).strip(), text)
|
||||||
text = map(lambda t: textwrap.wrap(t, width=56), text)
|
text = map(lambda t: textwrap.wrap(t, width=56), text)
|
||||||
|
@ -28,7 +28,7 @@ class WrappingFormatter(argparse.RawTextHelpFormatter):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def parse_args(args=[]):
|
def parse_args(args: list[str] = []) -> argparse.Namespace:
|
||||||
"""
|
"""
|
||||||
Argument parsing that is doable before the config is available.
|
Argument parsing that is doable before the config is available.
|
||||||
Everything else goes into "text" for later parsing.
|
Everything else goes into "text" for later parsing.
|
||||||
|
|
|
@ -16,7 +16,7 @@ from jrnl.messages import MsgText
|
||||||
from jrnl.output import print_msg
|
from jrnl.output import print_msg
|
||||||
|
|
||||||
|
|
||||||
def configure_logger(debug=False):
|
def configure_logger(debug: bool = False) -> None:
|
||||||
if not debug:
|
if not debug:
|
||||||
logging.disable()
|
logging.disable()
|
||||||
return
|
return
|
||||||
|
@ -31,7 +31,7 @@ def configure_logger(debug=False):
|
||||||
logging.getLogger("keyring.backend").setLevel(logging.ERROR)
|
logging.getLogger("keyring.backend").setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
|
||||||
def cli(manual_args=None):
|
def cli(manual_args: list[str] | None = None) -> int:
|
||||||
try:
|
try:
|
||||||
if manual_args is None:
|
if manual_args is None:
|
||||||
manual_args = sys.argv[1:]
|
manual_args = sys.argv[1:]
|
||||||
|
|
|
@ -4,16 +4,20 @@
|
||||||
import re
|
import re
|
||||||
from string import punctuation
|
from string import punctuation
|
||||||
from string import whitespace
|
from string import whitespace
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
|
|
||||||
from jrnl.os_compat import on_windows
|
from jrnl.os_compat import on_windows
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
|
||||||
if on_windows():
|
if on_windows():
|
||||||
colorama.init()
|
colorama.init()
|
||||||
|
|
||||||
|
|
||||||
def colorize(string, color, bold=False):
|
def colorize(string: str, color: str, bold: bool = False) -> str:
|
||||||
"""Returns the string colored with colorama.Fore.color. If the color set by
|
"""Returns the string colored with colorama.Fore.color. If the color set by
|
||||||
the user is "NONE" or the color doesn't exist in the colorama.Fore attributes,
|
the user is "NONE" or the color doesn't exist in the colorama.Fore attributes,
|
||||||
it returns the string without any modification."""
|
it returns the string without any modification."""
|
||||||
|
@ -26,7 +30,9 @@ def colorize(string, color, bold=False):
|
||||||
return colorama.Style.BRIGHT + color_escape + string + colorama.Style.RESET_ALL
|
return colorama.Style.BRIGHT + color_escape + string + colorama.Style.RESET_ALL
|
||||||
|
|
||||||
|
|
||||||
def highlight_tags_with_background_color(entry, text, color, is_title=False):
|
def highlight_tags_with_background_color(
|
||||||
|
entry: "Entry", text: str, color: str, is_title: bool = False
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Takes a string and colorizes the tags in it based upon the config value for
|
Takes a string and colorizes the tags in it based upon the config value for
|
||||||
color.tags, while colorizing the rest of the text based on `color`.
|
color.tags, while colorizing the rest of the text based on `color`.
|
||||||
|
@ -45,9 +51,9 @@ def highlight_tags_with_background_color(entry, text, color, is_title=False):
|
||||||
:returns [(colorized_str, original_str)]"""
|
:returns [(colorized_str, original_str)]"""
|
||||||
for part in fragments:
|
for part in fragments:
|
||||||
if part and part[0] not in config["tagsymbols"]:
|
if part and part[0] not in config["tagsymbols"]:
|
||||||
yield (colorize(part, color, bold=is_title), part)
|
yield colorize(part, color, bold=is_title), part
|
||||||
elif part:
|
elif part:
|
||||||
yield (colorize(part, config["colors"]["tags"], bold=True), part)
|
yield colorize(part, config["colors"]["tags"], bold=True), part
|
||||||
|
|
||||||
config = entry.journal.config
|
config = entry.journal.config
|
||||||
if config["highlight"]: # highlight tags
|
if config["highlight"]: # highlight tags
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
|
@ -57,7 +58,7 @@ def make_yaml_valid_dict(input: list) -> dict:
|
||||||
return runtime_modifications
|
return runtime_modifications
|
||||||
|
|
||||||
|
|
||||||
def save_config(config, alt_config_path=None):
|
def save_config(config: dict, alt_config_path: str | None = None) -> None:
|
||||||
"""Supply alt_config_path if using an alternate config through --config-file."""
|
"""Supply alt_config_path if using an alternate config through --config-file."""
|
||||||
config["version"] = __version__
|
config["version"] = __version__
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ def save_config(config, alt_config_path=None):
|
||||||
yaml.dump(config, f)
|
yaml.dump(config, f)
|
||||||
|
|
||||||
|
|
||||||
def get_config_path():
|
def get_config_path() -> str:
|
||||||
try:
|
try:
|
||||||
config_directory_path = xdg.BaseDirectory.save_config_path(XDG_RESOURCE)
|
config_directory_path = xdg.BaseDirectory.save_config_path(XDG_RESOURCE)
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
|
@ -91,7 +92,7 @@ def get_config_path():
|
||||||
return os.path.join(config_directory_path or home_dir(), DEFAULT_CONFIG_NAME)
|
return os.path.join(config_directory_path or home_dir(), DEFAULT_CONFIG_NAME)
|
||||||
|
|
||||||
|
|
||||||
def get_default_config():
|
def get_default_config() -> dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"version": __version__,
|
"version": __version__,
|
||||||
"journals": {"default": {"journal": get_default_journal_path()}},
|
"journals": {"default": {"journal": get_default_journal_path()}},
|
||||||
|
@ -114,12 +115,12 @@ def get_default_config():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_default_journal_path():
|
def get_default_journal_path() -> str:
|
||||||
journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir()
|
journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir()
|
||||||
return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME)
|
return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME)
|
||||||
|
|
||||||
|
|
||||||
def scope_config(config, journal_name):
|
def scope_config(config: dict, journal_name: str) -> dict:
|
||||||
if journal_name not in config["journals"]:
|
if journal_name not in config["journals"]:
|
||||||
return config
|
return config
|
||||||
config = config.copy()
|
config = config.copy()
|
||||||
|
@ -139,7 +140,7 @@ def scope_config(config, journal_name):
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def verify_config_colors(config):
|
def verify_config_colors(config: dict) -> bool:
|
||||||
"""
|
"""
|
||||||
Ensures the keys set for colors are valid colorama.Fore attributes, or "None"
|
Ensures the keys set for colors are valid colorama.Fore attributes, or "None"
|
||||||
:return: True if all keys are set correctly, False otherwise
|
:return: True if all keys are set correctly, False otherwise
|
||||||
|
@ -164,7 +165,7 @@ def verify_config_colors(config):
|
||||||
return all_valid_colors
|
return all_valid_colors
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_path):
|
def load_config(config_path: str) -> dict:
|
||||||
"""Tries to load a config file from YAML."""
|
"""Tries to load a config file from YAML."""
|
||||||
try:
|
try:
|
||||||
with open(config_path, encoding=YAML_FILE_ENCODING) as f:
|
with open(config_path, encoding=YAML_FILE_ENCODING) as f:
|
||||||
|
@ -187,13 +188,15 @@ def load_config(config_path):
|
||||||
return yaml.load(f)
|
return yaml.load(f)
|
||||||
|
|
||||||
|
|
||||||
def is_config_json(config_path):
|
def is_config_json(config_path: str) -> bool:
|
||||||
with open(config_path, "r", encoding="utf-8") as f:
|
with open(config_path, "r", encoding="utf-8") as f:
|
||||||
config_file = f.read()
|
config_file = f.read()
|
||||||
return config_file.strip().startswith("{")
|
return config_file.strip().startswith("{")
|
||||||
|
|
||||||
|
|
||||||
def update_config(config, new_config, scope, force_local=False):
|
def update_config(
|
||||||
|
config: dict, new_config: dict, scope: str | None, force_local: bool = False
|
||||||
|
) -> None:
|
||||||
"""Updates a config dict with new values - either global if scope is None
|
"""Updates a config dict with new values - either global if scope is None
|
||||||
or config['journals'][scope] is just a string pointing to a journal file,
|
or config['journals'][scope] is just a string pointing to a journal file,
|
||||||
or within the scope"""
|
or within the scope"""
|
||||||
|
@ -206,7 +209,7 @@ def update_config(config, new_config, scope, force_local=False):
|
||||||
config.update(new_config)
|
config.update(new_config)
|
||||||
|
|
||||||
|
|
||||||
def get_journal_name(args, config):
|
def get_journal_name(args: argparse.Namespace, config: dict) -> argparse.Namespace:
|
||||||
args.journal_name = DEFAULT_JOURNAL_KEY
|
args.journal_name = DEFAULT_JOURNAL_KEY
|
||||||
|
|
||||||
# The first arg might be a journal name
|
# The first arg might be a journal name
|
||||||
|
|
|
@ -17,7 +17,7 @@ from jrnl.os_compat import split_args
|
||||||
from jrnl.output import print_msg
|
from jrnl.output import print_msg
|
||||||
|
|
||||||
|
|
||||||
def get_text_from_editor(config, template=""):
|
def get_text_from_editor(config: dict, template: str = "") -> str:
|
||||||
suffix = ".jrnl"
|
suffix = ".jrnl"
|
||||||
if config["template"]:
|
if config["template"]:
|
||||||
template_filename = Path(config["template"]).name
|
template_filename = Path(config["template"]).name
|
||||||
|
@ -50,7 +50,7 @@ def get_text_from_editor(config, template=""):
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
|
|
||||||
def get_text_from_stdin():
|
def get_text_from_stdin() -> str:
|
||||||
print_msg(
|
print_msg(
|
||||||
Message(
|
Message(
|
||||||
MsgText.WritingEntryStart,
|
MsgText.WritingEntryStart,
|
||||||
|
|
|
@ -27,7 +27,7 @@ from jrnl.prompt import yesno
|
||||||
from jrnl.upgrade import is_old_version
|
from jrnl.upgrade import is_old_version
|
||||||
|
|
||||||
|
|
||||||
def upgrade_config(config_data, alt_config_path=None):
|
def upgrade_config(config_data: dict, alt_config_path: str | None = None) -> None:
|
||||||
"""Checks if there are keys missing in a given config dict, and if so, updates the config file accordingly.
|
"""Checks if there are keys missing in a given config dict, and if so, updates the config file accordingly.
|
||||||
This essentially automatically ports jrnl installations if new config parameters are introduced in later
|
This essentially automatically ports jrnl installations if new config parameters are introduced in later
|
||||||
versions.
|
versions.
|
||||||
|
@ -46,7 +46,7 @@ def upgrade_config(config_data, alt_config_path=None):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def find_default_config():
|
def find_default_config() -> str:
|
||||||
config_path = (
|
config_path = (
|
||||||
get_config_path()
|
get_config_path()
|
||||||
if os.path.exists(get_config_path())
|
if os.path.exists(get_config_path())
|
||||||
|
@ -55,7 +55,7 @@ def find_default_config():
|
||||||
return config_path
|
return config_path
|
||||||
|
|
||||||
|
|
||||||
def find_alt_config(alt_config):
|
def find_alt_config(alt_config: str) -> str:
|
||||||
if not os.path.exists(alt_config):
|
if not os.path.exists(alt_config):
|
||||||
raise JrnlException(
|
raise JrnlException(
|
||||||
Message(
|
Message(
|
||||||
|
@ -66,7 +66,7 @@ def find_alt_config(alt_config):
|
||||||
return alt_config
|
return alt_config
|
||||||
|
|
||||||
|
|
||||||
def load_or_install_jrnl(alt_config_path):
|
def load_or_install_jrnl(alt_config_path: str) -> dict:
|
||||||
"""
|
"""
|
||||||
If jrnl is already installed, loads and returns a default config object.
|
If jrnl is already installed, loads and returns a default config object.
|
||||||
If alternate config is specified via --config-file flag, it will be used.
|
If alternate config is specified via --config-file flag, it will be used.
|
||||||
|
@ -107,7 +107,7 @@ def load_or_install_jrnl(alt_config_path):
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def install():
|
def install() -> dict:
|
||||||
_initialize_autocomplete()
|
_initialize_autocomplete()
|
||||||
|
|
||||||
# Where to create the journal?
|
# Where to create the journal?
|
||||||
|
@ -143,7 +143,7 @@ def install():
|
||||||
return default_config
|
return default_config
|
||||||
|
|
||||||
|
|
||||||
def _initialize_autocomplete():
|
def _initialize_autocomplete() -> None:
|
||||||
# readline is not included in Windows Active Python and perhaps some other distributions
|
# readline is not included in Windows Active Python and perhaps some other distributions
|
||||||
if sys.modules.get("readline"):
|
if sys.modules.get("readline"):
|
||||||
import readline
|
import readline
|
||||||
|
@ -153,7 +153,7 @@ def _initialize_autocomplete():
|
||||||
readline.set_completer(_autocomplete_path)
|
readline.set_completer(_autocomplete_path)
|
||||||
|
|
||||||
|
|
||||||
def _autocomplete_path(text, state):
|
def _autocomplete_path(text: str, state: int) -> list[str | None]:
|
||||||
expansions = glob.glob(expand_path(text) + "*")
|
expansions = glob.glob(expand_path(text) + "*")
|
||||||
expansions = [e + "/" if os.path.isdir(e) else e for e in expansions]
|
expansions = [e + "/" if os.path.isdir(e) else e for e in expansions]
|
||||||
expansions.append(None)
|
expansions.append(None)
|
||||||
|
|
54
jrnl/jrnl.py
54
jrnl/jrnl.py
|
@ -1,8 +1,8 @@
|
||||||
# Copyright © 2012-2022 jrnl contributors
|
# Copyright © 2012-2022 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl import install
|
from jrnl import install
|
||||||
from jrnl import plugins
|
from jrnl import plugins
|
||||||
|
@ -14,6 +14,7 @@ from jrnl.config import scope_config
|
||||||
from jrnl.editor import get_text_from_editor
|
from jrnl.editor import get_text_from_editor
|
||||||
from jrnl.editor import get_text_from_stdin
|
from jrnl.editor import get_text_from_stdin
|
||||||
from jrnl.exception import JrnlException
|
from jrnl.exception import JrnlException
|
||||||
|
from jrnl.Journal import Journal
|
||||||
from jrnl.Journal import open_journal
|
from jrnl.Journal import open_journal
|
||||||
from jrnl.messages import Message
|
from jrnl.messages import Message
|
||||||
from jrnl.messages import MsgStyle
|
from jrnl.messages import MsgStyle
|
||||||
|
@ -23,8 +24,13 @@ from jrnl.output import print_msgs
|
||||||
from jrnl.override import apply_overrides
|
from jrnl.override import apply_overrides
|
||||||
from jrnl.path import expand_path
|
from jrnl.path import expand_path
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
def run(args):
|
from jrnl.Entry import Entry
|
||||||
|
|
||||||
|
|
||||||
|
def run(args: "Namespace"):
|
||||||
"""
|
"""
|
||||||
Flow:
|
Flow:
|
||||||
1. Run standalone command if it doesn't require config (help, version, etc), then exit
|
1. Run standalone command if it doesn't require config (help, version, etc), then exit
|
||||||
|
@ -72,10 +78,8 @@ def run(args):
|
||||||
search_mode(**kwargs)
|
search_mode(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def _is_write_mode(args, config, **kwargs):
|
def _is_write_mode(args: "Namespace", config: dict, **kwargs) -> bool:
|
||||||
"""Determines if we are in write mode (as opposed to search mode)"""
|
"""Determines if we are in write mode (as opposed to search mode)"""
|
||||||
write_mode = True
|
|
||||||
|
|
||||||
# Are any search filters present? If so, then search mode.
|
# Are any search filters present? If so, then search mode.
|
||||||
write_mode = not any(
|
write_mode = not any(
|
||||||
(
|
(
|
||||||
|
@ -115,7 +119,7 @@ def _is_write_mode(args, config, **kwargs):
|
||||||
return write_mode
|
return write_mode
|
||||||
|
|
||||||
|
|
||||||
def write_mode(args, config, journal, **kwargs):
|
def write_mode(args: "Namespace", config: dict, journal: Journal, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Gets input from the user to write to the journal
|
Gets input from the user to write to the journal
|
||||||
1. Check for input from cli
|
1. Check for input from cli
|
||||||
|
@ -159,7 +163,7 @@ def write_mode(args, config, journal, **kwargs):
|
||||||
logging.debug("Write mode: completed journal.write()")
|
logging.debug("Write mode: completed journal.write()")
|
||||||
|
|
||||||
|
|
||||||
def search_mode(args, journal, **kwargs):
|
def search_mode(args: "Namespace", journal: Journal, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Search for entries in a journal, then either:
|
Search for entries in a journal, then either:
|
||||||
1. Send them to configured editor for user manipulation (and also
|
1. Send them to configured editor for user manipulation (and also
|
||||||
|
@ -213,7 +217,7 @@ def search_mode(args, journal, **kwargs):
|
||||||
_display_search_results(**kwargs)
|
_display_search_results(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def _write_in_editor(config, template=None):
|
def _write_in_editor(config: dict, template: str | None = None) -> str:
|
||||||
if config["editor"]:
|
if config["editor"]:
|
||||||
logging.debug("Write mode: opening editor")
|
logging.debug("Write mode: opening editor")
|
||||||
if not template:
|
if not template:
|
||||||
|
@ -226,7 +230,7 @@ def _write_in_editor(config, template=None):
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
|
|
||||||
def _get_editor_template(config, **kwargs):
|
def _get_editor_template(config: dict, **kwargs) -> str:
|
||||||
logging.debug("Write mode: loading template for entry")
|
logging.debug("Write mode: loading template for entry")
|
||||||
|
|
||||||
if not config["template"]:
|
if not config["template"]:
|
||||||
|
@ -251,7 +255,7 @@ def _get_editor_template(config, **kwargs):
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def _has_search_args(args):
|
def _has_search_args(args: "Namespace") -> bool:
|
||||||
return any(
|
return any(
|
||||||
(
|
(
|
||||||
args.on_date,
|
args.on_date,
|
||||||
|
@ -271,7 +275,7 @@ def _has_search_args(args):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _filter_journal_entries(args, journal, **kwargs):
|
def _filter_journal_entries(args: "Namespace", journal: Journal, **kwargs) -> None:
|
||||||
"""Filter journal entries in-place based upon search args"""
|
"""Filter journal entries in-place based upon search args"""
|
||||||
if args.on_date:
|
if args.on_date:
|
||||||
args.start_date = args.end_date = args.on_date
|
args.start_date = args.end_date = args.on_date
|
||||||
|
@ -296,7 +300,7 @@ def _filter_journal_entries(args, journal, **kwargs):
|
||||||
journal.limit(args.limit)
|
journal.limit(args.limit)
|
||||||
|
|
||||||
|
|
||||||
def _print_entries_found_count(count, args):
|
def _print_entries_found_count(count: int, args: "Namespace") -> None:
|
||||||
if count == 0:
|
if count == 0:
|
||||||
if args.edit or args.change_time:
|
if args.edit or args.change_time:
|
||||||
print_msg(Message(MsgText.NothingToModify, MsgStyle.WARNING))
|
print_msg(Message(MsgText.NothingToModify, MsgStyle.WARNING))
|
||||||
|
@ -317,12 +321,14 @@ def _print_entries_found_count(count, args):
|
||||||
print_msg(Message(my_msg, MsgStyle.NORMAL, {"num": count}))
|
print_msg(Message(my_msg, MsgStyle.NORMAL, {"num": count}))
|
||||||
|
|
||||||
|
|
||||||
def _other_entries(journal, entries):
|
def _other_entries(journal: Journal, entries: list["Entry"]) -> list["Entry"]:
|
||||||
"""Find entries that are not in journal"""
|
"""Find entries that are not in journal"""
|
||||||
return [e for e in entries if e not in journal.entries]
|
return [e for e in entries if e not in journal.entries]
|
||||||
|
|
||||||
|
|
||||||
def _edit_search_results(config, journal, old_entries, **kwargs):
|
def _edit_search_results(
|
||||||
|
config: dict, journal: Journal, old_entries: list["Entry"], **kwargs
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
1. Send the given journal entries to the user-configured editor
|
1. Send the given journal entries to the user-configured editor
|
||||||
2. Print out stats on any modifications to journal
|
2. Print out stats on any modifications to journal
|
||||||
|
@ -356,7 +362,9 @@ def _edit_search_results(config, journal, old_entries, **kwargs):
|
||||||
journal.write()
|
journal.write()
|
||||||
|
|
||||||
|
|
||||||
def _print_edited_summary(journal, old_stats, **kwargs):
|
def _print_edited_summary(
|
||||||
|
journal: Journal, old_stats: dict[str, int], **kwargs
|
||||||
|
) -> None:
|
||||||
stats = {
|
stats = {
|
||||||
"added": len(journal) - old_stats["count"],
|
"added": len(journal) - old_stats["count"],
|
||||||
"deleted": old_stats["count"] - len(journal),
|
"deleted": old_stats["count"] - len(journal),
|
||||||
|
@ -395,11 +403,13 @@ def _print_edited_summary(journal, old_stats, **kwargs):
|
||||||
print_msgs(msgs)
|
print_msgs(msgs)
|
||||||
|
|
||||||
|
|
||||||
def _get_predit_stats(journal):
|
def _get_predit_stats(journal: Journal) -> dict[str, int]:
|
||||||
return {"count": len(journal)}
|
return {"count": len(journal)}
|
||||||
|
|
||||||
|
|
||||||
def _delete_search_results(journal, old_entries, **kwargs):
|
def _delete_search_results(
|
||||||
|
journal: Journal, old_entries: list["Entry"], **kwargs
|
||||||
|
) -> None:
|
||||||
entries_to_delete = journal.prompt_action_entries(MsgText.DeleteEntryQuestion)
|
entries_to_delete = journal.prompt_action_entries(MsgText.DeleteEntryQuestion)
|
||||||
|
|
||||||
if entries_to_delete:
|
if entries_to_delete:
|
||||||
|
@ -409,7 +419,13 @@ def _delete_search_results(journal, old_entries, **kwargs):
|
||||||
journal.write()
|
journal.write()
|
||||||
|
|
||||||
|
|
||||||
def _change_time_search_results(args, journal, old_entries, no_prompt=False, **kwargs):
|
def _change_time_search_results(
|
||||||
|
args: "Namespace",
|
||||||
|
journal: Journal,
|
||||||
|
old_entries: list["Entry"],
|
||||||
|
no_prompt: bool = False,
|
||||||
|
**kwargs
|
||||||
|
) -> None:
|
||||||
# separate entries we are not editing
|
# separate entries we are not editing
|
||||||
other_entries = _other_entries(journal, old_entries)
|
other_entries = _other_entries(journal, old_entries)
|
||||||
|
|
||||||
|
@ -432,7 +448,7 @@ def _change_time_search_results(args, journal, old_entries, no_prompt=False, **k
|
||||||
journal.write()
|
journal.write()
|
||||||
|
|
||||||
|
|
||||||
def _display_search_results(args, journal, **kwargs):
|
def _display_search_results(args: "Namespace", journal: Journal, **kwargs) -> None:
|
||||||
# Get export format from config file if not provided at the command line
|
# Get export format from config file if not provided at the command line
|
||||||
args.export = args.export or kwargs["config"].get("display_format")
|
args.export = args.export or kwargs["config"].get("display_format")
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ import shlex
|
||||||
from sys import platform
|
from sys import platform
|
||||||
|
|
||||||
|
|
||||||
def on_windows():
|
def on_windows() -> bool:
|
||||||
return "win32" in platform
|
return "win32" in platform
|
||||||
|
|
||||||
|
|
||||||
def on_posix():
|
def on_posix() -> bool:
|
||||||
return not on_windows()
|
return not on_windows()
|
||||||
|
|
||||||
|
|
||||||
def split_args(args):
|
def split_args(args: str) -> list[str]:
|
||||||
"""Split arguments and add escape characters as appropriate for the OS"""
|
"""Split arguments and add escape characters as appropriate for the OS"""
|
||||||
return shlex.split(args, posix=on_posix())
|
return shlex.split(args, posix=on_posix())
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from typing import Union
|
from typing import Callable
|
||||||
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
@ -12,7 +12,9 @@ from jrnl.messages import MsgStyle
|
||||||
from jrnl.messages import MsgText
|
from jrnl.messages import MsgText
|
||||||
|
|
||||||
|
|
||||||
def deprecated_cmd(old_cmd, new_cmd, callback=None, **kwargs):
|
def deprecated_cmd(
|
||||||
|
old_cmd: str, new_cmd: str, callback: Callable | None = None, **kwargs
|
||||||
|
) -> None:
|
||||||
print_msg(
|
print_msg(
|
||||||
Message(
|
Message(
|
||||||
MsgText.DeprecatedCommand,
|
MsgText.DeprecatedCommand,
|
||||||
|
@ -25,13 +27,13 @@ def deprecated_cmd(old_cmd, new_cmd, callback=None, **kwargs):
|
||||||
callback(**kwargs)
|
callback(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def journal_list_to_json(journal_list):
|
def journal_list_to_json(journal_list: dict) -> str:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
return json.dumps(journal_list)
|
return json.dumps(journal_list)
|
||||||
|
|
||||||
|
|
||||||
def journal_list_to_yaml(journal_list):
|
def journal_list_to_yaml(journal_list: dict) -> str:
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
|
@ -41,7 +43,7 @@ def journal_list_to_yaml(journal_list):
|
||||||
return output.getvalue()
|
return output.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def journal_list_to_stdout(journal_list):
|
def journal_list_to_stdout(journal_list: dict) -> str:
|
||||||
result = f"Journals defined in config ({journal_list['config_path']})\n"
|
result = f"Journals defined in config ({journal_list['config_path']})\n"
|
||||||
ml = min(max(len(k) for k in journal_list["journals"]), 20)
|
ml = min(max(len(k) for k in journal_list["journals"]), 20)
|
||||||
for journal, cfg in journal_list["journals"].items():
|
for journal, cfg in journal_list["journals"].items():
|
||||||
|
@ -51,7 +53,7 @@ def journal_list_to_stdout(journal_list):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def list_journals(configuration, format=None):
|
def list_journals(configuration: dict, format: str | None = None) -> str:
|
||||||
from jrnl import config
|
from jrnl import config
|
||||||
|
|
||||||
"""List the journals specified in the configuration file"""
|
"""List the journals specified in the configuration file"""
|
||||||
|
@ -69,7 +71,7 @@ def list_journals(configuration, format=None):
|
||||||
return journal_list_to_stdout(journal_list)
|
return journal_list_to_stdout(journal_list)
|
||||||
|
|
||||||
|
|
||||||
def print_msg(msg: Message, **kwargs) -> Union[None, str]:
|
def print_msg(msg: Message, **kwargs) -> str | None:
|
||||||
"""Helper function to print a single message"""
|
"""Helper function to print a single message"""
|
||||||
kwargs["style"] = msg.style
|
kwargs["style"] = msg.style
|
||||||
return print_msgs([msg], **kwargs)
|
return print_msgs([msg], **kwargs)
|
||||||
|
@ -81,7 +83,7 @@ def print_msgs(
|
||||||
style: MsgStyle = MsgStyle.NORMAL,
|
style: MsgStyle = MsgStyle.NORMAL,
|
||||||
get_input: bool = False,
|
get_input: bool = False,
|
||||||
hide_input: bool = False,
|
hide_input: bool = False,
|
||||||
) -> Union[None, str]:
|
) -> str | None:
|
||||||
# Same as print_msg, but for a list
|
# Same as print_msg, but for a list
|
||||||
text = Text("", end="")
|
text = Text("", end="")
|
||||||
kwargs = style.decoration.args
|
kwargs = style.decoration.args
|
||||||
|
@ -113,7 +115,7 @@ def _get_console(stderr: bool = True) -> Console:
|
||||||
return Console(stderr=stderr)
|
return Console(stderr=stderr)
|
||||||
|
|
||||||
|
|
||||||
def _add_extra_style_args_if_needed(args, msg):
|
def _add_extra_style_args_if_needed(args: dict, msg: Message):
|
||||||
args["border_style"] = msg.style.color
|
args["border_style"] = msg.style.color
|
||||||
args["title"] = msg.style.box_title
|
args["title"] = msg.style.box_title
|
||||||
return args
|
return args
|
||||||
|
|
|
@ -37,12 +37,12 @@ def apply_overrides(args: "Namespace", base_config: dict) -> dict:
|
||||||
return base_config
|
return base_config
|
||||||
|
|
||||||
|
|
||||||
def _get_key_and_value_from_pair(pairs):
|
def _get_key_and_value_from_pair(pairs: dict) -> tuple:
|
||||||
key_as_dots, override_value = list(pairs.items())[0]
|
key_as_dots, override_value = list(pairs.items())[0]
|
||||||
return key_as_dots, override_value
|
return key_as_dots, override_value
|
||||||
|
|
||||||
|
|
||||||
def _convert_dots_to_list(key_as_dots):
|
def _convert_dots_to_list(key_as_dots: str) -> list[str]:
|
||||||
keys = key_as_dots.split(".")
|
keys = key_as_dots.split(".")
|
||||||
keys = [k for k in keys if k != ""] # remove empty elements
|
keys = [k for k in keys if k != ""] # remove empty elements
|
||||||
return keys
|
return keys
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
def home_dir():
|
def home_dir() -> str:
|
||||||
return os.path.expanduser("~")
|
return os.path.expanduser("~")
|
||||||
|
|
||||||
|
|
||||||
def expand_path(path):
|
def expand_path(path: str) -> str:
|
||||||
return os.path.expanduser(os.path.expandvars(path))
|
return os.path.expanduser(os.path.expandvars(path))
|
||||||
|
|
||||||
|
|
||||||
def absolute_path(path):
|
def absolute_path(path: str) -> str:
|
||||||
return os.path.abspath(expand_path(path))
|
return os.path.abspath(expand_path(path))
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Copyright © 2012-2022 jrnl contributors
|
# Copyright © 2012-2022 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from jrnl.plugins.dates_exporter import DatesExporter
|
from jrnl.plugins.dates_exporter import DatesExporter
|
||||||
from jrnl.plugins.fancy_exporter import FancyExporter
|
from jrnl.plugins.fancy_exporter import FancyExporter
|
||||||
from jrnl.plugins.jrnl_importer import JRNLImporter
|
from jrnl.plugins.jrnl_importer import JRNLImporter
|
||||||
|
@ -32,14 +34,14 @@ EXPORT_FORMATS = sorted(__exporter_types.keys())
|
||||||
IMPORT_FORMATS = sorted(__importer_types.keys())
|
IMPORT_FORMATS = sorted(__importer_types.keys())
|
||||||
|
|
||||||
|
|
||||||
def get_exporter(format):
|
def get_exporter(format: str) -> Type[TextExporter] | None:
|
||||||
for exporter in __exporters:
|
for exporter in __exporters:
|
||||||
if hasattr(exporter, "names") and format in exporter.names:
|
if hasattr(exporter, "names") and format in exporter.names:
|
||||||
return exporter
|
return exporter
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_importer(format):
|
def get_importer(format: str) -> Type[JRNLImporter] | None:
|
||||||
for importer in __importers:
|
for importer in __importers:
|
||||||
if hasattr(importer, "names") and format in importer.names:
|
if hasattr(importer, "names") and format in importer.names:
|
||||||
return importer
|
return importer
|
||||||
|
|
|
@ -2,9 +2,14 @@
|
||||||
# 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 typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.plugins.text_exporter import TextExporter
|
from jrnl.plugins.text_exporter import TextExporter
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class DatesExporter(TextExporter):
|
class DatesExporter(TextExporter):
|
||||||
"""This Exporter lists dates and their respective counts, for heatingmapping etc."""
|
"""This Exporter lists dates and their respective counts, for heatingmapping etc."""
|
||||||
|
@ -13,11 +18,11 @@ class DatesExporter(TextExporter):
|
||||||
extension = "dates"
|
extension = "dates"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry: "Entry"):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "Journal") -> str:
|
||||||
"""Returns dates and their frequencies for an entire journal."""
|
"""Returns dates and their frequencies for an entire journal."""
|
||||||
date_counts = Counter()
|
date_counts = Counter()
|
||||||
for entry in journal.entries:
|
for entry in journal.entries:
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.exception import JrnlException
|
from jrnl.exception import JrnlException
|
||||||
from jrnl.messages import Message
|
from jrnl.messages import Message
|
||||||
|
@ -11,6 +12,10 @@ from jrnl.messages import MsgStyle
|
||||||
from jrnl.messages import MsgText
|
from jrnl.messages import MsgText
|
||||||
from jrnl.plugins.text_exporter import TextExporter
|
from jrnl.plugins.text_exporter import TextExporter
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class FancyExporter(TextExporter):
|
class FancyExporter(TextExporter):
|
||||||
"""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."""
|
||||||
|
@ -35,7 +40,7 @@ class FancyExporter(TextExporter):
|
||||||
border_m = "┘"
|
border_m = "┘"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry: "Entry") -> str:
|
||||||
"""Returns a fancy unicode representation of a single entry."""
|
"""Returns a fancy unicode representation of a single entry."""
|
||||||
date_str = entry.date.strftime(entry.journal.config["timeformat"])
|
date_str = entry.date.strftime(entry.journal.config["timeformat"])
|
||||||
|
|
||||||
|
@ -95,12 +100,12 @@ class FancyExporter(TextExporter):
|
||||||
return "\n".join(card)
|
return "\n".join(card)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal) -> str:
|
||||||
"""Returns a unicode representation of an entire journal."""
|
"""Returns a unicode representation of an entire journal."""
|
||||||
return "\n".join(cls.export_entry(entry) for entry in journal)
|
return "\n".join(cls.export_entry(entry) for entry in journal)
|
||||||
|
|
||||||
|
|
||||||
def check_provided_linewrap_viability(linewrap, card, journal):
|
def check_provided_linewrap_viability(linewrap: int, card: list[str], journal: "Journal"):
|
||||||
if len(card[0]) > linewrap:
|
if len(card[0]) > linewrap:
|
||||||
width_violation = len(card[0]) - linewrap
|
width_violation = len(card[0]) - linewrap
|
||||||
raise JrnlException(
|
raise JrnlException(
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.exception import JrnlException
|
from jrnl.exception import JrnlException
|
||||||
from jrnl.messages import Message
|
from jrnl.messages import Message
|
||||||
|
@ -9,6 +10,9 @@ from jrnl.messages import MsgStyle
|
||||||
from jrnl.messages import MsgText
|
from jrnl.messages import MsgText
|
||||||
from jrnl.output import print_msg
|
from jrnl.output import print_msg
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class JRNLImporter:
|
class JRNLImporter:
|
||||||
"""This plugin imports entries from other jrnl files."""
|
"""This plugin imports entries from other jrnl files."""
|
||||||
|
@ -16,7 +20,7 @@ class JRNLImporter:
|
||||||
names = ["jrnl"]
|
names = ["jrnl"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def import_(journal, input=None):
|
def import_(journal: "Journal", input: str | None = None) -> None:
|
||||||
"""Imports from an existing file if input is specified, and
|
"""Imports from an existing file if input is specified, and
|
||||||
standard input otherwise."""
|
standard input otherwise."""
|
||||||
old_cnt = len(journal.entries)
|
old_cnt = len(journal.entries)
|
||||||
|
|
|
@ -2,10 +2,15 @@
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.plugins.text_exporter import TextExporter
|
from jrnl.plugins.text_exporter import TextExporter
|
||||||
from jrnl.plugins.util import get_tags_count
|
from jrnl.plugins.util import get_tags_count
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class JSONExporter(TextExporter):
|
class JSONExporter(TextExporter):
|
||||||
"""This Exporter can convert entries and journals into json."""
|
"""This Exporter can convert entries and journals into json."""
|
||||||
|
@ -14,7 +19,7 @@ class JSONExporter(TextExporter):
|
||||||
extension = "json"
|
extension = "json"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def entry_to_dict(cls, entry):
|
def entry_to_dict(cls, entry: "Entry") -> dict:
|
||||||
entry_dict = {
|
entry_dict = {
|
||||||
"title": entry.title,
|
"title": entry.title,
|
||||||
"body": entry.body,
|
"body": entry.body,
|
||||||
|
@ -49,12 +54,12 @@ class JSONExporter(TextExporter):
|
||||||
return entry_dict
|
return entry_dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry: "Entry") -> str:
|
||||||
"""Returns a json representation of a single entry."""
|
"""Returns a json representation of a single entry."""
|
||||||
return json.dumps(cls.entry_to_dict(entry), indent=2) + "\n"
|
return json.dumps(cls.entry_to_dict(entry), indent=2) + "\n"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "Journal") -> str:
|
||||||
"""Returns a json representation of an entire journal."""
|
"""Returns a json representation of an entire journal."""
|
||||||
tags = get_tags_count(journal)
|
tags = get_tags_count(journal)
|
||||||
result = {
|
result = {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.messages import Message
|
from jrnl.messages import Message
|
||||||
from jrnl.messages import MsgStyle
|
from jrnl.messages import MsgStyle
|
||||||
|
@ -10,6 +11,10 @@ from jrnl.messages import MsgText
|
||||||
from jrnl.output import print_msg
|
from jrnl.output import print_msg
|
||||||
from jrnl.plugins.text_exporter import TextExporter
|
from jrnl.plugins.text_exporter import TextExporter
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class MarkdownExporter(TextExporter):
|
class MarkdownExporter(TextExporter):
|
||||||
"""This Exporter can convert entries and journals into Markdown."""
|
"""This Exporter can convert entries and journals into Markdown."""
|
||||||
|
@ -18,7 +23,7 @@ class MarkdownExporter(TextExporter):
|
||||||
extension = "md"
|
extension = "md"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry, to_multifile=True):
|
def export_entry(cls, entry: "Entry", to_multifile: bool = True) -> str:
|
||||||
"""Returns a markdown representation of a single entry."""
|
"""Returns a markdown representation of a single entry."""
|
||||||
date_str = entry.date.strftime(entry.journal.config["timeformat"])
|
date_str = entry.date.strftime(entry.journal.config["timeformat"])
|
||||||
body_wrapper = "\n" if entry.body else ""
|
body_wrapper = "\n" if entry.body else ""
|
||||||
|
@ -73,7 +78,7 @@ class MarkdownExporter(TextExporter):
|
||||||
return f"{heading} {date_str} {entry.title}\n{newbody} "
|
return f"{heading} {date_str} {entry.title}\n{newbody} "
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "Journal") -> str:
|
||||||
"""Returns a Markdown representation of an entire journal."""
|
"""Returns a Markdown representation of an entire journal."""
|
||||||
out = []
|
out = []
|
||||||
year, month = -1, -1
|
year, month = -1, -1
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
# Copyright © 2012-2022 jrnl contributors
|
# Copyright © 2012-2022 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.plugins.text_exporter import TextExporter
|
from jrnl.plugins.text_exporter import TextExporter
|
||||||
from jrnl.plugins.util import get_tags_count
|
from jrnl.plugins.util import get_tags_count
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class TagExporter(TextExporter):
|
class TagExporter(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."""
|
||||||
|
@ -12,12 +18,12 @@ class TagExporter(TextExporter):
|
||||||
extension = "tags"
|
extension = "tags"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry: "Entry") -> str:
|
||||||
"""Returns a list of tags for a single entry."""
|
"""Returns a list of tags for a single entry."""
|
||||||
return ", ".join(entry.tags)
|
return ", ".join(entry.tags)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "Journal") -> str:
|
||||||
"""Returns a list of tags and their frequency for an entire journal."""
|
"""Returns a list of tags and their frequency for an entire journal."""
|
||||||
tag_counts = get_tags_count(journal)
|
tag_counts = get_tags_count(journal)
|
||||||
result = ""
|
result = ""
|
||||||
|
|
|
@ -5,12 +5,17 @@ import errno
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.messages import Message
|
from jrnl.messages import Message
|
||||||
from jrnl.messages import MsgStyle
|
from jrnl.messages import MsgStyle
|
||||||
from jrnl.messages import MsgText
|
from jrnl.messages import MsgText
|
||||||
from jrnl.output import print_msg
|
from jrnl.output import print_msg
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class TextExporter:
|
class TextExporter:
|
||||||
"""This Exporter can convert entries and journals into text files."""
|
"""This Exporter can convert entries and journals into text files."""
|
||||||
|
@ -19,17 +24,17 @@ class TextExporter:
|
||||||
extension = "txt"
|
extension = "txt"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry):
|
def export_entry(cls, entry: "Entry") -> str:
|
||||||
"""Returns a string representation of a single entry."""
|
"""Returns a string representation of a single entry."""
|
||||||
return str(entry)
|
return str(entry)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "Journal") -> str:
|
||||||
"""Returns a string representation of an entire journal."""
|
"""Returns a string representation of an entire journal."""
|
||||||
return "\n".join(cls.export_entry(entry) for entry in journal)
|
return "\n".join(cls.export_entry(entry) for entry in journal)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write_file(cls, journal, path):
|
def write_file(cls, journal: "Journal", path: str) -> str:
|
||||||
"""Exports a journal into a single file."""
|
"""Exports a journal into a single file."""
|
||||||
export_str = cls.export_journal(journal)
|
export_str = cls.export_journal(journal)
|
||||||
with open(path, "w", encoding="utf-8") as f:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
@ -46,13 +51,13 @@ class TextExporter:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_filename(cls, entry):
|
def make_filename(cls, entry: "Entry") -> str:
|
||||||
return entry.date.strftime("%Y-%m-%d") + "_{}.{}".format(
|
return entry.date.strftime("%Y-%m-%d") + "_{}.{}".format(
|
||||||
cls._slugify(str(entry.title)), cls.extension
|
cls._slugify(str(entry.title)), cls.extension
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write_files(cls, journal, path):
|
def write_files(cls, journal: "Journal", path: str) -> str:
|
||||||
"""Exports a journal into individual files for each entry."""
|
"""Exports a journal into individual files for each entry."""
|
||||||
for entry in journal.entries:
|
for entry in journal.entries:
|
||||||
entry_is_written = False
|
entry_is_written = False
|
||||||
|
@ -82,7 +87,7 @@ class TextExporter:
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _slugify(string):
|
def _slugify(string: str) -> str:
|
||||||
"""Slugifies a string.
|
"""Slugifies a string.
|
||||||
Based on public domain code from https://github.com/zacharyvoase/slugify
|
Based on public domain code from https://github.com/zacharyvoase/slugify
|
||||||
"""
|
"""
|
||||||
|
@ -92,7 +97,7 @@ class TextExporter:
|
||||||
return slug
|
return slug
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export(cls, journal, output=None):
|
def export(cls, journal: "Journal", output: str | None = None) -> str:
|
||||||
"""Exports to individual files if output is an existing path, or into
|
"""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
|
a single file if output is a file name, or returns the exporter's
|
||||||
representation as string if output is None."""
|
representation as string if output is None."""
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
# Copyright © 2012-2022 jrnl contributors
|
# Copyright © 2012-2022 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
def get_tags_count(journal):
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
|
def get_tags_count(journal: "Journal") -> set[tuple[int, str]]:
|
||||||
"""Returns a set of tuples (count, tag) for all tags present in the journal."""
|
"""Returns a set of tuples (count, tag) for all tags present in the journal."""
|
||||||
# Astute reader: should the following line leave you as puzzled as me the first time
|
# Astute reader: should the following line leave you as puzzled as me the first time
|
||||||
# I came across this construction, worry not and embrace the ensuing moment of enlightment.
|
# I came across this construction, worry not and embrace the ensuing moment of enlightment.
|
||||||
|
@ -12,7 +17,7 @@ def get_tags_count(journal):
|
||||||
return tag_counts
|
return tag_counts
|
||||||
|
|
||||||
|
|
||||||
def oxford_list(lst):
|
def oxford_list(lst: list) -> str:
|
||||||
"""Return Human-readable list of things obeying the object comma)"""
|
"""Return Human-readable list of things obeying the object comma)"""
|
||||||
lst = sorted(lst)
|
lst = sorted(lst)
|
||||||
if not lst:
|
if not lst:
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
# Copyright © 2012-2022 jrnl contributors
|
# Copyright © 2012-2022 jrnl contributors
|
||||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
|
|
||||||
from jrnl.plugins.json_exporter import JSONExporter
|
from jrnl.plugins.json_exporter import JSONExporter
|
||||||
from jrnl.plugins.util import get_tags_count
|
from jrnl.plugins.util import get_tags_count
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class XMLExporter(JSONExporter):
|
class XMLExporter(JSONExporter):
|
||||||
"""This Exporter can convert entries and journals into XML."""
|
"""This Exporter can convert entries and journals into XML."""
|
||||||
|
@ -14,7 +19,9 @@ class XMLExporter(JSONExporter):
|
||||||
extension = "xml"
|
extension = "xml"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry, doc=None):
|
def export_entry(
|
||||||
|
cls, entry: "Entry", doc: minidom.Document | None = None
|
||||||
|
) -> minidom.Element | str:
|
||||||
"""Returns an XML representation of a single entry."""
|
"""Returns an XML representation of a single entry."""
|
||||||
doc_el = doc or minidom.Document()
|
doc_el = doc or minidom.Document()
|
||||||
entry_el = doc_el.createElement("entry")
|
entry_el = doc_el.createElement("entry")
|
||||||
|
@ -29,7 +36,7 @@ class XMLExporter(JSONExporter):
|
||||||
return entry_el
|
return entry_el
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def entry_to_xml(cls, entry, doc):
|
def entry_to_xml(cls, entry: "Entry", doc: minidom.Document) -> minidom.Element:
|
||||||
entry_el = doc.createElement("entry")
|
entry_el = doc.createElement("entry")
|
||||||
entry_el.setAttribute("date", entry.date.isoformat())
|
entry_el.setAttribute("date", entry.date.isoformat())
|
||||||
if hasattr(entry, "uuid"):
|
if hasattr(entry, "uuid"):
|
||||||
|
@ -44,7 +51,7 @@ class XMLExporter(JSONExporter):
|
||||||
return entry_el
|
return entry_el
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "Journal") -> str:
|
||||||
"""Returns an XML representation of an entire journal."""
|
"""Returns an XML representation of an entire journal."""
|
||||||
tags = get_tags_count(journal)
|
tags = get_tags_count(journal)
|
||||||
doc = minidom.Document()
|
doc = minidom.Document()
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from jrnl.exception import JrnlException
|
from jrnl.exception import JrnlException
|
||||||
from jrnl.messages import Message
|
from jrnl.messages import Message
|
||||||
|
@ -11,6 +12,10 @@ from jrnl.messages import MsgText
|
||||||
from jrnl.output import print_msg
|
from jrnl.output import print_msg
|
||||||
from jrnl.plugins.text_exporter import TextExporter
|
from jrnl.plugins.text_exporter import TextExporter
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jrnl.Entry import Entry
|
||||||
|
from jrnl.Journal import Journal
|
||||||
|
|
||||||
|
|
||||||
class YAMLExporter(TextExporter):
|
class YAMLExporter(TextExporter):
|
||||||
"""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."""
|
||||||
|
@ -19,7 +24,7 @@ class YAMLExporter(TextExporter):
|
||||||
extension = "md"
|
extension = "md"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_entry(cls, entry, to_multifile=True):
|
def export_entry(cls, entry: "Entry", to_multifile: bool = True) -> str:
|
||||||
"""Returns a markdown representation of a single entry, with YAML front matter."""
|
"""Returns a markdown representation of a single entry, with YAML front matter."""
|
||||||
if to_multifile is False:
|
if to_multifile is False:
|
||||||
raise JrnlException(Message(MsgText.YamlMustBeDirectory, MsgStyle.ERROR))
|
raise JrnlException(Message(MsgText.YamlMustBeDirectory, MsgStyle.ERROR))
|
||||||
|
@ -124,6 +129,6 @@ class YAMLExporter(TextExporter):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_journal(cls, journal):
|
def export_journal(cls, journal: "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."""
|
||||||
raise JrnlException(Message(MsgText.YamlMustBeDirectory, MsgStyle.ERROR))
|
raise JrnlException(Message(MsgText.YamlMustBeDirectory, MsgStyle.ERROR))
|
||||||
|
|
|
@ -42,7 +42,7 @@ def create_password(journal_name: str) -> str:
|
||||||
return pw
|
return pw
|
||||||
|
|
||||||
|
|
||||||
def yesno(prompt: Message, default: bool = True) -> bool:
|
def yesno(prompt: Message | str, default: bool = True) -> bool:
|
||||||
response = print_msgs(
|
response = print_msgs(
|
||||||
[
|
[
|
||||||
prompt,
|
prompt,
|
||||||
|
|
|
@ -22,8 +22,12 @@ def __get_pdt_calendar():
|
||||||
|
|
||||||
|
|
||||||
def parse(
|
def parse(
|
||||||
date_str, inclusive=False, default_hour=None, default_minute=None, bracketed=False
|
date_str: str | datetime.datetime,
|
||||||
):
|
inclusive: bool = False,
|
||||||
|
default_hour: int | None = None,
|
||||||
|
default_minute: int | None = None,
|
||||||
|
bracketed: bool = False,
|
||||||
|
) -> datetime.datetime | None:
|
||||||
"""Parses a string containing a fuzzy date and returns a datetime.datetime object"""
|
"""Parses a string containing a fuzzy date and returns a datetime.datetime object"""
|
||||||
if not date_str:
|
if not date_str:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -19,7 +19,7 @@ from jrnl.path import expand_path
|
||||||
from jrnl.prompt import yesno
|
from jrnl.prompt import yesno
|
||||||
|
|
||||||
|
|
||||||
def backup(filename, binary=False):
|
def backup(filename: str, binary: bool = False):
|
||||||
filename = expand_path(filename)
|
filename = expand_path(filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -42,14 +42,14 @@ def backup(filename, binary=False):
|
||||||
raise JrnlException(Message(MsgText.UpgradeAborted, MsgStyle.WARNING))
|
raise JrnlException(Message(MsgText.UpgradeAborted, MsgStyle.WARNING))
|
||||||
|
|
||||||
|
|
||||||
def check_exists(path):
|
def check_exists(path: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if a given path exists.
|
Checks if a given path exists.
|
||||||
"""
|
"""
|
||||||
return os.path.exists(path)
|
return os.path.exists(path)
|
||||||
|
|
||||||
|
|
||||||
def upgrade_jrnl(config_path):
|
def upgrade_jrnl(config_path: str) -> None:
|
||||||
config = load_config(config_path)
|
config = load_config(config_path)
|
||||||
|
|
||||||
print_msg(Message(MsgText.WelcomeToJrnl, MsgStyle.NORMAL, {"version": __version__}))
|
print_msg(Message(MsgText.WelcomeToJrnl, MsgStyle.NORMAL, {"version": __version__}))
|
||||||
|
@ -115,7 +115,7 @@ def upgrade_jrnl(config_path):
|
||||||
|
|
||||||
cont = yesno(Message(MsgText.ContinueUpgrade), default=False)
|
cont = yesno(Message(MsgText.ContinueUpgrade), default=False)
|
||||||
if not cont:
|
if not cont:
|
||||||
raise JrnlException(Message(MsgText.UpgradeAborted), MsgStyle.WARNING)
|
raise JrnlException(Message(MsgText.UpgradeAborted, MsgStyle.WARNING))
|
||||||
|
|
||||||
for journal_name, path in encrypted_journals.items():
|
for journal_name, path in encrypted_journals.items():
|
||||||
print_msg(
|
print_msg(
|
||||||
|
@ -178,7 +178,7 @@ def upgrade_jrnl(config_path):
|
||||||
print_msg(Message(MsgText.AllDoneUpgrade, MsgStyle.NORMAL))
|
print_msg(Message(MsgText.AllDoneUpgrade, MsgStyle.NORMAL))
|
||||||
|
|
||||||
|
|
||||||
def is_old_version(config_path):
|
def is_old_version(config_path: str) -> bool:
|
||||||
return is_config_json(config_path)
|
return is_config_json(config_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue