feat: add --export-template cli option

This commit is contained in:
Nathaniel Landau
2023-03-21 17:40:13 -04:00
parent 4e053bda29
commit 08999cb055
6 changed files with 61 additions and 25 deletions

View File

@@ -27,6 +27,7 @@ pip install obsidian-metadata
- `--dry-run`: Make no destructive changes - `--dry-run`: Make no destructive changes
- `--export-csv`: Specify a path and create a CSV export of all metadata - `--export-csv`: Specify a path and create a CSV export of all metadata
- `--export-json`: Specify a path and create a JSON export of all metadata - `--export-json`: Specify a path and create a JSON export of all metadata
- `--export-template`: Specify a path and export all notes with their associated metadata to a CSV file for use as a bulk import template
- `--help`: Shows interactive help and exits - `--help`: Shows interactive help and exits
- `--log-file`: Specify a log file location - `--log-file`: Specify a log file location
- `--log-to-file`: Will log to a file - `--log-to-file`: Will log to a file
@@ -64,7 +65,7 @@ Once installed, run `obsidian-metadata` in your terminal to enter an interactive
- **List and clear filters**: List all current filters and clear one or all - **List and clear filters**: List all current filters and clear one or all
- **List notes in scope**: List notes that will be processed. - **List notes in scope**: List notes that will be processed.
**Bulk Edit Metadata** from a CSV file (See the _making bulk edits_ section below) **Bulk Edit Metadata** from a CSV file (See the _[Make Bulk Updates](https://github.com/natelandau/obsidian-metadata#make-bulk-updates)_ section below)
**Add Metadata**: Add new metadata to your vault. **Add Metadata**: Add new metadata to your vault.
@@ -139,7 +140,7 @@ Below is an example with two vaults.
To bypass the configuration file and specify a vault to use at runtime use the `--vault-path` option. To bypass the configuration file and specify a vault to use at runtime use the `--vault-path` option.
### Making bulk edits ### Make Bulk Updates
Bulk edits are supported by importing a CSV file containing the following columns. Column headers must be lowercase. Bulk edits are supported by importing a CSV file containing the following columns. Column headers must be lowercase.
@@ -167,7 +168,10 @@ How bulk imports work:
- **Existing metadata in a matching note will be rewritten**. This may result in it's location and/or formatting within the note being changed - **Existing metadata in a matching note will be rewritten**. This may result in it's location and/or formatting within the note being changed
- Inline tags ignore any value added to the `key` column - Inline tags ignore any value added to the `key` column
You can export all your notes with their associated metadata in this format from the "Export Metadata" section of the script to be used as a template for your bulk changes. Create a CSV template for making bulk updates containing all your notes and their associated metadata by
1. Using the `--export-template` cli command; or
2. Selecting the `Metadata by note` option within the `Export Metadata` section of the app
# Contributing # Contributing

28
poetry.lock generated
View File

@@ -4,7 +4,7 @@
name = "argcomplete" name = "argcomplete"
version = "2.0.6" version = "2.0.6"
description = "Bash tab completion for argparse" description = "Bash tab completion for argparse"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -100,7 +100,7 @@ files = [
name = "charset-normalizer" name = "charset-normalizer"
version = "2.1.1" version = "2.1.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6.0" python-versions = ">=3.6.0"
files = [ files = [
@@ -142,7 +142,7 @@ files = [
name = "commitizen" name = "commitizen"
version = "2.42.1" version = "2.42.1"
description = "Python commitizen client tool" description = "Python commitizen client tool"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6.2,<4.0.0" python-versions = ">=3.6.2,<4.0.0"
files = [ files = [
@@ -231,7 +231,7 @@ toml = ["tomli"]
name = "decli" name = "decli"
version = "0.5.2" version = "0.5.2"
description = "Minimal, easy-to-use, declarative cli tool" description = "Minimal, easy-to-use, declarative cli tool"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -354,7 +354,7 @@ tests = ["pytest", "pytest-cov", "pytest-mock"]
name = "jinja2" name = "jinja2"
version = "3.1.2" version = "3.1.2"
description = "A very fast and expressive template engine." description = "A very fast and expressive template engine."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -416,7 +416,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
name = "markupsafe" name = "markupsafe"
version = "2.1.2" version = "2.1.2"
description = "Safely add untrusted strings to HTML/XML markup." description = "Safely add untrusted strings to HTML/XML markup."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -562,7 +562,7 @@ setuptools = "*"
name = "packaging" name = "packaging"
version = "23.0" version = "23.0"
description = "Core utilities for Python packages" description = "Core utilities for Python packages"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -596,14 +596,14 @@ files = [
[[package]] [[package]]
name = "pdoc" name = "pdoc"
version = "13.0.0" version = "13.0.1"
description = "API Documentation for Python Projects" description = "API Documentation for Python Projects"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pdoc-13.0.0-py3-none-any.whl", hash = "sha256:f9088b1c10f3296f46a08796e05e307470af5f4253f71d536781f6c305baf912"}, {file = "pdoc-13.0.1-py3-none-any.whl", hash = "sha256:16a24914280ed318896ad798674e2b0d11832297fdea95632fa472e3d171e247"},
{file = "pdoc-13.0.0.tar.gz", hash = "sha256:aadbf6c757c6e65c4754d6c26c4eb6c1bf8c7a9fb893f1fbe5a7b879dde59e46"}, {file = "pdoc-13.0.1.tar.gz", hash = "sha256:4d84056847728203b8789ca8a8d0c8003f25002b3caef3365f6f21a1e4228a1b"},
] ]
[package.dependencies] [package.dependencies]
@@ -839,7 +839,7 @@ testing = ["filelock"]
name = "pyyaml" name = "pyyaml"
version = "6.0" version = "6.0"
description = "YAML parser and emitter for Python" description = "YAML parser and emitter for Python"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@@ -1172,7 +1172,7 @@ widechars = ["wcwidth"]
name = "termcolor" name = "termcolor"
version = "2.2.0" version = "2.2.0"
description = "ANSI color formatting for output in terminal" description = "ANSI color formatting for output in terminal"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1275,7 +1275,7 @@ files = [
name = "typing-extensions" name = "typing-extensions"
version = "4.5.0" version = "4.5.0"
description = "Backported and Experimental Type Hints for Python 3.7+" description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@@ -1349,4 +1349,4 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "e9e2ff35a5ae15991d1d123dffa9f15fdf5afaf00624c26577412555d0464eaf" content-hash = "8fa62f96cc77eac773497573dcbdd5666173cbec56374fea73a814f3fb7f5338"

View File

@@ -26,7 +26,6 @@
shellingham = "^1.5.0.post1" shellingham = "^1.5.0.post1"
tomlkit = "^0.11.6" tomlkit = "^0.11.6"
typer = "^0.7.0" typer = "^0.7.0"
commitizen = "^2.42.1"
[tool.poetry.group.test.dependencies] [tool.poetry.group.test.dependencies]
pytest = "^7.2.2" pytest = "^7.2.2"
@@ -41,14 +40,14 @@
coverage = "^7.2.2" coverage = "^7.2.2"
interrogate = "^1.5.0" interrogate = "^1.5.0"
mypy = "^1.1.1" mypy = "^1.1.1"
pdoc = "^13.0.0" pdoc = "^13.0.1"
poethepoet = "^0.18.1" poethepoet = "^0.18.1"
pre-commit = "^3.2.0" pre-commit = "^3.2.0"
ruff = "0.0.257" ruff = "^0.0.257"
sh = "2.0.3"
typeguard = "^3.0.1" typeguard = "^3.0.1"
types-python-dateutil = "^2.8.19.10" types-python-dateutil = "^2.8.19.10"
vulture = "^2.7" vulture = "^2.7"
sh = "2.0.3"
[tool.black] [tool.black]
line-length = 100 line-length = 100

View File

@@ -143,7 +143,7 @@ for group in groups:
notice( notice(
f"Updating {p} from {packages[p]['current_version']} to {packages[p]['new_version']}" f"Updating {p} from {packages[p]['current_version']} to {packages[p]['new_version']}"
) )
sh.poetry("add", f"{p}@{packages[p]['new_version']}", "--group", group, _fg=True) sh.poetry("add", f"{p}@latest", "--group", group, _fg=True)
sh.poetry("update", _fg=True) sh.poetry("update", _fg=True)
success("All dependencies are up to date") success("All dependencies are up to date")

View File

@@ -34,14 +34,21 @@ def main(
), ),
export_csv: Path = typer.Option( export_csv: Path = typer.Option(
None, None,
help="Exports all metadata to a specified CSV file and exits. (Will overwrite any existing file)", help="Exports all metadata to a specified CSV file and exits.",
show_default=False, show_default=False,
dir_okay=False, dir_okay=False,
file_okay=True, file_okay=True,
), ),
export_json: Path = typer.Option( export_json: Path = typer.Option(
None, None,
help="Exports all metadata to a specified JSON file and exits. (Will overwrite any existing file)", 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, show_default=False,
dir_okay=False, dir_okay=False,
file_okay=True, file_okay=True,
@@ -142,6 +149,10 @@ def main(
path = Path(export_json).expanduser().resolve() path = Path(export_json).expanduser().resolve()
application.noninteractive_export_csv(path) application.noninteractive_export_csv(path)
raise typer.Exit(code=0) raise typer.Exit(code=0)
if export_template is not None:
path = Path(export_template).expanduser().resolve()
application.noninteractive_export_csv(path)
raise typer.Exit(code=0)
application.application_main() application.application_main()

View File

@@ -17,7 +17,7 @@ def test_version() -> None:
"""Test printing version and then exiting.""" """Test printing version and then exiting."""
result = runner.invoke(app, ["--version"]) result = runner.invoke(app, ["--version"])
assert result.exit_code == 0 assert result.exit_code == 0
assert result.output == Regex(r"obsidian_metadata: v\d+\.\d+\.\d+$") assert "obsidian_metadata: v" in result.output
def test_application(tmp_path) -> None: def test_application(tmp_path) -> None:
@@ -51,3 +51,25 @@ def test_application(tmp_path) -> None:
assert banner in result.output assert banner in result.output
assert result.exit_code == 1 assert result.exit_code == 1
def test_export_template(tmp_path) -> None:
"""Test the export template command."""
source_dir = Path(__file__).parent / "fixtures" / "test_vault"
dest_dir = Path(tmp_path / "vault")
if not source_dir.exists():
raise FileNotFoundError(f"Sample vault not found: {source_dir}")
shutil.copytree(source_dir, dest_dir)
config_path = tmp_path / "config.toml"
export_path = tmp_path / "export_template.csv"
result = runner.invoke(
app,
["--vault-path", dest_dir, "--config-file", config_path, "--export-template", export_path],
)
assert "SUCCESS | Exported metadata to" in result.output
assert result.exit_code == 0
assert export_path.exists()