# Python Showcase Project: CLI Journal

This project demonstrates how to combine multiple Python modules and concepts into a functional Command Line Interface (CLI) tool.

**Goal:** Create a tool named `pyjournal` that allows users to add and list journal entries from their terminal.

## The Code

Save the following code as `pyjournal.py`.

```python
import argparse
import json
import logging
import sys
from datetime import datetime
from pathlib import Path
from typing import List, Dict

# 1. Logging Setup
logging.basicConfig(level=logging.INFO, format='%(message)s')

# 2. Class Definition (OOP)
class JournalEntry:
    def __init__(self, title: str, content: str, timestamp: str = None):
        self.title = title
        self.content = content
        self.timestamp = timestamp or datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    def to_dict(self) -> Dict[str, str]:
        return {
            "title": self.title,
            "content": self.content,
            "timestamp": self.timestamp
        }

    @classmethod
    def from_dict(cls, data: Dict[str, str]):
        return cls(data["title"], data["content"], data["timestamp"])

# 3. File Management Class
class JournalManager:
    def __init__(self, filepath: Path):
        self.filepath = filepath
        self.entries: List[JournalEntry] = []
        self.load()

    def load(self):
        if not self.filepath.exists():
            return
        try:
            # 4. JSON Handling
            content = self.filepath.read_text()
            data = json.loads(content)
            self.entries = [JournalEntry.from_dict(e) for e in data]
        except (json.JSONDecodeError, OSError) as e:
            logging.error(f"Error loading journal: {e}")

    def save(self):
        try:
            data = [e.to_dict() for e in self.entries]
            # 4. JSON Handling
            self.filepath.write_text(json.dumps(data, indent=4))
        except OSError as e:
            logging.error(f"Error saving journal: {e}")

    def add_entry(self, title: str, content: str):
        entry = JournalEntry(title, content)
        self.entries.append(entry)
        self.save()
        logging.info(f"Entry '{title}' added.")

    def list_entries(self):
        if not self.entries:
            print("No entries found.")
            return
        print(f"{'TIMESTAMP':<20} | {'TITLE':<20} | {'CONTENT'}")
        print("-" * 60)
        for entry in self.entries:
            print(f"{entry.timestamp:<20} | {entry.title:<20} | {entry.content}")

def main():
    # 5. Argument Parsing
    parser = argparse.ArgumentParser(description="Simple CLI Journal")
    subparsers = parser.add_subparsers(dest="command", required=True)

    # 'add' command
    add_parser = subparsers.add_parser("add", help="Add a new entry")
    add_parser.add_argument("title", help="Title of the entry")
    add_parser.add_argument("content", help="Content of the entry")

    # 'list' command
    subparsers.add_parser("list", help="List all entries")

    args = parser.parse_args()

    # 6. Pathlib for cross-platform paths
    journal_file = Path.home() / "my_journal.json"
    manager = JournalManager(journal_file)

    if args.command == "add":
        manager.add_entry(args.title, args.content)
    elif args.command == "list":
        manager.list_entries()

if __name__ == "__main__":
    main()
```

## Concepts Used

1.  **[[programming/python/modules/logging-module|Logging]]**: Used instead of `print` for status messages, allowing for different severity levels.
2.  **[[programming/python/modules/classes|Classes & OOP]]**: The `JournalEntry` class encapsulates the data, while `JournalManager` encapsulates the logic for handling that data.
3.  **[[programming/python/modules/pathlib-module|Pathlib]]**: Used `Path.home()` to find the user's home directory reliably on Windows, Mac, or Linux.
4.  **[[programming/python/modules/json-module|JSON]]**: Used `json.loads` and `json.dumps` to serialize the Python objects into a text format for storage.
5.  **[[programming/python/modules/argparse-module|Argparse]]**: Used `subparsers` to create distinct commands (`add` vs `list`) with their own specific arguments.
6.  **[[programming/python/type-hinting|Type Hinting]]**: Used `List` and `Dict` to make the code self-documenting and IDE-friendly.

## How to Run

```bash
python pyjournal.py add "First Day" "Started learning Python today."
python pyjournal.py add "Progress" "Wrote a CLI tool."
python pyjournal.py list
```

[[programming/python/python]]