mlb-cli-py 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ """
2
+ Navigation widget for the MLB CLI application.
3
+ """
4
+ import pytermgui as ptg
5
+
6
+
7
+ class NavigationWidget(ptg.Container):
8
+ """
9
+ A persistent navigation bar widget shown at the top of every screen.
10
+ Displays available pages and their hotkeys, highlighting the active one.
11
+ """
12
+ # pylint: disable=too-many-instance-attributes
13
+
14
+ def __init__(self, active_page=None, **kwargs):
15
+ """
16
+ Initializes the NavigationWidget.
17
+
18
+ Args:
19
+ active_page (str, optional): The name of the currently active screen.
20
+ **kwargs: Additional arguments for ptg.Container.
21
+ """
22
+ super().__init__(**kwargs)
23
+ self.border = ptg.boxes.EMPTY
24
+
25
+ if active_page == "standings":
26
+ self.schedule_label = ptg.Label(
27
+ "Back to Schedule [bold][cyan]x[/]",
28
+ parent_align=ptg.HorizontalAlignment.CENTER)
29
+ self.calendar_label = ptg.Label(
30
+ "Back to Calendar [bold][cyan]c[/]",
31
+ parent_align=ptg.HorizontalAlignment.CENTER)
32
+ self.set_widgets([ptg.Splitter(self.schedule_label, self.calendar_label)])
33
+ return
34
+
35
+ if active_page == "calendar":
36
+ self.prev_label = ptg.Label(
37
+ "Prev Page [bold][cyan][[/]",
38
+ parent_align=ptg.HorizontalAlignment.CENTER)
39
+ self.next_label = ptg.Label(
40
+ "Next Page [bold][cyan]][/]",
41
+ parent_align=ptg.HorizontalAlignment.CENTER)
42
+ self.stand_label = ptg.Label(
43
+ "Standings [bold][cyan]x[/]",
44
+ parent_align=ptg.HorizontalAlignment.CENTER)
45
+ self.set_widgets([ptg.Splitter(self.prev_label, self.next_label, self.stand_label)])
46
+ return
47
+
48
+ # Default: Schedule page navigation
49
+ self.prev_label = ptg.Label(
50
+ "Prev [bold][cyan][[/]",
51
+ parent_align=ptg.HorizontalAlignment.CENTER)
52
+ self.next_label = ptg.Label(
53
+ "Next [bold][cyan]][/]",
54
+ parent_align=ptg.HorizontalAlignment.CENTER)
55
+ self.today_label = ptg.Label(
56
+ "Today [bold][cyan]t[/]",
57
+ parent_align=ptg.HorizontalAlignment.CENTER)
58
+ self.cal_label = ptg.Label(
59
+ "Calendar [bold][cyan]c[/]",
60
+ parent_align=ptg.HorizontalAlignment.CENTER)
61
+ self.stand_label = ptg.Label(
62
+ "Standings [bold][cyan]x[/]",
63
+ parent_align=ptg.HorizontalAlignment.CENTER)
64
+
65
+ self.splitter = ptg.Splitter(
66
+ self.prev_label,
67
+ self.next_label,
68
+ self.today_label,
69
+ self.cal_label,
70
+ self.stand_label,
71
+ )
72
+
73
+ self.set_widgets([self.splitter])
@@ -0,0 +1,11 @@
1
+ """
2
+ Separator widget for the MLB CLI application.
3
+ """
4
+ import pytermgui as ptg
5
+
6
+
7
+ class Separator(ptg.Label):
8
+ """A simple horizontal line separator widget."""
9
+
10
+ def __init__(self, **kwargs):
11
+ super().__init__("─" * 10, **kwargs)
@@ -0,0 +1,66 @@
1
+ """
2
+ Standing widget for the MLB CLI application.
3
+ """
4
+ import pytermgui as ptg
5
+ from app.models.data_service import get_team_abbr
6
+
7
+
8
+ class StandingWidget(ptg.Container):
9
+ """
10
+ A container widget displaying the standings for a specific MLB division.
11
+ """
12
+
13
+ def __init__(self, division_data, **kwargs):
14
+ """
15
+ Initializes the StandingWidget with division data.
16
+
17
+ Args:
18
+ division_data (dict): Dictionary containing division name and team records.
19
+ **kwargs: Additional arguments for ptg.Container.
20
+ """
21
+ super().__init__(**kwargs)
22
+ if not division_data:
23
+ self.inner_widgets = [ptg.Label("No Data")]
24
+ self.set_widgets(self.inner_widgets)
25
+ return
26
+
27
+ self._selectables_length = 1
28
+ self.border = ptg.boxes.SINGLE
29
+
30
+ name = division_data.get('div_name', 'Unknown')
31
+ # Replace full league names with abbreviations
32
+ name = name.replace(
33
+ "American League",
34
+ "AL").replace(
35
+ "National League",
36
+ "NL")
37
+
38
+ widgets = [ptg.Label(f"[bold]{name}[/]")]
39
+ widgets.append(self._create_header())
40
+
41
+ for team in division_data.get('teams', []):
42
+ widgets.append(self._create_team_row(team))
43
+
44
+ self.set_widgets(widgets)
45
+ self.inner_widgets = widgets
46
+
47
+ def _create_header(self):
48
+ """Creates the header row for the standings."""
49
+ tm_lbl = "TM".ljust(4)
50
+ w_lbl = "W".rjust(3)
51
+ l_lbl = "L".rjust(3)
52
+ gb_lbl = "GB".rjust(4)
53
+ pct_lbl = "PCT".rjust(6)
54
+ l10_lbl = "L10".rjust(6)
55
+ return ptg.Label(f"[bold]{tm_lbl} {w_lbl} {l_lbl} {gb_lbl} {pct_lbl} {l10_lbl}[/]")
56
+
57
+ def _create_team_row(self, team):
58
+ """Creates a data row for a single team."""
59
+ # Use team abbreviation instead of full name
60
+ abbr = get_team_abbr(team['team_id']).ljust(4)
61
+ w = str(team['w']).rjust(3)
62
+ l = str(team['l']).rjust(3)
63
+ gb = str(team['gb']).rjust(4)
64
+ pct = str(team['pct']).rjust(6)
65
+ l10 = str(team['l10']).rjust(6)
66
+ return ptg.Label(f"{abbr} {w} {l} {gb} {pct} {l10}")
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlb-cli-py
3
+ Version: 0.1.0
4
+ Summary: A modern TUI for MLB scores, standings, and schedules
5
+ Author: Conor Cleary
6
+ License: Copyright 2026 Conor Cleary
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13
+
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Environment :: Console
18
+ Classifier: Topic :: Terminals
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pytermgui==7.7.4
22
+ Requires-Dist: MLB-StatsAPI==1.9.0
23
+ Requires-Dist: redis==5.0.4
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest==8.2.0; extra == "dev"
26
+ Requires-Dist: pytest-cov==5.0.0; extra == "dev"
27
+ Requires-Dist: fakeredis==2.23.2; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # MLB Box Score TUI
31
+
32
+ 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.
33
+
34
+ ## Features
35
+
36
+ - **Full Season Schedule:** Navigate the entire 2026 MLB season through a dedicated calendar view or daily snapshots.
37
+ - **Dynamic Standings:** Real-time standings for all 6 MLB divisions (AL and NL), including comprehensive **Wild Card** rankings.
38
+ - **Interactive Calendar:** A multi-month calendar view for quick date selection, featuring intuitive **WASD** keyboard navigation and automatic focus management.
39
+ - **Smart Caching:** Built-in caching service to minimize API calls and ensure a responsive user experience.
40
+ - **Smooth Transitions:** Animated screen transitions for a modern, fluid feel.
41
+ - **Robust Architecture:** Surgical, class-based organization of widgets and screens for high maintainability.
42
+
43
+ ## Installation
44
+
45
+ ### From PyPI (Recommended)
46
+
47
+ Install the application directly using `pip`:
48
+
49
+ ```bash
50
+ pip install mlb-cli-py
51
+ ```
52
+
53
+ ### Local Setup (Development)
54
+
55
+ 1. **Clone the repository:**
56
+ ```bash
57
+ git clone https://github.com/conorpcleary/mlb-cli-py.git
58
+ cd mlb-cli-py
59
+ ```
60
+
61
+ 2. **Create and activate a virtual environment:**
62
+ ```bash
63
+ python3 -m venv venv
64
+ source venv/bin/activate
65
+ ```
66
+
67
+ 3. **Install in editable mode:**
68
+ ```bash
69
+ pip install -e ".[dev]"
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ After installation, run the application from anywhere in your terminal:
75
+
76
+ ```bash
77
+ mlb-cli
78
+ ```
79
+
80
+ ## Controls
81
+
82
+ The application is designed for rapid keyboard-driven navigation.
83
+
84
+ ### Global Controls
85
+
86
+ | Key | Action |
87
+ | --- | --- |
88
+ | `[` | Previous Day / Previous Calendar Page (with wrapping) |
89
+ | `]` | Next Day / Next Calendar Page (with wrapping) |
90
+ | `t` | Jump to Today's Schedule |
91
+ | `c` | Switch to Calendar View |
92
+ | `x` | Toggle Standings View |
93
+ | `ESC` | Exit the application |
94
+ | `Tab` | Cycle focus between UI components |
95
+
96
+ ### Calendar Navigation
97
+
98
+ When in the Calendar view, you can use specialized keys for precise date selection:
99
+
100
+ | Key | Action |
101
+ | --- | --- |
102
+ | `W` | Move focus up one week |
103
+ | `A` | Move focus left one day |
104
+ | `S` | Move focus down one week |
105
+ | `D` | Move focus right one day |
106
+ | `Enter` | Select the focused date and view its schedule |
107
+
108
+ ## Project Structure
109
+
110
+ The project follows a strict modular design optimized for distribution:
111
+
112
+ ```text
113
+ mlb-cli-py/
114
+ ├── pyproject.toml # Modern build system configuration and metadata
115
+ ├── app/
116
+ │ ├── mlb_cli.py # Main application controller and TUI entry point
117
+ │ ├── models/ # Data services (API fetching, caching, date utilities)
118
+ │ ├── widgets/ # Modular TUI components (Game, Standing, Calendar widgets)
119
+ │ └── screens/ # Class-based screen definitions (Schedule, Standings, Calendar)
120
+ ├── tests/ # 100% covered test suite (Unit and integration tests)
121
+ └── README.md # You are here!
122
+ ```
123
+
124
+ ## Development & Testing
125
+
126
+ The project maintains a perfect **10.00/10 Pylint score** and **100% test coverage**.
127
+
128
+ ### Running Tests
129
+
130
+ To run the full test suite using `pytest`:
131
+
132
+ ```bash
133
+ PYTHONPATH=. pytest
134
+ ```
135
+
136
+ ### Linting
137
+
138
+ To verify code quality:
139
+
140
+ ```bash
141
+ pylint app tests
142
+ ```
143
+
144
+ ## Release Workflow
145
+
146
+ This project uses an automated release pipeline to publish updates to PyPI:
147
+
148
+ 1. **Version Bump:** Update the version in `pyproject.toml`.
149
+ 2. **GitHub Release:** Create and publish a new Release on GitHub with a version tag (e.g., `v0.1.0`).
150
+ 3. **Automated Publish:** A GitHub Action is triggered by the release, which:
151
+ - Builds the source distribution and wheel.
152
+ - Publishes the package to PyPI using **Trusted Publishing (OIDC)**.
153
+
154
+ ## Acknowledgements
155
+
156
+ This project would not be possible without the [MLB-StatsAPI](https://github.com/toddrob99/MLB-StatsAPI) and [PyTermGUI](https://github.com/bczsalba/pytermgui) projects.
157
+
158
+ ## License
159
+
160
+ MIT License - see [LICENSE](LICENSE) for details.
@@ -0,0 +1,29 @@
1
+ app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ app/config.py,sha256=sOJF9v0EUmFhASFbgyidlhBuDfkN5-YLd7BlckkHRyE,476
3
+ app/exceptions.py,sha256=oGk3AtYy1qh8m9SEyB0uHDn3v50gW08fx_2mrTMA4Ec,159
4
+ app/logger.py,sha256=M3Lt5LyrS1-ROQUUolFgTyd9LOEN5q9XM5_eYMm4Ohg,891
5
+ app/mlb_cli.py,sha256=wuQkLv-kPDjJmCmivStDdIl618jCBFVIlULfNODddcM,11938
6
+ app/state.py,sha256=UotA4pH22UGfmNQ7jCLy2XWvhU-KzzuYreKnwWR0ze4,2428
7
+ app/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ app/models/base_data_source.py,sha256=aJEzSR_SWjDzGku2h4UzkYZUj4YjkojupR7gczXmhFI,768
9
+ app/models/cache_service.py,sha256=RMzZJmVIsJ6iIAyIXK_DSq1Z5nT9hYoCpss7QDDw5vQ,1408
10
+ app/models/data_service.py,sha256=jIdYD_APLpyTnDiYnzEJjf3Cx2_tGmLrbDd9jmHAUl0,5050
11
+ app/models/statsapi_source.py,sha256=uM7tYRFfBqYurP09SbAP4YOx7Ht_rpeV1JnzkdBhQFU,3012
12
+ app/screens/__init__.py,sha256=CVViC48tGT8OIqRGh519op59l8caenBLLH75zwSlSh8,307
13
+ app/screens/calendar_screen.py,sha256=9UWEEcze7bYTyugTSwqmFEd9UlTsXVfOXxhVT7zh9bo,1892
14
+ app/screens/error_screen.py,sha256=h4V4OFlLDuVfsQEXs29HjKUFroo0D5OPjCfMbghXBgE,1467
15
+ app/screens/schedule_screen.py,sha256=WC5ElTc10gjN7pnO7_yMcXPoiqL6LHng9AGTGjbN-Bc,901
16
+ app/screens/standings_screen.py,sha256=7AI74wv2N0cgCwE43Mvyd01qx7clBcwo1-avy8r8b7w,1361
17
+ app/widgets/__init__.py,sha256=kWfFCgLk_zGfgU9ipLwpVJqc8raehdjdBFaL2bA1TI8,615
18
+ app/widgets/animations.py,sha256=cbu1hL-A4YYEo5OMECRTky5Bvzk4uY_O6OF3USh7l_k,2679
19
+ app/widgets/calendar_widget.py,sha256=nUiyqRg7m72TNRcUsjkl6parw4BWSnbrqZvG9gNxn4c,2495
20
+ app/widgets/game_widget.py,sha256=rYWt9FHaSYKFPF-Ud6WsEGsHVMfTRUKH4jlDcAd6fW0,4502
21
+ app/widgets/navigation_widget.py,sha256=8j6GpsMWxWTD2fZM3lADAS5O72Dv2B5NMpRRxowyAoM,2701
22
+ app/widgets/separator.py,sha256=KDB6ku9gDTcV9Lqi7NWKKsS656zEcyrUCoY8qtnHtGo,243
23
+ app/widgets/standing_widget.py,sha256=__AYfPcpCpCJ93O1lVyUL0MnKIEG0TazXDxRM-MTm6k,2164
24
+ mlb_cli_py-0.1.0.dist-info/licenses/LICENSE,sha256=pL3q4twDCb-zVAcdm7MhjZF6ORgx9a2b0NBa5_zTesQ,1060
25
+ mlb_cli_py-0.1.0.dist-info/METADATA,sha256=MyWuJWmY_tRUb48VzlbnEtVrSi808q7qJUUMiB7KGKc,5844
26
+ mlb_cli_py-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
27
+ mlb_cli_py-0.1.0.dist-info/entry_points.txt,sha256=5GX60LnKQH4tJfZWNPa6JW2eKCg3YP9o8Yr7Rko05qc,45
28
+ mlb_cli_py-0.1.0.dist-info/top_level.txt,sha256=io9g7LCbfmTG1SFKgEOGXmCFB9uMP2H5lerm0HiHWQE,4
29
+ mlb_cli_py-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mlb-cli = app.mlb_cli:main
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Conor Cleary
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ app