commit e27893ad817031102b4f1eb5a641bf38256e0ebc Author: Luka Jankovic Date: Tue Nov 4 00:21:04 2025 +0100 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b47211 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Journal + +A simple markdown journal entry generator, with the following folder structure: + +``` +. +└2025 + └01 + ├01_monday.md + └02_tuesday.md + ... +``` + +## Usage + +``` +./journal.py [option] +``` + +| `option` | Action | +| -------- | -------------------------------- | +| empty | Create an entry for the next day | +| `-t` | Create an entry for today's date | +| `-n` | Create an entry for the next day | + +## Requirements + +* Python 3.6 or higher +* `EDITOR` environment variable defined diff --git a/journal.py b/journal.py new file mode 100755 index 0000000..4cb0b86 --- /dev/null +++ b/journal.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +import os +import sys +from datetime import date, timedelta +import subprocess +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 month_name(month: int) -> str: + return date(2000, month, 1).strftime("%B") + +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.""" + parts = latest_path.stem.split("_")[0].split("-") + # stem example: 04_tuesday -> day=4 + # full path: year/month/date_day.md + year = int(latest_path.parent.parent.name) + month = int(latest_path.parent.name) + day = int(parts[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): + if path.exists(): + return # Don't overwrite + header = f"# {entry_date.strftime('%A')}, {ordinal(entry_date.day)} {entry_date.strftime('%B %Y')}\n\n" + path.write_text(header, encoding="utf-8") + +# --- Main logic --- + +def main(): + base_path = Path.cwd() + + # Parse args + arg = sys.argv[1] if len(sys.argv) > 1 else None + latest = get_latest_entry(base_path) + + if arg == "-t": + entry_date = date.today() + elif arg == "-n": + if latest: + entry_date = next_date_from_latest(latest) + else: + entry_date = date.today() + else: + # Default behavior + 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) + + # Open in text editor + editor = os.environ.get("EDITOR", "nano") + subprocess.run([editor, str(entry_path)]) + +if __name__ == "__main__": + main() +