Files
obsidian-metadata/src/obsidian_metadata/models/metadata.py
Nathaniel Landau 2e61a92ad1 feat: greatly improve capturing all formats of inline metadata (#41)
feat: greatly improve capturing metadata all formats of inline metadata
2023-05-05 13:09:59 -04:00

139 lines
4.6 KiB
Python

"""Work with metadata items."""
import re
from io import StringIO
import rich.repr
from ruamel.yaml import YAML
from obsidian_metadata.models.enums import MetadataType, Wrapping
def dict_to_yaml(dictionary: dict[str, list[str]], sort_keys: bool = False) -> str:
"""Return the a dictionary of {key: [values]} as a YAML string.
Args:
dictionary (dict[str, list[str]]): Dictionary of {key: [values]}.
sort_keys (bool, optional): Sort the keys. Defaults to False.
Returns:
str: Frontmatter as a YAML string.
sort_keys (bool, optional): Sort the keys. Defaults to False.
"""
if sort_keys:
dictionary = dict(sorted(dictionary.items()))
for key, value in dictionary.items():
if len(value) == 1:
dictionary[key] = value[0] # type: ignore [assignment]
yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
string_stream = StringIO()
yaml.dump(dictionary, string_stream)
yaml_value = string_stream.getvalue()
string_stream.close()
if yaml_value == "{}\n":
return ""
return yaml_value
@rich.repr.auto
class InlineField:
"""Representation of a single inline field.
Attributes:
meta_type (MetadataType): Metadata category.
clean_key (str): Cleaned key - Key without surround markdown
key (str): Metadata key - Complete key found in note
key_close (str): Closing key markdown.
key_open (str): Opening key markdown.
normalized_key (str): Key converted to lowercase w. spaces replaced with dashes
normalized_value (str): Value stripped of leading and trailing whitespace.
value (str): Metadata value - Complete value found in note.
wrapping (Wrapping): Inline metadata may be wrapped with [] or ().
"""
def __init__(
self,
meta_type: MetadataType,
key: str,
value: str,
wrapping: Wrapping = Wrapping.NONE,
is_changed: bool = False,
) -> None:
self.meta_type = meta_type
self.key = key
self.value = value
self.wrapping = wrapping
self.is_changed = is_changed
# Clean keys of surrounding markdown and convert to lowercase
self.clean_key, self.normalized_key, self.key_open, self.key_close = (
self._clean_key(self.key) if self.key else (None, None, "", "")
)
# Normalize value for display
self.normalized_value = "-" if re.match(r"^\s*$", self.value) else self.value.strip()
def __rich_repr__(self) -> rich.repr.Result: # pragma: no cover
"""Rich representation of the inline field."""
yield "clean_key", self.clean_key
yield "is_changed", self.is_changed
yield "key_close", self.key_close
yield "key_open", self.key_open
yield "key", self.key
yield "meta_type", self.meta_type.value
yield "normalized_key", self.normalized_key
yield "normalized_value", self.normalized_value
yield "value", self.value
yield "wrapping", self.wrapping.value
def __eq__(self, other: object) -> bool:
"""Compare two InlineField objects."""
if not isinstance(other, InlineField):
return NotImplemented
return (
self.key == other.key
and self.value == other.value
and self.meta_type == other.meta_type
)
def __hash__(self) -> int:
"""Hash the InlineField object."""
return hash((self.key, self.value, self.meta_type))
def _clean_key(self, text: str) -> tuple[str, str, str, str]:
"""Remove markdown from the key.
Creates the following attributes:
clean_key : The key stripped of opening and closing markdown
normalized_key: The key converted to lowercase with spaces replaced with dashes
key_open : The opening markdown
key_close : The closing markdown.
Args:
text (str): Key to clean.
Returns:
tuple[str, str, str, str]: Cleaned key, normalized key, opening markdown, closing markdown.
"""
cleaned = text
if tmp := re.search(r"^([\*#_ `~]+)", text):
key_open = tmp.group(0)
cleaned = re.sub(rf"^{re.escape(key_open)}", "", text)
else:
key_open = ""
if tmp := re.search(r"([\*#_ `~]+)$", text):
key_close = tmp.group(0)
cleaned = re.sub(rf"{re.escape(key_close)}$", "", cleaned)
else:
key_close = ""
normalized = cleaned.replace(" ", "-").lower()
return cleaned, normalized, key_open, key_close