mirror of
https://github.com/natelandau/obsidian-metadata.git
synced 2025-11-16 17:03:48 -05:00
* feat: add custom exceptions to metadata creation * refactor: utility function for finding inline metadata in content * fix: use InlineTagError for exceptions parsing inline tags * fix: improve error messages * build(deps): bump dependencies * fix: use BadParameter exception when appropriate
173 lines
5.6 KiB
Python
173 lines
5.6 KiB
Python
"""obsidian-metadata CLI."""
|
|
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
import questionary
|
|
import typer
|
|
|
|
from obsidian_metadata._config import Config
|
|
from obsidian_metadata._utils import (
|
|
alerts,
|
|
clear_screen,
|
|
docstring_parameter,
|
|
version_callback,
|
|
)
|
|
from obsidian_metadata._utils.console import console
|
|
from obsidian_metadata.models import Application
|
|
|
|
app = typer.Typer(add_completion=False, no_args_is_help=True, rich_markup_mode="rich")
|
|
|
|
typer.rich_utils.STYLE_HELPTEXT = ""
|
|
|
|
HELP_TEXT = """
|
|
"""
|
|
|
|
|
|
@app.command()
|
|
@docstring_parameter(__package__)
|
|
def main(
|
|
config_file: Path = typer.Option(
|
|
Path(Path.home() / f".{__package__}.toml"),
|
|
help="Specify a custom path to a configuration file",
|
|
show_default=False,
|
|
),
|
|
export_csv: Path = typer.Option(
|
|
None,
|
|
help="Exports all metadata to a specified CSV file and exits.",
|
|
show_default=False,
|
|
dir_okay=False,
|
|
file_okay=True,
|
|
),
|
|
export_json: Path = typer.Option(
|
|
None,
|
|
help="Exports all metadata to a specified JSON file and exits.",
|
|
show_default=False,
|
|
dir_okay=False,
|
|
file_okay=True,
|
|
),
|
|
export_template: Path = typer.Option(
|
|
None,
|
|
help="Exports all notes and their metadata to a specified CSV file and exits. Use to create a template for batch updates.",
|
|
show_default=False,
|
|
dir_okay=False,
|
|
file_okay=True,
|
|
),
|
|
import_csv: Path = typer.Option(
|
|
None,
|
|
help="Import a CSV file with bulk updates to metadata.",
|
|
show_default=False,
|
|
dir_okay=False,
|
|
file_okay=True,
|
|
),
|
|
vault_path: Path = typer.Option(
|
|
None,
|
|
help="Path to Obsidian vault",
|
|
show_default=False,
|
|
),
|
|
dry_run: bool = typer.Option(
|
|
False,
|
|
"--dry-run",
|
|
"-n",
|
|
help="Dry run - don't actually change anything",
|
|
),
|
|
log_file: Path = typer.Option(
|
|
Path(Path.home() / "logs" / "obsidian_metadata.log"),
|
|
help="Path to log file",
|
|
show_default=True,
|
|
dir_okay=False,
|
|
file_okay=True,
|
|
exists=False,
|
|
),
|
|
log_to_file: bool = typer.Option(
|
|
False,
|
|
"--log-to-file",
|
|
help="Log to file",
|
|
show_default=True,
|
|
),
|
|
verbosity: int = typer.Option(
|
|
0,
|
|
"-v",
|
|
"--verbose",
|
|
show_default=False,
|
|
help="""Set verbosity level (0=WARN, 1=INFO, 2=DEBUG, 3=TRACE)""",
|
|
count=True,
|
|
),
|
|
version: Optional[bool] = typer.Option( # noqa: ARG001
|
|
None, "--version", help="Print version and exit", callback=version_callback, is_eager=True
|
|
),
|
|
) -> None:
|
|
r"""Make batch updates to metadata in an Obsidian vault. No changes are made to the Vault until they are explicitly committed.
|
|
|
|
[bold] [/]
|
|
[bold underline]It is strongly recommended that you back up your vault prior to committing changes.[/] This script makes changes directly to the markdown files in your vault. Once the changes are committed, there is no ability to recreate the original information unless you have a backup. Follow the instructions in the script to create a backup of your vault if needed. The author of this script is not responsible for any data loss that may occur. Use at your own risk.
|
|
|
|
[bold underline]Configuration:[/]
|
|
Configuration is specified in a configuration file. On First run, this file will be created at [tan]~/.{0}.env[/]. Any options specified on the command line will override the configuration file.
|
|
|
|
Full usage information is available at https://github.com/natelandau/obsidian-metadata
|
|
|
|
"""
|
|
# Instantiate logger
|
|
alerts.LoggerManager( # pragma: no cover
|
|
log_file,
|
|
verbosity,
|
|
log_to_file,
|
|
)
|
|
|
|
banner = r"""
|
|
___ _ _ _ _
|
|
/ _ \| |__ ___(_) __| (_) __ _ _ __
|
|
| | | | '_ \/ __| |/ _` | |/ _` | '_ \
|
|
| |_| | |_) \__ \ | (_| | | (_| | | | |
|
|
\___/|_.__/|___/_|\__,_|_|\__,_|_| |_|
|
|
| \/ | ___| |_ __ _ __| | __ _| |_ __ _
|
|
| |\/| |/ _ \ __/ _` |/ _` |/ _` | __/ _` |
|
|
| | | | __/ || (_| | (_| | (_| | || (_| |
|
|
|_| |_|\___|\__\__,_|\__,_|\__,_|\__\__,_|
|
|
"""
|
|
clear_screen()
|
|
console.print(banner)
|
|
|
|
config: Config = Config(config_path=config_file, vault_path=vault_path)
|
|
if len(config.vaults) == 0:
|
|
typer.echo("No vaults configured. Exiting.")
|
|
raise typer.BadParameter("No vaults configured. Exiting.")
|
|
|
|
if len(config.vaults) == 1:
|
|
application = Application(dry_run=dry_run, config=config.vaults[0])
|
|
else:
|
|
vault_names = [vault.name for vault in config.vaults]
|
|
vault_name = questionary.select(
|
|
"Select a vault to process:",
|
|
choices=vault_names,
|
|
).ask()
|
|
if vault_name is None:
|
|
raise typer.Exit(code=1)
|
|
|
|
vault_to_use = next(vault for vault in config.vaults if vault.name == vault_name)
|
|
application = Application(dry_run=dry_run, config=vault_to_use)
|
|
|
|
if export_json is not None:
|
|
path = Path(export_json).expanduser().resolve()
|
|
application.noninteractive_export_json(path)
|
|
raise typer.Exit(code=0)
|
|
if export_csv is not None:
|
|
path = Path(export_json).expanduser().resolve()
|
|
application.noninteractive_export_csv(path)
|
|
raise typer.Exit(code=0)
|
|
if export_template is not None:
|
|
path = Path(export_template).expanduser().resolve()
|
|
application.noninteractive_export_template(path)
|
|
raise typer.Exit(code=0)
|
|
if import_csv is not None:
|
|
path = Path(import_csv).expanduser().resolve()
|
|
application.noninteractive_bulk_import(path)
|
|
raise typer.Exit(code=0)
|
|
|
|
application.application_main()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app()
|