[![PyPI version](https://badge.fury.io/py/obsidian-metadata.svg)](https://badge.fury.io/py/obsidian-metadata) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/obsidian-metadata) [![Python Code Checker](https://github.com/natelandau/obsidian-metadata/actions/workflows/automated-tests.yml/badge.svg)](https://github.com/natelandau/obsidian-metadata/actions/workflows/automated-tests.yml) [![codecov](https://codecov.io/gh/natelandau/obsidian-metadata/branch/main/graph/badge.svg?token=3F2R43SSX4)](https://codecov.io/gh/natelandau/obsidian-metadata)
# obsidian-metadata
A script to make batch updates to metadata in an Obsidian vault. No changes are
made to the Vault until they are explicitly committed.
[![asciicast](https://asciinema.org/a/DQk0ufza1azwU3QFkE6XV33nm.svg)](https://asciinema.org/a/DQk0ufza1azwU3QFkE6XV33nm)
## Important Disclaimer
**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.
## Install
Requires Python v3.10 or above.
```bash
pip install obsidian-metadata
```
## Usage
### CLI Commands
- `--config-file`: Specify a custom configuration file location
- `--dry-run`: Make no destructive changes
- `--import-csv` Import a CSV file with bulk updates
- `--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-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
- `--log-file`: Specify a log file location
- `--log-to-file`: Will log to a file
- `--vault-path`: Specify a path to an Obsidian Vault
- `--verbose`: Set verbosity level (0=WARN, 1=INFO, 2=DEBUG, 3=TRACE)
- `--version`: Prints the version number and exits
### Running the script
Once installed, run `obsidian-metadata` in your terminal to enter an interactive menu of sub-commands.
**Vault Actions**
- Backup: Create a backup of the vault.
- Delete Backup: Delete a backup of the vault.
**Export Metadata**
- Export all metadata to a CSV organized by metadata type
- Export all metadata to a CSV organized by note path
- Export all metadata to a JSON file organized by metadata type
**Inspect Metadata**
- **View all metadata in the vault**
- View all **frontmatter**
- View all **inline metadata**
- View all **inline tags**
**Filter Notes in Scope**: Limit the scope of notes to be processed with one or more filters.
- **Path filter (regex)**: Limit scope based on the path or filename
- **Metadata filter**: Limit scope based on a key or key/value pair
- **Tag filter**: Limit scope based on an in-text tag
- **List and clear filters**: List all current filters and clear one or all
- **List notes in scope**: List notes that will be processed.
**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.
When adding a new key to inline metadata, the `insert location` value in the config file will specify where in the note it will be inserted.
- **Add new metadata to the frontmatter**
- **Add new inline metadata** - Set `insert_location` in the config to control where the new metadata is inserted. (Default: Bottom)
- **Add new inline tag** - Set `insert_location` in the config to control where the new tag is inserted. (Default: Bottom)
**Rename Metadata**: Rename either a key and all associated values, a specific value within a key. or an in-text tag.
- **Rename a key**
- **Rename a value**
- **Rename an inline tag**
**Delete Metadata**: Delete either a key and all associated values, or a specific value.
- **Delete a key and associated values**
- **Delete a value from a key**
- **Delete an inline tag**
**Move Inline Metadata**: Move inline metadata to a specified location with a note
- **Move to Top**: Move all inline metadata beneath the frontmatter
- **Move to After Title**: Move all inline metadata beneath the first markdown header
- **Move to Bottom**: Move all inline metadata to the bottom of the note
**Transpose Metadata**: Move metadata from inline to frontmatter or the reverse.
When transposing to inline metadata, the `insert location` value in the config file will specify where in the note it will be inserted.
- **Transpose all metadata** - Moves all frontmatter to inline metadata, or the reverse
- **Transpose key** - Transposes a specific key and all it's values
- **Transpose value**- Transpose a specific key:value pair
**Review Changes**: Prior to committing changes, review all changes that will be made.
- **View a diff of the changes** that will be made
**Commit Changes**: Write the changes to disk. This step is not undoable.
- **Commit changes to the vault**
### Known Limitations
Multi-level frontmatter is not supported.
```yaml
# This works perfectly well
---
key: "value"
key2:
- one
- two
- three
key3: ["foo", "bar", "baz"]
key4: value
# This will not work
---
key1:
key2:
- one
- two
- three
key3:
- one
- two
- three
---
```
### Configuration
`obsidian-metadata` requires a configuration file at `~/.obsidian_metadata.toml`. On first run, this file will be created. You can specify a new location for the configuration file with the `--config-file` option.
To add additional vaults, copy the default section and add the appropriate information. The script will prompt you to select a vault if multiple exist in the configuration file
Below is an example with two vaults.
```toml
["Vault One"] # Name of the vault.
# Path to your obsidian vault
# Note for Windows users: Windows paths must use `\\` as the path separator due to a limitation with how TOML parses strings.
# Example: "C:\\Users\\username\\Documents\\Obsidian"
path = "/path/to/vault"
# Folders within the vault to ignore when indexing metadata
exclude_paths = [".git", ".obsidian"]
# Location to add metadata. One of:
# TOP: Directly after frontmatter.
# AFTER_TITLE: After the first header following frontmatter.
# BOTTOM: The bottom of the note
insert_location = "BOTTOM"
["Vault Two"]
path = "/path/to/second_vault"
exclude_paths = [".git", ".obsidian", "daily_notes"]
insert_location = "AFTER_TITLE"
```
To bypass the configuration file and specify a vault to use at runtime use the `--vault-path` option.
**Note for Windows users:**
Due to how TOMML parses strings, Windows paths must use `\\` as the path separator.
For example: `C:\\Users\\username\\Documents\\Obsidian`
### Make Bulk Updates
Bulk edits are supported by importing a CSV file containing the following columns. Column headers must be lowercase.
1. `path` - Path to note relative to the vault root folder
2. `type` - Type of metadata. One of `frontmatter`, `inline_metadata`, or `tag`
3. `key` - The key to add (leave blank for a tag)
4. `value` - the value to add to the key
An example valid CSV file is
```csv
path,type,key,value
folder 1/note1.md,frontmatter,fruits,apple
folder 1/note1.md,frontmatter,fruits,banana
folder 1/note1.md,inline_metadata,cars,toyota
folder 1/note1.md,inline_metadata,cars,honda
folder 1/note1.md,tag,,tag1
folder 1/note1.md,tag,,tag2
```
How bulk imports work:
- **Only notes which match the path in the CSV file are updated**
- **Effected notes will have ALL of their metadata changed** to reflect the values in the CSV file
- **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
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
Once you have a template created you can import it using the `--import-csv` flag or by navigating to the `Import bulk changes from CSV` option.
# Contributing
## Setup: Once per project
There are two ways to contribute to this project.
### 1. Containerized development
1. Clone this repository. `git clone https://github.com/natelandau/obsidian-metadata`
2. Open the repository in Visual Studio Code
3. Start the [Dev Container](https://code.visualstudio.com/docs/remote/containers). Run <kbd>Ctrl/⌘</kbd> + <kbd>⇧</kbd> + <kbd>P</kbd> → _Remote-Containers: Reopen in Container_.
4. Run `poetry env info -p` to find the PATH to the Python interpreter if needed by VSCode.
### 2. Local development
1. Install Python 3.10 and [Poetry](https://python-poetry.org)
2. Clone this repository. `git clone https://github.com/natelandau/obsidian-metadata`
3. Install the Poetry environment with `poetry install`.
4. Activate your Poetry environment with `poetry shell`.
5. Install the pre-commit hooks with `pre-commit install --install-hooks`.
## Developing
- This project follows the [Conventional Commits](https://www.conventionalcommits.org/) standard to automate [Semantic Versioning](https://semver.org/) and [Keep A Changelog](https://keepachangelog.com/) with [Commitizen](https://github.com/commitizen-tools/commitizen).
- When you're ready to commit changes run `cz c`
- Run `poe` from within the development environment to print a list of [Poe the Poet](https://github.com/nat-n/poethepoet) tasks available to run on this project. Common commands:
- `poe lint` runs all linters
- `poe test` runs all tests with Pytest
- Run `poetry add {package}` from within the development environment to install a run time dependency and add it to `pyproject.toml` and `poetry.lock`.
- Run `poetry remove {package}` from within the development environment to uninstall a run time dependency and remove it from `pyproject.toml` and `poetry.lock`.
- Run `poetry update` from within the development environment to upgrade all dependencies to the latest versions allowed by `pyproject.toml`.
Raw data
{
"_id": null,
"home_page": "https://github.com/natelandau/obsidian-metadata",
"name": "obsidian-metadata",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.10,<4.0",
"maintainer_email": "",
"keywords": "obsidian",
"author": "Nate Landau",
"author_email": "github@natenate.org",
"download_url": "https://files.pythonhosted.org/packages/19/c9/27cb418f273da3e4aa3e9120097667fa3559bbbc912ed3039bddab61a363/obsidian_metadata-0.12.1.tar.gz",
"platform": null,
"description": "[![PyPI version](https://badge.fury.io/py/obsidian-metadata.svg)](https://badge.fury.io/py/obsidian-metadata) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/obsidian-metadata) [![Python Code Checker](https://github.com/natelandau/obsidian-metadata/actions/workflows/automated-tests.yml/badge.svg)](https://github.com/natelandau/obsidian-metadata/actions/workflows/automated-tests.yml) [![codecov](https://codecov.io/gh/natelandau/obsidian-metadata/branch/main/graph/badge.svg?token=3F2R43SSX4)](https://codecov.io/gh/natelandau/obsidian-metadata)\n\n# obsidian-metadata\n\nA script to make batch updates to metadata in an Obsidian vault. No changes are\nmade to the Vault until they are explicitly committed.\n\n[![asciicast](https://asciinema.org/a/DQk0ufza1azwU3QFkE6XV33nm.svg)](https://asciinema.org/a/DQk0ufza1azwU3QFkE6XV33nm)\n\n## Important Disclaimer\n\n**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.\n\n## Install\n\nRequires Python v3.10 or above.\n\n```bash\npip install obsidian-metadata\n```\n\n## Usage\n\n### CLI Commands\n\n- `--config-file`: Specify a custom configuration file location\n- `--dry-run`: Make no destructive changes\n- `--import-csv` Import a CSV file with bulk updates\n- `--export-csv`: Specify a path and create a CSV export of all metadata\n- `--export-json`: Specify a path and create a JSON export of all metadata\n- `--export-template`: Specify a path and export all notes with their associated metadata to a CSV file for use as a bulk import template\n- `--help`: Shows interactive help and exits\n- `--log-file`: Specify a log file location\n- `--log-to-file`: Will log to a file\n- `--vault-path`: Specify a path to an Obsidian Vault\n- `--verbose`: Set verbosity level (0=WARN, 1=INFO, 2=DEBUG, 3=TRACE)\n- `--version`: Prints the version number and exits\n\n### Running the script\n\nOnce installed, run `obsidian-metadata` in your terminal to enter an interactive menu of sub-commands.\n\n**Vault Actions**\n\n- Backup: Create a backup of the vault.\n- Delete Backup: Delete a backup of the vault.\n\n**Export Metadata**\n\n- Export all metadata to a CSV organized by metadata type\n- Export all metadata to a CSV organized by note path\n- Export all metadata to a JSON file organized by metadata type\n\n**Inspect Metadata**\n\n- **View all metadata in the vault**\n- View all **frontmatter**\n- View all **inline metadata**\n- View all **inline tags**\n\n**Filter Notes in Scope**: Limit the scope of notes to be processed with one or more filters.\n\n- **Path filter (regex)**: Limit scope based on the path or filename\n- **Metadata filter**: Limit scope based on a key or key/value pair\n- **Tag filter**: Limit scope based on an in-text tag\n- **List and clear filters**: List all current filters and clear one or all\n- **List notes in scope**: List notes that will be processed.\n\n**Bulk Edit Metadata** from a CSV file (See the _[Make Bulk Updates](https://github.com/natelandau/obsidian-metadata#make-bulk-updates)_ section below)\n\n**Add Metadata**: Add new metadata to your vault.\n\nWhen adding a new key to inline metadata, the `insert location` value in the config file will specify where in the note it will be inserted.\n\n- **Add new metadata to the frontmatter**\n- **Add new inline metadata** - Set `insert_location` in the config to control where the new metadata is inserted. (Default: Bottom)\n- **Add new inline tag** - Set `insert_location` in the config to control where the new tag is inserted. (Default: Bottom)\n\n**Rename Metadata**: Rename either a key and all associated values, a specific value within a key. or an in-text tag.\n\n- **Rename a key**\n- **Rename a value**\n- **Rename an inline tag**\n\n**Delete Metadata**: Delete either a key and all associated values, or a specific value.\n\n- **Delete a key and associated values**\n- **Delete a value from a key**\n- **Delete an inline tag**\n\n**Move Inline Metadata**: Move inline metadata to a specified location with a note\n\n- **Move to Top**: Move all inline metadata beneath the frontmatter\n- **Move to After Title**: Move all inline metadata beneath the first markdown header\n- **Move to Bottom**: Move all inline metadata to the bottom of the note\n\n**Transpose Metadata**: Move metadata from inline to frontmatter or the reverse.\n\nWhen transposing to inline metadata, the `insert location` value in the config file will specify where in the note it will be inserted.\n\n- **Transpose all metadata** - Moves all frontmatter to inline metadata, or the reverse\n- **Transpose key** - Transposes a specific key and all it's values\n- **Transpose value**- Transpose a specific key:value pair\n\n**Review Changes**: Prior to committing changes, review all changes that will be made.\n\n- **View a diff of the changes** that will be made\n\n**Commit Changes**: Write the changes to disk. This step is not undoable.\n\n- **Commit changes to the vault**\n\n### Known Limitations\n\nMulti-level frontmatter is not supported.\n\n```yaml\n# This works perfectly well\n---\nkey: \"value\"\nkey2:\n - one\n - two\n - three\nkey3: [\"foo\", \"bar\", \"baz\"]\nkey4: value\n\n# This will not work\n---\nkey1:\n key2:\n - one\n - two\n - three\n key3:\n - one\n - two\n - three\n---\n```\n\n### Configuration\n\n`obsidian-metadata` requires a configuration file at `~/.obsidian_metadata.toml`. On first run, this file will be created. You can specify a new location for the configuration file with the `--config-file` option.\n\nTo add additional vaults, copy the default section and add the appropriate information. The script will prompt you to select a vault if multiple exist in the configuration file\n\nBelow is an example with two vaults.\n\n```toml\n[\"Vault One\"] # Name of the vault.\n\n # Path to your obsidian vault\n # Note for Windows users: Windows paths must use `\\\\` as the path separator due to a limitation with how TOML parses strings.\n # Example: \"C:\\\\Users\\\\username\\\\Documents\\\\Obsidian\"\n path = \"/path/to/vault\"\n\n # Folders within the vault to ignore when indexing metadata\n exclude_paths = [\".git\", \".obsidian\"]\n\n # Location to add metadata. One of:\n # TOP: Directly after frontmatter.\n # AFTER_TITLE: After the first header following frontmatter.\n # BOTTOM: The bottom of the note\n insert_location = \"BOTTOM\"\n\n[\"Vault Two\"]\n path = \"/path/to/second_vault\"\n exclude_paths = [\".git\", \".obsidian\", \"daily_notes\"]\n insert_location = \"AFTER_TITLE\"\n```\n\nTo bypass the configuration file and specify a vault to use at runtime use the `--vault-path` option.\n\n**Note for Windows users:**\nDue to how TOMML parses strings, Windows paths must use `\\\\` as the path separator.\nFor example: `C:\\\\Users\\\\username\\\\Documents\\\\Obsidian`\n\n### Make Bulk Updates\n\nBulk edits are supported by importing a CSV file containing the following columns. Column headers must be lowercase.\n\n1. `path` - Path to note relative to the vault root folder\n2. `type` - Type of metadata. One of `frontmatter`, `inline_metadata`, or `tag`\n3. `key` - The key to add (leave blank for a tag)\n4. `value` - the value to add to the key\n\nAn example valid CSV file is\n\n```csv\npath,type,key,value\nfolder 1/note1.md,frontmatter,fruits,apple\nfolder 1/note1.md,frontmatter,fruits,banana\nfolder 1/note1.md,inline_metadata,cars,toyota\nfolder 1/note1.md,inline_metadata,cars,honda\nfolder 1/note1.md,tag,,tag1\nfolder 1/note1.md,tag,,tag2\n```\n\nHow bulk imports work:\n\n- **Only notes which match the path in the CSV file are updated**\n- **Effected notes will have ALL of their metadata changed** to reflect the values in the CSV file\n- **Existing metadata in a matching note will be rewritten**. This may result in it's location and/or formatting within the note being changed\n- Inline tags ignore any value added to the `key` column\n\nCreate a CSV template for making bulk updates containing all your notes and their associated metadata by\n\n1. Using the `--export-template` cli command; or\n2. Selecting the `Metadata by note` option within the `Export Metadata` section of the app\n\nOnce you have a template created you can import it using the `--import-csv` flag or by navigating to the `Import bulk changes from CSV` option.\n\n# Contributing\n\n## Setup: Once per project\n\nThere are two ways to contribute to this project.\n\n### 1. Containerized development\n\n1. Clone this repository. `git clone https://github.com/natelandau/obsidian-metadata`\n2. Open the repository in Visual Studio Code\n3. Start the [Dev Container](https://code.visualstudio.com/docs/remote/containers). Run <kbd>Ctrl/\u2318</kbd> + <kbd>\u21e7</kbd> + <kbd>P</kbd> \u2192 _Remote-Containers: Reopen in Container_.\n4. Run `poetry env info -p` to find the PATH to the Python interpreter if needed by VSCode.\n\n### 2. Local development\n\n1. Install Python 3.10 and [Poetry](https://python-poetry.org)\n2. Clone this repository. `git clone https://github.com/natelandau/obsidian-metadata`\n3. Install the Poetry environment with `poetry install`.\n4. Activate your Poetry environment with `poetry shell`.\n5. Install the pre-commit hooks with `pre-commit install --install-hooks`.\n\n## Developing\n\n- This project follows the [Conventional Commits](https://www.conventionalcommits.org/) standard to automate [Semantic Versioning](https://semver.org/) and [Keep A Changelog](https://keepachangelog.com/) with [Commitizen](https://github.com/commitizen-tools/commitizen).\n - When you're ready to commit changes run `cz c`\n- Run `poe` from within the development environment to print a list of [Poe the Poet](https://github.com/nat-n/poethepoet) tasks available to run on this project. Common commands:\n - `poe lint` runs all linters\n - `poe test` runs all tests with Pytest\n- Run `poetry add {package}` from within the development environment to install a run time dependency and add it to `pyproject.toml` and `poetry.lock`.\n- Run `poetry remove {package}` from within the development environment to uninstall a run time dependency and remove it from `pyproject.toml` and `poetry.lock`.\n- Run `poetry update` from within the development environment to upgrade all dependencies to the latest versions allowed by `pyproject.toml`.\n",
"bugtrack_url": null,
"license": "GNU AFFERO",
"summary": "Make batch updates to Obsidian metadata",
"version": "0.12.1",
"project_urls": {
"Homepage": "https://github.com/natelandau/obsidian-metadata",
"Repository": "https://github.com/natelandau/obsidian-metadata"
},
"split_keywords": [
"obsidian"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b7ac6d70729d4b8ef726fe4e108b0c8c763e0f75628bef9b22bc84ffe3ee5fd5",
"md5": "ea9620c8909d48e67c1e1d180869ed70",
"sha256": "050405b9c3ec897244ad8fb02515a85deef1c71b6bc099f67641e130449d6afd"
},
"downloads": -1,
"filename": "obsidian_metadata-0.12.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ea9620c8909d48e67c1e1d180869ed70",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10,<4.0",
"size": 53630,
"upload_time": "2023-09-02T21:09:04",
"upload_time_iso_8601": "2023-09-02T21:09:04.509692Z",
"url": "https://files.pythonhosted.org/packages/b7/ac/6d70729d4b8ef726fe4e108b0c8c763e0f75628bef9b22bc84ffe3ee5fd5/obsidian_metadata-0.12.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "19c927cb418f273da3e4aa3e9120097667fa3559bbbc912ed3039bddab61a363",
"md5": "3a8c7d1b610d272bd8b935154f04a310",
"sha256": "278c341093ae4633f15fa796f04616bff82b0f2ee55448e7134f21592bcc3249"
},
"downloads": -1,
"filename": "obsidian_metadata-0.12.1.tar.gz",
"has_sig": false,
"md5_digest": "3a8c7d1b610d272bd8b935154f04a310",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10,<4.0",
"size": 52218,
"upload_time": "2023-09-02T21:09:05",
"upload_time_iso_8601": "2023-09-02T21:09:05.691002Z",
"url": "https://files.pythonhosted.org/packages/19/c9/27cb418f273da3e4aa3e9120097667fa3559bbbc912ed3039bddab61a363/obsidian_metadata-0.12.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-09-02 21:09:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "natelandau",
"github_project": "obsidian-metadata",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "obsidian-metadata"
}