138 lines
4 KiB
Python
Executable file
138 lines
4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
from typer import Typer
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
from tempfile import NamedTemporaryFile
|
|
from datetime import datetime
|
|
from sh import age
|
|
from halig.config import (
|
|
get_example_config,
|
|
get_config,
|
|
DEFAULT_CONFIGURATION_PATH,
|
|
Config,
|
|
)
|
|
from halig.exceptions import NoteDoesNotExist
|
|
import subprocess
|
|
import os
|
|
import yaml
|
|
from halig.exceptions import handle_errors
|
|
from halig import logger
|
|
|
|
app = Typer(pretty_exceptions_enable=False)
|
|
|
|
|
|
TODAY = f"{datetime.now():%Y-%m-%d}"
|
|
TODAY_NOTE = f"{TODAY}.norg.age"
|
|
DEFAULT_NEORG_METADATA = f"""@document.meta
|
|
title: {TODAY}
|
|
description: {TODAY}
|
|
authors: {os.getenv("USER")}
|
|
categories: [
|
|
|
|
]
|
|
created: {TODAY}
|
|
version: 1.0.0
|
|
@end
|
|
|
|
"""
|
|
|
|
|
|
def _wait_for_editor(path):
|
|
subprocess.call([os.environ.get("EDITOR") or "vim", path])
|
|
|
|
|
|
def _edit_note(path: Path, config: Config):
|
|
path = path.expanduser()
|
|
if not path.exists():
|
|
raise NoteDoesNotExist
|
|
if path.is_dir():
|
|
path = path / f"{TODAY}.norg.age"
|
|
|
|
note_contents = age("-d", "-i", config.encryption_keys.private_key, path)
|
|
with NamedTemporaryFile(delete=False) as tmpfile:
|
|
tmpfile.write(str(note_contents).encode())
|
|
_wait_for_editor(tmpfile.name)
|
|
logger.info(f"encrypting updates into {path}")
|
|
age("-R", str(config.encryption_keys.public_key), tmpfile.name, _out=str(path))
|
|
Path(tmpfile.name).unlink()
|
|
|
|
|
|
def _get_template_data(path: Path) -> str | None:
|
|
template_path = path / "template.norg"
|
|
if not (template_path.exists() and template_path.is_file()):
|
|
return None
|
|
|
|
with open(template_path) as f:
|
|
return f.read()
|
|
|
|
|
|
def _new_note(path: Path, config: Config):
|
|
with NamedTemporaryFile(delete=False) as tempfile:
|
|
tempfile.write((_get_template_data(path) or DEFAULT_NEORG_METADATA).encode())
|
|
subprocess.call([os.environ.get("EDITOR") or "vim", tempfile.name])
|
|
file = Path(path / f"{TODAY_NOTE}")
|
|
file.touch(exist_ok=True)
|
|
print(f"age-encrypting {tempfile.name}")
|
|
age("-R", str(config.encryption_keys.public_key), tempfile.name, _out=str(file))
|
|
Path(tempfile.name).unlink()
|
|
|
|
|
|
@app.command()
|
|
def init(force_recreate: bool = False):
|
|
"""Create config file. If the config file already exists, it'll not
|
|
be overwritten unless `--force-recreate` flag is provided
|
|
"""
|
|
if DEFAULT_CONFIGURATION_PATH.exists() and not force_recreate:
|
|
logger.error(
|
|
"""$HOME/.config/halig/halig.yml already exists.
|
|
|
|
Execute again with --force-recreate in order to replace the configuration file's
|
|
contents with the default one"""
|
|
)
|
|
exit(1)
|
|
|
|
DEFAULT_CONFIGURATION_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
DEFAULT_CONFIGURATION_PATH.touch(mode=0o700, exist_ok=True)
|
|
with open(DEFAULT_CONFIGURATION_PATH, "w") as f:
|
|
yaml.dump(get_example_config().dict(), f, Dumper=yaml.SafeDumper)
|
|
|
|
|
|
@app.command()
|
|
@handle_errors
|
|
def notebooks(
|
|
print_files: bool = False,
|
|
print_hidden: bool = False,
|
|
configuration_path: Optional[Path] = None,
|
|
):
|
|
"""Print notebooks and their contents, tree-style"""
|
|
config = get_config(configuration_path)
|
|
logger.tree(
|
|
config.notes_root_path, print_files=print_files, print_hidden=print_hidden
|
|
)
|
|
|
|
|
|
@app.command()
|
|
@handle_errors
|
|
def edit(path: Path, configuration_path: Optional[Path] = None):
|
|
"""Edit a new or existing file by providing a path. Note that if only
|
|
a dir is provided, an attempt to create or open `<dir>/<current date>.norg.age`
|
|
will be made"""
|
|
config = get_config(configuration_path)
|
|
path = Path(path)
|
|
|
|
if not path.is_absolute():
|
|
path = config.notes_root_path / path
|
|
|
|
if not path.exists():
|
|
logger.error(f"{path} does not exist; available notebooks:\n")
|
|
logger.tree(config.notes_root_path)
|
|
exit(1)
|
|
if path.is_file() or any(
|
|
filter(lambda f: f.name == f"{TODAY_NOTE}", path.iterdir()) # type: ignore
|
|
):
|
|
_edit_note(path, config)
|
|
return
|
|
_new_note(path, config)
|
|
|
|
|
|
app()
|