Initial commit with Poetry
This commit is contained in:
commit
bba67271e9
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Common Ignores
|
||||
.vscode
|
||||
__pycache__
|
||||
|
||||
|
||||
# Personal Ignores
|
||||
# Mostly notes and testing
|
||||
dist
|
149
README.rst
Normal file
149
README.rst
Normal file
@ -0,0 +1,149 @@
|
||||
# Ergast-Py
|
||||
|
||||
![Stars](https://img.shields.io/github/stars/Samuel-Roach/ergast-py?color=purple&style=for-the-badge) ![Size](https://img.shields.io/github/languages/code-size/Samuel-Roach/ergast-py?style=for-the-badge) ![Commits](https://img.shields.io/github/commit-activity/m/Samuel-Roach/ergast-py?color=orange&style=for-the-badge)
|
||||
|
||||
A comprehensive Python wrapper for the Ergast API. Built for easy use and functionality, Ergast-py aims to bring the Ergast API into the Python network as seemlessly as possible.
|
||||
|
||||
## What is Ergast?
|
||||
|
||||
[Ergast](http://ergast.com/mrd/) is a free, experimental API for accessing motor-racing data, dating back to the beginning of World Championships in 1950. The website provides plenty of detail into how effective the API can be, and the many options that are available for data gathering using it.
|
||||
|
||||
## Why should I use Ergast-Py?
|
||||
|
||||
Ergast-Py provides a clean, Python orientated wrapper for this API. It has been designed to remove the heavy lifting of handling the API formatting behind the scenes, allowing developers to easily access the data that is relevant to them. All the data is conformed into clean class code, allowing for users to keep a Python-centric mindset whilst developing.
|
||||
|
||||
## How to install
|
||||
|
||||
Ergast-py is part of PyPi, so can be installed with the pip command:
|
||||
|
||||
```
|
||||
pip install ergast-py
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Once ergast-py is installed on your system you can then begin using the library in querying the ergast API. To begin, initialise an instance of the ``Ergast()`` class.
|
||||
|
||||
```python
|
||||
from ergast-py import Ergast
|
||||
|
||||
e = Ergast()
|
||||
```
|
||||
|
||||
Queries can then be built up with function calls in a sequential manner. Once you've built up a query, finally define what data you wish to get using a ``get_xyz()`` function.
|
||||
|
||||
```python
|
||||
# http://ergast.com/api/f1/2008/5/results
|
||||
race_results = e.season(2008).round(5).get_results()
|
||||
|
||||
# http://ergast.com/api/f1/drivers/massa
|
||||
felipe_massa = e.driver_str("massa").get_drivers()
|
||||
|
||||
# http://ergast.com/api/f1/current/constructorStandings/3
|
||||
constructor_standings = e.season().standing(3).get_constructor_standings()
|
||||
```
|
||||
|
||||
## Structure and Types
|
||||
|
||||
Ergast-py has many models which allow the user to more effectively use and manipulate the data available to them through Ergast. All models of data are available through ``.models.xyz``.
|
||||
|
||||
<details>
|
||||
<summary>Available models in ergast-py</summary>
|
||||
</br>
|
||||
|
||||
| Name | Description |
|
||||
| --------------------- | --------------------------------------------------------- |
|
||||
| AverageSpeed | The average speed achieved during a fastest lap |
|
||||
| Circuit | Details about a Formula One circuit |
|
||||
| ConstructorStanding | A single constructor's representation in the standings |
|
||||
| Constructor | A Formula One constructor |
|
||||
| DriverStanding | A single driver's representation in the standings |
|
||||
| Driver | A Formula One driver |
|
||||
| FastestLap | A fastest lap achieved by a driver |
|
||||
| Lap | Details about a drivers lap |
|
||||
| Location | The position of a circuit |
|
||||
| PitStop | Details about a driver's pit stop |
|
||||
| Race | Full representation of a Formula One race |
|
||||
| Result | Details about a driver's result |
|
||||
| Season | Details about a Formula One season |
|
||||
| StandingsList | A list of standings; constructors or drivers |
|
||||
| Status | Details about the final status of a driver in a race |
|
||||
| Timing | Details about the timings of a driver during a lap |
|
||||
|
||||
</details>
|
||||
|
||||
## Query building
|
||||
|
||||
There are 3 types of query available in the ``Ergast()`` class. <b>FILTER</b> functions build up the query, by filtering down the data that you will receive. <b>PAGING</b> functions control the flow of data if there is excessive amounts, limiting it to not overflow the API. <b>RETURN</b> functions detail what type of data you're expecting back from the query.
|
||||
|
||||
The order of the builder functions is inconsequential, however the final function called should be a <i>return</i> function.
|
||||
```
|
||||
Ergast().{paging/filter}.{return}
|
||||
```
|
||||
|
||||
More detail on the available functions within the ``Ergast()`` class is available below.
|
||||
|
||||
<details>
|
||||
<summary><b>FILTER</b> functions</summary>
|
||||
</br>
|
||||
|
||||
| Name | Arguments | Notes |
|
||||
| --------------- | ------------------------ | ------------------------------------------------------------------------------ |
|
||||
| season | year: int | If you call season with no arguments it will default to the current season |
|
||||
| round | round: int | If you call round with no arguments it will default to the last round |
|
||||
| driver | driver: Driver | The Driver equivalent of ``driver_str`` |
|
||||
| driver_str | driver: str | The String equivalent of ``driver``. Must use driver's driverId |
|
||||
| constructor | constructor: Constructor | The Constructor equivalent of ``constructor_str`` |
|
||||
| constructor_str | constructor: str | The String equivalent of ``constructor``. Must use constructor's constructorId |
|
||||
| qualifying | position: int | Position at the <i>end</i> of qualifying |
|
||||
| sprint | position: int | |
|
||||
| grid | position: int | Position lined up on the grid |
|
||||
| result | position: int | |
|
||||
| fastest | position: int | Ranking in list of each drivers fastest lap |
|
||||
| circuit | circuit: Circuit | The Circuit equivalent of ``circuit_str`` |
|
||||
| circuit_str | circuit: str | The String equivalent of ``circuit``. Must use circuit's circuitId |
|
||||
| status | status: int | The Integer equivalent of ``status_string``. Must use statusId |
|
||||
| status_str | status: str | The String equivalent of ``status`` |
|
||||
| standing | position: int | Position of Driver or Constructor in standing |
|
||||
| lap | lap_number: int | |
|
||||
| pit_stop | stop_number: int | |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>PAGING</b> functions</summary>
|
||||
</br>
|
||||
|
||||
| Name | Arguments |
|
||||
| ---------------- | ----------- |
|
||||
| limit | amount: int |
|
||||
| offset | amount: int |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>RETURN</b> functions</summary>
|
||||
</br>
|
||||
|
||||
| Name | Return Type |
|
||||
| ------------------------- | --------------------- |
|
||||
| get_circuits | list[Circuit] |
|
||||
| get_constructors | list[Constructor] |
|
||||
| get_drivers | list[Driver] |
|
||||
| get_qualifying | list[Race] |
|
||||
| get_sprints | list[Race] |
|
||||
| get_results | list[Race] |
|
||||
| get_races | list[Race] |
|
||||
| get_seasons | list[Season] |
|
||||
| get_statuses | list[Status] |
|
||||
| get_driver_standings | list[StandingsList] |
|
||||
| get_constructor_standings | list[StandingsList] |
|
||||
| get_laps | list[Race] |
|
||||
| get_pit_stops | list[Race] |
|
||||
|
||||
</details>
|
||||
|
||||
## Credits
|
||||
|
||||
This library would not be possible without the freely available [Ergast](http://ergast.com/mrd/) API. For full information about the API and it's responsible use, please refer to their website.
|
1
ergast_py/__init__.py
Normal file
1
ergast_py/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = '0.1.0'
|
0
ergast_py/constants/__init__.py
Normal file
0
ergast_py/constants/__init__.py
Normal file
139
ergast_py/constants/expected.py
Normal file
139
ergast_py/constants/expected.py
Normal file
@ -0,0 +1,139 @@
|
||||
class Expected():
|
||||
"""
|
||||
Storage of the expected keys returned and their types
|
||||
"""
|
||||
|
||||
location = {
|
||||
"lat": "float",
|
||||
"long": "float",
|
||||
"locality": "string",
|
||||
"country": "string",
|
||||
}
|
||||
|
||||
circuit = {
|
||||
"circuitId": "string",
|
||||
"url": "string",
|
||||
"circuitName": "string",
|
||||
"Location": "string",
|
||||
}
|
||||
|
||||
constructor = {
|
||||
"constructorId": "string",
|
||||
"url": "string",
|
||||
"name": "string",
|
||||
"nationality": "string",
|
||||
}
|
||||
|
||||
driver = {
|
||||
"driverId": "string",
|
||||
"permanentNumber": "int",
|
||||
"code": "string",
|
||||
"url": "string",
|
||||
"givenName": "string",
|
||||
"familyName": "string",
|
||||
"dateOfBirth": "string",
|
||||
"nationality": "string",
|
||||
}
|
||||
|
||||
race = {
|
||||
"season": "int",
|
||||
"round": "int",
|
||||
"url": "string",
|
||||
"raceName": "string",
|
||||
"Circuit": "dict",
|
||||
"date": "string",
|
||||
"time": "string",
|
||||
"Results": "dict",
|
||||
"FirstPractice": "dict",
|
||||
"SecondPractice": "dict",
|
||||
"ThirdPractice": "dict",
|
||||
"Sprint": "dict",
|
||||
"SprintResults": "dict",
|
||||
"Qualifying": "dict",
|
||||
"QualifyingResults": "dict",
|
||||
"PitStops": "dict",
|
||||
"Laps": "dict",
|
||||
}
|
||||
|
||||
result = {
|
||||
"number": "int",
|
||||
"position": "int",
|
||||
"positionText": "string",
|
||||
"points": "float",
|
||||
"Driver": "dict",
|
||||
"Constructor": "dict",
|
||||
"grid": "int",
|
||||
"laps": "int",
|
||||
"status": "string",
|
||||
"Time": "dict",
|
||||
"FastestLap": "dict",
|
||||
"Q1": "string",
|
||||
"Q2": "string",
|
||||
"Q3": "string",
|
||||
}
|
||||
|
||||
fastest_lap = {
|
||||
"rank": "int",
|
||||
"lap": "int",
|
||||
"Time": "dict",
|
||||
"AverageSpeed": "dict",
|
||||
}
|
||||
|
||||
average_speed = {
|
||||
"units": "string",
|
||||
"speed": "float",
|
||||
}
|
||||
|
||||
pit_stop = {
|
||||
"driverId": "string",
|
||||
"lap": "int",
|
||||
"stop": "int",
|
||||
"time": "string",
|
||||
"duration": "string",
|
||||
}
|
||||
|
||||
lap = {
|
||||
"number": "int",
|
||||
"Timings": "dict",
|
||||
}
|
||||
|
||||
timing = {
|
||||
"driverId": "string",
|
||||
"position": "int",
|
||||
"time": "string",
|
||||
}
|
||||
|
||||
season = {
|
||||
"season": "int",
|
||||
"url": "string",
|
||||
}
|
||||
|
||||
status = {
|
||||
"statusId": "int",
|
||||
"count": "int",
|
||||
"status": "string"
|
||||
}
|
||||
|
||||
driver_standing = {
|
||||
"position": "int",
|
||||
"positionText": "string",
|
||||
"points": "float",
|
||||
"wins": "int",
|
||||
"Driver": "dict",
|
||||
"Constructors": "dict"
|
||||
}
|
||||
|
||||
constructor_standing = {
|
||||
"position": "int",
|
||||
"positionText": "string",
|
||||
"points": "float",
|
||||
"wins": "int",
|
||||
"Constructor": "dict"
|
||||
}
|
||||
|
||||
standings_list = {
|
||||
"season": "int",
|
||||
"round": "int",
|
||||
"DriverStandings": "dict",
|
||||
"ConstructorStandings": "dict"
|
||||
}
|
142
ergast_py/constants/status_type.py
Normal file
142
ergast_py/constants/status_type.py
Normal file
@ -0,0 +1,142 @@
|
||||
class StatusType():
|
||||
"""
|
||||
A mapping of String onto the Status ID for that type
|
||||
"""
|
||||
status_map = {
|
||||
"": 0,
|
||||
"Finished": 1,
|
||||
"Disqualified": 2,
|
||||
"Accident": 3,
|
||||
"Collision": 4,
|
||||
"Engine": 5,
|
||||
"Gearbox": 6,
|
||||
"Transmission": 7,
|
||||
"Clutch": 8,
|
||||
"Hydraulics": 9,
|
||||
"Electrical": 10,
|
||||
"+1 Lap": 11,
|
||||
"+2 Laps": 12,
|
||||
"+3 Laps": 13,
|
||||
"+4 Laps": 14,
|
||||
"+5 Laps": 15,
|
||||
"+6 Laps": 16,
|
||||
"+7 Laps": 17,
|
||||
"+8 Laps": 18,
|
||||
"+9 Laps": 19,
|
||||
"Spun off": 20,
|
||||
"Radiator": 21,
|
||||
"Suspension": 22,
|
||||
"Brakes": 23,
|
||||
"Differential": 24,
|
||||
"Overheating": 25,
|
||||
"Mechanical": 26,
|
||||
"Tyre": 27,
|
||||
"Driver Seat": 28,
|
||||
"Puncture": 29,
|
||||
"Driveshaft": 30,
|
||||
"Retired": 31,
|
||||
"Fuel pressure": 32,
|
||||
"Front wing": 33,
|
||||
"Water pressure": 34,
|
||||
"Refuelling": 35,
|
||||
"Wheel": 36,
|
||||
"Throttle": 37,
|
||||
"Steering": 38,
|
||||
"Technical": 39,
|
||||
"Electronics": 40,
|
||||
"Broken wing": 41,
|
||||
"Heat shield fire": 42,
|
||||
"Exhaust": 43,
|
||||
"Oil leak": 44,
|
||||
"+11 Laps": 45,
|
||||
"Wheel rim": 46,
|
||||
"Water leak": 47,
|
||||
"Fuel pump": 48,
|
||||
"Track rod": 49,
|
||||
"+17 Laps": 50,
|
||||
"Oil pressure": 51,
|
||||
"+13 Laps": 53,
|
||||
"Withdrew": 54,
|
||||
"+12 Laps": 55,
|
||||
"Engine fire": 56,
|
||||
"+26 Laps": 58,
|
||||
"Tyre puncture": 59,
|
||||
"Out of fuel": 60,
|
||||
"Wheel nut": 61,
|
||||
"Not classified": 62,
|
||||
"Pneumatics": 63,
|
||||
"Handling": 64,
|
||||
"Rear wing": 65,
|
||||
"Fire": 66,
|
||||
"Wheel bearing": 67,
|
||||
"Physical": 68,
|
||||
"Fuel system": 69,
|
||||
"Oil line": 70,
|
||||
"Fuel rig": 71,
|
||||
"Launch control": 72,
|
||||
"Injured": 73,
|
||||
"Fuel": 74,
|
||||
"Power loss": 75,
|
||||
"Vibrations": 76,
|
||||
"107% Rule": 77,
|
||||
"Safety": 78,
|
||||
"Drivetrain": 79,
|
||||
"Ignition": 80,
|
||||
"Did not qualify": 81,
|
||||
"Injury": 82,
|
||||
"Chassis": 83,
|
||||
"Battery": 84,
|
||||
"Stalled": 85,
|
||||
"Halfshaft": 86,
|
||||
"Crankshaft": 87,
|
||||
"+10 Laps": 88,
|
||||
"Safety concerns": 89,
|
||||
"Not restarted": 90,
|
||||
"Alternator": 91,
|
||||
"Underweight": 92,
|
||||
"Safety belt": 93,
|
||||
"Oil pump": 94,
|
||||
"Fuel leak": 95,
|
||||
"Excluded": 96,
|
||||
"Did not prequalify": 97,
|
||||
"Injection": 98,
|
||||
"Distributor": 99,
|
||||
"Driver unwell": 100,
|
||||
"Turbo": 101,
|
||||
"CV joint": 102,
|
||||
"Water pump": 103,
|
||||
"Fatal accident": 104,
|
||||
"Spark plugs": 105,
|
||||
"Fuel pipe": 106,
|
||||
"Eye injury": 107,
|
||||
"Oil pipe": 108,
|
||||
"Axle": 109,
|
||||
"Water pipe": 110,
|
||||
"+14 Laps": 111,
|
||||
"+15 Laps": 112,
|
||||
"+25 Laps": 113,
|
||||
"+18 Laps": 114,
|
||||
"+22 Laps": 115,
|
||||
"+16 Laps": 116,
|
||||
"+24 Laps": 117,
|
||||
"+29 Laps": 118,
|
||||
"+23 Laps": 119,
|
||||
"+21 Laps": 120,
|
||||
"Magneto": 121,
|
||||
"+44 Laps": 122,
|
||||
"+30 Laps": 123,
|
||||
"+19 Laps": 124,
|
||||
"+46 Laps": 125,
|
||||
"Supercharger": 126,
|
||||
"+20 Laps": 127,
|
||||
"+42 Laps": 128,
|
||||
"Engine misfire": 129,
|
||||
"Collision damage": 130,
|
||||
"Power Unit": 131,
|
||||
"ERS": 132,
|
||||
"Brake duct": 135,
|
||||
"Seat": 136,
|
||||
"Damage": 137,
|
||||
"Debris": 138,
|
||||
"Illness": 139,
|
||||
}
|
224
ergast_py/ergast.py
Normal file
224
ergast_py/ergast.py
Normal file
@ -0,0 +1,224 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ergast_py.models.driver import Driver
|
||||
from ergast_py.models.circuit import Circuit
|
||||
from ergast_py.models.constructor import Constructor
|
||||
from ergast_py.models.standings_list import StandingsList
|
||||
from ergast_py.models.status import Status
|
||||
from ergast_py.constants.status_type import StatusType
|
||||
from ergast_py.models.season import Season
|
||||
from ergast_py.models.race import Race
|
||||
from ergast_py.models.lap import Lap
|
||||
from ergast_py.models.pit_stop import PitStop
|
||||
|
||||
from ergast_py.requester import Requester
|
||||
from ergast_py.type_constructor import TypeConstructor
|
||||
|
||||
class Ergast():
|
||||
"""
|
||||
Class for querying the Ergast API
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.reset()
|
||||
self.requester = Requester()
|
||||
self.type_constructor = TypeConstructor()
|
||||
|
||||
|
||||
def reset(self) -> None:
|
||||
self.params = {
|
||||
"season": None,
|
||||
"seasons": None,
|
||||
"round": None,
|
||||
"driver": None,
|
||||
"constructor": None,
|
||||
"grid": None,
|
||||
"qualifying": None,
|
||||
"sprint": None,
|
||||
"result": None,
|
||||
"fastest": None,
|
||||
"circuit": None,
|
||||
"status": None,
|
||||
"standing": None,
|
||||
"races": None,
|
||||
"limit": None,
|
||||
"offset": None,
|
||||
"lap": None,
|
||||
"pit_stop": None,
|
||||
}
|
||||
|
||||
#
|
||||
# FILTER FUNCTIONS
|
||||
#
|
||||
|
||||
def season(self, year: int="current") -> Ergast:
|
||||
self.params["season"] = year
|
||||
return self
|
||||
|
||||
def round(self, round: int="last") -> Ergast:
|
||||
self.params["round"] = round
|
||||
return self
|
||||
|
||||
def driver(self, driver: Driver) -> Ergast:
|
||||
self.params["driver"] = driver.driverId
|
||||
return self
|
||||
|
||||
def driver_str(self, driver: str) -> Ergast:
|
||||
self.params["driver"] = driver
|
||||
return self
|
||||
|
||||
def constructor(self, constructor: Constructor) -> Ergast:
|
||||
self.params["constructor"] = constructor.constructorId
|
||||
return self
|
||||
|
||||
def constructor_str(self, constructor: str) -> Ergast:
|
||||
self.params["constructor"] = constructor
|
||||
return self
|
||||
|
||||
def qualifying(self, position: int) -> Ergast:
|
||||
self.params["qualifying"] = position
|
||||
return self
|
||||
|
||||
def sprint(self, position: int) -> Ergast:
|
||||
self.params["sprint"] = position
|
||||
return self
|
||||
|
||||
def grid(self, position: int) -> Ergast:
|
||||
self.params["grid"] = position
|
||||
return self
|
||||
|
||||
def result(self, position: int) -> Ergast:
|
||||
self.params["result"] = position
|
||||
return self
|
||||
|
||||
def fastest(self, position: int) -> Ergast:
|
||||
self.params["fastest"] = position
|
||||
return self
|
||||
|
||||
def circuit(self, circuit: Circuit) -> Ergast:
|
||||
self.params["circuit"] = circuit.circuitId
|
||||
return self
|
||||
|
||||
def circuit_str(self, circuit: str) -> Ergast:
|
||||
self.params["circuit"] = circuit
|
||||
return self
|
||||
|
||||
def status(self, status: int) -> Ergast:
|
||||
self.params["status"] = status
|
||||
return self
|
||||
|
||||
def status_str(self, status: str) -> Ergast:
|
||||
self.params["status"] = StatusType().status_map[status]
|
||||
return self
|
||||
|
||||
def standing(self, position: int) -> Ergast:
|
||||
self.params["standing"] = position
|
||||
return self
|
||||
|
||||
def lap(self, lap_number: int) -> Ergast:
|
||||
self.params["lap"] = lap_number
|
||||
return self
|
||||
|
||||
def pit_stop(self, stop_number: int) -> Ergast:
|
||||
self.params["pit_stop"] = stop_number
|
||||
return self
|
||||
|
||||
#
|
||||
# PAGING FUNCTIONS
|
||||
#
|
||||
|
||||
def limit(self, amount: int) -> Ergast:
|
||||
self.params["limit"] = amount
|
||||
return self
|
||||
|
||||
def offset(self, amount: int) -> Ergast:
|
||||
self.params["offset"] = amount
|
||||
return self
|
||||
|
||||
#
|
||||
# RETURN FUNCTIONS
|
||||
#
|
||||
|
||||
# Race and Results Queries
|
||||
|
||||
def get_circuits(self) -> list[Circuit]:
|
||||
circuits_json = self.requester.get_circuits(self.params)
|
||||
circuits = self.type_constructor.construct_circuits(circuits_json)
|
||||
self.reset()
|
||||
return circuits
|
||||
|
||||
def get_constructors(self) -> list[Constructor]:
|
||||
constructors_json = self.requester.get_constructors(self.params)
|
||||
constructors = self.type_constructor.construct_constructors(constructors_json)
|
||||
self.reset()
|
||||
return constructors
|
||||
|
||||
def get_drivers(self) -> list[Driver]:
|
||||
drivers_json = self.requester.get_drivers(self.params)
|
||||
drivers = self.type_constructor.construct_drivers(drivers_json)
|
||||
self.reset()
|
||||
return drivers
|
||||
|
||||
def get_qualifying(self) -> list[Race]:
|
||||
qualify_json = self.requester.get_qualifying(self.params)
|
||||
qualifying = self.type_constructor.construct_races(qualify_json)
|
||||
self.reset()
|
||||
return qualifying
|
||||
|
||||
def get_sprints(self) -> list[Race]:
|
||||
sprint_json = self.requester.get_sprints(self.params)
|
||||
sprint = self.type_constructor.construct_races(sprint_json)
|
||||
self.reset()
|
||||
return sprint
|
||||
|
||||
def get_results(self) -> list[Race]:
|
||||
results_json = self.requester.get_results(self.params)
|
||||
results = self.type_constructor.construct_races(results_json)
|
||||
self.reset()
|
||||
return results
|
||||
|
||||
def get_races(self) -> list[Race]:
|
||||
races_json = self.requester.get_races(self.params)
|
||||
races = self.type_constructor.construct_races(races_json)
|
||||
self.reset()
|
||||
return races
|
||||
|
||||
def get_seasons(self) -> list[Season]:
|
||||
seasons_json = self.requester.get_seasons(self.params)
|
||||
seasons = self.type_constructor.construct_seasons(seasons_json)
|
||||
self.reset()
|
||||
return seasons
|
||||
|
||||
def get_statuses(self) -> list[Status]:
|
||||
statuses_json = self.requester.get_statuses(self.params)
|
||||
statuses = self.type_constructor.construct_statuses(statuses_json)
|
||||
self.reset()
|
||||
return statuses
|
||||
|
||||
# Standings Queries
|
||||
|
||||
def get_driver_standings(self) -> list[StandingsList]:
|
||||
standings_lists_json = self.requester.get_driver_standings(self.params)
|
||||
standings_lists = self.type_constructor.construct_standings_lists(standings_lists_json)
|
||||
self.reset()
|
||||
return standings_lists
|
||||
|
||||
def get_constructor_standings(self) -> list[StandingsList]:
|
||||
standings_lists_json = self.requester.get_constructor_standings(self.params)
|
||||
standings_lists = self.type_constructor.construct_standings_lists(standings_lists_json)
|
||||
self.reset()
|
||||
return standings_lists
|
||||
|
||||
# Laps and Pit Stops Queries
|
||||
|
||||
def get_laps(self) -> list[Race]:
|
||||
laps_json = self.requester.get_laps(self.params)
|
||||
laps = self.type_constructor.construct_races(laps_json)
|
||||
self.reset()
|
||||
return laps
|
||||
|
||||
def get_pit_stops(self) -> list[Race]:
|
||||
pit_stops_json = self.requester.get_pit_stops(self.params)
|
||||
pit_stops = self.type_constructor.construct_races(pit_stops_json)
|
||||
self.reset()
|
||||
return pit_stops
|
45
ergast_py/helpers.py
Normal file
45
ergast_py/helpers.py
Normal file
@ -0,0 +1,45 @@
|
||||
import datetime
|
||||
|
||||
from ergast_py.constants.status_type import StatusType
|
||||
|
||||
class Helpers:
|
||||
def construct_datetime_str(self, date: str, time: str) -> datetime.datetime:
|
||||
new_datetime = datetime.datetime.strptime(f"{date} {time}", "%Y-%m-%d %H:%M:%SZ")
|
||||
new_datetime = new_datetime.replace(tzinfo=datetime.timezone.utc)
|
||||
return new_datetime
|
||||
|
||||
def construct_datetime_dict(self, dict: dict) -> datetime.datetime:
|
||||
if "date" not in dict or "time" not in dict:
|
||||
return None
|
||||
return self.construct_datetime_str(dict["date"], dict["time"])
|
||||
|
||||
def construct_date(self, date: str) -> datetime.date:
|
||||
elements = date.split("-")
|
||||
return datetime.date(year=int(elements[0]), month=int(elements[1]), day=int(elements[2]))
|
||||
|
||||
def construct_lap_time_millis(self, millis: dict) -> datetime.time:
|
||||
if "millis" in millis:
|
||||
value = int(millis["millis"])
|
||||
return datetime.datetime.fromtimestamp(value/1000.0).time()
|
||||
return None
|
||||
|
||||
def format_lap_time(self, time: str) -> datetime.time:
|
||||
if time != "":
|
||||
return datetime.datetime.strptime(time, "%M:%S.%f").time()
|
||||
return None
|
||||
|
||||
def construct_lap_time(self, time: dict) -> datetime.time:
|
||||
if "time" in time:
|
||||
value = time["time"]
|
||||
return self.format_lap_time(value)
|
||||
return None
|
||||
|
||||
def construct_local_time(self, time: str) -> datetime.time:
|
||||
if "time" != "":
|
||||
return datetime.datetime.strptime(f"{time}", "%H:%M:%S").time()
|
||||
return None
|
||||
|
||||
def construct_pitstop_duration(self, time: str) -> datetime.time:
|
||||
if "time" != "":
|
||||
return datetime.datetime.strptime(f"{time}", "%S.%f").time()
|
||||
return None
|
0
ergast_py/models/__init__.py
Normal file
0
ergast_py/models/__init__.py
Normal file
21
ergast_py/models/average_speed.py
Normal file
21
ergast_py/models/average_speed.py
Normal file
@ -0,0 +1,21 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class AverageSpeed:
|
||||
"""
|
||||
Representation of a Drivers Average Speed
|
||||
Average Speeds may contain:
|
||||
units: String
|
||||
speed: Float
|
||||
"""
|
||||
|
||||
def __init__(self, units: str, speed: float) -> None:
|
||||
self.units = units
|
||||
self.speed = speed
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"AverageSpeed(units={self.units}, speed={self.speed})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"AverageSpeed(units={self.units}, speed={self.speed})"
|
26
ergast_py/models/circuit.py
Normal file
26
ergast_py/models/circuit.py
Normal file
@ -0,0 +1,26 @@
|
||||
from dataclasses import dataclass
|
||||
from ergast_py.models.location import Location
|
||||
|
||||
@dataclass
|
||||
class Circuit:
|
||||
"""
|
||||
Representation of a Formula One Circuit
|
||||
Circuits may contain:
|
||||
circuitId: String
|
||||
url: String
|
||||
circuitName: String
|
||||
location: Location
|
||||
"""
|
||||
|
||||
def __init__(self, circuitId: str, url: str, circuitName: str, location: Location) -> None:
|
||||
self.circuitId = circuitId
|
||||
self.url = url
|
||||
self.circuitName = circuitName
|
||||
self.location = location
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Circuit(circuitId={self.circuitId}, url={self.url}, circuitName={self.circuitName}, location={self.location})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Circuit(circuitId={self.circuitId}, url={self.url}, circuitName={self.circuitName}, location={self.location})"
|
25
ergast_py/models/constructor.py
Normal file
25
ergast_py/models/constructor.py
Normal file
@ -0,0 +1,25 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Constructor:
|
||||
"""
|
||||
Representation of a Formula One Team
|
||||
Constructors may contain:
|
||||
constructorId: String
|
||||
url: String
|
||||
name: String
|
||||
nationality: String
|
||||
"""
|
||||
|
||||
def __init__(self, constructorId: str, url: str, name: str, nationality: str) -> None:
|
||||
self.constructorId = constructorId
|
||||
self.url = url
|
||||
self.name = name
|
||||
self.nationality = nationality
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Constructor(constructorId={self.constructorId}, url={self.url}, name={self.name}, nationality={self.nationality})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Constructor(constructorId={self.constructorId}, url={self.url}, name={self.name}, nationality={self.nationality})"
|
29
ergast_py/models/constructor_standing.py
Normal file
29
ergast_py/models/constructor_standing.py
Normal file
@ -0,0 +1,29 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ergast_py.models.constructor import Constructor
|
||||
|
||||
@dataclass
|
||||
class ConstructorStanding:
|
||||
"""
|
||||
Representation of a Formula One Constructor's standing in a Season
|
||||
Constructor Standings may contain:
|
||||
position: Integer
|
||||
positionText: String
|
||||
points: Float
|
||||
wins: Integer
|
||||
constructor: Constructor
|
||||
"""
|
||||
|
||||
def __init__(self, position: int, positionText: str, points: float, wins: int, constructor: Constructor) -> None:
|
||||
self.position = position
|
||||
self.positionText = positionText
|
||||
self.points = points
|
||||
self.wins = wins
|
||||
self.constructor = constructor
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"ConstructorStanding(position={self.position}, positionText={self.positionText}, points={self.points}, wins={self.wins}, constructor={self.constructor})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"ConstructorStanding(position={self.position}, positionText={self.positionText}, points={self.points}, wins={self.wins}, constructor={self.constructor})"
|
37
ergast_py/models/driver.py
Normal file
37
ergast_py/models/driver.py
Normal file
@ -0,0 +1,37 @@
|
||||
from dataclasses import dataclass
|
||||
from ergast_py.helpers import Helpers
|
||||
import datetime
|
||||
|
||||
@dataclass
|
||||
class Driver:
|
||||
"""
|
||||
Representation of a Formula One driver
|
||||
Drivers may contain:
|
||||
driverId: String
|
||||
permanentNumber: Integer
|
||||
code: String
|
||||
url: String
|
||||
givenName: String
|
||||
familyName: String
|
||||
dateOfBirth: datetime.date
|
||||
nationality: String
|
||||
"""
|
||||
|
||||
def __init__(self, driverId: str, code: str, url: str, givenName: str, familyName: str, dateOfBirth: datetime.date,
|
||||
nationality: str, permanentNumber: int) -> None:
|
||||
self.driverId = driverId
|
||||
self.permanentNumber = permanentNumber
|
||||
self.code = code
|
||||
self.url = url
|
||||
self.givenName = givenName
|
||||
self.familyName = familyName
|
||||
self.dateOfBirth = dateOfBirth
|
||||
self.nationality = nationality
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Driver(driverId={self.driverId}, permanentNumber={self.permanentNumber}, code={self.code}, url={self.url}, givenName={self.givenName}, familyName={self.familyName}, dateOfBirth={self.dateOfBirth}, nationality={self.nationality})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Driver(driverId={self.driverId}, permanentNumber={self.permanentNumber}, code={self.code}, url={self.url}, givenName={self.givenName}, familyName={self.familyName}, dateOfBirth={self.dateOfBirth}, nationality={self.nationality})"
|
||||
|
33
ergast_py/models/driver_standing.py
Normal file
33
ergast_py/models/driver_standing.py
Normal file
@ -0,0 +1,33 @@
|
||||
from dataclasses import dataclass
|
||||
from ergast_py.models.constructor import Constructor
|
||||
|
||||
from ergast_py.models.driver import Driver
|
||||
|
||||
@dataclass
|
||||
class DriverStanding:
|
||||
"""
|
||||
Representation of a Formula One Driver's standing in a Season
|
||||
Driver Standings may contain:
|
||||
position: Integer
|
||||
positionText: String
|
||||
points: Float
|
||||
wins: Integer
|
||||
driver: Driver
|
||||
constructors: Constructor[]
|
||||
"""
|
||||
|
||||
def __init__(self, position: int, positionText: str, points: float, wins: int, driver: Driver,
|
||||
constructors: list[Constructor]) -> None:
|
||||
self.position = position
|
||||
self.positionText = positionText
|
||||
self.points = points
|
||||
self.wins = wins
|
||||
self.driver = driver
|
||||
self.constructors = constructors
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"DriverStanding(position={self.position}, positionText={self.positionText}, points={self.points}, wins={self.wins}, driver={self.driver}, constructors={self.constructors})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"DriverStanding(position={self.position}, positionText={self.positionText}, points={self.points}, wins={self.wins}, driver={self.driver}, constructors={self.constructors})"
|
27
ergast_py/models/fastest_lap.py
Normal file
27
ergast_py/models/fastest_lap.py
Normal file
@ -0,0 +1,27 @@
|
||||
from dataclasses import dataclass
|
||||
from ergast_py.models.average_speed import AverageSpeed
|
||||
import datetime
|
||||
|
||||
@dataclass
|
||||
class FastestLap:
|
||||
"""
|
||||
Representation of a Fastest Lap for a Formula One Driver
|
||||
Fastest Laps may contain:
|
||||
rank: Integer
|
||||
lap: Integer
|
||||
time: datetime.time
|
||||
averageSpeed: AverageSpeed
|
||||
"""
|
||||
|
||||
def __init__(self, rank: int, lap: int, time: datetime.time, averageSpeed: AverageSpeed) -> None:
|
||||
self.rank = rank
|
||||
self.lap = lap
|
||||
self.time = time
|
||||
self.averageSpeed = averageSpeed
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"FastestLap(rank={self.rank}, lap={self.lap}, time={self.time}, averageSpeed={self.averageSpeed})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"FastestLap(rank={self.rank}, lap={self.lap}, time={self.time}, averageSpeed={self.averageSpeed})"
|
23
ergast_py/models/lap.py
Normal file
23
ergast_py/models/lap.py
Normal file
@ -0,0 +1,23 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ergast_py.models.timing import Timing
|
||||
|
||||
@dataclass
|
||||
class Lap:
|
||||
"""
|
||||
Representation of a single Lap from a Formula One race
|
||||
Laps may contain:
|
||||
number: Integer
|
||||
timings: Timing[]
|
||||
"""
|
||||
|
||||
def __init__(self, number: int, timings: list[Timing]) -> None:
|
||||
self.number = number
|
||||
self.timings = timings
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Lap(number={self.number}, timings={self.timings})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Lap(number={self.number}, timings={self.timings})"
|
25
ergast_py/models/location.py
Normal file
25
ergast_py/models/location.py
Normal file
@ -0,0 +1,25 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Location:
|
||||
"""
|
||||
Representation of a Location for a Formula One Circuit
|
||||
Locations may contain:
|
||||
lat: Float
|
||||
long: Float
|
||||
locality: String
|
||||
country: String
|
||||
"""
|
||||
|
||||
def __init__(self, latitude: float, longitude: float, locality: str, country: str) -> None:
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.locality = locality
|
||||
self.country = country
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Location(latitude={self.latitude},longitude={self.longitude}, locality={self.locality}, country={self.country})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Location(latitude={self.latitude},longitude={self.longitude}, locality={self.locality}, country={self.country})"
|
29
ergast_py/models/pit_stop.py
Normal file
29
ergast_py/models/pit_stop.py
Normal file
@ -0,0 +1,29 @@
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
|
||||
@dataclass
|
||||
class PitStop:
|
||||
"""
|
||||
Representation of a single Pit Stop from a Formula One race
|
||||
PitStops may contain:
|
||||
driverId: String
|
||||
lap: Integer
|
||||
stop: Integer
|
||||
localTime: datetime.datetime
|
||||
duration: datetime.time
|
||||
"""
|
||||
|
||||
def __init__(self, driverId: str, lap: int, stop: int, localTime: datetime.datetime,
|
||||
duration: datetime.time) -> None:
|
||||
self.driverId = driverId
|
||||
self.lap = lap
|
||||
self.stop = stop
|
||||
self.localTime = localTime
|
||||
self.duration = duration
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"PitStop(driverId={self.driverId}, lap={self.lap}, stop={self.stop}, localTime={self.localTime}, duration={self.duration})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"PitStop(driverId={self.driverId}, lap={self.lap}, stop={self.stop}, localTime={self.localTime}, duration={self.duration})"
|
61
ergast_py/models/race.py
Normal file
61
ergast_py/models/race.py
Normal file
@ -0,0 +1,61 @@
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
|
||||
from ergast_py.helpers import Helpers
|
||||
from ergast_py.models.circuit import Circuit
|
||||
from ergast_py.models.lap import Lap
|
||||
from ergast_py.models.result import Result
|
||||
from ergast_py.models.pit_stop import PitStop
|
||||
|
||||
@dataclass
|
||||
class Race:
|
||||
"""
|
||||
Representation of a single Race from a Formula One season
|
||||
Races may contain:
|
||||
season: Integer
|
||||
round: Integer
|
||||
url: String
|
||||
raceName: String
|
||||
circuit: Circuit
|
||||
date: datetime.datetime
|
||||
results: Result[]
|
||||
firstPractice: datetime.datetime
|
||||
secondPractice: datetime.datetime
|
||||
thirdPractice: datetime.datetime
|
||||
sprint: datetime.datetime
|
||||
sprintResults: Result[]
|
||||
qualifying: datetime.datetime
|
||||
qualifyingResults: Result[]
|
||||
pitStops: PitStop[]
|
||||
laps: Lap[]
|
||||
"""
|
||||
|
||||
def __init__(self, season: int, round: int, url: str, raceName: str, circuit: Circuit, date: datetime.datetime,
|
||||
results: list[Result], firstPractice: datetime.datetime, secondPractice: datetime.datetime,
|
||||
thirdPractice: datetime.datetime, sprint: datetime.datetime, sprintResults: list[Result],
|
||||
qualifying: datetime.datetime, qualifyingResults: list[Result], pitStops: list[PitStop],
|
||||
laps: list[Lap]) -> None:
|
||||
self.season = season
|
||||
self.round = round
|
||||
self.url = url
|
||||
self.raceName = raceName
|
||||
self.circuit = circuit
|
||||
self.date = date
|
||||
self.results = results
|
||||
self.firstPractice = firstPractice
|
||||
self.secondPractice = secondPractice
|
||||
self.thirdPractice = thirdPractice
|
||||
self.sprint = sprint
|
||||
self.sprintResults = sprintResults
|
||||
self.qualifying = qualifying
|
||||
self.qualifyingResults = qualifyingResults
|
||||
self.pitStops = pitStops
|
||||
self.laps = laps
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Race(season={self.season}, round={self.round}, url={self.url}, raceName={self.raceName}, circuit={self.circuit}, date={self.date}, results={self.results}, firstPractice={self.firstPractice}, secondPractice={self.secondPractice}, thirdPractice={self.thirdPractice}, sprint={self.sprint}, sprintResults={self.sprintResults}, qualifying={self.qualifying}, qualifyingResults={self.qualifyingResults}, pitStops={self.pitStops}, laps={self.laps})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Race(season={self.season}, round={self.round}, url={self.url}, raceName={self.raceName}, circuit={self.circuit}, date={self.date}, results={self.results}, firstPractice={self.firstPractice}, secondPractice={self.secondPractice}, thirdPractice={self.thirdPractice}, sprint={self.sprint}, sprintResults={self.sprintResults}, qualifying={self.qualifying}, qualifyingResults={self.qualifyingResults}, pitStops={self.pitStops}, laps={self.laps})"
|
||||
|
52
ergast_py/models/result.py
Normal file
52
ergast_py/models/result.py
Normal file
@ -0,0 +1,52 @@
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
from ergast_py.models.constructor import Constructor
|
||||
|
||||
from ergast_py.models.driver import Driver
|
||||
from ergast_py.models.fastest_lap import FastestLap
|
||||
|
||||
@dataclass
|
||||
class Result:
|
||||
"""
|
||||
Representation of a single Result from a Formula One race
|
||||
Results may contain:
|
||||
number: Integer
|
||||
position: Integer
|
||||
positionText: String
|
||||
points: Integer
|
||||
driver: Driver
|
||||
constructor: Constructor
|
||||
grid: Integer
|
||||
laps: Integer
|
||||
status: Integer
|
||||
time: datetime.time
|
||||
fastestLap: FastestLap
|
||||
q1: datetime.time
|
||||
q2: datetime.time
|
||||
q3: datetime.time
|
||||
"""
|
||||
|
||||
def __init__(self, number: int, position: int, positionText: str, points: float, driver: Driver,
|
||||
constructor: Constructor, grid: int, laps: int, status: int, time: datetime.time,
|
||||
fastestLap: FastestLap, q1: datetime.time, q2: datetime.time, q3: datetime.time) -> None:
|
||||
self.number = number
|
||||
self.position = position
|
||||
self.positionText = positionText
|
||||
self.points = points
|
||||
self.driver = driver
|
||||
self.constructor = constructor
|
||||
self.grid = grid
|
||||
self.laps = laps
|
||||
self.status = status
|
||||
self.time = time
|
||||
self.fastestLap = fastestLap
|
||||
self.q1 = q1
|
||||
self.q2 = q2
|
||||
self.q3 = q3
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Result(number={self.number}, position={self.position}, positionText={self.positionText}, points={self.points}, driver={self.driver}, constructor={self.constructor}, grid={self.grid}, laps={self.laps}, status={self.status}, time={self.time}, fastestLap={self.fastestLap}, q1={self.q1}, q2={self.q2}, q3={self.q3})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Result(number={self.number}, position={self.position}, positionText={self.positionText}, points={self.points}, driver={self.driver}, constructor={self.constructor}, grid={self.grid}, laps={self.laps}, status={self.status}, time={self.time}, fastestLap={self.fastestLap}, q1={self.q1}, q2={self.q2}, q3={self.q3})"
|
21
ergast_py/models/season.py
Normal file
21
ergast_py/models/season.py
Normal file
@ -0,0 +1,21 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Season:
|
||||
"""
|
||||
Representation of a single Season in Formula One
|
||||
Seasons may contain:
|
||||
season: Integer
|
||||
url: String
|
||||
"""
|
||||
|
||||
def __init__(self, season: int, url: str) -> None:
|
||||
self.season = season
|
||||
self.url = url
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Season(season={self.season}, url={self.url})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Season(season={self.season}, url={self.url})"
|
28
ergast_py/models/standings_list.py
Normal file
28
ergast_py/models/standings_list.py
Normal file
@ -0,0 +1,28 @@
|
||||
from dataclasses import dataclass
|
||||
from ergast_py.models.driver_standing import DriverStanding
|
||||
from ergast_py.models.constructor_standing import ConstructorStanding
|
||||
|
||||
@dataclass
|
||||
class StandingsList:
|
||||
"""
|
||||
Representation of a set of Standings from a time in Formula One
|
||||
StandingsLists may contain:
|
||||
season: Integer
|
||||
round: Integer
|
||||
driverStandings: DriverStanding[]
|
||||
constructorStandings: ConstructorStanding[]
|
||||
"""
|
||||
|
||||
def __init__(self, season: int, round: int, driverStandings: list[DriverStanding],
|
||||
constructorStandings: list[ConstructorStanding]) -> None:
|
||||
self.season = season
|
||||
self.round = round
|
||||
self.driverStandings = driverStandings
|
||||
self.constructorStandings = constructorStandings
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"StandingsList(season={self.season}, round={self.round}, driverStandings={self.driverStandings}, constructorStandings={self.constructorStandings})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"StandingsList(season={self.season}, round={self.round}, driverStandings={self.driverStandings}, constructorStandings={self.constructorStandings})"
|
23
ergast_py/models/status.py
Normal file
23
ergast_py/models/status.py
Normal file
@ -0,0 +1,23 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Status:
|
||||
"""
|
||||
Representation of the finishing status of a Driver in a Race
|
||||
Statuses may contain:
|
||||
statusId: Integer
|
||||
count: Integer
|
||||
status: String
|
||||
"""
|
||||
|
||||
def __init__(self, statusId: int, count: int, status: str) -> None:
|
||||
self.statusId = statusId
|
||||
self.count = count
|
||||
self.status = status
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Status(statusId={self.statusId}, count={self.count}, status={self.status})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Status(statusId={self.statusId}, count={self.count}, status={self.status})"
|
24
ergast_py/models/timing.py
Normal file
24
ergast_py/models/timing.py
Normal file
@ -0,0 +1,24 @@
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
|
||||
@dataclass
|
||||
class Timing:
|
||||
"""
|
||||
Representation of a single timing from a lap in Formula One
|
||||
Timings may contain:
|
||||
driverId: String
|
||||
position: Integer
|
||||
time: datetime.time
|
||||
"""
|
||||
|
||||
def __init__(self, driverId: str, position: int, time: datetime.time) -> None:
|
||||
self.driverId = driverId
|
||||
self.position = position
|
||||
self.time = time
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f"Timing(driverId={self.driverId}, position={self.position}, time={self.time})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Timing(driverId={self.driverId}, position={self.position}, time={self.time})"
|
280
ergast_py/requester.py
Normal file
280
ergast_py/requester.py
Normal file
@ -0,0 +1,280 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
from ergast_py.models.circuit import Circuit
|
||||
from ergast_py.models.constructor import Constructor
|
||||
from ergast_py.models.driver import Driver
|
||||
from uritemplate import URITemplate
|
||||
|
||||
host = 'https://ergast.com/api'
|
||||
series = 'f1'
|
||||
|
||||
class Requester():
|
||||
"""
|
||||
Perform requests to the Ergast API
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def _get_race_results_params(self, param: dict) -> dict:
|
||||
""" Acquire only the appropriate filters for Race/Results data """
|
||||
return {
|
||||
"season": param["season"],
|
||||
"round": param["round"],
|
||||
"filters": {
|
||||
"drivers": param["driver"],
|
||||
"constructors": param["constructor"],
|
||||
"grid": param["grid"],
|
||||
"qualifying": param["qualifying"],
|
||||
"sprint": param["sprint"],
|
||||
"fastest": param["fastest"],
|
||||
"circuits": param["circuit"],
|
||||
"status": param["status"],
|
||||
"results": param["result"],
|
||||
"races": param["races"],
|
||||
"seasons": param["seasons"],
|
||||
},
|
||||
"paging": {
|
||||
"limit": param["limit"],
|
||||
"offset": param["offset"],
|
||||
}
|
||||
}
|
||||
|
||||
def _get_race_results_criteria(self, params: dict, resource: str) -> dict:
|
||||
""" Split the data into criteria and resource for Race/Results """
|
||||
criteria = []
|
||||
for key, value in params["filters"].items():
|
||||
if (key != resource and value != None):
|
||||
criteria.append(key)
|
||||
criteria.append(value)
|
||||
|
||||
value = params["filters"][resource]
|
||||
|
||||
return {
|
||||
"resource": resource,
|
||||
"value": value,
|
||||
"criteria": criteria
|
||||
}
|
||||
|
||||
def _get_standings_params(self, param: dict) -> dict:
|
||||
""" Acquire only the appropriate filters for Standings data """
|
||||
return {
|
||||
"season": param["season"],
|
||||
"round": param["round"],
|
||||
"filters": {
|
||||
"standing": param["standing"],
|
||||
"drivers": param["driver"],
|
||||
"constructors": param["constructor"]
|
||||
},
|
||||
"paging": {
|
||||
"limit": param["limit"],
|
||||
"offset": param["offset"],
|
||||
}
|
||||
}
|
||||
|
||||
def _get_standings_criteria(self, params: dict, resource: str) -> dict:
|
||||
""" Split the data into criteria and resource for standings """
|
||||
criteria = []
|
||||
for key, value in params["filters"].items():
|
||||
if (key != "standing" and value != None):
|
||||
criteria.append(key)
|
||||
criteria.append(value)
|
||||
|
||||
value = params["filters"]["standing"]
|
||||
|
||||
return {
|
||||
"resource": resource,
|
||||
"value": value,
|
||||
"criteria": criteria
|
||||
}
|
||||
|
||||
def _get_laps_pit_stops_params(self, param: dict) -> dict:
|
||||
""" Acquire only the appropriate filters for Laps and Pit Stops data """
|
||||
return {
|
||||
"season": param["season"],
|
||||
"round": param["round"],
|
||||
"filters": {
|
||||
"pitstops": param["pit_stop"],
|
||||
"laps": param["lap"],
|
||||
"drivers": param["driver"],
|
||||
},
|
||||
"paging": {
|
||||
"limit": param["limit"],
|
||||
"offset": param["offset"],
|
||||
}
|
||||
}
|
||||
|
||||
def _get_laps_pit_stops_criteria(self, params: dict, resource: str) -> dict:
|
||||
""" Split the data into criteria and resource for Laps and Pit Stops """
|
||||
criteria = []
|
||||
for key, value in params["filters"].items():
|
||||
if (key != resource and value != None):
|
||||
criteria.append(key)
|
||||
criteria.append(value)
|
||||
|
||||
value = params["filters"][resource]
|
||||
|
||||
return {
|
||||
"resource": resource,
|
||||
"value": value,
|
||||
"criteria": criteria
|
||||
}
|
||||
|
||||
|
||||
def run_request(self, season, round, criteria, resource, value, limit, offset) -> dict:
|
||||
""" Takes values to run the request and return a dict """
|
||||
url_tmpl = URITemplate('https://ergast.com/api{/series}{/season}{/round}'
|
||||
'{/criteria*}{/resource}{/value}.json{?limit,offset}')
|
||||
url = url_tmpl.expand(host=host, series=series,
|
||||
season=season, round=round,
|
||||
criteria=criteria, resource=resource,
|
||||
value=value, limit=limit, offset=offset)
|
||||
|
||||
return json.loads(requests.get(url).text)
|
||||
|
||||
#
|
||||
# Race and Results
|
||||
#
|
||||
|
||||
def get_circuits(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "circuits")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["CircuitTable"]["Circuits"]
|
||||
|
||||
|
||||
def get_constructors(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "constructors")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["ConstructorTable"]["Constructors"]
|
||||
|
||||
|
||||
def get_drivers(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "drivers")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["DriverTable"]["Drivers"]
|
||||
|
||||
|
||||
def get_qualifying(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "qualifying")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["RaceTable"]["Races"]
|
||||
|
||||
def get_sprints(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "sprint")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["RaceTable"]["Races"]
|
||||
|
||||
def get_results(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "results")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["RaceTable"]["Races"]
|
||||
|
||||
def get_races(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "races")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["RaceTable"]["Races"]
|
||||
|
||||
def get_seasons(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "seasons")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["SeasonTable"]["Seasons"]
|
||||
|
||||
def get_statuses(self, param: dict) -> dict:
|
||||
params = self._get_race_results_params(param)
|
||||
filters = self._get_race_results_criteria(params, "status")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["StatusTable"]["Status"]
|
||||
|
||||
#
|
||||
# Standings
|
||||
#
|
||||
|
||||
def get_driver_standings(self, param: dict) -> dict:
|
||||
params = self._get_standings_params(param)
|
||||
filters = self._get_standings_criteria(params, "driverStandings")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["StandingsTable"]["StandingsLists"]
|
||||
|
||||
def get_constructor_standings(self, param: dict) -> dict:
|
||||
params = self._get_standings_params(param)
|
||||
filters = self._get_standings_criteria(params, "constructorStandings")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["StandingsTable"]["StandingsLists"]
|
||||
|
||||
#
|
||||
# Laps and Pit Stops
|
||||
#
|
||||
|
||||
def get_laps(self, param: dict) -> dict:
|
||||
params = self._get_laps_pit_stops_params(param)
|
||||
filters = self._get_laps_pit_stops_criteria(params, "laps")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["RaceTable"]["Races"]
|
||||
|
||||
def get_pit_stops(self, param: dict) -> dict:
|
||||
params = self._get_laps_pit_stops_params(param)
|
||||
filters = self._get_laps_pit_stops_criteria(params, "pitstops")
|
||||
|
||||
json = self.run_request(season=params["season"], round=params["round"],
|
||||
criteria=filters["criteria"], resource=filters["resource"], value=filters["value"],
|
||||
limit=params["paging"]["limit"], offset=params["paging"]["offset"])
|
||||
|
||||
return json["MRData"]["RaceTable"]["Races"]
|
301
ergast_py/type_constructor.py
Normal file
301
ergast_py/type_constructor.py
Normal file
@ -0,0 +1,301 @@
|
||||
from ergast_py.models.average_speed import AverageSpeed
|
||||
from ergast_py.models.constructor_standing import ConstructorStanding
|
||||
from ergast_py.models.driver import Driver
|
||||
from ergast_py.models.driver_standing import DriverStanding
|
||||
from ergast_py.models.fastest_lap import FastestLap
|
||||
from ergast_py.models.lap import Lap
|
||||
from ergast_py.models.location import Location
|
||||
from ergast_py.models.circuit import Circuit
|
||||
from ergast_py.models.constructor import Constructor
|
||||
from ergast_py.models.pit_stop import PitStop
|
||||
from ergast_py.models.race import Race
|
||||
from ergast_py.models.result import Result
|
||||
from ergast_py.helpers import Helpers
|
||||
from ergast_py.constants.status_type import StatusType
|
||||
from ergast_py.models.season import Season
|
||||
from ergast_py.models.standings_list import StandingsList
|
||||
from ergast_py.models.status import Status
|
||||
from ergast_py.models.timing import Timing
|
||||
from ergast_py.constants.expected import Expected
|
||||
|
||||
class TypeConstructor():
|
||||
"""
|
||||
Class for constructing types out of dicts
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
#
|
||||
# PRIVATE METHODS
|
||||
#
|
||||
|
||||
def _populate_missing(self, expected: dict, actual: dict) -> dict:
|
||||
for item in expected:
|
||||
if item not in actual:
|
||||
if expected[item] == "dict":
|
||||
actual[item] = {}
|
||||
elif expected[item] == "float":
|
||||
actual[item] = "0.0"
|
||||
elif expected[item] == "int":
|
||||
actual[item] = "0"
|
||||
else:
|
||||
actual[item] = ""
|
||||
return actual
|
||||
|
||||
def _populate_missing_location(self, location: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().location, actual=location)
|
||||
|
||||
def _populate_missing_circuit(self, circuit: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().circuit, actual=circuit)
|
||||
|
||||
def _populate_missing_constructor(self, constructor: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().constructor, actual=constructor)
|
||||
|
||||
def _populate_missing_driver(self, driver: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().driver, actual=driver)
|
||||
|
||||
def _populate_missing_race(self, race: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().race, actual=race)
|
||||
|
||||
def _populate_missing_result(self, result: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().result, actual=result)
|
||||
|
||||
def _populate_missing_fastest_lap(self, fastest_lap: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().fastest_lap, actual=fastest_lap)
|
||||
|
||||
def _populate_missing_average_speed(self, average_speed: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().average_speed, actual=average_speed)
|
||||
|
||||
def _populate_missing_pit_stop(self, pit_stop: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().pit_stop, actual=pit_stop)
|
||||
|
||||
def _populate_missing_lap(self, lap: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().lap, actual=lap)
|
||||
|
||||
def _populate_missing_timing(self, timing: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().timing, actual=timing)
|
||||
|
||||
def _populate_missing_season(self, season: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().season, actual=season)
|
||||
|
||||
def _populate_missing_status(self, status: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().status, actual=status)
|
||||
|
||||
def _populate_missing_driver_standing(self, standing: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().driver_standing, actual=standing)
|
||||
|
||||
def _populate_missing_constructor_standing(self, standing: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().constructor_standing, actual=standing)
|
||||
|
||||
def _populate_missing_standings_list(self, standings_list: dict) -> dict:
|
||||
return self._populate_missing(expected=Expected().standings_list, actual=standings_list)
|
||||
|
||||
#
|
||||
# PUBLIC METHODS
|
||||
#
|
||||
|
||||
def construct_location(self, location: dict) -> Location:
|
||||
location = self._populate_missing_location(location=location)
|
||||
return Location(
|
||||
latitude=float(location["lat"]),
|
||||
longitude=float(location["long"]),
|
||||
locality=location["locality"],
|
||||
country=location["country"]
|
||||
)
|
||||
|
||||
def construct_circuit(self, circuit: dict) -> Circuit:
|
||||
circuit = self._populate_missing_circuit(circuit)
|
||||
return Circuit(
|
||||
circuitId=circuit["circuitId"],
|
||||
url=circuit["url"],
|
||||
circuitName=circuit["circuitName"],
|
||||
location=self.construct_location(circuit["Location"])
|
||||
)
|
||||
|
||||
def construct_circuits(self, circuits: dict) -> list[Circuit]:
|
||||
return [self.construct_circuit(circuit) for circuit in circuits]
|
||||
|
||||
def construct_constructor(self, constructor: dict) -> Constructor:
|
||||
constructor = self._populate_missing_constructor(constructor)
|
||||
return Constructor(
|
||||
constructorId=constructor["constructorId"],
|
||||
url=constructor["url"],
|
||||
name=constructor["name"],
|
||||
nationality=constructor["nationality"]
|
||||
)
|
||||
|
||||
def construct_constructors(self, constructors: dict) -> list[Constructor]:
|
||||
return [self.construct_constructor(constructor) for constructor in constructors]
|
||||
|
||||
def construct_driver(self, driver: dict) -> Driver:
|
||||
driver = self._populate_missing_driver(driver)
|
||||
return Driver(
|
||||
driverId=driver["driverId"],
|
||||
permanentNumber=int(driver["permanentNumber"]),
|
||||
code=driver["code"],
|
||||
url=driver["url"],
|
||||
givenName=driver["givenName"],
|
||||
familyName=driver["familyName"],
|
||||
dateOfBirth=Helpers().construct_date(driver["dateOfBirth"]),
|
||||
nationality=driver["nationality"]
|
||||
)
|
||||
|
||||
def construct_drivers(self, drivers: dict) -> list[Driver]:
|
||||
return [self.construct_driver(driver) for driver in drivers]
|
||||
|
||||
def construct_race(self, race: dict) -> Race:
|
||||
race = self._populate_missing_race(race)
|
||||
return Race(
|
||||
season=int(race["season"]),
|
||||
round=int(race["round"]),
|
||||
url=race["url"],
|
||||
raceName=race["raceName"],
|
||||
circuit=self.construct_circuit(race["Circuit"]),
|
||||
date=Helpers().construct_datetime_str(date=race["date"], time=race["time"]),
|
||||
results=self.construct_results(race["Results"]),
|
||||
firstPractice=Helpers().construct_datetime_dict(race["FirstPractice"]),
|
||||
secondPractice=Helpers().construct_datetime_dict(race["SecondPractice"]),
|
||||
thirdPractice=Helpers().construct_datetime_dict(race["ThirdPractice"]),
|
||||
sprint=Helpers().construct_datetime_dict(race["Sprint"]),
|
||||
sprintResults=self.construct_results(race["SprintResults"]),
|
||||
qualifying=Helpers().construct_datetime_dict(race["Qualifying"]),
|
||||
qualifyingResults=self.construct_results(race["QualifyingResults"]),
|
||||
pitStops=self.construct_pit_stops(race["PitStops"]),
|
||||
laps=self.construct_laps(race["Laps"])
|
||||
)
|
||||
|
||||
def construct_races(self, races: dict) -> list[Race]:
|
||||
return [self.construct_race(race) for race in races]
|
||||
|
||||
def construct_result(self, result: dict) -> Result:
|
||||
result = self._populate_missing_result(result)
|
||||
return Result(
|
||||
number=int(result["number"]),
|
||||
position=int(result["position"]),
|
||||
positionText=result["positionText"],
|
||||
points=float(result["points"]),
|
||||
driver=self.construct_driver(result["Driver"]),
|
||||
constructor=self.construct_constructor(result["Constructor"]),
|
||||
grid=int(result["grid"]),
|
||||
laps=int(result["laps"]),
|
||||
status=int(StatusType().status_map[result["status"]]),
|
||||
time=Helpers().construct_lap_time_millis(millis=result["Time"]),
|
||||
fastestLap=self.construct_fastest_lap(result["FastestLap"]),
|
||||
q1=Helpers().format_lap_time(time=result["Q1"]),
|
||||
q2=Helpers().format_lap_time(time=result["Q2"]),
|
||||
q3=Helpers().format_lap_time(time=result["Q3"]),
|
||||
)
|
||||
|
||||
def construct_results(self, results: dict) -> list[Result]:
|
||||
return [self.construct_result(result) for result in results]
|
||||
|
||||
def construct_fastest_lap(self, fastest_lap: dict) -> FastestLap:
|
||||
fastest_lap = self._populate_missing_fastest_lap(fastest_lap)
|
||||
return FastestLap(
|
||||
rank=int(fastest_lap["rank"]),
|
||||
lap=int(fastest_lap["lap"]),
|
||||
time=Helpers().construct_lap_time(time=fastest_lap["Time"]),
|
||||
averageSpeed=self.construct_average_speed(fastest_lap["AverageSpeed"])
|
||||
)
|
||||
|
||||
def construct_average_speed(self, average_speed: dict) -> AverageSpeed:
|
||||
average_speed = self._populate_missing_average_speed(average_speed)
|
||||
return AverageSpeed(
|
||||
units=average_speed["units"],
|
||||
speed=float(average_speed["speed"])
|
||||
)
|
||||
|
||||
def construct_pit_stop(self, pit_stop: dict) -> PitStop:
|
||||
pit_stop = self._populate_missing_pit_stop(pit_stop)
|
||||
return PitStop(
|
||||
driverId=pit_stop["driverId"],
|
||||
lap=int(pit_stop["lap"]),
|
||||
stop=int(pit_stop["stop"]),
|
||||
localTime=Helpers().construct_local_time(pit_stop["time"]),
|
||||
duration=Helpers().construct_pitstop_duration(pit_stop["duration"])
|
||||
)
|
||||
|
||||
def construct_pit_stops(self, pit_stops: dict) -> list[PitStop]:
|
||||
return [self.construct_pit_stop(pit_stop) for pit_stop in pit_stops]
|
||||
|
||||
def construct_lap(self, lap: dict) -> Lap:
|
||||
lap = self._populate_missing_lap(lap)
|
||||
return Lap(
|
||||
number=int(lap["number"]),
|
||||
timings=self.construct_timings(lap["Timings"])
|
||||
)
|
||||
|
||||
def construct_laps(self, laps: dict) -> list[Lap]:
|
||||
return [self.construct_lap(lap) for lap in laps]
|
||||
|
||||
def construct_timing(self, timing: dict) -> Timing:
|
||||
timing = self._populate_missing_timing(timing)
|
||||
return Timing(
|
||||
driverId=timing["driverId"],
|
||||
position=int(timing["position"]),
|
||||
time=Helpers().format_lap_time(time=timing["time"])
|
||||
)
|
||||
|
||||
def construct_timings(self, timings: dict) -> list[Timing]:
|
||||
return [self.construct_timing(timing) for timing in timings]
|
||||
|
||||
def construct_season(self, season: dict) -> Season:
|
||||
season = self._populate_missing_season(season)
|
||||
return Season(
|
||||
season=int(season["season"]),
|
||||
url=season["url"]
|
||||
)
|
||||
|
||||
def construct_seasons(self, seasons: dict) -> list[Season]:
|
||||
return [self.construct_season(season) for season in seasons]
|
||||
|
||||
def construct_status(self, status: dict) -> Status:
|
||||
status = self._populate_missing_status(status)
|
||||
return Status(
|
||||
statusId=int(status["statusId"]),
|
||||
count=int(status["count"]),
|
||||
status=status["status"]
|
||||
)
|
||||
|
||||
def construct_statuses(self, statuses: dict) -> list[Season]:
|
||||
return [self.construct_status(status) for status in statuses]
|
||||
|
||||
def construct_driver_standing(self, standing: dict) -> DriverStanding:
|
||||
standing = self._populate_missing_driver_standing(standing)
|
||||
return DriverStanding(
|
||||
position=int(standing["position"]),
|
||||
positionText=standing["positionText"],
|
||||
points=float(standing["points"]),
|
||||
wins=int(standing["wins"]),
|
||||
driver=self.construct_driver(standing["Driver"]),
|
||||
constructors=self.construct_constructors(standing["Constructors"])
|
||||
)
|
||||
|
||||
def construct_driver_standings(self, standings: dict) -> list[DriverStanding]:
|
||||
return [self.construct_driver_standing(standing) for standing in standings]
|
||||
|
||||
def construct_constructor_standing(self, standing: dict) -> ConstructorStanding:
|
||||
standing = self._populate_missing_constructor_standing(standing)
|
||||
return ConstructorStanding(
|
||||
position=int(standing["position"]),
|
||||
positionText=standing["positionText"],
|
||||
points=float(standing["points"]),
|
||||
wins=int(standing["wins"]),
|
||||
constructor=self.construct_constructor(standing["Constructor"])
|
||||
)
|
||||
|
||||
def construct_constructor_standings(self, standings: dict) -> list[ConstructorStanding]:
|
||||
return [self.construct_constructor_standing(standing) for standing in standings]
|
||||
|
||||
def construct_standings_list(self, standings_list: dict) -> StandingsList:
|
||||
standings_list = self._populate_missing_standings_list(standings_list)
|
||||
return StandingsList(
|
||||
season=int(standings_list["season"]),
|
||||
round=int(standings_list["round"]),
|
||||
driverStandings=self.construct_driver_standings(standings_list["DriverStandings"]),
|
||||
constructorStandings=self.construct_constructor_standings(standings_list["ConstructorStandings"])
|
||||
)
|
||||
|
||||
def construct_standings_lists(self, standings_lists: dict) -> list[StandingsList]:
|
||||
return [self.construct_standings_list(standings_list) for standings_list in standings_lists]
|
245
poetry.lock
generated
Normal file
245
poetry.lock
generated
Normal file
@ -0,0 +1,245 @@
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "21.4.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
|
||||
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2022.5.18.1"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "2.0.12"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5.0"
|
||||
|
||||
[package.extras]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.3"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "8.13.0"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "0.13.1"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.11.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.8"
|
||||
|
||||
[package.extras]
|
||||
diagrams = ["railroad-diagrams", "jinja2"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "5.4.3"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=17.4.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
more-itertools = ">=4.0.0"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0"
|
||||
py = ">=1.5.0"
|
||||
wcwidth = "*"
|
||||
|
||||
[package.extras]
|
||||
checkqa-mypy = ["mypy (==v0.761)"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.27.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
|
||||
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
|
||||
|
||||
[[package]]
|
||||
name = "uritemplate"
|
||||
version = "4.1.1"
|
||||
description = "Implementation of RFC 6570 URI Templates"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.9"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.5"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "46627c9e8eb39de84bfe1ad76fabe2b766d95aaa41505a3351adb8de89dbe319"
|
||||
|
||||
[metadata.files]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"},
|
||||
{file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
|
||||
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
more-itertools = [
|
||||
{file = "more-itertools-8.13.0.tar.gz", hash = "sha256:a42901a0a5b169d925f6f217cd5a190e32ef54360905b9c39ee7db5313bfec0f"},
|
||||
{file = "more_itertools-8.13.0-py3-none-any.whl", hash = "sha256:c5122bffc5f104d37c1626b8615b511f3427aa5389b94d61e5ef8236bfbc3ddb"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
|
||||
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
||||
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
||||
]
|
||||
uritemplate = [
|
||||
{file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
|
||||
{file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
|
||||
{file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
|
||||
]
|
||||
wcwidth = [
|
||||
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
||||
]
|
17
pyproject.toml
Normal file
17
pyproject.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[tool.poetry]
|
||||
name = "ergast-py"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Samuel Roach <samuelroach.2000@gmail.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
requests = "^2.27.1"
|
||||
uritemplate = "^4.1.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^5.2"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
5
tests/test_ergast_py.py
Normal file
5
tests/test_ergast_py.py
Normal file
@ -0,0 +1,5 @@
|
||||
from ergast_py import __version__
|
||||
|
||||
|
||||
def test_version():
|
||||
assert __version__ == '0.1.0'
|
Reference in New Issue
Block a user