Files
ObsidianAI/obsidian_automator/note_server.py

137 lines
5.0 KiB
Python
Raw Normal View History

2026-01-03 10:23:05 -06:00
import os
import shutil
from abc import ABC, abstractmethod
from typing import List
class NoteServer(ABC):
@abstractmethod
def list_notes(self, directory: str) -> List[str]:
"""List all markdown files in the directory."""
pass
@abstractmethod
def read_note(self, file_path: str) -> str:
"""Read content of a note."""
pass
@abstractmethod
def move_note(self, file_path: str, target_folder: str) -> str:
"""Move a note to a target folder."""
pass
@abstractmethod
def flag_rewrite(self, file_path: str, reason: str, rewrite_tag: str) -> str:
"""Append a rewrite tag and reason to the note."""
pass
@abstractmethod
def list_subfolders(self, directory: str) -> List[str]:
"""List immediate subdirectories."""
pass
class FileSystemServer(NoteServer):
def list_subfolders(self, directory: str) -> List[str]:
if not os.path.exists(directory):
return []
try:
return [d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d)) and not d.startswith(".")]
except Exception:
return []
def list_notes(self, directory: str) -> List[str]:
notes = []
if not os.path.exists(directory):
return []
for root, _, files in os.walk(directory):
for file in files:
if file.endswith(".md"):
# Only return absolute paths to keep things simple
notes.append(os.path.abspath(os.path.join(root, file)))
return notes
def read_note(self, file_path: str) -> str:
try:
with open(file_path, "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
return f"Error reading file: {str(e)}"
def move_note(self, file_path: str, target_folder: str) -> str:
try:
filename = os.path.basename(file_path)
# Ensure target folder exists
if not os.path.exists(target_folder):
os.makedirs(target_folder)
new_path = os.path.join(target_folder, filename)
# Prevent overwriting by appending timestamp if exists, or just fail safely
if os.path.exists(new_path):
return f"Error: File {filename} already exists in {target_folder}"
shutil.move(file_path, new_path)
return f"Moved {filename} to {target_folder}"
except Exception as e:
return f"Error moving file: {str(e)}"
def flag_rewrite(self, file_path: str, reason: str, rewrite_tag: str) -> str:
try:
# Check if file ends with newline to avoid appending on same line
with open(file_path, "r+", encoding="utf-8") as f:
content = f.read()
prefix = "\n" if content and not content.endswith("\n") else ""
f.write(f"{prefix}\n{rewrite_tag} {reason}\n")
return f"Flagged {os.path.basename(file_path)} for rewrite: {reason}"
except Exception as e:
return f"Error flagging file: {str(e)}"
from .couch_manager import CouchDBManager
class CouchDBNoteServer(NoteServer):
def __init__(self, url, user, password, db_name):
self.manager = CouchDBManager(url, user, password, db_name)
def list_notes(self, directory: str) -> List[str]:
# Directory here acts as a prefix filter
files_dict = self.manager.list_files(prefix_filter=directory)
return list(files_dict.keys())
def list_subfolders(self, directory: str) -> List[str]:
# CouchDB is flat. We simulate folders by looking at paths.
# This is expensive (scan all), but accurate.
all_files_dict = self.manager.list_files()
subfolders = set()
# If directory is "(All Notes)", we look at root folders
# If directory is "Inbox", we look at "Inbox/Subfolder"
prefix = directory if directory != "(All Notes)" else ""
prefix = prefix.strip("/")
for doc_id, path in all_files_dict.items():
# Normalize path
path = path.replace("\\", "/")
if prefix and not path.startswith(prefix + "/"):
continue
# Strip prefix
relative_path = path[len(prefix)+1:] if prefix else path
if "/" in relative_path:
top_level = relative_path.split("/")[0]
subfolders.add(top_level)
return sorted(list(subfolders))
def read_note(self, file_path: str) -> str:
return self.manager.read_file_content(file_path)
def move_note(self, file_path: str, target_folder: str) -> str:
return self.manager.move_file(file_path, target_folder)
def flag_rewrite(self, file_path: str, reason: str, rewrite_tag: str) -> str:
return self.manager.flag_rewrite(file_path, reason, rewrite_tag)