feat(mnemosyne): add tag management — add_tags, remove_tags, retag
Closes #1236. Three new methods on MnemosyneArchive: - add_tags: add new tags (dedup, case-insensitive) - remove_tags: remove specific tags - retag: replace all tags at once All methods return the entry's final topic list.
This commit is contained in:
@@ -212,6 +212,79 @@ class MnemosyneArchive:
|
||||
def count(self) -> int:
|
||||
return len(self._entries)
|
||||
|
||||
|
||||
|
||||
def add_tags(self, entry_id: str, tags: list[str]) -> list[str]:
|
||||
"""Add tags to an existing entry. Returns the entry's full topic list after addition.
|
||||
|
||||
Args:
|
||||
entry_id: The entry ID to modify.
|
||||
tags: List of tags to add (duplicates are ignored).
|
||||
|
||||
Raises:
|
||||
KeyError: If entry_id does not exist.
|
||||
"""
|
||||
entry = self._entries.get(entry_id)
|
||||
if entry is None:
|
||||
raise KeyError(f"Entry not found: {entry_id}")
|
||||
|
||||
existing_lower = {t.lower() for t in entry.topics}
|
||||
for tag in tags:
|
||||
tag = tag.strip()
|
||||
if tag and tag.lower() not in existing_lower:
|
||||
entry.topics.append(tag)
|
||||
existing_lower.add(tag.lower())
|
||||
|
||||
self._save()
|
||||
return entry.topics
|
||||
|
||||
def remove_tags(self, entry_id: str, tags: list[str]) -> list[str]:
|
||||
"""Remove tags from an existing entry. Returns the entry's topic list after removal.
|
||||
|
||||
Args:
|
||||
entry_id: The entry ID to modify.
|
||||
tags: List of tags to remove (case-insensitive).
|
||||
|
||||
Raises:
|
||||
KeyError: If entry_id does not exist.
|
||||
"""
|
||||
entry = self._entries.get(entry_id)
|
||||
if entry is None:
|
||||
raise KeyError(f"Entry not found: {entry_id}")
|
||||
|
||||
remove_lower = {t.strip().lower() for t in tags}
|
||||
entry.topics = [t for t in entry.topics if t.lower() not in remove_lower]
|
||||
|
||||
self._save()
|
||||
return entry.topics
|
||||
|
||||
def retag(self, entry_id: str, tags: list[str]) -> list[str]:
|
||||
"""Replace all tags on an existing entry. Returns the new topic list.
|
||||
|
||||
Args:
|
||||
entry_id: The entry ID to modify.
|
||||
tags: The new complete tag list (replaces all existing tags).
|
||||
|
||||
Raises:
|
||||
KeyError: If entry_id does not exist.
|
||||
"""
|
||||
entry = self._entries.get(entry_id)
|
||||
if entry is None:
|
||||
raise KeyError(f"Entry not found: {entry_id}")
|
||||
|
||||
# Deduplicate, preserve order
|
||||
seen: set[str] = set()
|
||||
deduped: list[str] = []
|
||||
for tag in tags:
|
||||
tag = tag.strip()
|
||||
if tag and tag.lower() not in seen:
|
||||
deduped.append(tag)
|
||||
seen.add(tag.lower())
|
||||
|
||||
entry.topics = deduped
|
||||
self._save()
|
||||
return entry.topics
|
||||
|
||||
def graph_data(
|
||||
self,
|
||||
topic_filter: Optional[str] = None,
|
||||
|
||||
Reference in New Issue
Block a user