diff --git a/trainvel-front/src/App.js b/trainvel-front/src/App.js
index 166c4b4..b7a77f4 100644
--- a/trainvel-front/src/App.js
+++ b/trainvel-front/src/App.js
@@ -18,7 +18,7 @@ function App() {
element: ,
},
{
- path: "/station/:theme/:stopId",
+ path: "/station/:theme/:stationSlug",
element:
}
])
diff --git a/trainvel-front/src/AutocompleteStop.jsx b/trainvel-front/src/AutocompleteStation.jsx
similarity index 66%
rename from trainvel-front/src/AutocompleteStop.jsx
rename to trainvel-front/src/AutocompleteStation.jsx
index 658f6aa..84b9ef3 100644
--- a/trainvel-front/src/AutocompleteStop.jsx
+++ b/trainvel-front/src/AutocompleteStation.jsx
@@ -1,7 +1,7 @@
import {Autocomplete, TextField} from "@mui/material";
import {useRef, useState} from "react";
-function AutocompleteStop(params) {
+function AutocompleteStation(params) {
const [options, setOptions] = useState([])
const previousController = useRef()
@@ -17,7 +17,7 @@ function AutocompleteStop(params) {
const controller = new AbortController()
const signal = controller.signal
previousController.current = controller
- fetch("/api/gtfs/stop/?location_type=1&search=" + value, {signal})
+ fetch("/api/core/station/?search=" + value, {signal})
.then(response => response.json())
.then(data => data.results)
.then(setOptions)
@@ -40,24 +40,7 @@ function AutocompleteStop(params) {
}
function getOptionGroup(option) {
- switch (option.gtfs_feed) {
- case "FR-SNCF-TGV":
- case "FR-SNCF-IC":
- case "FR-SNCF-TER":
- return "TGV/TER/Intercités"
- case "FR-IDF-TN":
- return "Transilien"
- case "FR-EUROSTAR":
- return "Eurostar"
- case "IT-FRA-TI":
- return "Trenitalia France"
- case "ES-RENFE":
- return "RENFE"
- case "AT-OBB":
- return "ÖBB"
- default:
- return option.gtfs_feed
- }
+ return option.country
}
-export default AutocompleteStop;
+export default AutocompleteStation;
diff --git a/trainvel-front/src/Home.js b/trainvel-front/src/Home.js
index 80c7c8e..af19209 100644
--- a/trainvel-front/src/Home.js
+++ b/trainvel-front/src/Home.js
@@ -1,11 +1,11 @@
-import AutocompleteStop from "./AutocompleteStop"
+import AutocompleteStation from "./AutocompleteStation"
import {useNavigate} from "react-router-dom"
function Home() {
const navigate = useNavigate()
- function onStationSelected(event, stop) {
- navigate(`/station/sncf/${stop.id}/`)
+ function onStationSelected(event, station) {
+ navigate(`/station/sncf/${station.slug}/`)
}
return <>
@@ -13,7 +13,7 @@ function Home() {
Choisissez une gare dont vous désirez connaître le tableau des prochains départs et arrivées :
-
+
>
}
diff --git a/trainvel-front/src/Station.js b/trainvel-front/src/Station.js
index 6823469..c35b420 100644
--- a/trainvel-front/src/Station.js
+++ b/trainvel-front/src/Station.js
@@ -5,14 +5,14 @@ import {Box, Button, FormLabel} from "@mui/material";
import {DatePicker, TimePicker} from "@mui/x-date-pickers";
import dayjs from "dayjs";
import {useQuery, useQueryClient} from "@tanstack/react-query";
-import AutocompleteStop from "./AutocompleteStop";
+import AutocompleteStation from "./AutocompleteStation";
-function DateTimeSelector({stop, date, time}) {
+function DateTimeSelector({station, date, time}) {
const navigate = useNavigate()
- function onStationSelected(event, stop) {
- if (stop !== null)
- navigate(`/station/sncf/${stop.id}/`)
+ function onStationSelected(event, station) {
+ if (station !== null)
+ navigate(`/station/sncf/${station.slug}/`)
}
return <>
@@ -20,7 +20,7 @@ function DateTimeSelector({stop, date, time}) {
Changer la gare recherchée :
-
+
Modifier la date et l'heure de recherche :
@@ -32,7 +32,7 @@ function DateTimeSelector({stop, date, time}) {
}
function Station() {
- let {stopId, theme} = useParams()
+ let {theme, stationSlug} = useParams()
let [searchParams, _setSearchParams] = useSearchParams()
const now = new Date()
let dateNow = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
@@ -41,13 +41,13 @@ function Station() {
let [time, setTime] = useState(searchParams.get('time') || timeNow)
useQueryClient()
- const stopQuery = useQuery({
- queryKey: ['stop', stopId],
- queryFn: () => fetch(`/api/gtfs/stop/${stopId}/`)
+ const stationQuery = useQuery({
+ queryKey: ['station', stationSlug],
+ queryFn: () => fetch(`/api/core/station/${stationSlug}/`)
.then(response => response.json()),
- enabled: !!stopId,
+ enabled: !!stationSlug,
})
- const stop = stopQuery.data ?? {name: "Chargement…"}
+ const station = stationQuery.data ?? {name: "Chargement…"}
if (time === timeNow) {
setInterval(() => {
@@ -62,13 +62,13 @@ function Station() {
return (
- Horaires en gare de {stop.name}
+ Horaires en gare de {station.name}
-
-
-
+
+
+
)
diff --git a/trainvel-front/src/TrainsTable.js b/trainvel-front/src/TrainsTable.js
index 176177d..b81bfca 100644
--- a/trainvel-front/src/TrainsTable.js
+++ b/trainvel-front/src/TrainsTable.js
@@ -26,12 +26,12 @@ const StyledTableRow = styled(TableRow)(({ theme, tabletype }) => ({
},
}));
-function TrainsTable({stop, date, time, tableType}) {
+function TrainsTable({station, date, time, tableType}) {
return <>
>
@@ -49,7 +49,7 @@ function TrainsTableHeader({tableType}) {
>
}
-function TrainsTableBody({stop, date, time, tableType}) {
+function TrainsTableBody({station, date, time, tableType}) {
const filterTime = useCallback((train) => {
if (tableType === "departures")
return `${train.departure_date}T${train.departure_time_24h}` >= `${date}T${time}`
@@ -58,16 +58,16 @@ function TrainsTableBody({stop, date, time, tableType}) {
}, [date, time, tableType])
const updateTrains = useCallback(() => {
- return fetch(`/api/station/next_${tableType}/?stop_id=${stop.id}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
+ return fetch(`/api/station/next_${tableType}/?station_slug=${station.slug}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
.then(response => response.json())
.then(data => data.results)
.then(data => [...data])
- }, [stop.id, date, time, tableType])
+ }, [station.id, date, time, tableType])
const trainsQuery = useQuery({
- queryKey: ['trains', stop.id, tableType],
+ queryKey: ['trains', station.id, tableType],
queryFn: updateTrains,
- enabled: !!stop.id,
+ enabled: !!station.id,
})
const trains = useMemo(() => trainsQuery.data ?? [], [trainsQuery.data])
@@ -114,7 +114,7 @@ function TrainRow({train, tableType, date, time}) {
const stopTimesQuery = useQuery({
queryKey: ['stop_times', trip.id],
- queryFn: () => fetch(`/api/gtfs/stop_time/?trip=${trip.id}&order=stop_sequence&limit=1000`)
+ queryFn: () => fetch(`/api/gtfs/stop_time/?${new URLSearchParams({trip: trip.id, order: 'stop_sequence', limit: 1000})}`)
.then(response => response.json())
.then(data => data.results),
enabled: !!trip.id,
diff --git a/trainvel/api/serializers.py b/trainvel/api/serializers.py
index 1205c55..4b97ac7 100644
--- a/trainvel/api/serializers.py
+++ b/trainvel/api/serializers.py
@@ -1,9 +1,18 @@
from rest_framework import serializers
+from trainvel.core.models import Station
from trainvel.gtfs.models import Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate, \
Transfer, FeedInfo, TripUpdate, StopTimeUpdate
+class StationSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Station
+ lookup_field = 'slug'
+ fields = ('id', 'slug', 'name', 'uic', 'uic8_sncf', 'latitude', 'longitude', 'country',
+ 'country_hint', 'main_station_hint',)
+
+
class AgencySerializer(serializers.ModelSerializer):
class Meta:
model = Agency
diff --git a/trainvel/api/views.py b/trainvel/api/views.py
index 25e2a64..4e031d8 100644
--- a/trainvel/api/views.py
+++ b/trainvel/api/views.py
@@ -10,13 +10,28 @@ from rest_framework.filters import OrderingFilter, SearchFilter
from trainvel.api.serializers import AgencySerializer, StopSerializer, RouteSerializer, TripSerializer, \
StopTimeSerializer, CalendarSerializer, CalendarDateSerializer, TransferSerializer, \
- FeedInfoSerializer, TripUpdateSerializer, StopTimeUpdateSerializer
-from trainvel.gtfs.models import Agency, Calendar, CalendarDate, FeedInfo, GTFSFeed, Route, Stop, StopTime, StopTimeUpdate, \
- Transfer, Trip, TripUpdate
+ FeedInfoSerializer, TripUpdateSerializer, StopTimeUpdateSerializer, StationSerializer
+from trainvel.core.models import Station
+from trainvel.gtfs.models import Agency, Calendar, CalendarDate, FeedInfo, GTFSFeed, Route, Stop, StopTime, \
+ StopTimeUpdate, \
+ Transfer, Trip, TripUpdate, PickupType
-CACHE_CONTROL = cache_control(max_age=7200)
+CACHE_CONTROL = cache_control(max_age=30)
LAST_MODIFIED = last_modified(lambda *args, **kwargs: GTFSFeed.objects.order_by('-last_modified').first().last_modified)
-LOOKUP_VALUE_REGEX = r"[\w.: |-]+"
+LOOKUP_VALUE_REGEX = r"[\w.: |+-]+"
+
+
+class StationViewSet(viewsets.ReadOnlyModelViewSet):
+ queryset = Station.objects.filter(is_suggestable=True)
+ serializer_class = StationSerializer
+ filter_backends = [DjangoFilterBackend, SearchFilter]
+ filterset_fields = '__all__'
+ search_fields = ['name', 'slug',
+ 'info_de', 'info_en', 'info_es', 'info_fr', 'info_it', 'info_nb', 'info_nl', 'info_cs',
+ 'info_da', 'info_hu', 'info_ja', 'info_ko', 'info_pl', 'info_pt', 'info_ru', 'info_sv',
+ 'info_tr', 'info_zh', ]
+ lookup_field = 'slug'
+ lookup_value_regex = LOOKUP_VALUE_REGEX
@method_decorator(name='list', decorator=[CACHE_CONTROL, LAST_MODIFIED])
@@ -135,8 +150,7 @@ class NextDeparturesViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
now = datetime.now()
- stop_id = self.request.query_params.get('stop_id', None)
- stop_name = self.request.query_params.get('stop_name', None)
+ station_slug = self.request.query_params.get('station_slug', None)
query_date = date.fromisoformat(self.request.query_params.get('date', now.date().isoformat()))
query_time = self.request.query_params.get('time', now.time().isoformat(timespec='seconds'))
query_time = timedelta(seconds=int(query_time[:2]) * 3600
@@ -148,16 +162,10 @@ class NextDeparturesViewSet(viewsets.ReadOnlyModelViewSet):
tomorrow = query_date + timedelta(days=1)
stop_filter = Q(stop__location_type=0)
- if stop_id:
- stop = Stop.objects.get(id=stop_id)
- stops = Stop.objects.filter(Q(id=stop_id)
- | Q(parent_station=stop_id))
- if stop.location_type == 0 and stop.parent_station_id is not None:
- stops |= Stop.objects.filter(parent_station=stop.parent_station_id)
- stop_filter = Q(stop__in=stops.values_list('id', flat=True))
- elif stop_name:
- stops = Stop.objects.filter(name__iexact=stop_name).values_list('id', flat=True)
- stop_filter = Q(stop__in=stops)
+ if station_slug:
+ station = Station.objects.get(is_suggestable=True, slug=station_slug)
+ near_stops = station.get_near_stops()
+ stop_filter = Q(stop_id__in=near_stops.values_list('id', flat=True))
def calendar_filter(d: date):
return Q(trip__service_id__in=CalendarDate.objects.filter(date=d, exception_type=1)
@@ -189,7 +197,7 @@ class NextDeparturesViewSet(viewsets.ReadOnlyModelViewSet):
qs_today = StopTime.objects.filter(stop_filter) \
.annotate(departure_time_real=departure_time_real(query_date)) \
.filter(departure_time_real__gte=query_time) \
- .filter(Q(pickup_type=0) | canceled_filter(query_date)) \
+ .filter(Q(pickup_type=PickupType.REGULAR) | canceled_filter(query_date)) \
.filter(calendar_filter(query_date)) \
.annotate(departure_date=Value(query_date)) \
.annotate(departure_time_24h=F('departure_time'))
@@ -221,8 +229,7 @@ class NextArrivalsViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
now = datetime.now()
- stop_id = self.request.query_params.get('stop_id', None)
- stop_name = self.request.query_params.get('stop_name', None)
+ station_slug = self.request.query_params.get('station_slug', None)
query_date = date.fromisoformat(self.request.query_params.get('date', now.date().isoformat()))
query_time = self.request.query_params.get('time', now.time().isoformat(timespec='seconds'))
query_time = timedelta(seconds=int(query_time[:2]) * 3600
@@ -235,16 +242,10 @@ class NextArrivalsViewSet(viewsets.ReadOnlyModelViewSet):
tomorrow = query_date + timedelta(days=1)
stop_filter = Q(stop__location_type=0)
- if stop_id:
- stop = Stop.objects.get(id=stop_id)
- stops = Stop.objects.filter(Q(id=stop_id)
- | Q(parent_station=stop_id))
- if stop.location_type == 0 and stop.parent_station_id is not None:
- stops |= Stop.objects.filter(parent_station=stop.parent_station_id)
- stop_filter = Q(stop__in=stops.values_list('id', flat=True))
- elif stop_name:
- stops = Stop.objects.filter(name__iexact=stop_name).values_list('id', flat=True)
- stop_filter = Q(stop__in=stops)
+ if station_slug:
+ station = Station.objects.get(is_suggestable=True, slug=station_slug)
+ near_stops = station.get_near_stops()
+ stop_filter = Q(stop_id__in=near_stops.values_list('id', flat=True))
def calendar_filter(d: date):
return Q(trip__service_id__in=CalendarDate.objects.filter(date=d, exception_type=1)
diff --git a/trainvel/core/models.py b/trainvel/core/models.py
index 42347cb..c596367 100644
--- a/trainvel/core/models.py
+++ b/trainvel/core/models.py
@@ -1,7 +1,10 @@
+from django.conf import settings
from django.db import models
+from django.db.models import F, QuerySet
+from django.db.models.functions import ACos, Sin, Radians, Cos
from django.utils.translation import gettext_lazy as _
-from trainvel.gtfs.models import Country
+from trainvel.gtfs.models import Country, Stop
class Station(models.Model):
@@ -498,6 +501,16 @@ class Station(models.Model):
default=None,
)
+ def get_near_stops(self, radius: float = settings.STATION_RADIUS) -> QuerySet[Stop]:
+ """
+ Returns a queryset of all stops that are in a radius of radius meters around the station.
+ It calculates a distance from each stop to the station using spatial coordinates.
+ """
+ return Stop.objects.annotate(distance=6371000 * ACos(
+ Sin(Radians(self.latitude)) * Sin(Radians(F('lat')))
+ + Cos(Radians(self.latitude)) * Cos(Radians(F('lat'))) * Cos(Radians(F('lon')) - Radians(self.longitude))))\
+ .filter(distance__lte=radius)
+
def __str__(self):
return self.name
diff --git a/trainvel/gtfs/management/commands/update_trainvel_gtfs.py b/trainvel/gtfs/management/commands/update_trainvel_gtfs.py
index 5af1bf8..9a58341 100644
--- a/trainvel/gtfs/management/commands/update_trainvel_gtfs.py
+++ b/trainvel/gtfs/management/commands/update_trainvel_gtfs.py
@@ -294,14 +294,14 @@ class Command(BaseCommand):
dep_h, dep_m, dep_s = map(int, dep_time.split(':'))
dep_time = dep_h * 3600 + dep_m * 60 + dep_s
- pickup_type = stop_time_dict.get('pickup_type', 0)
- drop_off_type = stop_time_dict.get('drop_off_type', 0)
- if stop_time_dict['stop_sequence'] == "1":
- # First stop
- drop_off_type = PickupType.NONE
- elif arr_time == dep_time:
- # Last stop
- pickup_type = PickupType.NONE
+ pickup_type = stop_time_dict.get('pickup_type', PickupType.REGULAR)
+ drop_off_type = stop_time_dict.get('drop_off_type', PickupType.REGULAR)
+ # if stop_time_dict['stop_sequence'] == "1":
+ # # First stop
+ # drop_off_type = PickupType.NONE
+ # elif arr_time == dep_time:
+ # # Last stop
+ # pickup_type = PickupType.NONE
st = StopTime(
id=f"{gtfs_code}-{stop_time_dict['trip_id']}-{stop_time_dict['stop_id']}"
@@ -349,7 +349,7 @@ class Command(BaseCommand):
from_stop_id=from_stop_id,
to_stop_id=to_stop_id,
transfer_type=transfer_dict['transfer_type'],
- min_transfer_time=transfer_dict['min_transfer_time'],
+ min_transfer_time=transfer_dict.get('min_transfer_time', 0) or 0,
)
transfers.append(transfer)
diff --git a/trainvel/gtfs/management/commands/update_trainvel_gtfs_rt.py b/trainvel/gtfs/management/commands/update_trainvel_gtfs_rt.py
index b9b0305..a50d9ce 100644
--- a/trainvel/gtfs/management/commands/update_trainvel_gtfs_rt.py
+++ b/trainvel/gtfs/management/commands/update_trainvel_gtfs_rt.py
@@ -30,7 +30,7 @@ class Command(BaseCommand):
headers = {}
if gtfs_code == "CH-ALL":
headers["Authorization"] = settings.OPENTRANSPORTDATA_SWISS_TOKEN
- resp = requests.get(gtfs_feed.rt_feed_url, allow_redirects=True)
+ resp = requests.get(gtfs_feed.rt_feed_url, allow_redirects=True, headers=headers)
feed_message = FeedMessage()
feed_message.ParseFromString(resp.content)
@@ -41,87 +41,88 @@ class Command(BaseCommand):
f.write(str(feed_message))
for entity in feed_message.entity:
- if entity.HasField("trip_update"):
- trip_update = entity.trip_update
- trip_id = trip_update.trip.trip_id
- trip_id = f"{gtfs_code}-{trip_id}"
+ try:
+ if entity.HasField("trip_update"):
+ trip_update = entity.trip_update
+ trip_id = trip_update.trip.trip_id
+ trip_id = f"{gtfs_code}-{trip_id}"
- start_date = date(year=int(trip_update.trip.start_date[:4]),
- month=int(trip_update.trip.start_date[4:6]),
- day=int(trip_update.trip.start_date[6:]))
- start_dt = datetime.combine(start_date, time(0), tzinfo=ZoneInfo("Europe/Paris"))
+ start_date = date(year=int(trip_update.trip.start_date[:4]),
+ month=int(trip_update.trip.start_date[4:6]),
+ day=int(trip_update.trip.start_date[6:]))
+ start_dt = datetime.combine(start_date, time(0), tzinfo=ZoneInfo("Europe/Paris"))
- if trip_update.trip.schedule_relationship == TripScheduleRelationship.ADDED:
- # C'est un trajet nouveau. On crée le trajet associé.
- self.create_trip(trip_update, trip_id, start_dt, gtfs_feed)
+ if trip_update.trip.schedule_relationship == TripScheduleRelationship.ADDED:
+ # C'est un trajet nouveau. On crée le trajet associé.
+ self.create_trip(trip_update, trip_id, start_dt, gtfs_feed)
- if not Trip.objects.filter(id=trip_id).exists():
- self.stdout.write(f"Trip {trip_id} does not exist in the GTFS feed.")
- continue
+ if not Trip.objects.filter(id=trip_id).exists():
+ self.stdout.write(f"Trip {trip_id} does not exist in the GTFS feed.")
+ continue
- # Création du TripUpdate
- tu, _created = TripUpdate.objects.update_or_create(
- trip_id=trip_id,
- start_date=trip_update.trip.start_date,
- start_time=trip_update.trip.start_time,
- defaults=dict(
- schedule_relationship=trip_update.trip.schedule_relationship,
- )
- )
-
- for stop_sequence, stop_time_update in enumerate(trip_update.stop_time_update):
- stop_id = stop_time_update.stop_id
- stop_id = f"{gtfs_code}-{stop_id}"
- if StopTime.objects.filter(trip_id=trip_id, stop=stop_id).exists():
- st = StopTime.objects.filter(trip_id=trip_id, stop=stop_id)
- if st.count() > 1:
- st = st.get(stop_sequence=stop_sequence)
- else:
- st = st.first()
- else:
- # Stop is added
- st = StopTime.objects.create(
- id=f"{trip_id}-{stop_time_update.stop_id}",
- trip_id=trip_id,
- stop_id=stop_id,
- defaults={
- "stop_sequence": stop_sequence,
- "arrival_time": datetime.fromtimestamp(stop_time_update.arrival.time,
- tz=ZoneInfo("Europe/Paris")) - start_dt,
- "departure_time": datetime.fromtimestamp(stop_time_update.departure.time,
- tz=ZoneInfo("Europe/Paris")) - start_dt,
- "pickup_type": (PickupType.REGULAR if stop_time_update.departure.time
- else PickupType.NONE),
- "drop_off_type": (PickupType.REGULAR if stop_time_update.arrival.time
- else PickupType.NONE),
- }
+ # Création du TripUpdate
+ tu, _created = TripUpdate.objects.update_or_create(
+ trip_id=trip_id,
+ start_date=trip_update.trip.start_date,
+ start_time=trip_update.trip.start_time,
+ defaults=dict(
+ schedule_relationship=trip_update.trip.schedule_relationship,
)
+ )
- if stop_time_update.schedule_relationship == StopScheduleRelationship.SKIPPED:
- if st.pickup_type != PickupType.NONE or st.drop_off_type != PickupType.NONE:
- st.pickup_type = PickupType.NONE
- st.drop_off_type = PickupType.NONE
+ for stop_sequence, stop_time_update in enumerate(trip_update.stop_time_update):
+ stop_id = stop_time_update.stop_id
+ stop_id = f"{gtfs_code}-{stop_id}"
+ if StopTime.objects.filter(trip_id=trip_id, stop=stop_id).exists():
+ st = StopTime.objects.filter(trip_id=trip_id, stop=stop_id)
+ if st.count() > 1:
+ st = st.get(stop_sequence=stop_sequence)
+ else:
+ st = st.first()
+ else:
+ # Stop is added
+ st = StopTime.objects.create(
+ id=f"{trip_id}-{stop_time_update.stop_id}",
+ trip_id=trip_id,
+ stop_id=stop_id,
+ stop_sequence=stop_sequence,
+ arrival_time=datetime.fromtimestamp(stop_time_update.arrival.time,
+ tz=ZoneInfo("Europe/Paris")) - start_dt,
+ departure_time=datetime.fromtimestamp(stop_time_update.departure.time,
+ tz=ZoneInfo("Europe/Paris")) - start_dt,
+ pickup_type=(PickupType.REGULAR if stop_time_update.departure.time
+ else PickupType.NONE),
+ drop_off_type=(PickupType.REGULAR if stop_time_update.arrival.time
+ else PickupType.NONE),
+ )
+
+ if stop_time_update.schedule_relationship == StopScheduleRelationship.SKIPPED:
+ if st.pickup_type != PickupType.NONE or st.drop_off_type != PickupType.NONE:
+ st.pickup_type = PickupType.NONE
+ st.drop_off_type = PickupType.NONE
+ st.save()
+
+ if st.stop_sequence != stop_sequence:
+ st.stop_sequence = stop_sequence
st.save()
- if st.stop_sequence != stop_sequence:
- st.stop_sequence = stop_sequence
- st.save()
-
- st_update = StopTimeUpdate(
- trip_update=tu,
- stop_time=st,
- arrival_delay=timedelta(seconds=stop_time_update.arrival.delay),
- arrival_time=datetime.fromtimestamp(stop_time_update.arrival.time,
- tz=ZoneInfo("Europe/Paris")),
- departure_delay=timedelta(seconds=stop_time_update.departure.delay),
- departure_time=datetime.fromtimestamp(stop_time_update.departure.time,
- tz=ZoneInfo("Europe/Paris")),
- schedule_relationship=stop_time_update.schedule_relationship
- or StopScheduleRelationship.SCHEDULED,
- )
- stop_times_updates.append(st_update)
- else:
- self.stdout.write(str(entity))
+ st_update = StopTimeUpdate(
+ trip_update=tu,
+ stop_time=st,
+ arrival_delay=timedelta(seconds=stop_time_update.arrival.delay),
+ arrival_time=datetime.fromtimestamp(stop_time_update.arrival.time,
+ tz=ZoneInfo("Europe/Paris")),
+ departure_delay=timedelta(seconds=stop_time_update.departure.delay),
+ departure_time=datetime.fromtimestamp(stop_time_update.departure.time,
+ tz=ZoneInfo("Europe/Paris")),
+ schedule_relationship=stop_time_update.schedule_relationship
+ or StopScheduleRelationship.SCHEDULED,
+ )
+ stop_times_updates.append(st_update)
+ else:
+ self.stdout.write(str(entity))
+ except Exception as e:
+ self.stderr.write(self.style.ERROR(f"Error while processing entity: {e}"))
StopTimeUpdate.objects.bulk_create(stop_times_updates,
update_conflicts=True,
diff --git a/trainvel/settings.py b/trainvel/settings.py
index e652440..2f16c90 100644
--- a/trainvel/settings.py
+++ b/trainvel/settings.py
@@ -151,6 +151,8 @@ REST_FRAMEWORK = {
'PAGE_SIZE': 20,
}
+STATION_RADIUS = 300
+
OPENTRANSPORTDATA_SWISS_TOKEN = "CHANGE ME"
diff --git a/trainvel/urls.py b/trainvel/urls.py
index c784a3c..3ce17bd 100644
--- a/trainvel/urls.py
+++ b/trainvel/urls.py
@@ -18,11 +18,12 @@ from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
-from trainvel.api.views import AgencyViewSet, StopViewSet, RouteViewSet, TripViewSet, StopTimeViewSet, \
+from trainvel.api.views import AgencyViewSet, StopViewSet, RouteViewSet, StationViewSet, TripViewSet, StopTimeViewSet, \
CalendarViewSet, CalendarDateViewSet, TransferViewSet, FeedInfoViewSet, NextDeparturesViewSet, NextArrivalsViewSet, \
TripUpdateViewSet, StopTimeUpdateViewSet
router = routers.DefaultRouter()
+router.register("core/station", StationViewSet)
router.register("gtfs/agency", AgencyViewSet)
router.register("gtfs/stop", StopViewSet)
router.register("gtfs/route", RouteViewSet)