104 lines
3.1 KiB
Python
Executable file
104 lines
3.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
||
import os
|
||
import sys
|
||
import random
|
||
import subprocess
|
||
from datetime import date, timedelta
|
||
from pathlib import Path
|
||
|
||
# --- Helper functions ---
|
||
|
||
|
||
def ordinal(n: int) -> str:
|
||
"""Return ordinal suffix for a given day number."""
|
||
if 10 <= n % 100 <= 20:
|
||
suffix = "th"
|
||
else:
|
||
suffix = {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
|
||
return f"{n}{suffix}"
|
||
|
||
|
||
def get_latest_entry(base_path: Path):
|
||
"""Return the latest existing journal file, or None if none exist."""
|
||
entries = sorted(base_path.glob("*/*/*.md"))
|
||
return entries[-1] if entries else None
|
||
|
||
|
||
def next_date_from_latest(latest_path: Path):
|
||
"""Compute the next date after the latest entry."""
|
||
year = int(latest_path.parent.parent.name)
|
||
month = int(latest_path.parent.name)
|
||
day = int(latest_path.stem.split("_")[0])
|
||
d = date(year, month, day)
|
||
return d + timedelta(days=1)
|
||
|
||
|
||
def make_entry_path(base_path: Path, entry_date: date):
|
||
year = entry_date.year
|
||
month = entry_date.month
|
||
day = entry_date.day
|
||
weekday = entry_date.strftime("%A").lower()
|
||
folder = base_path / f"{year}" / f"{month:02d}"
|
||
folder.mkdir(parents=True, exist_ok=True)
|
||
filename = f"{day:02d}_{weekday}.md"
|
||
return folder / filename
|
||
|
||
|
||
def write_entry_header(path: Path, entry_date: date):
|
||
"""Create new entry file with date header if it doesn’t exist."""
|
||
if path.exists():
|
||
return
|
||
header = f"# {entry_date.strftime('%A')}, {ordinal(entry_date.day)} {entry_date.strftime('%B %Y')}\n\n"
|
||
path.write_text(header, encoding="utf-8")
|
||
|
||
|
||
def pick_random_entry(base_path: Path):
|
||
"""Return path to a random existing entry, or None if none exist."""
|
||
entries = sorted(base_path.glob("*/*/*.md"))
|
||
return random.choice(entries) if entries else None
|
||
|
||
|
||
# --- Main logic ---
|
||
|
||
|
||
def main():
|
||
base_path = Path.cwd()
|
||
base_path.mkdir(exist_ok=True)
|
||
|
||
arg = sys.argv[1] if len(sys.argv) > 1 else None
|
||
latest = get_latest_entry(base_path)
|
||
|
||
if arg == "-t":
|
||
entry_date = date.today()
|
||
entry_path = make_entry_path(base_path, entry_date)
|
||
write_entry_header(entry_path, entry_date)
|
||
elif arg == "-n":
|
||
if latest:
|
||
entry_date = next_date_from_latest(latest)
|
||
else:
|
||
entry_date = date.today()
|
||
entry_path = make_entry_path(base_path, entry_date)
|
||
write_entry_header(entry_path, entry_date)
|
||
elif arg == "-r":
|
||
entry_path = pick_random_entry(base_path)
|
||
if not entry_path:
|
||
print("No journal entries found to review.")
|
||
sys.exit(1)
|
||
today = date.today()
|
||
review_header = f"\n## {today.strftime('%A')}, {ordinal(today.day)} {today.strftime('%B %Y')}\n\n"
|
||
with entry_path.open("a", encoding="utf-8") as f:
|
||
f.write(review_header)
|
||
else:
|
||
if latest:
|
||
entry_date = next_date_from_latest(latest)
|
||
else:
|
||
entry_date = date.today()
|
||
entry_path = make_entry_path(base_path, entry_date)
|
||
write_entry_header(entry_path, entry_date)
|
||
|
||
editor = os.environ.get("EDITOR", "nano")
|
||
subprocess.run([editor, str(entry_path)])
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|