diff --git a/README.md b/README.md
index b3d1a61..4494e6b 100644
--- a/README.md
+++ b/README.md
@@ -14,11 +14,17 @@ This is a Python library for interacting with the DataGolf APIs. DataGolf is a g
This is being built to support some ML projects I am working on. I will be
continuing to add more endpoints as I need them. If you have a specific endpoint you need, please open a ticket for submit a PR.
+---
+## Developer Note:
+This is in development. Code structure will change as I get
+all the endpoints added. So expect some renaming and convention changes.
+### Contact
+Im available on [Bluesky](https://bsky.app/profile/coreyjs.dev) for any questions or just general chats about enhancements.
---
-# Usage
-(Currently this only supports JSON formats, CSV is in progress)
+# Usage + Installation & Setup
+(Currently this only supports JSON formats, CSV is on the roadmap)
```python
pip install data_golf
@@ -33,16 +39,303 @@ client = DataGolfClient(api_key="YOUR_API_KEY")
client = DataGolfClient(api_key="YOUR_API_KEY", verbose=True)
```
+---
+
+# Main Modules
+
+These modules map directly to the [DataGolf API Documentation](https://datagolf.com/api-access) available on their site:
+
+1. General
+2. Predictions
+3. Live Predictions
+
+The Data Golf API is a paid service via [DataGolf.com](https://datagolf.com/api-access), there they will provide you with an API Key. This library is
+only a helper utility, to make interacting and consuming the API easier.
+---
# General APIs
+### Player List
+
+
+ API Endpoint Info
+
+**Endpoint:** `/get-player-list`
+**Method:** GET
+**Formats:** JSON
+
+
+
```python
-# Player List
players = client.general.player_list()
+```
+
+
+### Current Season Tour Schedule
+
+
+ API Endpoint Info
+
+**Endpoint:** `/get-schedule`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|-------|--------|---------------------------------------------------------------------------|
+| tour | str | all, pga, euro, kft, alt, liv |
-# Current Season Tour Schedule
+
+
+```python
# Can use optinal parameter 'tour' to filter by tour: pga, euro, kft, alt, liv
tour_schedule = client.general.tour_schedule()
tour_schedule = client.general.tour_schedule(tour="pga")
tour_schedule = client.general.tour_schedule(tour="liv")
-```
\ No newline at end of file
+```
+
+### Field Updates
+
+
+ API Endpoint Info
+
+**Endpoint:** `/field-updates`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|-------|--------|---------------------------------------------------------------------------|
+| tour | str | all, pga, euro, kft, alt, liv |
+
+
+
+```python
+# tour = pga (default), euro, kft, opp, alt
+rsp = client.general.field_updates() # defaults to pga
+rsp = client.general.field_updates(tour="kft")
+```
+
+---
+
+# Model Prediction APIs
+
+## Rankings
+
+
+ API Endpoint Info
+
+
+**Endpoint:** `/preds/get-dg-rankings`
+**Method:** GET
+**Formats:** JSON
+
+
+
+```python
+rankings = client.predictions.rankings()
+```
+
+## Pre Tournament Predictions
+
+
+ API Endpoint Info
+
+**Endpoint:** `/preds/pre-tournament`
+**Method:** GET
+**Formats:** JSON
+
+
+
+```python
+rsp = client.predictions.pre_tournament()
+
+rsp = client.predictions.pre_tournament(
+ tour='pga',
+ dead_heat=True,
+ odds_format='american'
+)
+```
+
+## Pre Tournament Prediction Archive
+
+
+ API Endpoint Info
+
+**Endpoint:** `/preds/pre-tournament-archive`
+**Method:** GET
+**Formats:** JSON
+
+
+
+```python
+# Supports optional parameters event_id:, year:, odds_format:
+rsp = client.predictions.pre_tournament_pred_archive()
+
+rsp = client.predictions.pre_tournament_pred_archive(
+ year=2021,
+)
+```
+
+## Player Skill Decomposition
+
+
+ API Endpoint Info
+
+**Endpoint:** `/preds/player-decompositions`
+**Method:** GET
+**Formats:** JSON
+
+
+
+```python
+# Supports optional parameters tour:
+rsp = client.predictions.player_skill_decompositions()
+rsp = client.predictions.player_skill_decompositions(tour='alt')
+```
+
+## Player Skill Ratings
+
+
+ API Endpoint Info
+
+**Endpoint:** `/preds/skill-ratings`
+**Method:** GET
+**Formats:** JSON
+
+
+
+```python
+# Supports optional param display: (value, rank)
+rsp = client.predictions.player_skill_ratings()
+rsp = client.predictions.player_skill_ratings(display="rank")
+```
+
+## Detailed Approach Skill
+
+
+ API Endpoint Info
+
+**Endpoint:** `/preds/approach-skill`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|--------|--------|---------------------------------------------------------------------------|
+| period | str | l24 (last 24 months) (default),
l12 (last 12 months), ytd (year to date) |
+
+
+
+```python
+rsp = client.predictions.detailed_approach_skill()
+rsp = client.predictions.detailed_approach_skill(period='ytd')
+```
+
+## Fantasy Projections
+
+ API Endpoint Info
+
+
+**Endpoint:** `/preds/fantasy-projection-defaults`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|-------|-----|---------------------------------------------------------------|
+| tour | str | pga (default), euro, opp (opposite field PGA TOUR event), alt |
+| site | str | draftkings (default), fanduel, yahoo |
+| slate | str | main (default), showdown, showdown_late, weekend, captain |
+
+
+
+```python
+rsp = client.predictions.fantasy_projection()
+rsp = client.predictions.fantasy_projection(tour='pga', site='fanduel', slate='showdown')
+```
+
+---
+
+# Live Predictions
+
+
+### Live Model Predictions
+
+
+ API Endpoint Info
+
+
+**Endpoint:** `/preds/in-play`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|-------------|------|---------------------------------------------------------------|
+| tour | str | pga (default), euro, opp (opposite field PGA TOUR event), alt |
+| dead_head | bool | False (default), True |
+| odds_format | str | percent (default), american, decimal, fraction |
+
+
+
+```python
+data = dg.live_predictions.live_in_play()
+data = dg.live_predictions.live_in_play(tour='kft', odds_format='american')
+```
+
+
+### Live Tournament Stats
+
+Returns live strokes-gained and traditional stats for every player during PGA Tour tournaments.
+
+
+
+ API Endpoint Info
+
+
+**Endpoint:** `/preds/live-tournament-stats`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|---------|---------|---------------------------------------------------------------|
+| stats | csv str | Comma-separated list of statistics to be returned. Supports: sg_putt, sg_arg, sg_app, sg_ott, sg_t2g, sg_bs, sg_total,
distance, accuracy, gir, prox_fw, prox_rgh, scrambling |
+| round | str | event_avg, 1, 2, 3, 4 |
+| display | str | value (default), rank |
+
+
+
+```python
+data = dg.live_predictions.live_tournament_stats()
+data = dg.live_predictions.live_tournament_stats(stats="sq_arg, sg_bs", disppaly="rank")
+```
+
+
+### Live Hole Scoring Distruibution
+
+Returns live hole scoring averages and distrubutions (birdies, pars, bogeys, etc.) broken down by tee time wave.
+
+
+
+ API Endpoint Info
+
+
+**Endpoint:** `/preds/live-hole-stats`
+**Method:** GET
+**Formats:** JSON
+
+| Param | Type | Ex |
+|---------|------|--------------------------------------------------------------|
+| tour | str | pga (default), euro, opp (opposite field PGA TOUR event), kft, alt |
+| round | str | event_avg, 1, 2, 3, 4 |
+| display | str | value (default), rank |
+
+
+
+```python
+data = dg.live_predictions.live_hole_stats()
+data = dg.live_predictions.live_hole_stats(tour='kft')
+```
+
+
+
+
+
+
+
diff --git a/data_golf/api/general.py b/data_golf/api/general.py
index 13a8c7a..25fbe7f 100644
--- a/data_golf/api/general.py
+++ b/data_golf/api/general.py
@@ -28,6 +28,7 @@ def field_updates(self, tour: str = "pga", f_format: str = "json") -> List[dict]
Up-to-the-minute field updates on WDs, Monday Qualifiers, tee times, and fantasy salaries for PGA Tour,
European Tour, and Korn Ferry Tour events. Includes data golf IDs and tour-specific IDs for
each player in the field.
+ :type tour: str := pga (default), euro, kft, opp, alt
:return:
"""
diff --git a/data_golf/api/live_prediction.py b/data_golf/api/live_prediction.py
new file mode 100644
index 0000000..14ed369
--- /dev/null
+++ b/data_golf/api/live_prediction.py
@@ -0,0 +1,74 @@
+class LivePrediction:
+ def __init__(self, client):
+ self.client = client
+ self._path = "/preds"
+
+ def live_in_play(
+ self,
+ tour: str = "pga",
+ dead_heat: bool = False,
+ odds_format: str = "percent",
+ f_format: str = "json",
+ ):
+ """
+ Returns live (updating at 5 minute intervals) finish probabilities for ongoing PGA and European Tour tournaments.
+ :param tour: pga (default), euro, opp (opposite field PGA TOUR event), kft, alt
+ :param dead_heat: False (default), True
+ :param odds_format: percent (default), american, decimal, fraction
+ :param f_format: Defaults to JSON.
+ :return:
+ """
+ query_p = {
+ "odds_format": odds_format,
+ "dead_heat": "yes" if dead_heat else "no",
+ "tour": tour,
+ }
+
+ return self.client.get(
+ resource=f"{self._path}/in-play", params=query_p, format=f_format
+ )
+
+ def live_tournament_stats(
+ self,
+ stats: str = None,
+ round: str = None,
+ display: str = "value",
+ f_format: str = "json",
+ ):
+ """
+ Returns live strokes-gained and traditional stats for every player during
+ PGA Tour tournaments
+ :param stats: Comma-seperated list of statistics to be returned. Accepts:
+ [sg_putt, sg_arg, sg_app, sg_ott, sg_t2g, sg_bs, sg_total, distance,
+ accuracy, gir, prox_fw, prox_rgh, scrambling]
+ :param round: Specifies the round: Accepts: (event_avg, 1, 2, 3, 4)
+ :param display: Specifies how stats are displayed. Accepts values: value (default), rank
+ :param f_format:
+ :return:
+ """
+ query_p = {
+ "display": display,
+ }
+
+ if stats:
+ query_p["stats"] = stats
+ if round:
+ query_p["round"] = round
+ return self.client.get(
+ resource=f"{self._path}/live-tournament-stats",
+ params=query_p,
+ format=f_format,
+ )
+
+ def live_hole_stats(self, tour: str = "pga", f_format: str = "json"):
+ """
+ Returns live hole scoring averages and distrubutions (birdies, pars, bogeys, etc.) broken down by tee time wave.
+ :param tour: pga (default), euro, kft, opp, alt
+ :param f_format: Defaults to JSON.
+ :return:
+ """
+ return self.client.get(
+ resource=f"{self._path}/live-hole-stats",
+ params={"tour": tour},
+ format=f_format,
+ )
diff --git a/data_golf/api/model.py b/data_golf/api/prediction.py
similarity index 95%
rename from data_golf/api/model.py
rename to data_golf/api/prediction.py
index 806c611..31eceb4 100644
--- a/data_golf/api/model.py
+++ b/data_golf/api/prediction.py
@@ -1,4 +1,4 @@
-class Model:
+class Prediction:
def __init__(self, client):
self.client = client
self._path = "/preds"
@@ -12,7 +12,7 @@ def rankings(self, f_format: str = "json") -> dict:
resource=f"{self._path}/get-dg-rankings", format=f_format
)
- def pre_tournament_pred(
+ def pre_tournament(
self,
tour: str = "pga",
add_position: str = None,
@@ -44,14 +44,14 @@ def pre_tournament_pred_archive(
self,
event_id: str = None,
year: str = None,
- odd_format: str = "percent",
+ odds_format: str = "percent",
f_format="json",
) -> dict:
"""
Returns pre-tournament predictions for a specific event or year.
:param event_id: The event id for the tournament.
:param year: The year for the tournament.
- :param odd_format: percent (default), american, decimal, fraction
+ :param odds_format: percent (default), american, decimal, fraction
:param f_format: json (default)
:return: dict
"""
@@ -62,7 +62,7 @@ def pre_tournament_pred_archive(
query_p["event_id"] = event_id
if year:
query_p["year"] = year
- query_p["odds_format"] = odd_format
+ query_p["odds_format"] = odds_format
query_p["file_format"] = f_format
return self.client.get(
diff --git a/data_golf/client.py b/data_golf/client.py
index d8ba7de..77aa943 100644
--- a/data_golf/client.py
+++ b/data_golf/client.py
@@ -1,7 +1,8 @@
-from data_golf.api.model import Model
+from data_golf.api.prediction import Prediction
from data_golf.config import DGConfig
-from data_golf.http import HttpClient
+from data_golf.http_client import HttpClient
from data_golf.api.general import General
+from data_golf.api.live_prediction import LivePrediction
class DGCInvalidApiKey(Exception):
@@ -25,7 +26,8 @@ def __init__(
# Endpoints
self.general = General(self._http_client)
- self.model = Model(self._http_client)
+ self.predictions = Prediction(self._http_client)
+ self.live_predictions = LivePrediction(self._http_client)
def _validate_api_key(self, api_key: str) -> None:
"""
diff --git a/data_golf/http.py b/data_golf/http_client.py
similarity index 86%
rename from data_golf/http.py
rename to data_golf/http_client.py
index 4a80570..d151aef 100644
--- a/data_golf/http.py
+++ b/data_golf/http_client.py
@@ -6,6 +6,14 @@
import logging
+class DGForbidden(Exception):
+ pass
+
+
+class DGBadRequest(Exception):
+ pass
+
+
class HttpClient:
def __init__(self, config) -> None:
self._config = config
@@ -51,6 +59,12 @@ def get(
**kwargs,
)
+ if r.status_code == 403:
+ raise DGForbidden("403 Forbidden: Check your API key.")
+
+ if r.status_code == 400:
+ raise DGBadRequest(r.content)
+
if self._config.verbose:
logging.info(f"API URL: {r.url}")
logging.info(kwargs["headers"])
diff --git a/poetry.lock b/poetry.lock
index d30407f..df7a167 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -256,13 +256,13 @@ files = [
[[package]]
name = "packaging"
-version = "24.0"
+version = "24.1"
description = "Core utilities for Python packages"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
- {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
@@ -408,4 +408,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
-content-hash = "e69e37072721d59a36230202707fcf652ecc3433720b517f7074980d5be089b9"
+content-hash = "99248cc0f1a31b4da2e604f9c2b2a709c68ecec2cb21481044be4ecb26a94a4c"
diff --git a/pyproject.toml b/pyproject.toml
index 1fd8c1b..0d2ad6f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,13 +1,30 @@
[tool.poetry]
name = "data_golf"
-version = "0.3.0"
-description = "API Wrapper for Data golf endpoints"
+version = "0.4.0"
+description = "API Wrapper for DataGolf.com endpoints."
authors = ["Corey Schaf "]
readme = "README.md"
+packages = [{include = "data_golf"}]
+license = "MIT"
+homepage = "https://github.com/coreyjs/data-golf-api"
+repository = "https://github.com/coreyjs/data-golf-api"
+keywords = ["golf", "datagolf", "data golf", "golf data", "golf AI", "golf machine learning", "golf ML", "golf stats"]
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: Software Development :: Libraries :: Python Modules"
+]
[tool.poetry.dependencies]
python = "^3.9"
-httpx = "*"
+httpx = "^0.27.0"
[tool.poetry.group.dev.dependencies]
pytest="^7.1.3"
@@ -19,3 +36,29 @@ black = "*"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
+
+[tool.ruff]
+exclude = [
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".git-rewrite",
+ ".hg",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".pytype",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "venv",
+]
+line-length = 121
\ No newline at end of file
diff --git a/tests/api/test_model.py b/tests/api/test_prediction.py
similarity index 88%
rename from tests/api/test_model.py
rename to tests/api/test_prediction.py
index 6748aa4..6af60a3 100644
--- a/tests/api/test_model.py
+++ b/tests/api/test_prediction.py
@@ -3,7 +3,7 @@
@mock.patch("httpx.Client.get")
def test_rankings(d_m, dg_client):
- dg_client.model.rankings()
+ dg_client.predictions.rankings()
d_m.assert_called_once()
assert (
"https://feeds.datagolf.com/preds/get-dg-rankings?" in d_m.call_args[1]["url"]
@@ -14,7 +14,7 @@ def test_rankings(d_m, dg_client):
@mock.patch("httpx.Client.get")
def test_pre_tournament_pred(d_m, dg_client):
- dg_client.model.pre_tournament_pred()
+ dg_client.predictions.pre_tournament()
d_m.assert_called_once()
assert "https://feeds.datagolf.com/preds/pre-tournament?" in d_m.call_args[1]["url"]
@@ -26,7 +26,7 @@ def test_pre_tournament_pred(d_m, dg_client):
@mock.patch("httpx.Client.get")
def test_pre_tournament_with_params(d_m, dg_client):
- dg_client.model.pre_tournament_pred(
+ dg_client.predictions.pre_tournament(
tour="euro", add_position="1,2,3", dead_heat=False, odds_format="american"
)
d_m.assert_called_once()
@@ -41,7 +41,7 @@ def test_pre_tournament_with_params(d_m, dg_client):
@mock.patch("httpx.Client.get")
def test_pre_tournament_pred_archive(d_m, dg_client):
- dg_client.model.pre_tournament_pred_archive(event_id="100")
+ dg_client.predictions.pre_tournament_pred_archive(event_id="100")
d_m.assert_called_once()
assert (
"https://feeds.datagolf.com/preds/pre-tournament-archive?"
@@ -55,7 +55,7 @@ def test_pre_tournament_pred_archive(d_m, dg_client):
@mock.patch("httpx.Client.get")
def test_player_skill_decomp(d_m, dg_client):
- dg_client.model.player_skill_decompositions(tour="alt")
+ dg_client.predictions.player_skill_decompositions(tour="alt")
d_m.assert_called_once()
assert (
"https://feeds.datagolf.com/preds/player-decompositions?"
@@ -68,7 +68,7 @@ def test_player_skill_decomp(d_m, dg_client):
@mock.patch("httpx.Client.get")
def test_player_skill_ratings(d_m, dg_client):
- dg_client.model.player_skill_ratings()
+ dg_client.predictions.player_skill_ratings()
d_m.assert_called_once()
assert "https://feeds.datagolf.com/preds/skill-ratings?" in d_m.call_args[1]["url"]
@@ -78,7 +78,7 @@ def test_player_skill_ratings(d_m, dg_client):
@mock.patch("httpx.Client.get")
def test_detailed_approach_skill(d_m, dg_client):
- dg_client.model.detailed_approach_skill()
+ dg_client.predictions.detailed_approach_skill()
d_m.assert_called_once()
assert "https://feeds.datagolf.com/preds/approach-skill?" in d_m.call_args[1]["url"]
diff --git a/tests/conftest.py b/tests/conftest.py
index 0f0796d..3ab7c36 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,6 +1,6 @@
import pytest
-from data_golf.client import DataGolfClient
+from data_golf import DataGolfClient
@pytest.fixture(scope="function")