traewelling-api 0.1.0__tar.gz
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.
- traewelling_api-0.1.0/LICENSE +8 -0
- traewelling_api-0.1.0/PKG-INFO +31 -0
- traewelling_api-0.1.0/README.md +12 -0
- traewelling_api-0.1.0/pyproject.toml +26 -0
- traewelling_api-0.1.0/setup.cfg +4 -0
- traewelling_api-0.1.0/src/traewelling_api/__init__.py +5 -0
- traewelling_api-0.1.0/src/traewelling_api/api/__init__.py +23 -0
- traewelling_api-0.1.0/src/traewelling_api/api/auth.py +52 -0
- traewelling_api-0.1.0/src/traewelling_api/api/checkin.py +226 -0
- traewelling_api-0.1.0/src/traewelling_api/api/community.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/configuration_information.py +16 -0
- traewelling_api-0.1.0/src/traewelling_api/api/dashboard.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/events.py +81 -0
- traewelling_api-0.1.0/src/traewelling_api/api/generic.py +100 -0
- traewelling_api-0.1.0/src/traewelling_api/api/ics_tokens.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/leaderboard.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/likes.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/model/__init__.py +2 -0
- traewelling_api-0.1.0/src/traewelling_api/api/model/request.py +107 -0
- traewelling_api-0.1.0/src/traewelling_api/api/model/response.py +71 -0
- traewelling_api-0.1.0/src/traewelling_api/api/notifications.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/polyline.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/report.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/security.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/settings.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/statistics.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/status.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/trips.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/user.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/user_follow.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/user_hide_and_block.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/api/webhooks.py +0 -0
- traewelling_api-0.1.0/src/traewelling_api/cli.py +120 -0
- traewelling_api-0.1.0/src/traewelling_api/client.py +125 -0
- traewelling_api-0.1.0/src/traewelling_api.egg-info/PKG-INFO +31 -0
- traewelling_api-0.1.0/src/traewelling_api.egg-info/SOURCES.txt +38 -0
- traewelling_api-0.1.0/src/traewelling_api.egg-info/dependency_links.txt +1 -0
- traewelling_api-0.1.0/src/traewelling_api.egg-info/entry_points.txt +2 -0
- traewelling_api-0.1.0/src/traewelling_api.egg-info/requires.txt +6 -0
- traewelling_api-0.1.0/src/traewelling_api.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright (c) 2026 Lambda Crime
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: traewelling-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://codeberg.org/lambda-crime/traewelling-api
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: fptf-pydantic>=0.1.0
|
|
13
|
+
Requires-Dist: pydantic-extra-types>=2.11.0
|
|
14
|
+
Requires-Dist: rich>=14.2.0
|
|
15
|
+
Requires-Dist: textual>=7.2.0
|
|
16
|
+
Requires-Dist: traewelling-pydantic>=0.2.1
|
|
17
|
+
Requires-Dist: typer>=0.21.1
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Traewelling-API
|
|
21
|
+
|
|
22
|
+
This is a simple Traewelling API wrapper.
|
|
23
|
+
|
|
24
|
+
The goal is to provide an easy way to talk with the Traewelling API from Python
|
|
25
|
+
|
|
26
|
+
## TODOs
|
|
27
|
+
|
|
28
|
+
- add every API endpoint for Traewelling
|
|
29
|
+
- add FPTF converterter for Traewelling API responses
|
|
30
|
+
- add command for every API endpoint to CLI
|
|
31
|
+
- [OPTIONAL] add TUI for interactive API requests
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Traewelling-API
|
|
2
|
+
|
|
3
|
+
This is a simple Traewelling API wrapper.
|
|
4
|
+
|
|
5
|
+
The goal is to provide an easy way to talk with the Traewelling API from Python
|
|
6
|
+
|
|
7
|
+
## TODOs
|
|
8
|
+
|
|
9
|
+
- add every API endpoint for Traewelling
|
|
10
|
+
- add FPTF converterter for Traewelling API responses
|
|
11
|
+
- add command for every API endpoint to CLI
|
|
12
|
+
- [OPTIONAL] add TUI for interactive API requests
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "traewelling-api"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"fptf-pydantic>=0.1.0",
|
|
9
|
+
"pydantic-extra-types>=2.11.0",
|
|
10
|
+
"rich>=14.2.0",
|
|
11
|
+
"textual>=7.2.0",
|
|
12
|
+
"traewelling-pydantic>=0.2.1",
|
|
13
|
+
"typer>=0.21.1",
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICENSE"]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
traewelling-cli = "traewelling_api:app"
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://codeberg.org/lambda-crime/traewelling-api"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from . import auth
|
|
2
|
+
from . import checkin
|
|
3
|
+
from . import community
|
|
4
|
+
from . import configuration_information
|
|
5
|
+
from . import dashboard
|
|
6
|
+
from . import events
|
|
7
|
+
from . import ics_tokens
|
|
8
|
+
from . import leaderboard
|
|
9
|
+
from . import likes
|
|
10
|
+
from . import notifications
|
|
11
|
+
from . import polyline
|
|
12
|
+
from . import report
|
|
13
|
+
from . import security
|
|
14
|
+
from . import settings
|
|
15
|
+
from . import statistics
|
|
16
|
+
from . import status
|
|
17
|
+
from . import trips
|
|
18
|
+
from . import user
|
|
19
|
+
from . import user_follow
|
|
20
|
+
from . import user_hide_and_block
|
|
21
|
+
from . import webhooks
|
|
22
|
+
from . import generic
|
|
23
|
+
from . import models
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging in, creating Accounts, etc.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from . import generic
|
|
8
|
+
import traewelling_pydantic.models as trwlm
|
|
9
|
+
from . import model
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def post_auth_logout(token: str) -> bool:
|
|
13
|
+
"""
|
|
14
|
+
Logout & invalidate current bearer token
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
generic.post(token=token, path="auth/logout")
|
|
18
|
+
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_auth_user(token: str) -> trwlm.UserAuth:
|
|
23
|
+
"""
|
|
24
|
+
Get authenticated user information
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
response, status_code = generic.get(token=token, path="auth/user")
|
|
28
|
+
|
|
29
|
+
return trwlm.UserAuth.model_validate(response["data"])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def post_auth_refresh(token: str) -> model.response.BearerTokenResponse:
|
|
33
|
+
"""
|
|
34
|
+
Refresh Bearer Token
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
response, status_code = generic.post(token=token, path="auth/refresh")
|
|
38
|
+
|
|
39
|
+
return model.response.BearerTokenResponse.model_validate(response["data"])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_user_statuses_active(token: str) -> Optional[trwlm.Status]:
|
|
43
|
+
"""
|
|
44
|
+
User state
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
response, status_code = generic.get(token=token, path="user/statuses/active")
|
|
48
|
+
|
|
49
|
+
if status_code == 204:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
return trwlm.Status.model_validate(response["data"])
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Checkin related endpoints. Regular process is departures -> trip -> checkin
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
import uuid
|
|
8
|
+
from pydantic_extra_types.coordinate import Latitude, Longitude
|
|
9
|
+
|
|
10
|
+
from . import generic
|
|
11
|
+
import traewelling_pydantic.api_models as trwla
|
|
12
|
+
import traewelling_pydantic.models as trwlm
|
|
13
|
+
from . import model
|
|
14
|
+
from requests.exceptions import HTTPError
|
|
15
|
+
from urllib.parse import quote
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_operators(token: str) -> list[trwlm.Operator]:
|
|
19
|
+
response, status_code = generic.get(token=token, path="operators")
|
|
20
|
+
|
|
21
|
+
return [
|
|
22
|
+
trwlm.Operator.model_validate(operator) for operator in response["data"]
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_stations(
|
|
27
|
+
token: str,
|
|
28
|
+
identifier_provider: Optional[str] = None,
|
|
29
|
+
query: Optional[str] = None,
|
|
30
|
+
identifier: Optional[str] = None,
|
|
31
|
+
min_lat: Optional[Latitude] = None,
|
|
32
|
+
max_lat: Optional[Latitude] = None,
|
|
33
|
+
min_lon: Optional[Longitude] = None,
|
|
34
|
+
max_lon: Optional[Longitude] = None,
|
|
35
|
+
limit: int = 50,
|
|
36
|
+
with_identifiers: bool = False,
|
|
37
|
+
) -> Optional[list[trwlm.Station]]:
|
|
38
|
+
"""
|
|
39
|
+
UNSTABLE: Returns stations by fuzzy text, exact identifier, or within a bounding box (BBOX).
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def valid_bounding_box(
|
|
43
|
+
min_lat: Optional[Latitude] = None,
|
|
44
|
+
max_lat: Optional[Latitude] = None,
|
|
45
|
+
min_lon: Optional[Longitude] = None,
|
|
46
|
+
max_lon: Optional[Longitude] = None,
|
|
47
|
+
) -> bool:
|
|
48
|
+
if min_lat is None:
|
|
49
|
+
return False
|
|
50
|
+
if max_lat is None:
|
|
51
|
+
return False
|
|
52
|
+
if min_lon is None:
|
|
53
|
+
return False
|
|
54
|
+
if max_lon is None:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
if min_lat > max_lat:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
if min_lon > max_lon:
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
query is not None
|
|
67
|
+
and (identifier is not None and identifier_provider is not None)
|
|
68
|
+
and valid_bounding_box(min_lat, max_lat, min_lon, max_lon)
|
|
69
|
+
):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
"Only specify one: query, identifier+identifier_provider or bounding box"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if query is not None:
|
|
75
|
+
params = {
|
|
76
|
+
"query": quote(query),
|
|
77
|
+
"limit": limit,
|
|
78
|
+
"withIdentifiers": with_identifiers,
|
|
79
|
+
}
|
|
80
|
+
elif identifier is not None:
|
|
81
|
+
params = {
|
|
82
|
+
"identifier_provider": identifier_provider,
|
|
83
|
+
"identifier": identifier,
|
|
84
|
+
"limit": limit,
|
|
85
|
+
"withIdentifiers": with_identifiers,
|
|
86
|
+
}
|
|
87
|
+
elif valid_bounding_box(min_lat, max_lat, min_lon, max_lon):
|
|
88
|
+
params = {
|
|
89
|
+
"min_lat": min_lat,
|
|
90
|
+
"max_lat": max_lat,
|
|
91
|
+
"min_lon": min_lon,
|
|
92
|
+
"max_lon": max_lon,
|
|
93
|
+
"limit": limit,
|
|
94
|
+
"withIdentifiers": with_identifiers,
|
|
95
|
+
}
|
|
96
|
+
else:
|
|
97
|
+
raise ValueError(
|
|
98
|
+
"Specify one: query, identifier+identifier_provider or bounding box"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
response, status_code = generic.get(token=token, path="stations", params=params)
|
|
103
|
+
|
|
104
|
+
return [trwlm.Station.model_validate(i) for i in response["data"]]
|
|
105
|
+
except HTTPError as error:
|
|
106
|
+
if error.response.status_code == 404:
|
|
107
|
+
return None
|
|
108
|
+
raise error
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_stations_by_id(
|
|
112
|
+
token: str, station_id: Union[uuid.UUID, int], with_identifiers: bool = False
|
|
113
|
+
) -> Optional[trwlm.Station]:
|
|
114
|
+
"""
|
|
115
|
+
This request returns a single station object
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
response, status_code = generic.get(
|
|
119
|
+
token=token,
|
|
120
|
+
path=f"station/{station_id}",
|
|
121
|
+
params={"withIdentifiers": with_identifiers},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return trwlm.Station.model_validate(response["data"])
|
|
125
|
+
except HTTPError as error:
|
|
126
|
+
if error.response.status_code == 404:
|
|
127
|
+
return None
|
|
128
|
+
raise error
|
|
129
|
+
|
|
130
|
+
def get_station_departures(token: str, station_id: Union[uuid.UUID, int], when: datetime, travelType: str) -> Optional[tuple[list[model.response.Departure], model.response.DepartureMeta]]:
|
|
131
|
+
"""
|
|
132
|
+
Get departures from a station.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
response, status_code = generic.get(
|
|
136
|
+
token=token,
|
|
137
|
+
path=f"station/{station_id}/departures",
|
|
138
|
+
params={"when": when, "travelType": travelType},
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return [model.response.Departure.model_validate(i) for i in response["data"]], model.response.DepartureMeta.model_validate(response["meta"])
|
|
142
|
+
except HTTPError as error:
|
|
143
|
+
if error.response.status_code == 404:
|
|
144
|
+
return None
|
|
145
|
+
raise error
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_trains_trip(token: str, hafasTripId: str, lineName: str) -> Optional[trwlm.Trip]:
|
|
149
|
+
"""
|
|
150
|
+
Get the stopovers and trip information for a given train
|
|
151
|
+
"""
|
|
152
|
+
try:
|
|
153
|
+
response, status_code = generic.get(
|
|
154
|
+
token=token,
|
|
155
|
+
path="trains/trip",
|
|
156
|
+
params={"hafasTripId": hafasTripId, "lineName": lineName},
|
|
157
|
+
)
|
|
158
|
+
return trwlm.Trip.model_validate(response["data"])
|
|
159
|
+
except HTTPError as error:
|
|
160
|
+
if error.response.status_code == 404:
|
|
161
|
+
return None
|
|
162
|
+
raise error
|
|
163
|
+
|
|
164
|
+
def get_trains_station_nearby(token: str, latitude: Latitude, longitude: Longitude) -> Optional[list[trwlm.Station]]:
|
|
165
|
+
"""
|
|
166
|
+
Location based search for stations
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
response, status_code = generic.get(
|
|
170
|
+
token=token,
|
|
171
|
+
path="trains/trip",
|
|
172
|
+
params={"latidtude": latitude, "longitude": longitude},
|
|
173
|
+
)
|
|
174
|
+
return [trwlm.Station.model_validate(i) for i in response["data"]]
|
|
175
|
+
except HTTPError as error:
|
|
176
|
+
if error.response.status_code == 404:
|
|
177
|
+
return None
|
|
178
|
+
raise error
|
|
179
|
+
|
|
180
|
+
def post_trains_checkin(token: str, checkin: model.request.CheckinRequest) -> trwla.CheckinResponse:
|
|
181
|
+
"""
|
|
182
|
+
Check in to a trip.
|
|
183
|
+
"""
|
|
184
|
+
response, status_code = generic.post(
|
|
185
|
+
token=token,
|
|
186
|
+
path="trains/checkin",
|
|
187
|
+
data=checkin.model_dump(mode="json")
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
return trwla.CheckinResponse.model_validate(response)
|
|
191
|
+
|
|
192
|
+
def put_station_as_home(token: str, station_id: int) -> Optional[trwlm.Station]:
|
|
193
|
+
"""
|
|
194
|
+
Set a station as home station
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
response, status_code = generic.put(
|
|
198
|
+
token=token,
|
|
199
|
+
path=f"station/{station_id}/home",
|
|
200
|
+
)
|
|
201
|
+
return trwlm.Station.model_validate(response["data"])
|
|
202
|
+
except HTTPError as error:
|
|
203
|
+
if error.response.status_code == 404:
|
|
204
|
+
return None
|
|
205
|
+
raise error
|
|
206
|
+
|
|
207
|
+
def get_trains_station_autocomplete(token: str, query: str) -> list[trwlm.Station]:
|
|
208
|
+
"""
|
|
209
|
+
This request returns an array of max. 10 station objects matching the query.
|
|
210
|
+
"""
|
|
211
|
+
response, status_code = generic.get(
|
|
212
|
+
token=token,
|
|
213
|
+
path=f"trains/station/autocomplete/{quote(query)}",
|
|
214
|
+
)
|
|
215
|
+
return [trwlm.Station.model_validate(i) for i in response["data"]]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def get_trains_station_history(token: str) -> list[trwlm.Station]:
|
|
219
|
+
"""
|
|
220
|
+
This request returns an array of max. 10 most recent station objects that the user has arrived at.
|
|
221
|
+
"""
|
|
222
|
+
response, status_code = generic.get(
|
|
223
|
+
token=token,
|
|
224
|
+
path="trains/station/history",
|
|
225
|
+
)
|
|
226
|
+
return [trwlm.Station.model_validate(i) for i in response["data"]]
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Endpoints related to application configuration information.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from . import generic
|
|
6
|
+
import traewelling_pydantic.config as trwlc
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_app_configuration(token: str) -> trwlc.ConfigurationInformation:
|
|
10
|
+
"""
|
|
11
|
+
Retrieves configuration information about the application, including features and supported languages.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
response, status_code = generic.get(token=token, path="app/configuration")
|
|
15
|
+
|
|
16
|
+
return trwlc.ConfigurationInformation.model_validate(response)
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Events that users can check in to
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import traewelling_pydantic.models as trwlm
|
|
9
|
+
from requests import HTTPError
|
|
10
|
+
|
|
11
|
+
from . import generic, model
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_event(token: str, slug: str) -> trwlm.Event:
|
|
15
|
+
"""
|
|
16
|
+
[Auth optional] Get basic information for event
|
|
17
|
+
"""
|
|
18
|
+
response, status_code = generic.get(token=token, path=f"event/{slug}")
|
|
19
|
+
|
|
20
|
+
return trwlm.Event.model_validate(response["data"])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_events(
|
|
24
|
+
token: str, timestamp: datetime, only_upcoming: bool = False
|
|
25
|
+
) -> list[trwlm.Event]:
|
|
26
|
+
"""
|
|
27
|
+
Returns all active or upcoming events for the given timestamp. Default timestamp is now. If upcoming is set to true, all events ending after the timestamp are returned.
|
|
28
|
+
"""
|
|
29
|
+
response, status_code = generic.get(
|
|
30
|
+
token=token,
|
|
31
|
+
path="events",
|
|
32
|
+
params={"timestamp": timestamp.isoformat(), "upcoming": only_upcoming},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return [trwlm.Event.model_validate(event) for event in response["data"]]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_event_details(token: str, slug: str) -> model.response.EventDetailsResponse:
|
|
39
|
+
"""
|
|
40
|
+
Returns overall travelled distance and duration for an event
|
|
41
|
+
"""
|
|
42
|
+
response, status_code = generic.get(token=token, path=f"event/{slug}/details")
|
|
43
|
+
|
|
44
|
+
return model.response.EventDetailsResponse.model_validate(response["data"])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_event_statuses(
|
|
48
|
+
token: str, slug: str, page: int
|
|
49
|
+
) -> Optional[tuple[list[trwlm.Event], model.response.Links, model.response.Meta]]:
|
|
50
|
+
"""
|
|
51
|
+
Returns all for user visible statuses for an event
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
response, status_code = generic.get(
|
|
55
|
+
token=token,
|
|
56
|
+
path=f"event/{slug}/statuses",
|
|
57
|
+
params={"page": page},
|
|
58
|
+
)
|
|
59
|
+
except HTTPError as error:
|
|
60
|
+
if error.response.status_code == 404:
|
|
61
|
+
return None
|
|
62
|
+
raise error
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
[trwlm.Event.model_validate(event) for event in response["data"]],
|
|
66
|
+
model.response.Links.model_validate(response["links"]),
|
|
67
|
+
model.response.Meta.model_validate(response["meta"]),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def post_event(
|
|
72
|
+
token: str, event_suggestion: model.request.EventSuggestionRequest
|
|
73
|
+
) -> bool:
|
|
74
|
+
"""
|
|
75
|
+
Submit a possible event for our administrators to publish
|
|
76
|
+
"""
|
|
77
|
+
response, status_code = generic.post(
|
|
78
|
+
token=token, path="event", data=event_suggestion.model_dump(mode="json")
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return True
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import requests
|
|
3
|
+
from urllib.parse import urljoin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def request(
|
|
7
|
+
token: str,
|
|
8
|
+
path: str,
|
|
9
|
+
method: str = "GET",
|
|
10
|
+
params: Optional[dict] = None,
|
|
11
|
+
data: Optional[dict] = None,
|
|
12
|
+
headers: Optional[dict] = None,
|
|
13
|
+
base_url: str = "https://traewelling.de/api/v1/",
|
|
14
|
+
) -> tuple[dict, int]:
|
|
15
|
+
response = requests.request(
|
|
16
|
+
method=method.upper(),
|
|
17
|
+
url=urljoin(base_url, path),
|
|
18
|
+
params=params,
|
|
19
|
+
headers={"Authorization": f"Bearer {token}"}
|
|
20
|
+
if headers is None
|
|
21
|
+
else {"Authorization": f"Bearer {token}", **headers},
|
|
22
|
+
data=data,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
response.raise_for_status()
|
|
26
|
+
|
|
27
|
+
return response.json(), response.status_code
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get(
|
|
31
|
+
token: str,
|
|
32
|
+
path: str,
|
|
33
|
+
params: Optional[dict] = None,
|
|
34
|
+
data: Optional[dict] = None,
|
|
35
|
+
headers: Optional[dict] = None,
|
|
36
|
+
base_url: str = "https://traewelling.de/api/v1/",
|
|
37
|
+
) -> tuple[dict, int]:
|
|
38
|
+
return request(
|
|
39
|
+
method="GET",
|
|
40
|
+
token=token,
|
|
41
|
+
path=path,
|
|
42
|
+
params=params,
|
|
43
|
+
data=data,
|
|
44
|
+
headers=headers,
|
|
45
|
+
base_url=base_url,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def put(
|
|
49
|
+
token: str,
|
|
50
|
+
path: str,
|
|
51
|
+
params: Optional[dict] = None,
|
|
52
|
+
data: Optional[dict] = None,
|
|
53
|
+
headers: Optional[dict] = None,
|
|
54
|
+
base_url: str = "https://traewelling.de/api/v1/",
|
|
55
|
+
) -> tuple[dict, int]:
|
|
56
|
+
return request(
|
|
57
|
+
method="PUT",
|
|
58
|
+
token=token,
|
|
59
|
+
path=path,
|
|
60
|
+
params=params,
|
|
61
|
+
data=data,
|
|
62
|
+
headers=headers,
|
|
63
|
+
base_url=base_url,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def post(
|
|
67
|
+
token: str,
|
|
68
|
+
path: str,
|
|
69
|
+
params: Optional[dict] = None,
|
|
70
|
+
data: Optional[dict] = None,
|
|
71
|
+
headers: Optional[dict] = None,
|
|
72
|
+
base_url: str = "https://traewelling.de/api/v1/",
|
|
73
|
+
) -> tuple[dict, int]:
|
|
74
|
+
return request(
|
|
75
|
+
method="POST",
|
|
76
|
+
token=token,
|
|
77
|
+
path=path,
|
|
78
|
+
params=params,
|
|
79
|
+
data=data,
|
|
80
|
+
headers=headers,
|
|
81
|
+
base_url=base_url,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def delete(
|
|
85
|
+
token: str,
|
|
86
|
+
path: str,
|
|
87
|
+
params: Optional[dict] = None,
|
|
88
|
+
data: Optional[dict] = None,
|
|
89
|
+
headers: Optional[dict] = None,
|
|
90
|
+
base_url: str = "https://traewelling.de/api/v1/",
|
|
91
|
+
) -> tuple[dict, int]:
|
|
92
|
+
return request(
|
|
93
|
+
method="DELETE",
|
|
94
|
+
token=token,
|
|
95
|
+
path=path,
|
|
96
|
+
params=params,
|
|
97
|
+
data=data,
|
|
98
|
+
headers=headers,
|
|
99
|
+
base_url=base_url,
|
|
100
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import traewelling_pydantic as trwl
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
import uuid
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TraewellingRequestModel(trwl.models.TraewellingModel):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UpdateProfileInformationRequest(TraewellingRequestModel):
|
|
14
|
+
username: str
|
|
15
|
+
displayName: str
|
|
16
|
+
privateProfile: Optional[bool] = None
|
|
17
|
+
preventIndex: Optional[bool] = None
|
|
18
|
+
privacyHideDays: Optional[int] = None
|
|
19
|
+
defaultStatusVisibility: Optional[trwl.enums.Visibility] = None
|
|
20
|
+
mastodonVisibility: Optional[trwl.enums.MastodonVisibility] = None
|
|
21
|
+
mapProvider: Optional[trwl.enums.MapProvider] = None
|
|
22
|
+
friendCheckin: Optional[trwl.enums.FriendCheckinSetting] = None
|
|
23
|
+
pointsEnabled: Optional[bool] = None
|
|
24
|
+
bio: Optional[str] = None
|
|
25
|
+
experimental: bool
|
|
26
|
+
profileLinks: Optional[list[trwl.models.ProfileLink]] = None
|
|
27
|
+
timezone: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CheckinRequest(TraewellingRequestModel):
|
|
31
|
+
body: str
|
|
32
|
+
business: trwl.enums.Business
|
|
33
|
+
visibility: trwl.enums.Visibility
|
|
34
|
+
eventId: int
|
|
35
|
+
toot: bool
|
|
36
|
+
chainPost: bool
|
|
37
|
+
ibnr: bool
|
|
38
|
+
tripId: str
|
|
39
|
+
lineName: str
|
|
40
|
+
start: int
|
|
41
|
+
destination: int
|
|
42
|
+
departure: datetime
|
|
43
|
+
arrival: datetime
|
|
44
|
+
force: bool
|
|
45
|
+
with_: list[Union[int, uuid.UUID]] = pydantic.Field(alias="with")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class EventSuggestionRequest(TraewellingRequestModel):
|
|
49
|
+
name: str
|
|
50
|
+
host: str
|
|
51
|
+
begin: datetime
|
|
52
|
+
end: datetime
|
|
53
|
+
url: Optional[str] = None
|
|
54
|
+
hashtag: Optional[str] = None
|
|
55
|
+
nearestStationId: Optional[int] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ICSTokenRequest(TraewellingRequestModel):
|
|
59
|
+
name: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ICSTokenDeleteRequest(TraewellingRequestModel):
|
|
63
|
+
tokenId: int
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class PolylineRequest(TraewellingRequestModel):
|
|
67
|
+
from_station_id: int
|
|
68
|
+
to_station_id: int
|
|
69
|
+
stopover_id: int
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ReportRequest(TraewellingRequestModel):
|
|
73
|
+
subjectType: str
|
|
74
|
+
subjectId: int
|
|
75
|
+
reason: str
|
|
76
|
+
description: str
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class UpdateEmailRequest(TraewellingRequestModel):
|
|
80
|
+
email: str
|
|
81
|
+
password: str
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AccountDeletionRequest(TraewellingRequestModel):
|
|
85
|
+
confirmation: str
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class UpdateStatusRequest(TraewellingRequestModel):
|
|
89
|
+
body: Optional[str] = None
|
|
90
|
+
business: Optional[trwl.enums.Business] = None
|
|
91
|
+
visibility: Optional[trwl.enums.Visibility] = None
|
|
92
|
+
eventId: Optional[str] = None
|
|
93
|
+
manualDeparture: Optional[datetime] = None
|
|
94
|
+
manualArrival: Optional[datetime] = None
|
|
95
|
+
destinationId: Optional[str] = None
|
|
96
|
+
destinationArrivalPlanned: Optional[datetime] = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class UpdateStatusTagRequest(TraewellingRequestModel):
|
|
100
|
+
key: str
|
|
101
|
+
value: str
|
|
102
|
+
visibility: trwl.enums.Visibility
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class UpdateUserTrustedRequest(TraewellingRequestModel):
|
|
106
|
+
userId: str
|
|
107
|
+
expiresAt: datetime
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Union, Optional
|
|
3
|
+
import uuid
|
|
4
|
+
import pydantic
|
|
5
|
+
import traewelling_pydantic as trwl
|
|
6
|
+
import fptf_pydantic.models as fptf
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TraewellingResponseModel(trwl.models.TraewellingModel):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Meta(TraewellingResponseModel):
|
|
14
|
+
current_page: int
|
|
15
|
+
from_: int = pydantic.Field(alias="from")
|
|
16
|
+
path: str
|
|
17
|
+
per_page: int
|
|
18
|
+
to: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Links(TraewellingResponseModel):
|
|
22
|
+
first: str
|
|
23
|
+
last: Optional[str] = None
|
|
24
|
+
prev: Optional[str] = None
|
|
25
|
+
next_: Optional[str] = pydantic.Field(default=None, alias="next")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BearerTokenResponse(TraewellingResponseModel):
|
|
29
|
+
token: str
|
|
30
|
+
expires_at: datetime
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LikeResponse(TraewellingResponseModel):
|
|
34
|
+
count: int
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CheckinResponse(TraewellingResponseModel):
|
|
38
|
+
status: trwl.models.Status
|
|
39
|
+
points: trwl.models.Points
|
|
40
|
+
alsoOnThisConnection: list[trwl.models.Status]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class EventDetailsResponse(TraewellingResponseModel):
|
|
44
|
+
id: int
|
|
45
|
+
slug: str
|
|
46
|
+
totalDistance: int
|
|
47
|
+
totalDuration: int
|
|
48
|
+
|
|
49
|
+
class Departure(trwl.models.TraewellingModel):
|
|
50
|
+
tripId: str
|
|
51
|
+
stop: fptf.Stop
|
|
52
|
+
when: datetime
|
|
53
|
+
plannedWhen: datetime
|
|
54
|
+
platform: str
|
|
55
|
+
plannedPlatform: str
|
|
56
|
+
direction: str
|
|
57
|
+
line: fptf.Line
|
|
58
|
+
station: trwl.models.Station
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class DepartureMetaTimes(TraewellingResponseModel):
|
|
62
|
+
now: datetime
|
|
63
|
+
prev: datetime
|
|
64
|
+
next_: datetime = pydantic.Field(alias="next")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DepartureMeta(TraewellingResponseModel):
|
|
68
|
+
station: trwl.models.Station
|
|
69
|
+
times: DepartureMetaTimes
|
|
70
|
+
removedLicenses: list[Union[str, trwl.models.LicenseDTO]]
|
|
71
|
+
removedCount: int
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Annotated, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic_extra_types.coordinate import Latitude, Longitude
|
|
5
|
+
|
|
6
|
+
from . import client
|
|
7
|
+
import traewelling_pydantic.models as traewelling
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from requests.exceptions import HTTPError
|
|
11
|
+
from requests import status_codes
|
|
12
|
+
|
|
13
|
+
app = typer.Typer()
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.command("station", no_args_is_help=True)
|
|
18
|
+
def get_station(
|
|
19
|
+
token: Annotated[str, typer.Option("--token", "-t", envvar="TRAEWELLING_TOKEN")],
|
|
20
|
+
station_id: Annotated[Optional[int], typer.Option("--station-id", "-i")] = None,
|
|
21
|
+
name: Annotated[Optional[str], typer.Option("--name", "-n")] = None,
|
|
22
|
+
identifier: Annotated[Optional[str], typer.Option("--identifier")] = None,
|
|
23
|
+
identifier_provider: Annotated[
|
|
24
|
+
Optional[str], typer.Option("--identifier-provider")
|
|
25
|
+
] = None,
|
|
26
|
+
bounding_box: Annotated[
|
|
27
|
+
Optional[tuple[float, float, float, float]], typer.Option("--bounding-box")
|
|
28
|
+
] = None,
|
|
29
|
+
with_identifiers: Annotated[bool, typer.Option("--with-identifiers")] = False,
|
|
30
|
+
as_json: Annotated[bool, typer.Option("--json", "-j", envvar="AS_JSON")] = False,
|
|
31
|
+
):
|
|
32
|
+
trwl = client.TraewellingClient(token)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
response = trwl.get_stations(
|
|
36
|
+
query=name,
|
|
37
|
+
identifier=identifier,
|
|
38
|
+
identifier_provider=identifier_provider,
|
|
39
|
+
min_lat=Latitude(bounding_box[0]) if bounding_box is not None else None,
|
|
40
|
+
max_lat=Latitude(bounding_box[1]) if bounding_box is not None else None,
|
|
41
|
+
min_lon=Longitude(bounding_box[2]) if bounding_box is not None else None,
|
|
42
|
+
max_lon=Longitude(bounding_box[3]) if bounding_box is not None else None,
|
|
43
|
+
with_identifiers=with_identifiers
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
except ValueError:
|
|
47
|
+
if station_id is None:
|
|
48
|
+
raise ValueError("No valid identifiers or bounding box specified")
|
|
49
|
+
|
|
50
|
+
response = trwl.get_station_by_id(station_id, with_identifiers)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if as_json:
|
|
54
|
+
if response is not None:
|
|
55
|
+
if isinstance(response, list):
|
|
56
|
+
response = json.dumps([i.model_dump(mode="json") for i in response])
|
|
57
|
+
else:
|
|
58
|
+
response = response.model_dump_json()
|
|
59
|
+
|
|
60
|
+
console.print_json(response)
|
|
61
|
+
else:
|
|
62
|
+
console.print_json(None)
|
|
63
|
+
else:
|
|
64
|
+
console.print(response)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@app.command("stations")
|
|
68
|
+
def get_stations(
|
|
69
|
+
token: Annotated[str, typer.Option("--token", "-t", envvar="TRAEWELLING_TOKEN")],
|
|
70
|
+
ids: list[int],
|
|
71
|
+
with_identifiers: Annotated[bool, typer.Option("--with-identifiers")] = False,
|
|
72
|
+
as_json: Annotated[bool, typer.Option("--json", "-j", envvar="AS_JSON")] = False,
|
|
73
|
+
):
|
|
74
|
+
trwl = client.TraewellingClient(token)
|
|
75
|
+
responses: list[Optional[traewelling.Station]] = []
|
|
76
|
+
|
|
77
|
+
for id in ids:
|
|
78
|
+
try:
|
|
79
|
+
responses.append(trwl.get_station_by_id(id, with_identifiers))
|
|
80
|
+
except HTTPError as error:
|
|
81
|
+
if not as_json:
|
|
82
|
+
if (
|
|
83
|
+
error.response.status_code == status_codes.codes.forbidden
|
|
84
|
+
or error.response.status_code == status_codes.codes.unauthorized
|
|
85
|
+
):
|
|
86
|
+
console.print(
|
|
87
|
+
f"Don't have permission to query the station for ID {id}"
|
|
88
|
+
)
|
|
89
|
+
elif error.response.status_code == status_codes.codes.too_many_requests:
|
|
90
|
+
console.print("Too many requests")
|
|
91
|
+
|
|
92
|
+
if as_json:
|
|
93
|
+
console.print_json(
|
|
94
|
+
json.dumps(
|
|
95
|
+
[
|
|
96
|
+
response.model_dump() if response is not None else None
|
|
97
|
+
for response in responses
|
|
98
|
+
]
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
console.print(responses)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@app.command("operators")
|
|
106
|
+
def get_operators(
|
|
107
|
+
token: Annotated[str, typer.Option("--token", "-t", envvar="TRAEWELLING_TOKEN")],
|
|
108
|
+
as_json: Annotated[bool, typer.Option("--json", "-j", envvar="AS_JSON")] = False,
|
|
109
|
+
):
|
|
110
|
+
trwl = client.TraewellingClient(token)
|
|
111
|
+
response = trwl.get_operators()
|
|
112
|
+
|
|
113
|
+
if as_json:
|
|
114
|
+
console.print_json(json.dumps([r.model_dump() for r in response]))
|
|
115
|
+
else:
|
|
116
|
+
console.print(response)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
app()
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
import traewelling_pydantic.models as traewelling
|
|
6
|
+
from requests.exceptions import HTTPError
|
|
7
|
+
from requests.status_codes import codes as httpCode
|
|
8
|
+
from pydantic_extra_types.coordinate import Latitude, Longitude
|
|
9
|
+
|
|
10
|
+
from . import api
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TraewellingClient:
|
|
14
|
+
"""
|
|
15
|
+
Traewelling Client object to manage the token
|
|
16
|
+
|
|
17
|
+
Behavior Note: 404s will return None
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, token: str):
|
|
21
|
+
self.token = token
|
|
22
|
+
|
|
23
|
+
def get_stations(
|
|
24
|
+
self,
|
|
25
|
+
identifier_provider: Optional[str] = None,
|
|
26
|
+
query: Optional[str] = None,
|
|
27
|
+
identifier: Optional[str] = None,
|
|
28
|
+
min_lat: Optional[Latitude] = None,
|
|
29
|
+
max_lat: Optional[Latitude] = None,
|
|
30
|
+
min_lon: Optional[Longitude] = None,
|
|
31
|
+
max_lon: Optional[Longitude] = None,
|
|
32
|
+
limit: int = 50,
|
|
33
|
+
with_identifiers: bool = False,
|
|
34
|
+
) -> Optional[list[traewelling.Station]]:
|
|
35
|
+
try:
|
|
36
|
+
return api.checkin.get_stations(
|
|
37
|
+
self.token,
|
|
38
|
+
identifier=identifier,
|
|
39
|
+
identifier_provider=identifier_provider,
|
|
40
|
+
min_lat=min_lat,
|
|
41
|
+
max_lat=max_lat,
|
|
42
|
+
min_lon=min_lon,
|
|
43
|
+
max_lon=max_lon,
|
|
44
|
+
limit=limit,
|
|
45
|
+
with_identifiers=with_identifiers,
|
|
46
|
+
query=query
|
|
47
|
+
)
|
|
48
|
+
except HTTPError as error:
|
|
49
|
+
if error.response.status_code == httpCode.not_found:
|
|
50
|
+
return None
|
|
51
|
+
raise error
|
|
52
|
+
|
|
53
|
+
def get_station_by_id(
|
|
54
|
+
self,
|
|
55
|
+
station_id: Union[int, uuid.UUID],
|
|
56
|
+
with_identifiers: bool = False,
|
|
57
|
+
) -> Optional[traewelling.Station]:
|
|
58
|
+
"""
|
|
59
|
+
This request returns a single station object
|
|
60
|
+
|
|
61
|
+
:param station_id: Either the traewelling integer ID or UUID
|
|
62
|
+
:type station_id: int, uuid.UUID
|
|
63
|
+
|
|
64
|
+
:param with_identifiers: If further identifiers should be requested from Traewelling
|
|
65
|
+
:type with_identifiers: bool
|
|
66
|
+
|
|
67
|
+
:raises HTTPError: Might raise a 401, 403, or 5xx error
|
|
68
|
+
|
|
69
|
+
:return: Either returns a Station object or in case of a 404 returns None
|
|
70
|
+
:rtype: Station(optional)
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
return api.checkin.get_stations_by_id(
|
|
74
|
+
self.token, station_id=station_id, with_identifiers=with_identifiers
|
|
75
|
+
)
|
|
76
|
+
except HTTPError as error:
|
|
77
|
+
if error.response.status_code == httpCode.not_found:
|
|
78
|
+
return None
|
|
79
|
+
raise error
|
|
80
|
+
|
|
81
|
+
def get_operators(self) -> list[traewelling.Operator]:
|
|
82
|
+
"""
|
|
83
|
+
Get a list of 'all' operators.
|
|
84
|
+
|
|
85
|
+
:raises HTTPError: Might raise a 401, 403, or 5xx error
|
|
86
|
+
|
|
87
|
+
:return: Returns a list of Operator objects
|
|
88
|
+
:rtype: list[Operator]
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
return api.checkin.get_operators(self.token)
|
|
92
|
+
|
|
93
|
+
def get_event(self, slug: str) -> Optional[traewelling.Event]:
|
|
94
|
+
"""
|
|
95
|
+
Get basic information for event
|
|
96
|
+
|
|
97
|
+
:param slug: A slug for an event
|
|
98
|
+
:type slug: str
|
|
99
|
+
|
|
100
|
+
:raises HTTPError: Might raise a 401, 403, or 5xx error
|
|
101
|
+
|
|
102
|
+
:return: Either returns an Event object or in case of a 404 returns None
|
|
103
|
+
:rtype: Event(optional)
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
return api.events.get_event(self.token, slug=slug)
|
|
107
|
+
except HTTPError as error:
|
|
108
|
+
if error.response.status_code == httpCode.not_found:
|
|
109
|
+
return None
|
|
110
|
+
raise error
|
|
111
|
+
|
|
112
|
+
def get_events(
|
|
113
|
+
self, timestamp: datetime, only_upcoming: bool = False
|
|
114
|
+
) -> list[traewelling.Event]:
|
|
115
|
+
"""
|
|
116
|
+
Returns all active or upcoming events for the given timestamp. Default timestamp is now. If upcoming is set to true, all events ending after the timestamp are returned.
|
|
117
|
+
|
|
118
|
+
:raises HTTPError: Might raise a 401, 403, or 5xx error
|
|
119
|
+
|
|
120
|
+
:return: Returns a list of Event objects
|
|
121
|
+
:rtype: list[Event]
|
|
122
|
+
"""
|
|
123
|
+
return api.events.get_events(
|
|
124
|
+
self.token, timestamp=timestamp, only_upcoming=only_upcoming
|
|
125
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: traewelling-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://codeberg.org/lambda-crime/traewelling-api
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: fptf-pydantic>=0.1.0
|
|
13
|
+
Requires-Dist: pydantic-extra-types>=2.11.0
|
|
14
|
+
Requires-Dist: rich>=14.2.0
|
|
15
|
+
Requires-Dist: textual>=7.2.0
|
|
16
|
+
Requires-Dist: traewelling-pydantic>=0.2.1
|
|
17
|
+
Requires-Dist: typer>=0.21.1
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Traewelling-API
|
|
21
|
+
|
|
22
|
+
This is a simple Traewelling API wrapper.
|
|
23
|
+
|
|
24
|
+
The goal is to provide an easy way to talk with the Traewelling API from Python
|
|
25
|
+
|
|
26
|
+
## TODOs
|
|
27
|
+
|
|
28
|
+
- add every API endpoint for Traewelling
|
|
29
|
+
- add FPTF converterter for Traewelling API responses
|
|
30
|
+
- add command for every API endpoint to CLI
|
|
31
|
+
- [OPTIONAL] add TUI for interactive API requests
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/traewelling_api/__init__.py
|
|
5
|
+
src/traewelling_api/cli.py
|
|
6
|
+
src/traewelling_api/client.py
|
|
7
|
+
src/traewelling_api.egg-info/PKG-INFO
|
|
8
|
+
src/traewelling_api.egg-info/SOURCES.txt
|
|
9
|
+
src/traewelling_api.egg-info/dependency_links.txt
|
|
10
|
+
src/traewelling_api.egg-info/entry_points.txt
|
|
11
|
+
src/traewelling_api.egg-info/requires.txt
|
|
12
|
+
src/traewelling_api.egg-info/top_level.txt
|
|
13
|
+
src/traewelling_api/api/__init__.py
|
|
14
|
+
src/traewelling_api/api/auth.py
|
|
15
|
+
src/traewelling_api/api/checkin.py
|
|
16
|
+
src/traewelling_api/api/community.py
|
|
17
|
+
src/traewelling_api/api/configuration_information.py
|
|
18
|
+
src/traewelling_api/api/dashboard.py
|
|
19
|
+
src/traewelling_api/api/events.py
|
|
20
|
+
src/traewelling_api/api/generic.py
|
|
21
|
+
src/traewelling_api/api/ics_tokens.py
|
|
22
|
+
src/traewelling_api/api/leaderboard.py
|
|
23
|
+
src/traewelling_api/api/likes.py
|
|
24
|
+
src/traewelling_api/api/notifications.py
|
|
25
|
+
src/traewelling_api/api/polyline.py
|
|
26
|
+
src/traewelling_api/api/report.py
|
|
27
|
+
src/traewelling_api/api/security.py
|
|
28
|
+
src/traewelling_api/api/settings.py
|
|
29
|
+
src/traewelling_api/api/statistics.py
|
|
30
|
+
src/traewelling_api/api/status.py
|
|
31
|
+
src/traewelling_api/api/trips.py
|
|
32
|
+
src/traewelling_api/api/user.py
|
|
33
|
+
src/traewelling_api/api/user_follow.py
|
|
34
|
+
src/traewelling_api/api/user_hide_and_block.py
|
|
35
|
+
src/traewelling_api/api/webhooks.py
|
|
36
|
+
src/traewelling_api/api/model/__init__.py
|
|
37
|
+
src/traewelling_api/api/model/request.py
|
|
38
|
+
src/traewelling_api/api/model/response.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
traewelling_api
|