aiomobilitydatabase 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiomobilitydatabase/__init__.py +101 -0
- aiomobilitydatabase/client.py +532 -0
- aiomobilitydatabase/const.py +11 -0
- aiomobilitydatabase/exceptions.py +33 -0
- aiomobilitydatabase/models.py +443 -0
- aiomobilitydatabase/py.typed +0 -0
- aiomobilitydatabase-0.1.0.dist-info/METADATA +111 -0
- aiomobilitydatabase-0.1.0.dist-info/RECORD +10 -0
- aiomobilitydatabase-0.1.0.dist-info/WHEEL +4 -0
- aiomobilitydatabase-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""Models for the Mobility Database catalog API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
|
|
9
|
+
from mashumaro import DataClassDictMixin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DataType(StrEnum):
|
|
13
|
+
"""Type of data a feed provides."""
|
|
14
|
+
|
|
15
|
+
GTFS = "gtfs"
|
|
16
|
+
GTFS_RT = "gtfs_rt"
|
|
17
|
+
GBFS = "gbfs"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FeedStatus(StrEnum):
|
|
21
|
+
"""Lifecycle status of a feed."""
|
|
22
|
+
|
|
23
|
+
ACTIVE = "active"
|
|
24
|
+
DEPRECATED = "deprecated"
|
|
25
|
+
INACTIVE = "inactive"
|
|
26
|
+
DEVELOPMENT = "development"
|
|
27
|
+
FUTURE = "future"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EntityType(StrEnum):
|
|
31
|
+
"""GTFS Realtime entity type."""
|
|
32
|
+
|
|
33
|
+
VEHICLE_POSITIONS = "vp"
|
|
34
|
+
TRIP_UPDATES = "tu"
|
|
35
|
+
SERVICE_ALERTS = "sa"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LocationType(StrEnum):
|
|
39
|
+
"""Type of a location in location search results."""
|
|
40
|
+
|
|
41
|
+
COUNTRY = "country"
|
|
42
|
+
SUBDIVISION = "subdivision"
|
|
43
|
+
MUNICIPALITY = "municipality"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class GbfsVersionSource(StrEnum):
|
|
47
|
+
"""Origin of GBFS version information."""
|
|
48
|
+
|
|
49
|
+
AUTODISCOVERY = "autodiscovery"
|
|
50
|
+
GBFS_VERSIONS = "gbfs_versions"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class BoundingFilterMethod(StrEnum):
|
|
54
|
+
"""Filtering method for bounding-box queries."""
|
|
55
|
+
|
|
56
|
+
COMPLETELY_ENCLOSED = "completely_enclosed"
|
|
57
|
+
PARTIALLY_ENCLOSED = "partially_enclosed"
|
|
58
|
+
DISJOINT = "disjoint"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SortOrder(StrEnum):
|
|
62
|
+
"""Sort order for list endpoints that support it."""
|
|
63
|
+
|
|
64
|
+
ASC = "asc"
|
|
65
|
+
DESC = "desc"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LicenseRuleType(StrEnum):
|
|
69
|
+
"""Type of a license rule."""
|
|
70
|
+
|
|
71
|
+
PERMISSION = "permission"
|
|
72
|
+
CONDITION = "condition"
|
|
73
|
+
LIMITATION = "limitation"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RequestMethod(StrEnum):
|
|
77
|
+
"""HTTP method used for a feed availability check."""
|
|
78
|
+
|
|
79
|
+
HEAD = "HEAD"
|
|
80
|
+
GET = "GET"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class Redirect(DataClassDictMixin):
|
|
85
|
+
"""A feed redirect to a replacement feed ID."""
|
|
86
|
+
|
|
87
|
+
target_id: str | None = None
|
|
88
|
+
comment: str | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class ExternalId(DataClassDictMixin):
|
|
93
|
+
"""An ID for the feed in an external or legacy database."""
|
|
94
|
+
|
|
95
|
+
external_id: str | None = None
|
|
96
|
+
source: str | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class SourceInfo(DataClassDictMixin):
|
|
101
|
+
"""Information about the feed producer's source URL and license."""
|
|
102
|
+
|
|
103
|
+
producer_url: str | None = None
|
|
104
|
+
is_producer_url_unstable: bool | None = None
|
|
105
|
+
authentication_type: int | None = None
|
|
106
|
+
authentication_info_url: str | None = None
|
|
107
|
+
api_key_parameter_name: str | None = None
|
|
108
|
+
license_url: str | None = None
|
|
109
|
+
license_id: str | None = None
|
|
110
|
+
license_is_spdx: bool | None = None
|
|
111
|
+
license_notes: str | None = None
|
|
112
|
+
license_tags: list[str] | None = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class BoundingBox(DataClassDictMixin):
|
|
117
|
+
"""Geographic bounding box of a dataset."""
|
|
118
|
+
|
|
119
|
+
minimum_latitude: float | None = None
|
|
120
|
+
maximum_latitude: float | None = None
|
|
121
|
+
minimum_longitude: float | None = None
|
|
122
|
+
maximum_longitude: float | None = None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class Location(DataClassDictMixin):
|
|
127
|
+
"""Geographic location served by a feed."""
|
|
128
|
+
|
|
129
|
+
country_code: str | None = None
|
|
130
|
+
country: str | None = None
|
|
131
|
+
subdivision_name: str | None = None
|
|
132
|
+
municipality: str | None = None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass
|
|
136
|
+
class FeedRelatedLink(DataClassDictMixin):
|
|
137
|
+
"""A link related to a feed."""
|
|
138
|
+
|
|
139
|
+
code: str | None = None
|
|
140
|
+
description: str | None = None
|
|
141
|
+
url: str | None = None
|
|
142
|
+
created_at: datetime | None = None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass
|
|
146
|
+
class ValidationReportSummary(DataClassDictMixin):
|
|
147
|
+
"""Validation counts embedded in a latest-dataset summary."""
|
|
148
|
+
|
|
149
|
+
features: list[str] | None = None
|
|
150
|
+
total_error: int | None = None
|
|
151
|
+
total_warning: int | None = None
|
|
152
|
+
total_info: int | None = None
|
|
153
|
+
unique_error_count: int | None = None
|
|
154
|
+
unique_warning_count: int | None = None
|
|
155
|
+
unique_info_count: int | None = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class LatestDataset(DataClassDictMixin):
|
|
160
|
+
"""Summary of the latest dataset for a GTFS feed."""
|
|
161
|
+
|
|
162
|
+
id: str | None = None
|
|
163
|
+
hosted_url: str | None = None
|
|
164
|
+
bounding_box: BoundingBox | None = None
|
|
165
|
+
downloaded_at: datetime | None = None
|
|
166
|
+
hash: str | None = None
|
|
167
|
+
hash_md5: str | None = None
|
|
168
|
+
service_date_range_start: datetime | None = None
|
|
169
|
+
service_date_range_end: datetime | None = None
|
|
170
|
+
agency_timezone: str | None = None
|
|
171
|
+
zipped_folder_size_mb: float | None = None
|
|
172
|
+
unzipped_folder_size_mb: float | None = None
|
|
173
|
+
validation_report: ValidationReportSummary | None = None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class Feed(DataClassDictMixin):
|
|
178
|
+
"""Common feed fields shared by all feed types."""
|
|
179
|
+
|
|
180
|
+
id: str | None = None
|
|
181
|
+
data_type: DataType | None = None
|
|
182
|
+
created_at: datetime | None = None
|
|
183
|
+
external_ids: list[ExternalId] | None = None
|
|
184
|
+
provider: str | None = None
|
|
185
|
+
feed_contact_email: str | None = None
|
|
186
|
+
source_info: SourceInfo | None = None
|
|
187
|
+
redirects: list[Redirect] | None = None
|
|
188
|
+
status: FeedStatus | None = None
|
|
189
|
+
official: bool | None = None
|
|
190
|
+
official_updated_at: datetime | None = None
|
|
191
|
+
feed_name: str | None = None
|
|
192
|
+
note: str | None = None
|
|
193
|
+
related_links: list[FeedRelatedLink] | None = None
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@dataclass
|
|
197
|
+
class GtfsFeed(Feed):
|
|
198
|
+
"""A GTFS schedule feed."""
|
|
199
|
+
|
|
200
|
+
locations: list[Location] | None = None
|
|
201
|
+
latest_dataset: LatestDataset | None = None
|
|
202
|
+
bounding_box: BoundingBox | None = None
|
|
203
|
+
visualization_dataset_id: str | None = None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass
|
|
207
|
+
class GtfsRtFeed(Feed):
|
|
208
|
+
"""A GTFS Realtime feed."""
|
|
209
|
+
|
|
210
|
+
entity_types: list[EntityType] | None = None
|
|
211
|
+
feed_references: list[str] | None = None
|
|
212
|
+
locations: list[Location] | None = None
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@dataclass
|
|
216
|
+
class GbfsEndpoint(DataClassDictMixin):
|
|
217
|
+
"""An endpoint available in a GBFS version."""
|
|
218
|
+
|
|
219
|
+
name: str | None = None
|
|
220
|
+
url: str | None = None
|
|
221
|
+
language: str | None = None
|
|
222
|
+
is_feature: bool | None = None
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@dataclass
|
|
226
|
+
class GbfsValidationReport(DataClassDictMixin):
|
|
227
|
+
"""A validation report for a GBFS feed version."""
|
|
228
|
+
|
|
229
|
+
validated_at: datetime | None = None
|
|
230
|
+
total_error: int | None = None
|
|
231
|
+
report_summary_url: str | None = None
|
|
232
|
+
validator_version: str | None = None
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@dataclass
|
|
236
|
+
class GbfsVersion(DataClassDictMixin):
|
|
237
|
+
"""A GBFS specification version supported by a feed."""
|
|
238
|
+
|
|
239
|
+
version: str | None = None
|
|
240
|
+
created_at: datetime | None = None
|
|
241
|
+
last_updated_at: datetime | None = None
|
|
242
|
+
source: GbfsVersionSource | None = None
|
|
243
|
+
endpoints: list[GbfsEndpoint] | None = None
|
|
244
|
+
latest_validation_report: GbfsValidationReport | None = None
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@dataclass
|
|
248
|
+
class GbfsFeed(Feed):
|
|
249
|
+
"""A GBFS feed.
|
|
250
|
+
|
|
251
|
+
The spec derives GbfsFeed from BasicFeed (without status/official/related
|
|
252
|
+
fields); sharing the Feed base here is harmless since all fields are
|
|
253
|
+
optional and unknown keys are ignored.
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
locations: list[Location] | None = None
|
|
257
|
+
system_id: str | None = None
|
|
258
|
+
provider_url: str | None = None
|
|
259
|
+
versions: list[GbfsVersion] | None = None
|
|
260
|
+
bounding_box: BoundingBox | None = None
|
|
261
|
+
bounding_box_generated_at: datetime | None = None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@dataclass
|
|
265
|
+
class SearchFeedItemResult(DataClassDictMixin):
|
|
266
|
+
"""A single feed result from the search endpoint (union of feed types)."""
|
|
267
|
+
|
|
268
|
+
id: str
|
|
269
|
+
data_type: DataType
|
|
270
|
+
status: FeedStatus
|
|
271
|
+
created_at: datetime | None = None
|
|
272
|
+
official: bool | None = None
|
|
273
|
+
external_ids: list[ExternalId] | None = None
|
|
274
|
+
provider: str | None = None
|
|
275
|
+
feed_name: str | None = None
|
|
276
|
+
note: str | None = None
|
|
277
|
+
feed_contact_email: str | None = None
|
|
278
|
+
source_info: SourceInfo | None = None
|
|
279
|
+
redirects: list[Redirect] | None = None
|
|
280
|
+
locations: list[Location] | None = None
|
|
281
|
+
latest_dataset: LatestDataset | None = None
|
|
282
|
+
entity_types: list[EntityType] | None = None
|
|
283
|
+
versions: list[str] | None = None
|
|
284
|
+
feed_references: list[str] | None = None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@dataclass
|
|
288
|
+
class SearchResults(DataClassDictMixin):
|
|
289
|
+
"""Response from the feed search endpoint."""
|
|
290
|
+
|
|
291
|
+
total: int | None = None
|
|
292
|
+
results: list[SearchFeedItemResult] = field(default_factory=list)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@dataclass
|
|
296
|
+
class ValidationReport(DataClassDictMixin):
|
|
297
|
+
"""Full validation report for a GTFS dataset."""
|
|
298
|
+
|
|
299
|
+
validated_at: datetime | None = None
|
|
300
|
+
features: list[str] | None = None
|
|
301
|
+
validator_version: str | None = None
|
|
302
|
+
total_error: int | None = None
|
|
303
|
+
total_warning: int | None = None
|
|
304
|
+
total_info: int | None = None
|
|
305
|
+
unique_error_count: int | None = None
|
|
306
|
+
unique_warning_count: int | None = None
|
|
307
|
+
unique_info_count: int | None = None
|
|
308
|
+
url_json: str | None = None
|
|
309
|
+
url_html: str | None = None
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@dataclass
|
|
313
|
+
class GtfsDataset(DataClassDictMixin):
|
|
314
|
+
"""A GTFS dataset for a feed."""
|
|
315
|
+
|
|
316
|
+
id: str | None = None
|
|
317
|
+
feed_id: str | None = None
|
|
318
|
+
hosted_url: str | None = None
|
|
319
|
+
note: str | None = None
|
|
320
|
+
downloaded_at: datetime | None = None
|
|
321
|
+
hash: str | None = None
|
|
322
|
+
hash_md5: str | None = None
|
|
323
|
+
bounding_box: BoundingBox | None = None
|
|
324
|
+
validation_report: ValidationReport | None = None
|
|
325
|
+
service_date_range_start: datetime | None = None
|
|
326
|
+
service_date_range_end: datetime | None = None
|
|
327
|
+
agency_timezone: str | None = None
|
|
328
|
+
zipped_folder_size_mb: float | None = None
|
|
329
|
+
unzipped_folder_size_mb: float | None = None
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@dataclass
|
|
333
|
+
class GtfsFeedAvailabilityCheck(DataClassDictMixin):
|
|
334
|
+
"""A single availability check for a GTFS feed."""
|
|
335
|
+
|
|
336
|
+
checked_at: datetime
|
|
337
|
+
success: bool
|
|
338
|
+
request_method: RequestMethod
|
|
339
|
+
status_code: int | None = None
|
|
340
|
+
latency_ms: float | None = None
|
|
341
|
+
error_type: str | None = None
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@dataclass
|
|
345
|
+
class GtfsFeedAvailability(DataClassDictMixin):
|
|
346
|
+
"""Availability check history for a GTFS feed."""
|
|
347
|
+
|
|
348
|
+
feed_id: str
|
|
349
|
+
total: int
|
|
350
|
+
offset: int
|
|
351
|
+
limit: int
|
|
352
|
+
checks: list[GtfsFeedAvailabilityCheck] = field(default_factory=list)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@dataclass
|
|
356
|
+
class LocationSearchResult(DataClassDictMixin):
|
|
357
|
+
"""A single location from the locations search endpoint."""
|
|
358
|
+
|
|
359
|
+
location_id: int | None = None
|
|
360
|
+
parent_location_id: int | None = None
|
|
361
|
+
name: str | None = None
|
|
362
|
+
alt_name: str | None = None
|
|
363
|
+
location_type: LocationType | None = None
|
|
364
|
+
country_name: str | None = None
|
|
365
|
+
country_code: str | None = None
|
|
366
|
+
subdivision_name: str | None = None
|
|
367
|
+
subdivision_code: str | None = None
|
|
368
|
+
path_names: list[str] | None = None
|
|
369
|
+
display_name: str | None = None
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@dataclass
|
|
373
|
+
class LocationSearchResults(DataClassDictMixin):
|
|
374
|
+
"""Response from the locations search endpoint."""
|
|
375
|
+
|
|
376
|
+
total: int | None = None
|
|
377
|
+
results: list[LocationSearchResult] = field(default_factory=list)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
@dataclass
|
|
381
|
+
class LicenseRule(DataClassDictMixin):
|
|
382
|
+
"""A rule (permission/condition/limitation) of a license."""
|
|
383
|
+
|
|
384
|
+
name: str | None = None
|
|
385
|
+
label: str | None = None
|
|
386
|
+
description: str | None = None
|
|
387
|
+
type: LicenseRuleType | None = None
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@dataclass
|
|
391
|
+
class License(DataClassDictMixin):
|
|
392
|
+
"""A license in the Mobility Database."""
|
|
393
|
+
|
|
394
|
+
id: str | None = None
|
|
395
|
+
type: str | None = None
|
|
396
|
+
is_spdx: bool | None = None
|
|
397
|
+
name: str | None = None
|
|
398
|
+
url: str | None = None
|
|
399
|
+
description: str | None = None
|
|
400
|
+
created_at: datetime | None = None
|
|
401
|
+
updated_at: datetime | None = None
|
|
402
|
+
license_tags: list[str] | None = None
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
@dataclass
|
|
406
|
+
class LicenseWithRules(License):
|
|
407
|
+
"""A license including its rules."""
|
|
408
|
+
|
|
409
|
+
license_rules: list[LicenseRule] | None = None
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
@dataclass
|
|
413
|
+
class MatchingLicense(DataClassDictMixin):
|
|
414
|
+
"""A license matched from a license URL."""
|
|
415
|
+
|
|
416
|
+
license_id: str | None = None
|
|
417
|
+
license_url: str | None = None
|
|
418
|
+
normalized_url: str | None = None
|
|
419
|
+
match_type: str | None = None
|
|
420
|
+
confidence: float | None = None
|
|
421
|
+
spdx_id: str | None = None
|
|
422
|
+
matched_name: str | None = None
|
|
423
|
+
matched_catalog_url: str | None = None
|
|
424
|
+
matched_source: str | None = None
|
|
425
|
+
notes: str | None = None
|
|
426
|
+
regional_id: str | None = None
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@dataclass
|
|
430
|
+
class Metadata(DataClassDictMixin):
|
|
431
|
+
"""Metadata about the API itself."""
|
|
432
|
+
|
|
433
|
+
version: str | None = None
|
|
434
|
+
commit_hash: str | None = None
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@dataclass
|
|
438
|
+
class AccessToken(DataClassDictMixin):
|
|
439
|
+
"""An access token response from the token endpoint."""
|
|
440
|
+
|
|
441
|
+
access_token: str
|
|
442
|
+
expiration_datetime_utc: datetime
|
|
443
|
+
token_type: str | None = None
|
|
File without changes
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aiomobilitydatabase
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async Python client for the Mobility Database catalog API
|
|
5
|
+
Project-URL: Source Code, https://github.com/raman325/aiomobilitydatabase
|
|
6
|
+
Project-URL: Bug Reports, https://github.com/raman325/aiomobilitydatabase/issues
|
|
7
|
+
Author: Raman Gupta
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: asyncio,gbfs,gtfs,gtfs-rt,mobility-database,transit
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: aiohttp>=3.9
|
|
20
|
+
Requires-Dist: mashumaro>=3.13
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# aiomobilitydatabase
|
|
24
|
+
|
|
25
|
+
Async Python client for the [Mobility Database](https://mobilitydatabase.org/) catalog API.
|
|
26
|
+
|
|
27
|
+
The Mobility Database is a catalog of GTFS, GTFS Realtime, and GBFS feeds from
|
|
28
|
+
around the world. This library wraps its
|
|
29
|
+
[catalog API](https://mobilitydata.github.io/mobility-feed-api/SwaggerUI/index.html)
|
|
30
|
+
with a fully typed asyncio client.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install aiomobilitydatabase
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Requires Python 3.12+.
|
|
39
|
+
|
|
40
|
+
## Authentication
|
|
41
|
+
|
|
42
|
+
Sign up at [mobilitydatabase.org](https://mobilitydatabase.org) and copy your
|
|
43
|
+
**refresh token** from the account page. The client exchanges it for short-lived
|
|
44
|
+
access tokens automatically (including proactive refresh before expiry).
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import asyncio
|
|
50
|
+
|
|
51
|
+
from aiomobilitydatabase import DataType, MobilityDatabaseClient
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def main() -> None:
|
|
55
|
+
async with MobilityDatabaseClient("YOUR_REFRESH_TOKEN") as client:
|
|
56
|
+
# Free-text search across feed name, provider, and location
|
|
57
|
+
results = await client.search_feeds(
|
|
58
|
+
search_query="new york", data_types=[DataType.GTFS_RT], limit=10
|
|
59
|
+
)
|
|
60
|
+
for item in results.results:
|
|
61
|
+
print(item.id, item.provider, item.status)
|
|
62
|
+
|
|
63
|
+
# Full detail for one feed, including the producer URL
|
|
64
|
+
feed = await client.get_gtfs_rt_feed(results.results[0].id)
|
|
65
|
+
assert feed.source_info is not None
|
|
66
|
+
print(feed.source_info.producer_url)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
With an externally managed session (e.g. Home Assistant's shared session), the
|
|
73
|
+
client never closes it:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import aiohttp
|
|
77
|
+
|
|
78
|
+
from aiomobilitydatabase import MobilityDatabaseClient
|
|
79
|
+
|
|
80
|
+
session = aiohttp.ClientSession()
|
|
81
|
+
client = MobilityDatabaseClient("YOUR_REFRESH_TOKEN", session)
|
|
82
|
+
try:
|
|
83
|
+
metadata = await client.get_metadata()
|
|
84
|
+
finally:
|
|
85
|
+
await client.close() # session remains open; you own it
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Error handling
|
|
89
|
+
|
|
90
|
+
All errors derive from `MobilityDatabaseError`:
|
|
91
|
+
|
|
92
|
+
| Exception | Meaning |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `MobilityDatabaseConnectionError` | Network failure, timeout, or invalid response body |
|
|
95
|
+
| `MobilityDatabaseAuthenticationError` | Invalid refresh token, or unauthorized after a token refresh |
|
|
96
|
+
| `MobilityDatabaseNotFoundError` | Resource not found (404) |
|
|
97
|
+
| `MobilityDatabaseRateLimitError` | Rate limited (429) |
|
|
98
|
+
| `MobilityDatabaseApiError` | Any other 4xx/5xx (carries `.status` and `.body`) |
|
|
99
|
+
|
|
100
|
+
## Development
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uv sync --dev
|
|
104
|
+
uv run pytest
|
|
105
|
+
uv run ruff check . && uv run ruff format --check .
|
|
106
|
+
uv run mypy src
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
[Apache-2.0](LICENSE)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
aiomobilitydatabase/__init__.py,sha256=OYqrl5PcadwHwegdevvhgTZkVry2t9gNYcmVu_xB9fI,2210
|
|
2
|
+
aiomobilitydatabase/client.py,sha256=pMBtgPfYH00krf6ZzI7gtptt2SZ9myhYDl_OJpthtDw,19593
|
|
3
|
+
aiomobilitydatabase/const.py,sha256=eE_LpabmaS0NEmYBYfZ5LxvdctV5K0G1vVpiKF2cFAY,313
|
|
4
|
+
aiomobilitydatabase/exceptions.py,sha256=HG3YDraqWV3SRCNNTT-lG18cjYscVZsmE1zWpeILjIM,1052
|
|
5
|
+
aiomobilitydatabase/models.py,sha256=2cVy7GaDDRgTQFkxM3OSy3KisHLwq0VFwKl65ljnlZc,11889
|
|
6
|
+
aiomobilitydatabase/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
aiomobilitydatabase-0.1.0.dist-info/METADATA,sha256=yPQ_2v3aFO0Aokc9kDjtToAm6470zk-whzUyD0VWpEo,3394
|
|
8
|
+
aiomobilitydatabase-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
9
|
+
aiomobilitydatabase-0.1.0.dist-info/licenses/LICENSE,sha256=ggh1G2G-hlnC0YFJjNmS0-ZFFhtlgSW29NEcJH9scVM,11342
|
|
10
|
+
aiomobilitydatabase-0.1.0.dist-info/RECORD,,
|