Skip to content

Commit 32878bd

Browse files
authored
Add method to get details for the rules for a given game type (#104)
Closes #98 by adding a `game_type_rules` method
1 parent cbe027a commit 32878bd

File tree

13 files changed

+832
-1
lines changed

13 files changed

+832
-1
lines changed

docs/api.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ from draft_kings import Client
105105
Client().draftables(draft_group_id=41793)
106106
```
107107

108+
### Get Rules For A Game Type
109+
110+
```python
111+
from draft_kings import Client
112+
113+
Client().game_type_rules(game_type_id=1)
114+
```
115+
108116
### Usage
109117

110118
To the best of my knowledge, I have not identified an endpoint that delivers data for a window of time.

draft_kings/client.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
from draft_kings.response.schema.regions import RegionsSchema
3333
from draft_kings.url_builder import URLBuilder
3434
from draft_kings.utilities import translate_formatted_datetime, from_unix_milliseconds_to_datetime
35+
from draft_kings.response.schema.game_type_rules import GameTypeRulesSchema
36+
from draft_kings.output.transformers.game_type_rules import transform_roster_slot, transform_salary_cap, \
37+
LineupTemplateTransformer, GameTypeRulesTransformer
38+
from draft_kings.output.objects.game_type_rules import GameTypeRulesDetails
3539

3640

3741
class Client:
@@ -41,13 +45,15 @@ class Client:
4145
countries_transformer: CountriesTransformer
4246
regions_transformer: RegionsTransformer
4347
draftables_transformer: DraftablesTransformer
48+
game_type_rules_transformer: GameTypeRulesTransformer
4449

4550
contests_schema: ContestsSchema
4651
players_schema: PlayersDetailsSchema
4752
draft_group_schema: DraftGroupResponseSchema
4853
countries_schema: CountriesSchema
4954
regions_schema: RegionsSchema
5055
draftables_schema: DraftablesSchema
56+
game_type_rules_schema: GameTypeRulesSchema
5157

5258
http_client: HTTPClient
5359

@@ -99,12 +105,20 @@ def __init__(self):
99105
draft_alert_transformer=transform_draft_alert,
100106
)
101107
)
108+
self.game_type_rules_transformer=GameTypeRulesTransformer(
109+
salary_cap_transformer=transform_salary_cap,
110+
lineup_template_transformer=LineupTemplateTransformer(
111+
roster_slot_transformer=transform_roster_slot,
112+
)
113+
)
102114
self.contests_schema = ContestsSchema()
103115
self.players_schema = PlayersDetailsSchema()
104116
self.draft_group_schema = DraftGroupResponseSchema()
105117
self.countries_schema = CountriesSchema()
106118
self.regions_schema = RegionsSchema()
107119
self.draftables_schema = DraftablesSchema()
120+
self.game_type_rules_schema = GameTypeRulesSchema()
121+
108122
self.http_client = HTTPClient(url_builder=URLBuilder())
109123

110124
def contests(self, sport: Sport) -> ContestsDetails:
@@ -137,4 +151,9 @@ def draftables(self, draft_group_id: int) -> DraftablesDetails:
137151
deserialized_response = self.draftables_schema.loads(response.text)
138152
return self.draftables_transformer.transform(response_draftables=deserialized_response)
139153

154+
def game_type_rules(self, game_type_id: int) -> GameTypeRulesDetails:
155+
response = self.http_client.game_type_rules(game_type_id=game_type_id)
156+
deserialized_response = self.game_type_rules_schema.loads(response.text)
157+
return self.game_type_rules_transformer.transform(game_type_rules=deserialized_response)
158+
140159
# pylint: enable=too-many-instance-attributes

draft_kings/http_client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,11 @@ def draftables(self, draft_group_id: int) -> Response:
5454
response.raise_for_status()
5555

5656
return response
57+
58+
def game_type_rules(self, game_type_id: int) -> Response:
59+
response = requests.get(url=self.url_builder.build_game_type_rules_url(game_type_id=game_type_id),
60+
params={'format': 'json'})
61+
62+
response.raise_for_status()
63+
64+
return response
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from dataclasses import dataclass
2+
from typing import Optional, List
3+
4+
5+
@dataclass(frozen=True)
6+
class SalaryCapDetails:
7+
is_enabled: Optional[bool]
8+
maximum_value: Optional[float]
9+
minimum_value: Optional[float]
10+
11+
12+
@dataclass(frozen=True)
13+
class RosterSlotDetails:
14+
description: Optional[str]
15+
name: Optional[str]
16+
roster_slot_id: Optional[int]
17+
18+
19+
@dataclass(frozen=True)
20+
class LineupTemplateDetails:
21+
roster_slot_details: Optional[RosterSlotDetails]
22+
23+
24+
@dataclass(frozen=True)
25+
class GameTypeRulesDetails:
26+
allow_late_swaps: Optional[bool]
27+
description: Optional[str]
28+
enforce_selecting_unique_players: Optional[bool]
29+
draft_type_name: Optional[str]
30+
game_type_id: Optional[int]
31+
lineup_templates: List[LineupTemplateDetails]
32+
name: Optional[str]
33+
salary_cap_details: Optional[SalaryCapDetails]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# pylint: disable=unused-argument, no-self-use
2+
3+
from marshmallow import Schema, fields, post_load
4+
5+
from draft_kings.output.objects.game_type_rules import SalaryCapDetails, RosterSlotDetails, LineupTemplateDetails, \
6+
GameTypeRulesDetails
7+
8+
9+
class SalaryCapDetailsSchema(Schema):
10+
is_enabled = fields.Bool(allow_none=True, required=True)
11+
maximum_value = fields.Float(allow_none=True, required=True)
12+
minimum_value = fields.Float(allow_none=True, required=True)
13+
14+
@post_load
15+
def make_salary_cap_details(self, data, **kwargs):
16+
return SalaryCapDetails(**data)
17+
18+
19+
class RosterSlotDetailsSchema(Schema):
20+
description = fields.Str(allow_none=True, required=True)
21+
name = fields.Str(allow_none=True, required=True)
22+
roster_slot_id = fields.Int(allow_none=True, required=True)
23+
24+
@post_load
25+
def make_roster_slot_details(self, data, **kwargs):
26+
return RosterSlotDetails(**data)
27+
28+
29+
class LineupTemplateDetailsSchema(Schema):
30+
roster_slot_details = fields.Nested(RosterSlotDetailsSchema, allow_none=True, required=True)
31+
32+
@post_load
33+
def make_lineup_template(self, data, **kwargs):
34+
return LineupTemplateDetails(**data)
35+
36+
37+
class GameTypeRulesDetailsSchema(Schema):
38+
allow_late_swaps = fields.Bool(allow_none=True, required=True)
39+
description = fields.Str(allow_none=True, required=True)
40+
enforce_unique_players = fields.Bool(allow_none=True, required=True)
41+
draft_type_name = fields.Str(allow_none=True, required=True)
42+
game_type_id = fields.Int(allow_none=True, required=True)
43+
lineup_templates = fields.List(fields.Nested(LineupTemplateDetailsSchema, allow_none=False, required=True),
44+
allow_none=False, required=True)
45+
name = fields.Str(allow_none=True, required=True)
46+
salary_cap_details = fields.Nested(SalaryCapDetailsSchema, allow_none=True, required=True)
47+
48+
@post_load
49+
def make_game_type_details(self, data, **kwargs):
50+
return GameTypeRulesDetails(**data)
51+
52+
# pylint: enable=unused-argument, no-self-use
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from typing import Callable
2+
3+
from draft_kings.response.objects.game_type_rules import SalaryCap as ResponseSalaryCap, RosterSlot as \
4+
ResponseRosterSlot, LineupTemplate as ResponseLineupTemplate, GameTypeRules as ResponseGameTypeRules
5+
from draft_kings.output.objects.game_type_rules import SalaryCapDetails, RosterSlotDetails, LineupTemplateDetails, \
6+
GameTypeRulesDetails
7+
8+
9+
def transform_salary_cap(salary_cap: ResponseSalaryCap) -> SalaryCapDetails:
10+
return SalaryCapDetails(
11+
is_enabled=salary_cap.is_enabled,
12+
maximum_value=salary_cap.max_value,
13+
minimum_value=salary_cap.min_value
14+
)
15+
16+
17+
def transform_roster_slot(roster_slot: ResponseRosterSlot) -> RosterSlotDetails:
18+
return RosterSlotDetails(
19+
description=roster_slot.description,
20+
name=roster_slot.name,
21+
roster_slot_id=roster_slot.roster_slot_id
22+
)
23+
24+
25+
class LineupTemplateTransformer:
26+
def __init__(self, roster_slot_transformer: Callable[[ResponseRosterSlot], RosterSlotDetails]):
27+
self.roster_slot_transformer = roster_slot_transformer
28+
29+
def transform(self, lineup_template: ResponseLineupTemplate) -> LineupTemplateDetails:
30+
return LineupTemplateDetails(
31+
roster_slot_details=self.roster_slot_transformer(lineup_template.roster_slot) if
32+
lineup_template.roster_slot is not None else None
33+
)
34+
35+
36+
class GameTypeRulesTransformer:
37+
def __init__(self, salary_cap_transformer: Callable[[ResponseSalaryCap], SalaryCapDetails],
38+
lineup_template_transformer: LineupTemplateTransformer):
39+
self.salary_cap__transformer = salary_cap_transformer
40+
self.lineup_template_transformer = lineup_template_transformer
41+
42+
def transform(self, game_type_rules: ResponseGameTypeRules) -> GameTypeRulesDetails:
43+
return GameTypeRulesDetails(
44+
allow_late_swaps=game_type_rules.allow_late_swap,
45+
description=game_type_rules.description,
46+
enforce_selecting_unique_players=game_type_rules.unique_players,
47+
draft_type_name=game_type_rules.draft_type,
48+
game_type_id=game_type_rules.game_type_id,
49+
lineup_templates=list(map(
50+
lambda template:
51+
self.lineup_template_transformer.transform(lineup_template=template),
52+
game_type_rules.lineup_template
53+
)),
54+
name=game_type_rules.name,
55+
salary_cap_details=self.salary_cap__transformer(game_type_rules.salary_cap)
56+
if game_type_rules.salary_cap is not None else None
57+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from dataclasses import dataclass
2+
from typing import Optional, List
3+
4+
5+
@dataclass(frozen=True)
6+
class SalaryCap:
7+
is_enabled: Optional[bool]
8+
max_value: Optional[float]
9+
min_value: Optional[float]
10+
11+
12+
@dataclass(frozen=True)
13+
class RosterSlot:
14+
description: Optional[str]
15+
name: Optional[str]
16+
roster_slot_id: Optional[int]
17+
18+
19+
@dataclass(frozen=True)
20+
class LineupTemplate:
21+
roster_slot: Optional[RosterSlot]
22+
23+
24+
@dataclass(frozen=True)
25+
class GameTypeRules:
26+
allow_late_swap: Optional[bool]
27+
description: Optional[str]
28+
draft_type: Optional[str]
29+
game_type_id: Optional[int]
30+
lineup_template: List[LineupTemplate]
31+
name: Optional[str]
32+
salary_cap: Optional[SalaryCap]
33+
unique_players: Optional[bool]
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# pylint: disable=unused-argument, no-self-use
2+
3+
from marshmallow import Schema, fields, EXCLUDE, post_load
4+
5+
from draft_kings.response.objects.game_type_rules import SalaryCap, RosterSlot, LineupTemplate, GameTypeRules
6+
7+
8+
class SalaryCapSchema(Schema):
9+
class Meta:
10+
unknown = EXCLUDE
11+
12+
isEnabled = fields.Bool(attribute="is_enabled", missing=None)
13+
maxValue = fields.Float(attribute="max_value", missing=None)
14+
minValue = fields.Float(attribute="min_value", missing=None)
15+
16+
@post_load
17+
def make_salary_cap(self, data, **kwargs):
18+
return SalaryCap(**data)
19+
20+
21+
class RosterSlotSchema(Schema):
22+
class Meta:
23+
unknown = EXCLUDE
24+
25+
id = fields.Int(attribute="roster_slot_id", missing=None)
26+
description = fields.Str(attribute="description", missing=None)
27+
name = fields.Str(attribute="name", missing=None)
28+
29+
@post_load
30+
def make_roster_slot(self, data, **kwargs):
31+
return RosterSlot(**data)
32+
33+
34+
class LineupTemplateSchema(Schema):
35+
class Meta:
36+
unknown = EXCLUDE
37+
38+
rosterSlot = fields.Nested(RosterSlotSchema, attribute="roster_slot", missing=None)
39+
40+
@post_load
41+
def make_lineup_template(self, data, **kwargs):
42+
return LineupTemplate(**data)
43+
44+
45+
class GameTypeRulesSchema(Schema):
46+
class Meta:
47+
unknown = EXCLUDE
48+
49+
allowLateSwap = fields.Bool(attribute="allow_late_swap", missing=None)
50+
draftType = fields.Str(attribute="draft_type", missing=None)
51+
gameTypeDescription = fields.Str(attribute="description", missing=None)
52+
gameTypeId = fields.Int(attribute="game_type_id", missing=None)
53+
gameTypeName = fields.Str(attribute="name", missing=None)
54+
lineupTemplate = fields.List(fields.Nested(LineupTemplateSchema), attribute="lineup_template", missing=[])
55+
salaryCap = fields.Nested(SalaryCapSchema, attribute="salary_cap", missing=None)
56+
uniquePlayers = fields.Bool(attribute="unique_players", missing=None)
57+
58+
@post_load
59+
def make_game_type_rules(self, data, **kwargs):
60+
return GameTypeRules(**data)
61+
62+
# pylint: enable=unused-argument, no-self-use

draft_kings/url_builder.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ def build_draftables_url(self, draft_group_id: int) -> str:
3333
API_BASE_URL=self.api_base_path,
3434
draft_group_id=draft_group_id
3535
)
36+
37+
def build_game_type_rules_url(self, game_type_id: int) -> str:
38+
return "{API_BASE_URL}/lineups/v1/gametypes/{game_type_id}/rules".format(
39+
API_BASE_URL=self.api_base_path,
40+
game_type_id=game_type_id,
41+
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "draft_kings"
3-
version = "3.0.0"
3+
version = "3.1.0"
44
description = "A client to access data on draftkings.com"
55
authors = ["Jae Bradley <jae.b.bradley@gmail.com>"]
66
maintainers = ["Jae Bradley <jae.b.bradley@gmail.com>"]

0 commit comments

Comments
 (0)