4 Commits

Author SHA1 Message Date
Nathaniel Landau
a2d69d034d bump(release): v0.7.0 → v0.8.0 2023-03-12 14:11:00 -04:00
Nathaniel Landau
556acc0d46 docs: include move metadata in documentation 2023-03-12 14:08:44 -04:00
Nathaniel Landau
8cefca2639 feat: move inline metadata to specific location in note (#27) 2023-03-12 13:58:55 -04:00
Nathaniel Landau
82e1cba34a fix: add back option to transpose menus 2023-03-12 11:19:53 -04:00
15 changed files with 597 additions and 256 deletions

View File

@@ -1,3 +1,13 @@
## v0.8.0 (2023-03-12)
### Feat
- move inline metadata to specific location in note (#27)
### Fix
- add `back` option to transpose menus
## v0.7.0 (2023-03-11)
### Feat

View File

@@ -60,6 +60,7 @@ Once installed, run `obsidian-metadata` in your terminal to enter an interactive
- **List notes in scope**: List notes that will be processed.
**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**
@@ -78,7 +79,14 @@ When adding a new key to inline metadata, the `insert location` value in the con
- **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

View File

@@ -11,7 +11,7 @@
name = "obsidian-metadata"
readme = "README.md"
repository = "https://github.com/natelandau/obsidian-metadata"
version = "0.7.0"
version = "0.8.0"
[tool.poetry.scripts] # https://python-poetry.org/docs/pyproject/#scripts
obsidian-metadata = "obsidian_metadata.cli:app"
@@ -142,7 +142,7 @@
changelog_incremental = true
tag_format = "v$version"
update_changelog_on_bump = true
version = "0.7.0"
version = "0.8.0"
version_files = ["pyproject.toml:version", "src/obsidian_metadata/__version__.py:__version__"]
[tool.interrogate]

View File

@@ -1,2 +1,2 @@
"""obsidian-metadata version."""
__version__ = "0.7.0"
__version__ = "0.8.0"

View File

@@ -135,13 +135,22 @@ def main(
• Delete a value from a key
• Delete an inline tag
[bold underline]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
[bold underline]Transpose Metadata[/]
Move metadata from inline to frontmatter or the reverse.
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
• Transpose value - Transpose a specific key:value pair
[bold underline]Review Changes[/]
Prior to committing changes, review all changes that will be made.

View File

@@ -12,7 +12,7 @@ from rich.table import Table
from obsidian_metadata._config import VaultConfig
from obsidian_metadata._utils import alerts
from obsidian_metadata._utils.console import console
from obsidian_metadata.models import Vault, VaultFilter
from obsidian_metadata.models import InsertLocation, Vault, VaultFilter
from obsidian_metadata.models.enums import MetadataType
from obsidian_metadata.models.questions import Questions
@@ -63,8 +63,8 @@ class Application:
self.application_rename_metadata()
case "delete_metadata":
self.application_delete_metadata()
case "transpose_metadata":
self.application_transpose_metadata()
case "reorganize_metadata":
self.application_reorganize_metadata()
case "review_changes":
self.review_changes()
case "commit_changes":
@@ -332,13 +332,27 @@ class Application:
case _:
return
def application_transpose_metadata(self) -> None:
"""Transpose metadata."""
alerts.usage("Transpose metadata from frontmatter to inline or vice versa.")
def application_reorganize_metadata(self) -> None:
"""Reorganize metadata.
This portion of the application deals with moving metadata between types (inline to frontmatter, etc.) and moving the location of inline metadata within a note.
"""
alerts.usage("Move metadata within notes.")
alerts.usage(" 1. Transpose frontmatter to inline or vice versa.")
alerts.usage(" 2. Move the location of inline metadata within a note.")
choices = [
{"name": "Move inline metadata to top of note", "value": "move_to_top"},
{
"name": "Move inline metadata beneath the first header",
"value": "move_to_after_header",
},
{"name": "Move inline metadata to bottom of the note", "value": "move_to_bottom"},
{"name": "Transpose frontmatter to inline", "value": "frontmatter_to_inline"},
{"name": "Transpose inline to frontmatter", "value": "inline_to_frontmatter"},
questionary.Separator(),
{"name": "Back", "value": "back"},
]
match self.questions.ask_selection(
choices=choices, question="Select metadata to transpose"
@@ -347,6 +361,12 @@ class Application:
self.transpose_metadata(begin=MetadataType.FRONTMATTER, end=MetadataType.INLINE)
case "inline_to_frontmatter":
self.transpose_metadata(begin=MetadataType.INLINE, end=MetadataType.FRONTMATTER)
case "move_to_top":
self.move_inline_metadata(location=InsertLocation.TOP)
case "move_to_after_header":
self.move_inline_metadata(location=InsertLocation.AFTER_TITLE)
case "move_to_bottom":
self.move_inline_metadata(location=InsertLocation.BOTTOM)
case _: # pragma: no cover
return
@@ -451,6 +471,15 @@ class Application:
return
def move_inline_metadata(self, location: InsertLocation) -> None:
"""Move inline metadata to the selected location."""
num_changed = self.vault.move_inline_metadata(location)
if num_changed == 0:
alerts.warning("No notes were changed")
return
alerts.success(f"Moved inline metadata to {location.value} in {num_changed} notes")
def noninteractive_export_csv(self, path: Path) -> None:
"""Export the vault metadata to CSV."""
self._load_vault()
@@ -566,6 +595,7 @@ class Application:
{"name": f"Transpose all {begin.value} to {end.value}", "value": "transpose_all"},
{"name": "Transpose a key", "value": "transpose_key"},
{"name": "Transpose a value", "value": "transpose_value"},
questionary.Separator(),
{"name": "Back", "value": "back"},
]
match self.questions.ask_selection(choices=choices, question="Select an action to perform"):

View File

@@ -448,20 +448,27 @@ class Note:
Returns:
bool: Whether the note was updated.
"""
for _k, _v in self.inline_metadata.dict.items():
if re.search(key, _k):
for _value in _v:
if value is None:
if self.inline_metadata.dict != {}:
if key is None:
for _k, _v in self.inline_metadata.dict.items():
for _value in _v:
_k = re.escape(_k)
_value = re.escape(_value)
self.sub(rf"\[?{_k}:: ?{_value}]?", "", is_regex=True)
return True
self.sub(rf"\[?{_k}:: ?\[?\[?{_value}\]?\]?", "", is_regex=True)
return True
if re.search(value, _value):
_k = re.escape(_k)
_value = re.escape(_value)
self.sub(rf"({_k}::) ?{_value}", r"\1", is_regex=True)
return True
for _k, _v in self.inline_metadata.dict.items():
if re.search(key, _k):
for _value in _v:
if value is None:
_k = re.escape(_k)
_value = re.escape(_value)
self.sub(rf"\[?{_k}:: \[?\[?{_value}\]?\]?", "", is_regex=True)
elif re.search(value, _value):
_k = re.escape(_k)
_value = re.escape(_value)
self.sub(rf"\[?({_k}::) ?\[?\[?{_value}\]?\]?", r"\1", is_regex=True)
return True
return False
def write_frontmatter(self, sort_keys: bool = False) -> bool:

View File

@@ -282,7 +282,7 @@ class Questions:
{"name": "Add Metadata", "value": "add_metadata"},
{"name": "Delete Metadata", "value": "delete_metadata"},
{"name": "Rename Metadata", "value": "rename_metadata"},
{"name": "Transpose Metadata", "value": "transpose_metadata"},
{"name": "Reorganize Metadata", "value": "reorganize_metadata"},
questionary.Separator("-------------------------------"),
{"name": "Review Changes", "value": "review_changes"},
{"name": "Commit Changes", "value": "commit_changes"},

View File

@@ -8,6 +8,7 @@ from dataclasses import dataclass
from pathlib import Path
import rich.repr
import typer
from rich import box
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.prompt import Confirm
@@ -53,8 +54,9 @@ class Vault:
self.insert_location: InsertLocation = self._find_insert_location()
self.dry_run: bool = dry_run
self.backup_path: Path = self.vault_path.parent / f"{self.vault_path.name}.bak"
self.exclude_paths: list[Path] = []
self.metadata = VaultMetadata()
self.exclude_paths: list[Path] = []
for p in config.exclude_paths:
self.exclude_paths.append(Path(self.vault_path / p))
@@ -76,13 +78,16 @@ class Vault:
def __rich_repr__(self) -> rich.repr.Result: # pragma: no cover
"""Define rich representation of Vault."""
yield "vault_path", self.vault_path
yield "dry_run", self.dry_run
yield "backup_path", self.backup_path
yield "num_notes", len(self.all_notes)
yield "num_notes_in_scope", len(self.notes_in_scope)
yield "config", self.config
yield "dry_run", self.dry_run
yield "exclude_paths", self.exclude_paths
yield "filters", self.filters
yield "insert_location", self.insert_location
yield "name", self.name
yield "num_notes_in_scope", len(self.notes_in_scope)
yield "num_notes", len(self.all_notes)
yield "vault_path", self.vault_path
def _filter_notes(self) -> list[Note]:
"""Filter notes by path and metadata using the filters defined in self.filters.
@@ -209,6 +214,7 @@ class Vault:
for _note in self.notes_in_scope:
if _note.add_metadata(area=area, key=key, value=value, location=location):
log.trace(f"Added metadata to {_note.note_path}")
num_changed += 1
if num_changed > 0:
@@ -279,6 +285,7 @@ class Vault:
for _note in self.notes_in_scope:
if _note.delete_inline_tag(tag):
log.trace(f"Deleted tag from {_note.note_path}")
num_changed += 1
if num_changed > 0:
@@ -300,6 +307,7 @@ class Vault:
for _note in self.notes_in_scope:
if _note.delete_metadata(key, value):
log.trace(f"Deleted metadata from {_note.note_path}")
num_changed += 1
if num_changed > 0:
@@ -315,6 +323,9 @@ class Vault:
export_format (str, optional): Export as 'csv' or 'json'. Defaults to "csv".
"""
export_file = Path(path).expanduser().resolve()
if not export_file.parent.exists():
alerts.error(f"Path does not exist: {export_file.parent}")
raise typer.Exit(code=1)
match export_format:
case "csv":
@@ -350,7 +361,7 @@ class Vault:
json.dump(dict_to_dump, f, indent=4, ensure_ascii=False, sort_keys=True)
def get_changed_notes(self) -> list[Note]:
"""Returns a list of notes that have changes.
"""Return a list of notes that have changes.
Returns:
list[Note]: List of notes that have changes.
@@ -386,6 +397,29 @@ class Vault:
table.add_row(str(_n), str(_note.note_path.relative_to(self.vault_path)))
console.print(table)
def move_inline_metadata(self, location: InsertLocation) -> int:
"""Move all inline metadata to the selected location.
Args:
location (InsertLocation): Location to move inline metadata to.
Returns:
int: Number of notes that had inline metadata moved.
"""
num_changed = 0
for _note in self.notes_in_scope:
if _note.write_delete_inline_metadata():
log.trace(f"Deleted inline metadata from {_note.note_path}")
num_changed += 1
_note.write_all_inline_metadata(location)
log.trace(f"Wrote all inline metadata to {_note.note_path}")
if num_changed > 0:
self._rebuild_vault_metadata()
return num_changed
def num_excluded_notes(self) -> int:
"""Count number of excluded notes."""
return len(self.all_notes) - len(self.notes_in_scope)
@@ -404,6 +438,7 @@ class Vault:
for _note in self.notes_in_scope:
if _note.rename_inline_tag(old_tag, new_tag):
log.trace(f"Renamed inline tag in {_note.note_path}")
num_changed += 1
if num_changed > 0:
@@ -412,7 +447,7 @@ class Vault:
return num_changed
def rename_metadata(self, key: str, value_1: str, value_2: str = None) -> int:
"""Renames a key or key-value pair in the note's metadata.
"""Rename a key or key-value pair in the note's metadata.
If no value is provided, will rename an entire key.
@@ -428,6 +463,7 @@ class Vault:
for _note in self.notes_in_scope:
if _note.rename_metadata(key, value_1, value_2):
log.trace(f"Renamed metadata in {_note.note_path}")
num_changed += 1
if num_changed > 0:
@@ -468,6 +504,7 @@ class Vault:
location=location,
):
num_changed += 1
log.trace(f"Transposed metadata in {_note.note_path}")
if num_changed > 0:
self._rebuild_vault_metadata()

View File

@@ -17,7 +17,12 @@ from tests.helpers import Regex, remove_ansi
def test_instantiate_application(test_application) -> None:
"""Test application."""
"""Test application.
GIVEN an application
WHEN the application is instantiated
THEN check the attributes are set correctly
"""
app = test_application
app._load_vault()
@@ -29,7 +34,12 @@ def test_instantiate_application(test_application) -> None:
def test_abort(test_application, mocker, capsys) -> None:
"""Test renaming a key."""
"""Test aborting the application.
GIVEN an application
WHEN the users selects "abort" from the main menu
THEN check the application exits
"""
app = test_application
app._load_vault()
mocker.patch(
@@ -43,7 +53,12 @@ def test_abort(test_application, mocker, capsys) -> None:
def test_add_metadata_frontmatter(test_application, mocker, capsys) -> None:
"""Test adding new metadata to the vault."""
"""Test adding new metadata to the vault.
GIVEN an application
WHEN the wants to update a key in the frontmatter
THEN check the application updates the key
"""
app = test_application
app._load_vault()
mocker.patch(
@@ -70,7 +85,12 @@ def test_add_metadata_frontmatter(test_application, mocker, capsys) -> None:
def test_add_metadata_inline(test_application, mocker, capsys) -> None:
"""Test adding new metadata to the vault."""
"""Test adding new metadata to the vault.
GIVEN an application
WHEN the user wants to add a key in the inline metadata
THEN check the application updates the key
"""
app = test_application
app._load_vault()
mocker.patch(
@@ -97,7 +117,12 @@ def test_add_metadata_inline(test_application, mocker, capsys) -> None:
def test_add_metadata_tag(test_application, mocker, capsys) -> None:
"""Test adding new metadata to the vault."""
"""Test adding new metadata to the vault.
GIVEN an application
WHEN the user wants to add a tag
THEN check the application adds the tag
"""
app = test_application
app._load_vault()
mocker.patch(
@@ -119,8 +144,41 @@ def test_add_metadata_tag(test_application, mocker, capsys) -> None:
assert captured == Regex(r"SUCCESS +\| Added metadata to \d+ notes", re.DOTALL)
def test_delete_inline_tag(test_application, mocker, capsys) -> None:
"""Test renaming an inline tag."""
def test_delete_inline_tag_1(test_application, mocker, capsys) -> None:
"""Test renaming an inline tag.
GIVEN an application
WHEN the user wants to delete an inline tag
THEN check the application deletes the tag
"""
app = test_application
app._load_vault()
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_application_main",
side_effect=["delete_metadata", KeyError],
)
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_selection",
side_effect=["delete_inline_tag", "back"],
)
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_existing_inline_tag",
return_value="breakfast",
)
with pytest.raises(KeyError):
app.application_main()
captured = remove_ansi(capsys.readouterr().out)
assert captured == Regex(r"SUCCESS +\| Deleted inline tag: breakfast in \d+ notes", re.DOTALL)
def test_delete_inline_tag_2(test_application, mocker, capsys) -> None:
"""Test renaming an inline tag.
GIVEN an application
WHEN the user wants to delete an inline tag that does not exist
THEN check the application does not update any notes
"""
app = test_application
app._load_vault()
mocker.patch(
@@ -141,24 +199,6 @@ def test_delete_inline_tag(test_application, mocker, capsys) -> None:
captured = remove_ansi(capsys.readouterr().out)
assert "WARNING | No notes were changed" in captured
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_application_main",
side_effect=["delete_metadata", KeyError],
)
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_selection",
side_effect=["delete_inline_tag", "back"],
)
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_existing_inline_tag",
return_value="breakfast",
)
with pytest.raises(KeyError):
app.application_main()
captured = remove_ansi(capsys.readouterr().out)
assert captured == Regex(r"SUCCESS +\| Deleted inline tag: breakfast in \d+ notes", re.DOTALL)
def test_delete_key(test_application, mocker, capsys) -> None:
"""Test renaming an inline tag."""
@@ -545,15 +585,20 @@ def test_review_changes(test_application, mocker, capsys) -> None:
assert "+ new_tags:" in captured
def test_transpose_metadata(test_application, mocker, capsys) -> None:
"""Transpose metadata."""
def test_transpose_metadata_1(test_application, mocker, capsys) -> None:
"""Transpose metadata.
GIVEN a test application
WHEN the user wants to transpose all inline metadata to frontmatter
THEN the metadata is transposed
"""
app = test_application
app._load_vault()
assert app.vault.metadata.inline_metadata["inline_key"] == ["inline_key_value"]
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_application_main",
side_effect=["transpose_metadata", KeyError],
side_effect=["reorganize_metadata", KeyError],
)
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_selection",
@@ -561,18 +606,27 @@ def test_transpose_metadata(test_application, mocker, capsys) -> None:
)
with pytest.raises(KeyError):
app.application_main()
assert app.vault.metadata.inline_metadata == {}
assert app.vault.metadata.frontmatter["inline_key"] == ["inline_key_value"]
captured = remove_ansi(capsys.readouterr().out)
assert "SUCCESS | Transposed Inline Metadata to Frontmatter in 5 notes" in captured
def test_transpose_metadata_2(test_application, mocker, capsys) -> None:
"""Transpose metadata.
GIVEN a test application
WHEN the user wants to transpose all frontmatter to inline metadata
THEN the metadata is transposed
"""
app = test_application
app._load_vault()
assert app.vault.metadata.frontmatter["date_created"] == ["2022-12-21", "2022-12-22"]
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_application_main",
side_effect=["transpose_metadata", KeyError],
side_effect=["reorganize_metadata", KeyError],
)
mocker.patch(
"obsidian_metadata.models.application.Questions.ask_selection",

View File

@@ -1,6 +1,9 @@
# type: ignore
"""Test obsidian-metadata CLI."""
import shutil
from pathlib import Path
from typer.testing import CliRunner
from obsidian_metadata.cli import app
@@ -17,13 +20,20 @@ def test_version() -> None:
assert result.output == Regex(r"obsidian_metadata: v\d+\.\d+\.\d+$")
def test_application(test_vault, tmp_path) -> None:
def test_application(tmp_path) -> None:
"""Test the application."""
vault_path = test_vault
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"
result = runner.invoke(
app,
["--vault-path", vault_path, "--config-file", config_path],
["--vault-path", dest_dir, "--config-file", config_path],
# input=KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.ENTER, # noqa: ERA001
)

View File

@@ -9,6 +9,13 @@ import pytest
from obsidian_metadata._config import Config
from obsidian_metadata.models.application import Application
CONFIG_1 = """
["Test Vault"]
exclude_paths = [".git", ".obsidian", "ignore_folder"]
insert_location = "TOP"
path = "TMPDIR_VAULT_PATH"
"""
def remove_all(root: Path):
"""Remove all files and directories in a directory."""
@@ -95,10 +102,16 @@ def test_vault(tmp_path) -> Path:
raise FileNotFoundError(f"Sample vault not found: {source_dir}")
shutil.copytree(source_dir, dest_dir)
yield dest_dir
config_path = Path(tmp_path / "config.toml")
config_path.write_text(CONFIG_1.replace("TMPDIR_VAULT_PATH", str(dest_dir)))
config = Config(config_path=config_path)
vault_config = config.vaults[0]
yield vault_config
# after test - remove fixtures
shutil.rmtree(dest_dir)
config_path.unlink()
if backup_dir.exists():
shutil.rmtree(backup_dir)

View File

@@ -4,7 +4,6 @@ tags:
- shared_tag
- frontmatter_tag1
- frontmatter_tag2
-
- 📅/frontmatter_tag3
frontmatter_Key1: author name
frontmatter_Key2: ["article", "note"]

View File

@@ -824,7 +824,7 @@ def test_write_delete_inline_metadata_2(sample_note) -> None:
GIVEN a note object with write_delete_inline_metadata() called
WHEN a key is specified that is within a body of text
THEN the key/value is removed from the note content
THEN the key and all associated values are removed from the note content
"""
note = Note(note_path=sample_note)
@@ -847,6 +847,26 @@ def test_write_delete_inline_metadata_3(sample_note) -> None:
assert note.file_content != Regex(r"bottom_key1::")
def test_write_delete_inline_metadata_4(sample_note) -> None:
"""Twrite_delete_inline_metadata() method.
GIVEN a note object with write_delete_inline_metadata() called
WHEN no key or value is specified
THEN all inline metadata is removed from the note content
"""
note = Note(note_path=sample_note)
note.write_delete_inline_metadata()
assert note.file_content == Regex(r"codeblock_key::")
assert note.file_content != Regex(r"key📅::")
assert note.file_content != Regex(r"top_key1::")
assert note.file_content != Regex(r"top_key3::")
assert note.file_content != Regex(r"intext_key::")
assert note.file_content != Regex(r"shared_key1::")
assert note.file_content != Regex(r"shared_key2::")
assert note.file_content != Regex(r"bottom_key1::")
assert note.file_content != Regex(r"bottom_key2::")
def test_write_frontmatter_1(sample_note) -> None:
"""Test writing frontmatter.

View File

@@ -3,35 +3,38 @@
from pathlib import Path
import pytest
import typer
from rich import print
from obsidian_metadata._config import Config
from obsidian_metadata.models import Vault, VaultFilter
from obsidian_metadata.models.enums import InsertLocation, MetadataType
from tests.helpers import Regex
def test_vault_creation(test_vault):
"""Test creating a Vault object."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_vault_creation(test_vault, tmp_path):
"""Test creating a Vault object.
GIVEN a Config object
WHEN a Vault object is created
THEN the Vault object is created with the correct attributes.
"""
vault = Vault(config=test_vault)
assert vault.name == "vault"
assert vault.vault_path == vault_path
assert vault.insert_location == InsertLocation.BOTTOM
assert vault.backup_path == Path(f"{vault_path}.bak")
assert vault.insert_location == InsertLocation.TOP
assert vault.backup_path == Path(tmp_path, "vault.bak")
assert vault.dry_run is False
assert str(vault.exclude_paths[0]) == Regex(r".*\.git")
assert len(vault.all_notes) == 3
assert len(vault.all_notes) == 2
assert vault.metadata.dict == {
"author": ["author name"],
"bottom_key1": ["bottom_key1_value"],
"bottom_key2": ["bottom_key2_value"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"intext_key": ["intext_value"],
"key📅": ["📅_key_value"],
"shared_key1": [
@@ -43,19 +46,15 @@ def test_vault_creation(test_vault):
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"top_key1": ["top_key1_value"],
"top_key2": ["top_key2_value"],
"top_key3": ["top_key3_value_as_link"],
"type": ["article", "note"],
}
assert vault.metadata.tags == [
"ignored_file_tag2",
"inline_tag_bottom1",
"inline_tag_bottom2",
"inline_tag_top1",
@@ -76,55 +75,51 @@ def test_vault_creation(test_vault):
"top_key3": ["top_key3_value_as_link"],
}
assert vault.metadata.frontmatter == {
"author": ["author name"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"shared_key1": ["shared_key1_value", "shared_key1_value3"],
"shared_key2": ["shared_key2_value1"],
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"type": ["article", "note"],
}
def set_insert_location(test_vault):
"""Test setting a new insert location."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
"""Test setting a new insert location.
GIVEN a vault object
WHEN the insert location is changed
THEN the insert location is changed
"""
vault = Vault(config=test_vault)
assert vault.name == "vault"
assert vault.vault_path == vault_path
assert vault.insert_location == InsertLocation.BOTTOM
vault.insert_location = InsertLocation.TOP
assert vault.insert_location == InsertLocation.TOP
vault.insert_location = InsertLocation.BOTTOM
assert vault.insert_location == InsertLocation.BOTTOM
def test_add_metadata(test_vault) -> None:
"""Test adding metadata to the vault."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_add_metadata_1(test_vault) -> None:
"""Test adding metadata to the vault.
assert vault.add_metadata(MetadataType.FRONTMATTER, "new_key") == 3
GIVEN a vault object
WHEN a new metadata key is added
THEN the metadata is added to the vault
"""
vault = Vault(config=test_vault)
assert vault.add_metadata(MetadataType.FRONTMATTER, "new_key") == 2
assert vault.metadata.dict == {
"author": ["author name"],
"bottom_key1": ["bottom_key1_value"],
"bottom_key2": ["bottom_key2_value"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"intext_key": ["intext_value"],
"key📅": ["📅_key_value"],
"new_key": [],
@@ -137,47 +132,46 @@ def test_add_metadata(test_vault) -> None:
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"top_key1": ["top_key1_value"],
"top_key2": ["top_key2_value"],
"top_key3": ["top_key3_value_as_link"],
"type": ["article", "note"],
}
assert vault.metadata.frontmatter == {
"author": ["author name"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"new_key": [],
"shared_key1": ["shared_key1_value", "shared_key1_value3"],
"shared_key2": ["shared_key2_value1"],
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"type": ["article", "note"],
}
assert vault.add_metadata(MetadataType.FRONTMATTER, "new_key2", "new_key2_value") == 3
def test_add_metadata_2(test_vault) -> None:
"""Test adding metadata to the vault.
GIVEN a vault object
WHEN a new metadata key and value is added
THEN the metadata is added to the vault
"""
vault = Vault(config=test_vault)
assert vault.add_metadata(MetadataType.FRONTMATTER, "new_key2", "new_key2_value") == 2
assert vault.metadata.dict == {
"author": ["author name"],
"bottom_key1": ["bottom_key1_value"],
"bottom_key2": ["bottom_key2_value"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"intext_key": ["intext_value"],
"key📅": ["📅_key_value"],
"new_key": [],
"new_key2": ["new_key2_value"],
"shared_key1": [
"shared_key1_value",
@@ -188,49 +182,76 @@ def test_add_metadata(test_vault) -> None:
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"top_key1": ["top_key1_value"],
"top_key2": ["top_key2_value"],
"top_key3": ["top_key3_value_as_link"],
"type": ["article", "note"],
}
assert vault.metadata.frontmatter == {
"author": ["author name"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"new_key": [],
"new_key2": ["new_key2_value"],
"shared_key1": ["shared_key1_value", "shared_key1_value3"],
"shared_key2": ["shared_key2_value1"],
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"type": ["article", "note"],
}
def test_backup(test_vault, capsys):
"""Test backing up the vault."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_commit_changes_1(test_vault, tmp_path):
"""Test committing changes to content in the vault.
GIVEN a vault object
WHEN the commit_changes method is called
THEN the changes are committed to the vault
"""
vault = Vault(config=test_vault)
content = Path(f"{tmp_path}/vault/test1.md").read_text()
assert "new_key: new_key_value" not in content
vault.add_metadata(MetadataType.FRONTMATTER, "new_key", "new_key_value")
vault.commit_changes()
committed_content = Path(f"{tmp_path}/vault/test1.md").read_text()
assert "new_key: new_key_value" in committed_content
def test_commit_changes_2(test_vault, tmp_path):
"""Test committing changes to content in the vault in dry run mode.
GIVEN a vault object
WHEN dry_run is set to True
THEN no changes are committed to the vault
"""
vault = Vault(config=test_vault, dry_run=True)
content = Path(f"{tmp_path}/vault/test1.md").read_text()
assert "new_key: new_key_value" not in content
vault.add_metadata(MetadataType.FRONTMATTER, "new_key", "new_key_value")
vault.commit_changes()
committed_content = Path(f"{tmp_path}/vault/test1.md").read_text()
assert "new_key: new_key_value" not in committed_content
def test_backup_1(test_vault, tmp_path, capsys):
"""Test the backup method.
GIVEN a vault object
WHEN the backup method is called
THEN the vault is backed up
"""
vault = Vault(config=test_vault)
vault.backup()
captured = capsys.readouterr()
assert Path(f"{vault_path}.bak").exists() is True
assert vault.backup_path.exists() is True
assert captured.out == Regex(r"SUCCESS +| backed up to")
vault.info()
@@ -239,42 +260,15 @@ def test_backup(test_vault, capsys):
assert captured.out == Regex(r"Backup path +\│[\s ]+/[\d\w]+")
def test_commit(test_vault, tmp_path):
"""Test committing changes to content in the vault."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
content = Path(f"{tmp_path}/vault/test1.md").read_text()
assert "new_key: new_key_value" not in content
def test_backup_2(test_vault, capsys):
"""Test the backup method.
vault.add_metadata(MetadataType.FRONTMATTER, "new_key", "new_key_value")
vault.commit_changes()
assert "new_key: new_key_value" not in content
GIVEN a vault object
WHEN dry_run is set to True and the backup method is called
THEN the vault is not backed up
"""
vault = Vault(config=test_vault, dry_run=True)
def test_commit_dry_run(test_vault, tmp_path):
"""Test committing changes to content in the vault in dry run mode."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config, dry_run=True)
content = Path(f"{tmp_path}/vault/test1.md").read_text()
assert "new_key: new_key_value" not in content
vault.add_metadata(MetadataType.FRONTMATTER, "new_key", "new_key_value")
vault.commit_changes()
assert "new_key: new_key_value" not in content
def test_backup_dryrun(test_vault, capsys):
"""Test backing up the vault."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config, dry_run=True)
print(f"vault.dry_run: {vault.dry_run}")
vault.backup()
captured = capsys.readouterr()
@@ -282,12 +276,14 @@ def test_backup_dryrun(test_vault, capsys):
assert captured.out == Regex(r"DRYRUN +| Backup up vault to")
def test_delete_backup(test_vault, capsys):
"""Test deleting the vault backup."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_delete_backup_1(test_vault, capsys):
"""Test deleting the vault backup.
GIVEN a vault object
WHEN the delete_backup method is called
THEN the backup is deleted
"""
vault = Vault(config=test_vault)
vault.backup()
vault.delete_backup()
@@ -302,12 +298,14 @@ def test_delete_backup(test_vault, capsys):
assert captured.out == Regex(r"Backup +\│ None")
def test_delete_backup_dryrun(test_vault, capsys):
"""Test deleting the vault backup."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config, dry_run=True)
def test_delete_backup_2(test_vault, capsys):
"""Test delete_backup method in dry run mode.
GIVEN a vault object
WHEN the dry_run is True and the delete_backup method is called
THEN the backup is not deleted
"""
vault = Vault(config=test_vault, dry_run=True)
Path.mkdir(vault.backup_path)
vault.delete_backup()
@@ -317,17 +315,17 @@ def test_delete_backup_dryrun(test_vault, capsys):
assert vault.backup_path.exists() is True
def test_delete_inline_tag(test_vault) -> None:
"""Test deleting an inline tag."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_delete_inline_tag_1(test_vault) -> None:
"""Test delete_inline_tag() method.
assert vault.delete_inline_tag("no tag") == 0
assert vault.delete_inline_tag("intext_tag2") == 2
GIVEN a vault object
WHEN the delete_inline_tag method is called
THEN the inline tag is deleted
"""
vault = Vault(config=test_vault)
assert vault.delete_inline_tag("intext_tag2") == 1
assert vault.metadata.tags == [
"ignored_file_tag2",
"inline_tag_bottom1",
"inline_tag_bottom2",
"inline_tag_top1",
@@ -337,29 +335,65 @@ def test_delete_inline_tag(test_vault) -> None:
]
def test_delete_metadata(test_vault) -> None:
"""Test deleting a metadata key/value."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_delete_inline_tag_2(test_vault) -> None:
"""Test delete_inline_tag() method.
GIVEN a vault object
WHEN the delete_inline_tag method is called with a tag that does not exist
THEN no changes are made
"""
vault = Vault(config=test_vault)
assert vault.delete_inline_tag("no tag") == 0
def test_delete_metadata_1(test_vault) -> None:
"""Test deleting a metadata key/value.
GIVEN a vault object
WHEN the delete_metadata method is called with a key and value
THEN the specified metadata key/value is deleted
"""
vault = Vault(config=test_vault)
assert vault.delete_metadata("top_key1", "top_key1_value") == 1
assert vault.metadata.dict["top_key1"] == []
def test_delete_metadata_2(test_vault) -> None:
"""Test deleting a metadata key/value.
GIVEN a vault object
WHEN the delete_metadata method is called with a key
THEN the specified metadata key is deleted
"""
vault = Vault(config=test_vault)
assert vault.delete_metadata("top_key2") == 1
assert "top_key2" not in vault.metadata.dict
def test_delete_metadata_3(test_vault) -> None:
"""Test deleting a metadata key/value.
GIVEN a vault object
WHEN the delete_metadata method is called with a key and/or value that does not exist
THEN no changes are made
"""
vault = Vault(config=test_vault)
assert vault.delete_metadata("no key") == 0
assert vault.delete_metadata("top_key1", "no_value") == 0
assert vault.delete_metadata("top_key1", "top_key1_value") == 2
assert vault.metadata.dict["top_key1"] == []
assert vault.delete_metadata("top_key2") == 2
assert "top_key2" not in vault.metadata.dict
def test_export_csv_1(tmp_path, test_vault):
"""Test exporting the vault to a CSV file.
def test_export_csv(tmp_path, test_vault):
"""Test exporting the vault to a CSV file."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
GIVEN a vault object
WHEN the export_metadata method is called with a path and export_format of csv
THEN the vault metadata is exported to a CSV file
"""
vault = Vault(config=test_vault)
export_file = Path(f"{tmp_path}/export.csv")
vault.export_metadata(path=export_file, export_format="csv")
@@ -367,12 +401,29 @@ def test_export_csv(tmp_path, test_vault):
assert "frontmatter,date_created,2022-12-22" in export_file.read_text()
def test_export_csv_2(tmp_path, test_vault):
"""Test exporting the vault to a CSV file.
GIVEN a vault object
WHEN the export_metadata method is called with a path that does not exist and export_format of csv
THEN an error is raised
"""
vault = Vault(config=test_vault)
export_file = Path(f"{tmp_path}/does_not_exist/export.csv")
with pytest.raises(typer.Exit):
vault.export_metadata(path=export_file, export_format="csv")
assert export_file.exists() is False
def test_export_json(tmp_path, test_vault):
"""Test exporting the vault to a CSV file."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
"""Test exporting the vault to a JSON file.
GIVEN a vault object
WHEN the export_metadata method is called with a path and export_format of csv
THEN the vault metadata is exported to a JSON file
"""
vault = Vault(config=test_vault)
export_file = Path(f"{tmp_path}/export.json")
vault.export_metadata(path=export_file, export_format="json")
@@ -380,8 +431,13 @@ def test_export_json(tmp_path, test_vault):
assert '"frontmatter": {' in export_file.read_text()
def test_get_filtered_notes(sample_vault) -> None:
"""Test filtering notes."""
def test_get_filtered_notes_1(sample_vault) -> None:
"""Test filtering notes.
GIVEN a vault object
WHEN the get_filtered_notes method is called with a path filter
THEN the notes in scope are filtered
"""
vault_path = sample_vault
config = Config(config_path="tests/fixtures/sample_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
@@ -396,21 +452,66 @@ def test_get_filtered_notes(sample_vault) -> None:
assert len(vault.all_notes) == 13
assert len(vault.notes_in_scope) == 1
def test_get_filtered_notes_2(sample_vault) -> None:
"""Test filtering notes.
GIVEN a vault object
WHEN the get_filtered_notes method is called with a key filter
THEN the notes in scope are filtered
"""
vault_path = sample_vault
config = Config(config_path="tests/fixtures/sample_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
filters = [VaultFilter(key_filter="on_one_note")]
vault = Vault(config=vault_config, filters=filters)
assert len(vault.all_notes) == 13
assert len(vault.notes_in_scope) == 1
def test_get_filtered_notes_3(sample_vault) -> None:
"""Test filtering notes.
GIVEN a vault object
WHEN the get_filtered_notes method is called with a key and a value filter
THEN the notes in scope are filtered
"""
vault_path = sample_vault
config = Config(config_path="tests/fixtures/sample_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
filters = [VaultFilter(key_filter="type", value_filter="book")]
vault = Vault(config=vault_config, filters=filters)
assert len(vault.all_notes) == 13
assert len(vault.notes_in_scope) == 10
def test_get_filtered_notes_4(sample_vault) -> None:
"""Test filtering notes.
GIVEN a vault object
WHEN the get_filtered_notes method is called with a tag filter
THEN the notes in scope are filtered
"""
vault_path = sample_vault
config = Config(config_path="tests/fixtures/sample_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
filters = [VaultFilter(tag_filter="brunch")]
vault = Vault(config=vault_config, filters=filters)
assert len(vault.all_notes) == 13
assert len(vault.notes_in_scope) == 1
def test_get_filtered_notes_5(sample_vault) -> None:
"""Test filtering notes.
GIVEN a vault object
WHEN the get_filtered_notes method is called with a tag and a path filter
THEN the notes in scope are filtered
"""
vault_path = sample_vault
config = Config(config_path="tests/fixtures/sample_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
filters = [VaultFilter(tag_filter="brunch"), VaultFilter(path_filter="inbox")]
vault = Vault(config=vault_config, filters=filters)
assert len(vault.all_notes) == 13
@@ -418,11 +519,13 @@ def test_get_filtered_notes(sample_vault) -> None:
def test_info(test_vault, capsys):
"""Test printing vault information."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
"""Test info() method.
GIVEN a vault object
WHEN the info method is called
THEN the vault info is printed
"""
vault = Vault(config=test_vault)
vault.info()
@@ -433,11 +536,13 @@ def test_info(test_vault, capsys):
def test_list_editable_notes(test_vault, capsys) -> None:
"""Test listing editable notes."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
"""Test list_editable_notes() method.
GIVEN a vault object
WHEN the list_editable_notes() method is called
THEN the editable notes in scope are printed
"""
vault = Vault(config=test_vault)
vault.list_editable_notes()
captured = capsys.readouterr()
@@ -445,17 +550,29 @@ def test_list_editable_notes(test_vault, capsys) -> None:
assert captured.out == Regex(r"\d +test1\.md")
def test_rename_inline_tag(test_vault) -> None:
"""Test renaming an inline tag."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_move_inline_metadata_1(test_vault) -> None:
"""Test move_inline_metadata() method.
assert vault.rename_inline_tag("no tag", "new_tag") == 0
assert vault.rename_inline_tag("intext_tag2", "new_tag") == 2
GIVEN a vault with inline metadata.
WHEN the move_inline_metadata() method is called.
THEN the inline metadata is moved to the top of the file.
"""
vault = Vault(config=test_vault)
assert vault.move_inline_metadata(location=InsertLocation.TOP) == 1
def test_rename_inline_tag_1(test_vault) -> None:
"""Test rename_inline_tag() method.
GIVEN a vault object
WHEN the rename_inline_tag() method is called with a tag that is found
THEN the inline tag is renamed
"""
vault = Vault(config=test_vault)
assert vault.rename_inline_tag("intext_tag2", "new_tag") == 1
assert vault.metadata.tags == [
"ignored_file_tag2",
"inline_tag_bottom1",
"inline_tag_bottom2",
"inline_tag_top1",
@@ -466,32 +583,62 @@ def test_rename_inline_tag(test_vault) -> None:
]
def test_rename_metadata(test_vault) -> None:
"""Test renaming a metadata key/value."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
def test_rename_inline_tag_2(test_vault) -> None:
"""Test rename_inline_tag() method.
GIVEN a vault object
WHEN the rename_inline_tag() method is called with a tag that is not found
THEN the inline tag is not renamed
"""
vault = Vault(config=test_vault)
assert vault.rename_inline_tag("no tag", "new_tag") == 0
def test_rename_metadata_1(test_vault) -> None:
"""Test rename_metadata() method.
GIVEN a vault object
WHEN the rename_metadata() method is called with a key or key/value that is found
THEN the metadata is not renamed
"""
vault = Vault(config=test_vault)
assert vault.rename_metadata("no key", "new_key") == 0
assert vault.rename_metadata("tags", "nonexistent_value", "new_vaule") == 0
assert vault.rename_metadata("tags", "frontmatter_tag1", "new_vaule") == 2
assert vault.metadata.dict["tags"] == [
def test_rename_metadata_2(test_vault) -> None:
"""Test rename_metadata() method.
GIVEN a vault object
WHEN the rename_metadata() method with a key and no value
THEN the metadata key is renamed
"""
vault = Vault(config=test_vault)
assert vault.rename_metadata("tags", "new_key") == 1
assert "tags" not in vault.metadata.dict
assert vault.metadata.dict["new_key"] == [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"new_vaule",
"shared_tag",
"📅/frontmatter_tag3",
]
assert vault.rename_metadata("tags", "new_key") == 2
assert "tags" not in vault.metadata.dict
assert vault.metadata.dict["new_key"] == [
def test_rename_metadata_3(test_vault) -> None:
"""Test rename_metadata() method.
GIVEN a vault object
WHEN the rename_metadata() method is called with a key and value
THEN the metadata key/value is renamed
"""
vault = Vault(config=test_vault)
assert vault.rename_metadata("tags", "frontmatter_tag1", "new_vaule") == 1
assert vault.metadata.dict["tags"] == [
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"new_vaule",
"shared_tag",
"📅/frontmatter_tag3",
@@ -499,23 +646,23 @@ def test_rename_metadata(test_vault) -> None:
def test_transpose_metadata(test_vault) -> None:
"""Test transposing metadata."""
vault_path = test_vault
config = Config(config_path="tests/fixtures/test_vault_config.toml", vault_path=vault_path)
vault_config = config.vaults[0]
vault = Vault(config=vault_config)
"""Test transpose_metadata() method.
assert vault.transpose_metadata(begin=MetadataType.INLINE, end=MetadataType.FRONTMATTER) == 2
GIVEN a vault object
WHEN the transpose_metadata() method is called
THEN the metadata is transposed
"""
vault = Vault(config=test_vault)
assert vault.transpose_metadata(begin=MetadataType.INLINE, end=MetadataType.FRONTMATTER) == 1
assert vault.metadata.inline_metadata == {}
assert vault.metadata.frontmatter == {
"author": ["author name"],
"bottom_key1": ["bottom_key1_value"],
"bottom_key2": ["bottom_key2_value"],
"date_created": ["2022-12-22"],
"frontmatter_Key1": ["author name"],
"frontmatter_Key2": ["article", "note"],
"ignored_frontmatter": ["ignore_me"],
"intext_key": ["intext_value"],
"key📅": ["📅_key_value"],
"shared_key1": [
@@ -527,15 +674,12 @@ def test_transpose_metadata(test_vault) -> None:
"tags": [
"frontmatter_tag1",
"frontmatter_tag2",
"frontmatter_tag3",
"ignored_file_tag1",
"shared_tag",
"📅/frontmatter_tag3",
],
"top_key1": ["top_key1_value"],
"top_key2": ["top_key2_value"],
"top_key3": ["top_key3_value_as_link"],
"type": ["article", "note"],
}
assert (