Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Publish to PyPI

on:
release:
types: [published]

jobs:
build-n-publish:
name: Build and publish to PyPI
runs-on: ubuntu-latest
permissions:
id-token: write # Mandatory for Trusted Publishing (OIDC)
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build

- name: Build binary wheel and source tarball
run: python -m build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
6 changes: 3 additions & 3 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
pip install -r requirements.txt
pip install ".[dev]"
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
pylint app tests
9 changes: 6 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@ on:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ".[dev]"
- name: Run tests with pytest
run: |
pytest --cov=app --cov-fail-under=100
PYTHONPATH=. pytest --cov=app --cov-fail-under=100
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
**.log
.coverage
**/__pycache__
htmlcov/
.pytest_cache/
*.egg-info/
.eggs/
*.egg
build/
dist/
10 changes: 9 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
## Development Instructions
To ensure clean code structure and organization:
- All new widget types should be generated in their own file and class within the @app/widgets/ directory.
- All new screens should be generated in their own file and cliss within the @app/screens/ directory.
- Larger functionality requests should be broken down in reusable functions for single pieces of functionality.
- When updating or adding functionality, ensure relevant usage information is added to the project README.
- This application uses a redis cache for data pulled from the MLB-StatsAPI library. If new functionality requires new data_service requests ensure these requests leverage the application caching strategy with reasonable TTLs.

## Testing Instructions
- All changes MUST have their style verified. Use pylint as described in @.github/workflows/pylint.yml after making any code changes.
- All changes MUST have their style verified. Use pylint as described in @.github/workflows/pylint.yml after making any code changes. Ensure tests are included in linting checks.
- All changes MUST be verified by running pytest as described in the README. All tests must pass and code coverage in @app/ must remain at 100%.
- Add unit tests for any new functionality to ensure 100% coverage.
124 changes: 71 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# MLB Box Score TUI

A Python-based Terminal User Interface (TUI) for Major League Baseball (MLB) scores, schedules, and standings. Built with `pytermgui` and the `MLB-StatsAPI`.
A Python-based Terminal User Interface (TUI) for Major League Baseball (MLB) scores, schedules, and standings. Built with `pytermgui` and the `MLB-StatsAPI`, featuring a modular architecture and 100% test coverage.

## Features

- **Yesterday's Schedule:** See all games from the previous day, including matchups and final scores.
- **Today's Schedule:** See upcoming games for the current day, including matchups and status.
- **League Standings:** Display current standings for all 6 MLB divisions (AL and NL).

## Screenshots

*(Insert screenshots of your TUI here)*
- **Full Season Schedule:** Navigate the entire 2026 MLB season through a dedicated calendar view or daily snapshots.
- **Dynamic Standings:** Real-time standings for all 6 MLB divisions (AL and NL), including comprehensive **Wild Card** rankings.
- **Interactive Calendar:** A multi-month calendar view for quick date selection, featuring intuitive **WASD** keyboard navigation and automatic focus management.
- **Smart Caching:** Built-in caching service to minimize API calls and ensure a responsive user experience.
- **Smooth Transitions:** Animated screen transitions for a modern, fluid feel.
- **Robust Architecture:** Surgical, class-based organization of widgets and screens for high maintainability.

## Installation

### Prerequisites
### From PyPI (Recommended)

- Python 3.10+
- A terminal with support for box-drawing characters and ANSI colors.
Install the application directly using `pip`:

```bash
pip install mlb-cli-py
```

### Setup
### Local Setup (Development)

1. **Clone the repository:**
```bash
Expand All @@ -33,80 +35,96 @@ A Python-based Terminal User Interface (TUI) for Major League Baseball (MLB) sco
source venv/bin/activate
```

3. **Install dependencies:**
3. **Install in editable mode:**
```bash
pip install -r requirements.txt
pip install -e ".[dev]"
```

## Usage

Run the application using the main entry point:
After installation, run the application from anywhere in your terminal:

```bash
python3 mlb_cli.py
mlb-cli
```

## Testing

The project includes a comprehensive unit test suite covering data services, screen layouts, and custom widgets.

### Running Tests

To run the full test suite using `pytest`:
## Controls

```bash
pytest
```
The application is designed for rapid keyboard-driven navigation.

To run tests with coverage reporting:
### Global Controls

```bash
pytest --cov=app tests/
```
| Key | Action |
| --- | --- |
| `[` | Previous Day / Previous Calendar Page (with wrapping) |
| `]` | Next Day / Next Calendar Page (with wrapping) |
| `t` | Jump to Today's Schedule |
| `c` | Switch to Calendar View |
| `x` | Toggle Standings View |
| `ESC` | Exit the application |
| `Tab` | Cycle focus between UI components |

The tests utilize `unittest.mock` to ensure isolation from external APIs and UI side effects.
### Calendar Navigation

### Controls
When in the Calendar view, you can use specialized keys for precise date selection:

| Key | Action |
| --- | --- |
| `[` | Switch to Yesterday's Scores |
| `]` | Switch to Today's Schedule |
| `s` | Switch to League Standings |
| `ESC` | Exit the application |
| `Arrow Keys` / `Tab` | Navigate between focusable items (games, divisions, buttons) |
| `Enter` | Trigger navigation buttons |
| `W` | Move focus up one week |
| `A` | Move focus left one day |
| `S` | Move focus down one week |
| `D` | Move focus right one day |
| `Enter` | Select the focused date and view its schedule |

## Project Structure

The project follows a strict modular design optimized for distribution:

```text
mlb-cli-py/
├── mlb_cli.py # Main entry point
├── pyproject.toml # Modern build system configuration and metadata
├── app/
│ ├── models/ # Data fetching and processing (MLB-StatsAPI)
│ ├── widgets/ # Custom TUI components (GameWidget, StandingWidget)
│ └── screens/ # High-level window and layout definitions
├── requirements.txt # Project dependencies
│ ├── mlb_cli.py # Main application controller and TUI entry point
│ ├── models/ # Data services (API fetching, caching, date utilities)
│ ├── widgets/ # Modular TUI components (Game, Standing, Calendar widgets)
│ └── screens/ # Class-based screen definitions (Schedule, Standings, Calendar)
├── tests/ # 100% covered test suite (Unit and integration tests)
└── README.md # You are here!
```

## Contributing
## Development & Testing

Future planned features include:
The project maintains a perfect **10.00/10 Pylint score** and **100% test coverage**.

- Cycling through games to view full box scores.
- Live score updates for ongoing games.
- Team-specific filtering.
### Running Tests

Feel free to open issues or submit pull requests!
To run the full test suite using `pytest`:

## Acknowledgements
```bash
PYTHONPATH=. pytest
```

This project would not be possible without the [MLB-StatsAPI](https://github.com/toddrob99/MLB-StatsAPI) and [PyTermGUI](https://github.com/bczsalba/pytermgui) projects and if you are interested in this application you should explore those projects further!
### Linting

## AI Disclaimer
To verify code quality:

```bash
pylint app tests
```

## Release Workflow

This project uses an automated release pipeline to publish updates to PyPI:

1. **Version Bump:** Update the version in `pyproject.toml`.
2. **GitHub Release:** Create and publish a new Release on GitHub with a version tag (e.g., `v0.1.0`).
3. **Automated Publish:** A GitHub Action is triggered by the release, which:
- Builds the source distribution and wheel.
- Publishes the package to PyPI using **Trusted Publishing (OIDC)**.

## Acknowledgements

This project was generated by AI, specifically the gemini-3-pro-preview model.
This project would not be possible without the [MLB-StatsAPI](https://github.com/toddrob99/MLB-StatsAPI) and [PyTermGUI](https://github.com/bczsalba/pytermgui) projects.

## License

Expand Down
26 changes: 26 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Configuration module for the MLB CLI application.
Centralizes all constants and environment-specific settings.
"""

# TUI Settings
STATIC_WIDTH = 80
INITIAL_HEIGHT = 36

# Redis Settings
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0

# Data Service Settings
LIVE_DATA_TTL = 300 # 5 minutes

# Division name mapping
DIVISION_NAMES = {
200: "AL West",
201: "AL East",
202: "AL Central",
203: "NL West",
204: "NL East",
205: "NL Central"
}
6 changes: 6 additions & 0 deletions app/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
Custom exceptions for the MLB CLI application.
"""

class APIError(Exception):
"""Raised when an error occurs while fetching data from the MLB API."""
29 changes: 29 additions & 0 deletions app/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Logging configuration for the MLB CLI application.
Provides a standard logger that writes to a local log file.
"""
import os
import logging
from logging.handlers import RotatingFileHandler

# Ensure log directory exists
LOG_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOG_FILE = os.path.join(LOG_DIR, "mlb_cli.log")

def get_logger(name):
"""
Configures and returns a logger instance.
"""
logger = logging.getLogger(name)
if not logger.handlers:
logger.setLevel(logging.DEBUG)

# Rotating file handler (5MB per file, keeps 2 backups)
handler = RotatingFileHandler(LOG_FILE, maxBytes=5*1024*1024, backupCount=2)
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)

return logger
Loading
Loading