import asyncio import subprocess from datetime import datetime from pathlib import Path class Journal: def __init__( self, vault_path: Path, daily_notes_dir: str, note_path_format: str, message_time_format: str, ) -> None: self.vault_path = vault_path self.notes_path = vault_path / daily_notes_dir self.note_path_format = note_path_format self.message_time_format = message_time_format self._lock = asyncio.Lock() async def append(self, text: str) -> None: async with self._lock: await asyncio.to_thread(self._append_and_sync, text) def _append_and_sync(self, text: str) -> None: self._git("pull", "--rebase") now = datetime.now().astimezone() note_path = self.notes_path / now.strftime(self.note_path_format) note_path.parent.mkdir(parents=True, exist_ok=True) entry = f"- {now.strftime(self.message_time_format)} {text.strip()}\n" with note_path.open("a", encoding="utf-8") as file: file.write(entry) relative_note_path = note_path.relative_to(self.vault_path) self._git("add", str(relative_note_path)) if not self._has_staged_changes(): return self._git("commit", "-m", f"Update daily note {note_path.stem}") self._git("push") def _has_staged_changes(self) -> bool: process = self._git("diff", "--cached", "--quiet", check=False) return process.returncode == 1 def _git(self, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]: return subprocess.run( ("git", *args), cwd=self.vault_path, check=check, text=True, capture_output=True, )