File size: 13,683 Bytes
7206ec7
 
 
 
fa395d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7206ec7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
from enum import Enum
from dataclasses import dataclass
from typing import Dict, Set, List, Optional

api_endpoints = {
    "sessions": "sessions?",
    "weather": "weather?",
    "locations": "locations?",
    "drivers": "drivers?",
    "intervals": "intervals?",
    "laps": "laps?",
    "car_data": "car_data?",
    "pit": "pit?",
    "position": "position?",
    "race_control": "race_control?",
    "stints": "stints?",
    "team_radio": "team_radio?",
}

class FilterType(Enum):
    EQUALITY = "equality"       # exact match
    COMPARISON = "comparison"   # >, <, >=, <=

class DataType(Enum):
    STRING = "string"
    INTEGER = "integer"
    DATETIME = "datetime"
    BINARY = "binary"          # true/false filters

@dataclass
class FilterSpec:
    """Specification for an API filter parameter"""
    name: str
    filter_type: FilterType
    data_type: DataType
    description: str = ""
    allowed_values: Optional[List[str]] = None  # For equality filters with restricted values
    
    def get_query_examples(self) -> List[str]:
        """Generate example query parameters for this filter"""
        examples = []
        
        if self.data_type == DataType.BINARY:
            examples = [f"{self.name}=true", f"{self.name}=false"]
        
        elif self.filter_type == FilterType.EQUALITY:
            if self.allowed_values:
                examples = [f"{self.name}={val}" for val in self.allowed_values[:2]]
            elif self.data_type == DataType.STRING:
                examples = [f"{self.name}=example_value"]
            elif self.data_type == DataType.INTEGER:
                examples = [f"{self.name}=42"]
            elif self.data_type == DataType.DATETIME:
                examples = [f"{self.name}=2024-01-01T00:00:00Z", f"{self.name}=2024-01-01T10:30:00Z"]
        
        elif self.filter_type == FilterType.COMPARISON:
            if self.data_type == DataType.INTEGER:
                examples = [f"{self.name}>=10", f"{self.name}<100"]
            elif self.data_type == DataType.DATETIME:
                examples = [f"{self.name}>=2024-01-01T00:00:00Z", f"{self.name}<2024-12-31T00:00:00Z"]
            elif self.data_type == DataType.STRING:
                examples = [f"{self.name}>M", f"{self.name}<Z"]  # alphabetical comparison
        
        return examples
    
    def help_text(self) -> str:
        """Generate help text for this filter"""
        text = f"Filter: {self.name}\n"
        text += f"  Type: {self.filter_type.value} ({self.data_type.value})\n"
        
        if self.description:
            text += f"  Description: {self.description}\n"
        
        if self.allowed_values:
            text += f"  Allowed values: {', '.join(self.allowed_values)}\n"
        
        examples = self.get_query_examples()
        if examples:
            text += f"  Examples: {', '.join(examples)}"
        
        return text



class APIEndpointRegistry:
    """Registry for API endpoints and their supported filters"""
    
    def __init__(self, base_url: str = ""):
        self.base_url = base_url
        self.endpoints: Dict[str, Set[str]] = {}  # endpoint -> filter names
        self.filters: Dict[str, FilterSpec] = {}  # global filter definitions
    
    def define_filter(
        self, 
        name: str, 
        filter_type: FilterType, 
        data_type: DataType,
        description: str = "",
        allowed_values: Optional[List[str]] = None
    ) -> 'APIEndpointRegistry':
        """Define a filter that can be used by endpoints"""
        filter_spec = FilterSpec(name, filter_type, data_type, description, allowed_values)
        self.filters[name] = filter_spec
        return self
    
    def register_endpoint(self, endpoint: str, *filter_names: str) -> 'APIEndpointRegistry':
        """Register an API endpoint with its supported filters"""

        # Validate all filters exist
        for filter_name in filter_names:
            if filter_name not in self.filters:
                raise ValueError(f"Filter '{filter_name}' not defined. Use define_filter() first.")
        
        # Store endpoint -> filters mapping
        self.endpoints[endpoint] = set(filter_names)
        
        return self
    




    def get_endpoint_filters(self, endpoint: str) -> Dict[str, FilterSpec]:
        """Get all filters supported by an endpoint"""
        filter_names = self.endpoints.get(endpoint, set())
        return {name: self.filters[name] for name in filter_names}
        
    def get_filter_help(self, filter_name: str) -> str:
        """Get help text for a specific filter"""

        if filter_name in self.filters:
            return self.filters[filter_name].help_text()
        return f"Filter '{filter_name}' not found."
    
    def get_endpoint_help(self, endpoint: str) -> str:
        """Get help text for all filters supported by an endpoint"""

        filters = self.get_endpoint_filters(endpoint)
        if not filters:
            return f"Endpoint '{endpoint}' has no registered filters."
        
        help_text = f"API Endpoint: {self.base_url}{endpoint}\n"
        help_text += f"Supported filters ({len(filters)}):\n\n"
        
        for name, spec in sorted(filters.items()):
            help_text += spec.help_text() + "\n\n"
        
        return help_text.strip()
    
    def list_all_endpoints(self) -> List[str]:
        """Get list of all registered endpoints"""
        return sorted(self.endpoints.keys())
    
    def list_all_filters(self) -> List[str]:
        """Get list of all defined filters"""
        return sorted(self.filters.keys())


# Create registry with base URL
f1_api = APIEndpointRegistry("https://api.openf1.org/v1/")

# Define filters with their specifications
f1_api.define_filter("date", FilterType.COMPARISON, DataType.DATETIME, "The UTC date and time, in ISO 8601 format.")
f1_api.define_filter("driver_number", FilterType.EQUALITY, DataType.INTEGER, "The unique number assigned to an F1 driver")
f1_api.define_filter("meeting_key", FilterType.EQUALITY, DataType.STRING, "The unique identifier for the meeting. Use 'latest' to identify the latest or current meeting.")
f1_api.define_filter("session_key", FilterType.EQUALITY, DataType.STRING, "The unique identifier for the session. Use 'latest' to identify the latest or current session.")
f1_api.define_filter("speed", FilterType.COMPARISON, DataType.INTEGER, "Velocity of the car in km/h.")
f1_api.define_filter("country_code", FilterType.EQUALITY, DataType.STRING, "A code that uniquely identifies the country.")
f1_api.define_filter("first_name", FilterType.EQUALITY, DataType.STRING, "The first name of the driver.")
f1_api.define_filter("last_name", FilterType.EQUALITY, DataType.STRING, "The last name of the driver.")
f1_api.define_filter("full_name", FilterType.EQUALITY, DataType.STRING, "The full name of the driver.")
f1_api.define_filter("name_acronym", FilterType.EQUALITY, DataType.STRING, "Three-letter acronym of the driver's name.")
f1_api.define_filter("team_name", FilterType.EQUALITY, DataType.STRING, "The name of the driver's team.")
f1_api.define_filter("gap_to_leader", FilterType.COMPARISON, DataType.INTEGER, "The time gap to the race leader in seconds, +1 LAP if lapped, or null for the race leader.")
f1_api.define_filter("interval", FilterType.COMPARISON, DataType.INTEGER, "The time gap to the car ahead in seconds, +1 LAP if lapped, or null for the race leader.")
f1_api.define_filter("date_start", FilterType.COMPARISON, DataType.DATETIME, "The UTC starting date and time, in ISO 8601 format.")
f1_api.define_filter("date_end", FilterType.COMPARISON, DataType.DATETIME, "The UTC ending date and time, in ISO 8601 format.")
f1_api.define_filter("is_pit_out_lap", FilterType.EQUALITY, DataType.BINARY, "A boolean value indicating whether the lap is an out lap from the pit (true if it is, false otherwise).")
f1_api.define_filter("lap_duration", FilterType.COMPARISON, DataType.INTEGER, "The total time taken, in seconds, to complete the entire lap.")
f1_api.define_filter("lap_number", FilterType.EQUALITY, DataType.INTEGER, "The sequential number of the lap within the session (starts at 1).")
f1_api.define_filter("circuit_key", FilterType.EQUALITY, DataType.STRING, "The unique identifier for the circuit where the event takes place.")
f1_api.define_filter("circuit_short_name", FilterType.EQUALITY, DataType.STRING, "The short or common name of the circuit where the event takes place.")
f1_api.define_filter("country_key", FilterType.EQUALITY, DataType.STRING, "The unique identifier for the country where the event takes place.")
f1_api.define_filter("country_name", FilterType.EQUALITY, DataType.STRING, "The name of the country where the event takes place.")
f1_api.define_filter("location", FilterType.EQUALITY, DataType.STRING, "The city or geographical location where the event takes place.")
f1_api.define_filter("meeting_name", FilterType.EQUALITY, DataType.STRING, "The name of the meeting.")
f1_api.define_filter("meeting_official_name", FilterType.EQUALITY, DataType.STRING, "The official name of the meeting.")
f1_api.define_filter("year", FilterType.EQUALITY, DataType.INTEGER, "The year of the event.")
f1_api.define_filter("pit_duration", FilterType.COMPARISON, DataType.INTEGER, "The time spent in the pit, from entering to leaving the pit lane, in seconds.")
f1_api.define_filter("position", FilterType.EQUALITY, DataType.INTEGER, "Position of the driver (starts at 1).", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
f1_api.define_filter("category", FilterType.EQUALITY, DataType.STRING, "The category of the event (CarEvent, Drs, Flag, SafetyCar)", ["CarEvent", "Drs", "Flag", "SafetyCar"])
f1_api.define_filter("flag", FilterType.EQUALITY, DataType.STRING, "The flag displayed to the drivers.", ["Green", "Yellow", "Red", "Black", "White", "Blue", "Checkered", "White"])
f1_api.define_filter("message", FilterType.EQUALITY, DataType.STRING, "Description of the event or action.")
f1_api.define_filter("session_name", FilterType.EQUALITY, DataType.STRING, "The name of the session (Practice 1, Qualifying, Race, ...).")
f1_api.define_filter("session_type", FilterType.EQUALITY, DataType.STRING, "The type of the session (Practice, Qualifying, Race, ...).")
f1_api.define_filter("compound", FilterType.EQUALITY, DataType.STRING, "The specific compound of tyre used during the stint (SOFT, MEDIUM, HARD, ...).", ["SOFT", "MEDIUM", "HARD", "INTERMEDIATE", "WET"])
f1_api.define_filter("lap_end", FilterType.COMPARISON, DataType.INTEGER, "Number of the last completed lap in this stint.")
f1_api.define_filter("lap_start", FilterType.COMPARISON, DataType.INTEGER, "Number of the initial lap in this stint (starts at 1).")
f1_api.define_filter("stint_number", FilterType.EQUALITY, DataType.INTEGER, "The sequential number of the stint within the session (starts at 1).")
f1_api.define_filter("tyre_age_at_start", FilterType.COMPARISON, DataType.INTEGER, "The age of the tyres at the start of the stint, in laps completed.")
f1_api.define_filter("air_temperature", FilterType.COMPARISON, DataType.INTEGER, "Air temperature (°C).")
f1_api.define_filter("humidity", FilterType.COMPARISON, DataType.INTEGER, "Humidity percentage.")
f1_api.define_filter("pressure", FilterType.COMPARISON, DataType.INTEGER, "Air pressure (mbar).")
f1_api.define_filter("rainfall", FilterType.COMPARISON, DataType.INTEGER, "Whether there is rainfall.")
f1_api.define_filter("track_temperature", FilterType.COMPARISON, DataType.INTEGER, "Track temperature (°C).")
f1_api.define_filter("wind_direction", FilterType.COMPARISON, DataType.INTEGER, "Wind direction (°), from 0° to 359°.")
f1_api.define_filter("wind_speed", FilterType.COMPARISON, DataType.INTEGER, "Wind speed (m/s).")


# Register API endpoints with their supported filters
f1_api.register_endpoint("car_data", "date", "driver_number", "meeting_key", "session_key", "speed")
f1_api.register_endpoint("drivers", "session_key", "meeting_key", "country_code", "driver_number", "first_name", "last_name", "full_name", "name_acronym", "team_name")
f1_api.register_endpoint("intervals", "date", "driver_number", "meeting_key", "session_key", "gap_to_leader", "interval")
f1_api.register_endpoint("laps", "date_start", "driver_number", "meeting_key", "session_key", "lap_duration", "lap_number", "is_pit_out_lap")
f1_api.register_endpoint("location", "date", "driver_number", "meeting_key", "session_key")
f1_api.register_endpoint("meetings", "circuit_key", "circuit_short_name", "country_code", "country_key", "country_name", "date_start", "location", "meeting_key", "meeting_name", "meeting_official_name", "year")
f1_api.register_endpoint("pit", "date", "driver_number", "lap_number", "meeting_key", "session_key", "pit_duration")
f1_api.register_endpoint("position", "date", "driver_number", "meeting_key", "session_key", "position")
f1_api.register_endpoint("race_control", "category", "date", "driver_number", "meeting_key", "session_key", "flag", "message", "lap_number")
f1_api.register_endpoint("sessions", "circuit_key", "circuit_short_name", "country_code", "country_key", "country_name", "date_start", "date_end", "location", "session_name", "session_type", "session_key", "meeting_key", "year")
f1_api.register_endpoint("stints", "compound", "driver_number", "lap_end", "lap_start", "meeting_key", "session_key", "stint_number", "tyre_age_at_start")
f1_api.register_endpoint("team_radio", "date", "driver_number", "meeting_key", "session_key")
f1_api.register_endpoint("weather", "air_temperature", "date", "humidity", "pressure", "rainfall", "track_temperature", "wind_direction", "wind_speed", "meeting_key", "session_key")