Compare commits

..

No commits in common. "070849c427ad1f41ad7bd0ad32e8ca1b5119b596" and "6884084f2a250dd530e5dda24fe0d6f25a5726a3" have entirely different histories.

24 changed files with 163 additions and 1688 deletions

View File

@ -18,7 +18,7 @@ function App() {
element: <Home />,
},
{
path: "/station/:theme/:stationSlug",
path: "/station/:theme/:stopId",
element: <Station />
}
])

View File

@ -1,7 +1,7 @@
import {Autocomplete, TextField} from "@mui/material";
import {useRef, useState} from "react";
function AutocompleteStation(params) {
function AutocompleteStop(params) {
const [options, setOptions] = useState([])
const previousController = useRef()
@ -17,7 +17,7 @@ function AutocompleteStation(params) {
const controller = new AbortController()
const signal = controller.signal
previousController.current = controller
fetch("/api/core/station/?search=" + value, {signal})
fetch("/api/gtfs/stop/?location_type=1&search=" + value, {signal})
.then(response => response.json())
.then(data => data.results)
.then(setOptions)
@ -40,7 +40,24 @@ function AutocompleteStation(params) {
}
function getOptionGroup(option) {
return option.country
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
}
}
export default AutocompleteStation;
export default AutocompleteStop;

View File

@ -1,11 +1,11 @@
import AutocompleteStation from "./AutocompleteStation"
import AutocompleteStop from "./AutocompleteStop"
import {useNavigate} from "react-router-dom"
function Home() {
const navigate = useNavigate()
function onStationSelected(event, station) {
navigate(`/station/sncf/${station.slug}/`)
function onStationSelected(event, stop) {
navigate(`/station/sncf/${stop.id}/`)
}
return <>
@ -13,7 +13,7 @@ function Home() {
<h2>
Choisissez une gare dont vous désirez connaître le tableau des prochains départs et arrivées :
</h2>
<AutocompleteStation onChange={onStationSelected} />
<AutocompleteStop onChange={onStationSelected} />
</>
}

View File

@ -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 AutocompleteStation from "./AutocompleteStation";
import AutocompleteStop from "./AutocompleteStop";
function DateTimeSelector({station, date, time}) {
function DateTimeSelector({stop, date, time}) {
const navigate = useNavigate()
function onStationSelected(event, station) {
if (station !== null)
navigate(`/station/sncf/${station.slug}/`)
function onStationSelected(event, stop) {
if (stop !== null)
navigate(`/station/sncf/${stop.id}/`)
}
return <>
@ -20,7 +20,7 @@ function DateTimeSelector({station, date, time}) {
<FormLabel>
Changer la gare recherchée :
</FormLabel>
<AutocompleteStation onChange={onStationSelected} />
<AutocompleteStop onChange={onStationSelected} />
<FormLabel>
Modifier la date et l'heure de recherche :
</FormLabel>
@ -32,7 +32,7 @@ function DateTimeSelector({station, date, time}) {
}
function Station() {
let {theme, stationSlug} = useParams()
let {stopId, theme} = 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 stationQuery = useQuery({
queryKey: ['station', stationSlug],
queryFn: () => fetch(`/api/core/station/${stationSlug}/`)
const stopQuery = useQuery({
queryKey: ['stop', stopId],
queryFn: () => fetch(`/api/gtfs/stop/${stopId}/`)
.then(response => response.json()),
enabled: !!stationSlug,
enabled: !!stopId,
})
const station = stationQuery.data ?? {name: "Chargement…"}
const stop = stopQuery.data ?? {name: "Chargement…"}
if (time === timeNow) {
setInterval(() => {
@ -62,13 +62,13 @@ function Station() {
return (
<div className="Station">
<header className="App-header">
<h1>Horaires en gare de {station.name}</h1>
<h1>Horaires en gare de {stop.name}</h1>
</header>
<main>
<DateTimeSelector station={station} date={date} time={time} />
<TrainsTable station={station} date={date} time={time} tableType="departures" />
<TrainsTable station={station} date={date} time={time} tableType="arrivals" />
<DateTimeSelector stop={stop} date={date} time={time} />
<TrainsTable stop={stop} date={date} time={time} tableType="departures" />
<TrainsTable stop={stop} date={date} time={time} tableType="arrivals" />
</main>
</div>
)

View File

@ -26,12 +26,12 @@ const StyledTableRow = styled(TableRow)(({ theme, tabletype }) => ({
},
}));
function TrainsTable({station, date, time, tableType}) {
function TrainsTable({stop, date, time, tableType}) {
return <>
<TableContainer>
<Table>
<TrainsTableHeader tableType={tableType} />
<TrainsTableBody station={station} date={date} time={time} tableType={tableType} />
<TrainsTableBody stop={stop} date={date} time={time} tableType={tableType} />
</Table>
</TableContainer>
</>
@ -49,7 +49,7 @@ function TrainsTableHeader({tableType}) {
</>
}
function TrainsTableBody({station, date, time, tableType}) {
function TrainsTableBody({stop, 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({station, date, time, tableType}) {
}, [date, time, tableType])
const updateTrains = useCallback(() => {
return fetch(`/api/station/next_${tableType}/?station_slug=${station.slug}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
return fetch(`/api/station/next_${tableType}/?stop_id=${stop.id}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
.then(response => response.json())
.then(data => data.results)
.then(data => [...data])
}, [station.id, date, time, tableType])
}, [stop.id, date, time, tableType])
const trainsQuery = useQuery({
queryKey: ['trains', station.id, tableType],
queryKey: ['trains', stop.id, tableType],
queryFn: updateTrains,
enabled: !!station.id,
enabled: !!stop.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/?${new URLSearchParams({trip: trip.id, order: 'stop_sequence', limit: 1000})}`)
queryFn: () => fetch(`/api/gtfs/stop_time/?trip=${trip.id}&order=stop_sequence&limit=1000`)
.then(response => response.json())
.then(data => data.results),
enabled: !!trip.id,

View File

@ -1,18 +1,9 @@
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

View File

@ -10,28 +10,13 @@ from rest_framework.filters import OrderingFilter, SearchFilter
from trainvel.api.serializers import AgencySerializer, StopSerializer, RouteSerializer, TripSerializer, \
StopTimeSerializer, CalendarSerializer, CalendarDateSerializer, TransferSerializer, \
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
FeedInfoSerializer, TripUpdateSerializer, StopTimeUpdateSerializer
from trainvel.gtfs.models import Agency, Calendar, CalendarDate, FeedInfo, GTFSFeed, Route, Stop, StopTime, StopTimeUpdate, \
Transfer, Trip, TripUpdate
CACHE_CONTROL = cache_control(max_age=30)
CACHE_CONTROL = cache_control(max_age=7200)
LAST_MODIFIED = last_modified(lambda *args, **kwargs: GTFSFeed.objects.order_by('-last_modified').first().last_modified)
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
LOOKUP_VALUE_REGEX = r"[\w.: |-]+"
@method_decorator(name='list', decorator=[CACHE_CONTROL, LAST_MODIFIED])
@ -150,7 +135,8 @@ class NextDeparturesViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
now = datetime.now()
station_slug = self.request.query_params.get('station_slug', None)
stop_id = self.request.query_params.get('stop_id', None)
stop_name = self.request.query_params.get('stop_name', 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
@ -162,10 +148,16 @@ class NextDeparturesViewSet(viewsets.ReadOnlyModelViewSet):
tomorrow = query_date + timedelta(days=1)
stop_filter = Q(stop__location_type=0)
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))
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)
def calendar_filter(d: date):
return Q(trip__service_id__in=CalendarDate.objects.filter(date=d, exception_type=1)
@ -197,7 +189,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=PickupType.REGULAR) | canceled_filter(query_date)) \
.filter(Q(pickup_type=0) | canceled_filter(query_date)) \
.filter(calendar_filter(query_date)) \
.annotate(departure_date=Value(query_date)) \
.annotate(departure_time_24h=F('departure_time'))
@ -229,7 +221,8 @@ class NextArrivalsViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
now = datetime.now()
station_slug = self.request.query_params.get('station_slug', None)
stop_id = self.request.query_params.get('stop_id', None)
stop_name = self.request.query_params.get('stop_name', 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
@ -242,10 +235,16 @@ class NextArrivalsViewSet(viewsets.ReadOnlyModelViewSet):
tomorrow = query_date + timedelta(days=1)
stop_filter = Q(stop__location_type=0)
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))
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)
def calendar_filter(d: date):
return Q(trip__service_id__in=CalendarDate.objects.filter(date=d, exception_type=1)

View File

@ -1,23 +0,0 @@
from django.contrib import admin
from trainvel.core.models import Station
class StationInline(admin.TabularInline):
model = Station
extra = 0
autocomplete_fields = ('parent_station', 'same_as',)
show_change_link = True
ordering = ('name',)
readonly_fields = ('id',)
fk_name = 'parent_station'
@admin.register(Station)
class StationAdmin(admin.ModelAdmin):
list_display = ('name', 'country', 'uic', 'latitude', 'longitude',)
list_filter = ('country', 'is_city', 'is_main_station', 'is_airport', 'is_suggestable',
'country_hint', 'main_station_hint',)
search_fields = ('name', 'slug',)
autocomplete_fields = ('parent_station', 'same_as',)
inlines = [StationInline]

View File

@ -1,8 +0,0 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CoreConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "trainvel.core"
verbose_name = _("Trainvel - Core")

View File

@ -1,325 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-09 22:04+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <ynerant@emy.lu>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: trainvel/core/apps.py:8
msgid "Trainvel - Core"
msgstr "Trainvel - Cœur"
#: trainvel/core/models.py:15
msgid "name"
msgstr "nom"
#: trainvel/core/models.py:20
msgid "slug"
msgstr "slug"
#: trainvel/core/models.py:24
msgid "UIC"
msgstr "UIC"
#: trainvel/core/models.py:31
msgid "UIC8 SNCF"
msgstr "UIC8 SNCF"
#: trainvel/core/models.py:38
msgid "latitude"
msgstr "latitude"
#: trainvel/core/models.py:45
msgid "longitude"
msgstr "longitude"
#: trainvel/core/models.py:54
msgid "parent station"
msgstr "gare parente"
#: trainvel/core/models.py:63
msgid "country"
msgstr "pays"
#: trainvel/core/models.py:69
msgid "timezone"
msgstr "fuseau horaire"
#: trainvel/core/models.py:73
msgid "is city"
msgstr "est une ville"
#: trainvel/core/models.py:77
msgid "is main station"
msgstr "est une gare principale"
#: trainvel/core/models.py:81
msgid "is airport"
msgstr "est un aéroport"
#: trainvel/core/models.py:85
msgid "is suggestable"
msgstr "est suggérable"
#: trainvel/core/models.py:89
msgid "country hint"
msgstr "indice de pays"
#: trainvel/core/models.py:93
msgid "main station hint"
msgstr "indice de gare principale"
#: trainvel/core/models.py:98
msgid "SNCF ID"
msgstr "ID SNCF"
#: trainvel/core/models.py:106
msgid "SNCF TVS ID"
msgstr "ID SNCF TVS"
#: trainvel/core/models.py:113
msgid "SNCF is enabled"
msgstr "SNCF est activé"
#: trainvel/core/models.py:118
msgid "Entur ID"
msgstr "ID Entur"
#: trainvel/core/models.py:125
msgid "Entur is enabled"
msgstr "Entur est activé"
#: trainvel/core/models.py:129
msgid "DB ID"
msgstr "ID DB"
#: trainvel/core/models.py:136
msgid "DB is enabled"
msgstr "DB est activé"
#: trainvel/core/models.py:141
msgid "Busbud ID"
msgstr "ID Busbud"
#: trainvel/core/models.py:148
msgid "Busbud is enabled"
msgstr "Busbud est activé"
#: trainvel/core/models.py:153
msgid "distribusion ID"
msgstr "ID distribusion"
#: trainvel/core/models.py:160
msgid "distribusion is enabled"
msgstr "distribusion est activé"
#: trainvel/core/models.py:164
msgid "Flixbus ID"
msgstr "ID Flixbus"
#: trainvel/core/models.py:171
msgid "Flixbus is enabled"
msgstr "Flixbus est activé"
#: trainvel/core/models.py:175
msgid "CFF ID"
msgstr "ID CFF"
#: trainvel/core/models.py:182
msgid "CFF is enabled"
msgstr "CFF est activé"
#: trainvel/core/models.py:187
msgid "Leo Express ID"
msgstr "ID Leo Express"
#: trainvel/core/models.py:194
msgid "Leo Express is enabled"
msgstr "Leo Express est activé"
#: trainvel/core/models.py:198
msgid "ÖBB ID"
msgstr "ID ÖBB"
#: trainvel/core/models.py:205
msgid "ÖBB is enabled"
msgstr "ÖBB est activé"
#: trainvel/core/models.py:210
msgid "Ouigo ID"
msgstr "ID Ouigo"
#: trainvel/core/models.py:217
msgid "Ouigo is enabled"
msgstr "Ouigo est activé"
#: trainvel/core/models.py:221
msgid "Trenitalia ID"
msgstr "ID Trenitalia"
#: trainvel/core/models.py:228
msgid "Trenitalia is enabled"
msgstr "Trenitalia est activé"
#: trainvel/core/models.py:233
msgid "Trenitalia RTVT ID"
msgstr "ID Trenitalia RTVT"
#: trainvel/core/models.py:241
msgid "Trenord ID"
msgstr "ID Trenord"
#: trainvel/core/models.py:249
msgid "NTV RTIV ID"
msgstr "ID NTV RTIV"
#: trainvel/core/models.py:257
msgid "NTV ID"
msgstr "ID NTV"
#: trainvel/core/models.py:264
msgid "NTV is enabled"
msgstr "NTV est activé"
#: trainvel/core/models.py:269
msgid "HKX ID"
msgstr "ID HKX"
#: trainvel/core/models.py:276
msgid "HKX is enabled"
msgstr "HKX est activé"
#: trainvel/core/models.py:280
msgid "Renfe ID"
msgstr "ID Renfe"
#: trainvel/core/models.py:287
msgid "Renfe is enabled"
msgstr "Renfe est activé"
#: trainvel/core/models.py:292
msgid "ATOC ID"
msgstr "ID ATOC"
#: trainvel/core/models.py:299
msgid "ATOC is enabled"
msgstr "ATOC est activé"
#: trainvel/core/models.py:304
msgid "Benerail ID"
msgstr "ID Benerail"
#: trainvel/core/models.py:311
msgid "Benerail is enabled"
msgstr "Benerail est activé"
#: trainvel/core/models.py:316
msgid "Westbahn ID"
msgstr "ID Westbahn"
#: trainvel/core/models.py:323
msgid "Westbahn is enabled"
msgstr "Westbahn est activé"
#: trainvel/core/models.py:327
msgid "SNCF self-service machine"
msgstr "Automate self-service SNCF"
#: trainvel/core/models.py:333
msgid "same as"
msgstr "identique à"
#: trainvel/core/models.py:342
msgid "info (DE)"
msgstr "info (DE)"
#: trainvel/core/models.py:350
msgid "info (EN)"
msgstr "info (EN)"
#: trainvel/core/models.py:358
msgid "info (ES)"
msgstr "info (ES)"
#: trainvel/core/models.py:366
msgid "info (FR)"
msgstr "info (FR)"
#: trainvel/core/models.py:374
msgid "info (IT)"
msgstr "info (IT)"
#: trainvel/core/models.py:382
msgid "info (NB)"
msgstr "info (NB)"
#: trainvel/core/models.py:390
msgid "info (NL)"
msgstr "info (NL)"
#: trainvel/core/models.py:398
msgid "info (CS)"
msgstr "info (CS)"
#: trainvel/core/models.py:406
msgid "info (DA)"
msgstr "info (DA)"
#: trainvel/core/models.py:414
msgid "info (HU)"
msgstr "info (HU)"
#: trainvel/core/models.py:422
msgid "info (JA)"
msgstr "info (JA)"
#: trainvel/core/models.py:430
msgid "info (KO)"
msgstr "info (KO)"
#: trainvel/core/models.py:438
msgid "info (PL)"
msgstr "info (PL)"
#: trainvel/core/models.py:446
msgid "info (PT)"
msgstr "info (PT)"
#: trainvel/core/models.py:454
msgid "info (RU)"
msgstr "info (RU)"
#: trainvel/core/models.py:462
msgid "info (SV)"
msgstr "info (SV)"
#: trainvel/core/models.py:470
msgid "info (TR)"
msgstr "info (TR)"
#: trainvel/core/models.py:478
msgid "info (ZH)"
msgstr "info (ZH)"
#: trainvel/core/models.py:486
msgid "normalized code (Trainline)"
msgstr "code normalisé (Trainline)"
#: trainvel/core/models.py:491
msgid "IATA airport code"
msgstr "code aéroport IATA"
#: trainvel/core/models.py:501
msgid "station"
msgstr "gare"
#: trainvel/core/models.py:502
msgid "stations"
msgstr "gares"

View File

@ -1,33 +0,0 @@
import csv
import requests
from django.core.management import BaseCommand
from tqdm import tqdm
from trainvel.core.models import Station
class Command(BaseCommand):
def handle(self, *args, **options):
def convert_value(value: str) -> str:
return True if value == 't' else False if value == 'f' else (value or None)
stations, stations_without_fk = [], []
STATIONS_URL = "https://raw.githubusercontent.com/trainline-eu/stations/master/stations.csv"
with requests.get(STATIONS_URL, stream=True) as resp:
for row in csv.DictReader(tqdm(resp.iter_lines(decode_unicode=True)), delimiter=';'):
row: dict
values = {k.replace(':', '_').replace('normalised_code', 'normalized_code_trainline')
.replace('same_as', 'same_as_id'): convert_value(v)
for k, v in row.items()}
values_without_fk = values.copy()
del values_without_fk['same_as_id']
del values_without_fk['parent_station_id']
stations.append(Station(**values))
stations_without_fk.append(Station(**values_without_fk))
Station.objects.bulk_create(stations_without_fk, update_conflicts=True, unique_fields=['id'],
update_fields=[k for k in values_without_fk.keys() if k != 'id'])
Station.objects.bulk_create(stations, update_conflicts=True, unique_fields=['id'],
update_fields=['same_as_id', 'parent_station_id'])

View File

@ -1,603 +0,0 @@
# Generated by Django 5.0.1 on 2024-05-09 22:15
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Station",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255, verbose_name="name")),
("slug", models.SlugField(max_length=255, verbose_name="slug")),
(
"uic",
models.IntegerField(
blank=True, default=None, null=True, verbose_name="UIC"
),
),
(
"uic8_sncf",
models.IntegerField(
blank=True, default=None, null=True, verbose_name="UIC8 SNCF"
),
),
(
"latitude",
models.FloatField(
blank=True, default=None, null=True, verbose_name="latitude"
),
),
(
"longitude",
models.FloatField(
blank=True, default=None, null=True, verbose_name="longitude"
),
),
(
"country",
models.CharField(
choices=[
("AL", "Albania"),
("AD", "Andorra"),
("AM", "Armenia"),
("AT", "Austria"),
("AZ", "Azerbaijan"),
("BE", "Belgium"),
("BA", "Bosnia and Herzegovina"),
("BG", "Bulgaria"),
("HR", "Croatia"),
("CY", "Cyprus"),
("CZ", "Czech Republic"),
("DK", "Denmark"),
("EE", "Estonia"),
("FI", "Finland"),
("FR", "France"),
("GE", "Georgia"),
("DE", "Germany"),
("GR", "Greece"),
("HU", "Hungary"),
("IS", "Iceland"),
("IE", "Ireland"),
("IT", "Italy"),
("LV", "Latvia"),
("LI", "Liechtenstein"),
("LT", "Lithuania"),
("LU", "Luxembourg"),
("MT", "Malta"),
("MD", "Moldova"),
("MC", "Monaco"),
("ME", "Montenegro"),
("NL", "Netherlands"),
("MK", "North Macedonia"),
("NO", "Norway"),
("PL", "Poland"),
("PT", "Portugal"),
("RO", "Romania"),
("SM", "San Marino"),
("RS", "Serbia"),
("SK", "Slovakia"),
("SI", "Slovenia"),
("ES", "Spain"),
("SE", "Sweden"),
("CH", "Switzerland"),
("TR", "Turkey"),
("GB", "United Kingdom"),
("UA", "Ukraine"),
],
max_length=255,
verbose_name="country",
),
),
(
"time_zone",
models.CharField(max_length=255, verbose_name="timezone"),
),
("is_city", models.BooleanField(verbose_name="is city")),
(
"is_main_station",
models.BooleanField(verbose_name="is main station"),
),
("is_airport", models.BooleanField(verbose_name="is airport")),
("is_suggestable", models.BooleanField(verbose_name="is suggestable")),
("country_hint", models.BooleanField(verbose_name="country hint")),
(
"main_station_hint",
models.BooleanField(verbose_name="main station hint"),
),
(
"sncf_id",
models.CharField(
blank=True,
default=None,
max_length=5,
null=True,
verbose_name="SNCF ID",
),
),
(
"sncf_tvs_id",
models.CharField(
blank=True,
default=None,
max_length=3,
null=True,
verbose_name="SNCF TVS ID",
),
),
(
"sncf_is_enabled",
models.BooleanField(verbose_name="SNCF is enabled"),
),
(
"entur_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Entur ID",
),
),
(
"entur_is_enabled",
models.BooleanField(verbose_name="Entur is enabled"),
),
(
"db_id",
models.IntegerField(
blank=True, default=None, null=True, verbose_name="DB ID"
),
),
("db_is_enabled", models.BooleanField(verbose_name="DB is enabled")),
(
"busbud_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Busbud ID",
),
),
(
"busbud_is_enabled",
models.BooleanField(verbose_name="Busbud is enabled"),
),
(
"distribusion_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="distribusion ID",
),
),
(
"distribusion_is_enabled",
models.BooleanField(verbose_name="distribusion is enabled"),
),
(
"flixbus_id",
models.IntegerField(
blank=True, default=None, null=True, verbose_name="Flixbus ID"
),
),
(
"flixbus_is_enabled",
models.BooleanField(verbose_name="Flixbus is enabled"),
),
(
"cff_id",
models.IntegerField(
blank=True, default=None, null=True, verbose_name="CFF ID"
),
),
("cff_is_enabled", models.BooleanField(verbose_name="CFF is enabled")),
(
"leoexpress_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Leo Express ID",
),
),
(
"leoexpress_is_enabled",
models.BooleanField(verbose_name="Leo Express is enabled"),
),
(
"obb_id",
models.IntegerField(
blank=True, default=None, null=True, verbose_name="ÖBB ID"
),
),
("obb_is_enabled", models.BooleanField(verbose_name="ÖBB is enabled")),
(
"ouigo_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Ouigo ID",
),
),
(
"ouigo_is_enabled",
models.BooleanField(verbose_name="Ouigo is enabled"),
),
(
"trenitalia_id",
models.IntegerField(
blank=True,
default=None,
null=True,
verbose_name="Trenitalia ID",
),
),
(
"trenitalia_is_enabled",
models.BooleanField(verbose_name="Trenitalia is enabled"),
),
(
"trenitalia_rtvt_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Trenitalia RTVT ID",
),
),
(
"trenord_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Trenord ID",
),
),
(
"ntv_rtiv_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="NTV RTIV ID",
),
),
(
"ntv_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="NTV ID",
),
),
("ntv_is_enabled", models.BooleanField(verbose_name="NTV is enabled")),
(
"hkx_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="HKX ID",
),
),
("hkx_is_enabled", models.BooleanField(verbose_name="HKX is enabled")),
(
"renfe_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Renfe ID",
),
),
(
"renfe_is_enabled",
models.BooleanField(verbose_name="Renfe is enabled"),
),
(
"atoc_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="ATOC ID",
),
),
(
"atoc_is_enabled",
models.BooleanField(verbose_name="ATOC is enabled"),
),
(
"benerail_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Benerail ID",
),
),
(
"benerail_is_enabled",
models.BooleanField(verbose_name="Benerail is enabled"),
),
(
"westbahn_id",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Westbahn ID",
),
),
(
"westbahn_is_enabled",
models.BooleanField(verbose_name="Westbahn is enabled"),
),
(
"sncf_self_service_machine",
models.BooleanField(verbose_name="SNCF self-service machine"),
),
(
"info_de",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (DE)",
),
),
(
"info_en",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (EN)",
),
),
(
"info_es",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (ES)",
),
),
(
"info_fr",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (FR)",
),
),
(
"info_it",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (IT)",
),
),
(
"info_nb",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (NB)",
),
),
(
"info_nl",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (NL)",
),
),
(
"info_cs",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (CS)",
),
),
(
"info_da",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (DA)",
),
),
(
"info_hu",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (HU)",
),
),
(
"info_ja",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (JA)",
),
),
(
"info_ko",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (KO)",
),
),
(
"info_pl",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (PL)",
),
),
(
"info_pt",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (PT)",
),
),
(
"info_ru",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (RU)",
),
),
(
"info_sv",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (SV)",
),
),
(
"info_tr",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (TR)",
),
),
(
"info_zh",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="info (ZH)",
),
),
(
"normalized_code_trainline",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="normalized code (Trainline)",
),
),
(
"iata_airport_code",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="IATA airport code",
),
),
(
"parent_station",
models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="children",
to="core.station",
verbose_name="parent station",
),
),
(
"same_as",
models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="same_as_other",
to="core.station",
verbose_name="same as",
),
),
],
options={
"verbose_name": "station",
"verbose_name_plural": "stations",
},
),
]

View File

@ -1,519 +0,0 @@
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, Stop
class Station(models.Model):
"""
Describes the content of the stations CSV file generated by Trainline.
The CSV file can be found at https://raw.githubusercontent.com/trainline-eu/stations/master/stations.csv
"""
name = models.CharField(
max_length=255,
verbose_name=_("name"),
)
slug = models.SlugField(
max_length=255,
verbose_name=_("slug"),
)
uic = models.IntegerField(
verbose_name=_("UIC"),
blank=True,
null=True,
default=None,
)
uic8_sncf = models.IntegerField(
verbose_name=_("UIC8 SNCF"),
blank=True,
null=True,
default=None,
)
latitude = models.FloatField(
verbose_name=_("latitude"),
blank=True,
null=True,
default=None,
)
longitude = models.FloatField(
verbose_name=_("longitude"),
blank=True,
null=True,
default=None,
)
parent_station = models.ForeignKey(
"Station",
on_delete=models.CASCADE,
verbose_name=_("parent station"),
blank=True,
null=True,
default=None,
related_name="children",
)
country = models.CharField(
max_length=255,
verbose_name=_("country"),
choices=Country,
)
time_zone = models.CharField(
max_length=255,
verbose_name=_("timezone"),
)
is_city = models.BooleanField(
verbose_name=_("is city"),
)
is_main_station = models.BooleanField(
verbose_name=_("is main station"),
)
is_airport = models.BooleanField(
verbose_name=_("is airport"),
)
is_suggestable = models.BooleanField(
verbose_name=_("is suggestable"),
)
country_hint = models.BooleanField(
verbose_name=_("country hint"),
)
main_station_hint = models.BooleanField(
verbose_name=_("main station hint"),
)
sncf_id = models.CharField(
max_length=5,
verbose_name=_("SNCF ID"),
blank=True,
null=True,
default=None,
)
sncf_tvs_id = models.CharField(
max_length=3,
verbose_name=_("SNCF TVS ID"),
blank=True,
null=True,
default=None,
)
sncf_is_enabled = models.BooleanField(
verbose_name=_("SNCF is enabled"),
)
entur_id = models.CharField(
max_length=255,
verbose_name=_("Entur ID"),
blank=True,
null=True,
default=None,
)
entur_is_enabled = models.BooleanField(
verbose_name=_("Entur is enabled"),
)
db_id = models.IntegerField(
verbose_name=_("DB ID"),
blank=True,
null=True,
default=None,
)
db_is_enabled = models.BooleanField(
verbose_name=_("DB is enabled"),
)
busbud_id = models.CharField(
max_length=255,
verbose_name=_("Busbud ID"),
blank=True,
null=True,
default=None,
)
busbud_is_enabled = models.BooleanField(
verbose_name=_("Busbud is enabled"),
)
distribusion_id = models.CharField(
max_length=255,
verbose_name=_("distribusion ID"),
blank=True,
null=True,
default=None,
)
distribusion_is_enabled = models.BooleanField(
verbose_name=_("distribusion is enabled"),
)
flixbus_id = models.IntegerField(
verbose_name=_("Flixbus ID"),
blank=True,
null=True,
default=None,
)
flixbus_is_enabled = models.BooleanField(
verbose_name=_("Flixbus is enabled"),
)
cff_id = models.IntegerField(
verbose_name=_("CFF ID"),
blank=True,
null=True,
default=None,
)
cff_is_enabled = models.BooleanField(
verbose_name=_("CFF is enabled"),
)
leoexpress_id = models.CharField(
max_length=255,
verbose_name=_("Leo Express ID"),
blank=True,
null=True,
default=None,
)
leoexpress_is_enabled = models.BooleanField(
verbose_name=_("Leo Express is enabled"),
)
obb_id = models.IntegerField(
verbose_name=_("ÖBB ID"),
blank=True,
null=True,
default=None,
)
obb_is_enabled = models.BooleanField(
verbose_name=_("ÖBB is enabled"),
)
ouigo_id = models.CharField(
max_length=255,
verbose_name=_("Ouigo ID"),
blank=True,
null=True,
default=None,
)
ouigo_is_enabled = models.BooleanField(
verbose_name=_("Ouigo is enabled"),
)
trenitalia_id = models.IntegerField(
verbose_name=_("Trenitalia ID"),
blank=True,
null=True,
default=None,
)
trenitalia_is_enabled = models.BooleanField(
verbose_name=_("Trenitalia is enabled"),
)
trenitalia_rtvt_id = models.CharField(
max_length=255,
verbose_name=_("Trenitalia RTVT ID"),
blank=True,
null=True,
default=None,
)
trenord_id = models.CharField(
max_length=255,
verbose_name=_("Trenord ID"),
blank=True,
null=True,
default=None,
)
ntv_rtiv_id = models.CharField(
max_length=255,
verbose_name=_("NTV RTIV ID"),
blank=True,
null=True,
default=None,
)
ntv_id = models.CharField(
max_length=255,
verbose_name=_("NTV ID"),
blank=True,
null=True,
default=None,
)
ntv_is_enabled = models.BooleanField(
verbose_name=_("NTV is enabled"),
)
hkx_id = models.CharField(
max_length=255,
verbose_name=_("HKX ID"),
blank=True,
null=True,
default=None,
)
hkx_is_enabled = models.BooleanField(
verbose_name=_("HKX is enabled"),
)
renfe_id = models.CharField(
max_length=255,
verbose_name=_("Renfe ID"),
blank=True,
null=True,
default=None,
)
renfe_is_enabled = models.BooleanField(
verbose_name=_("Renfe is enabled"),
)
atoc_id = models.CharField(
max_length=255,
verbose_name=_("ATOC ID"),
blank=True,
null=True,
default=None,
)
atoc_is_enabled = models.BooleanField(
verbose_name=_("ATOC is enabled"),
)
benerail_id = models.CharField(
max_length=255,
verbose_name=_("Benerail ID"),
blank=True,
null=True,
default=None,
)
benerail_is_enabled = models.BooleanField(
verbose_name=_("Benerail is enabled"),
)
westbahn_id = models.CharField(
max_length=255,
verbose_name=_("Westbahn ID"),
blank=True,
null=True,
default=None,
)
westbahn_is_enabled = models.BooleanField(
verbose_name=_("Westbahn is enabled"),
)
sncf_self_service_machine = models.BooleanField(
verbose_name=_("SNCF self-service machine"),
)
same_as = models.ForeignKey(
"Station",
on_delete=models.CASCADE,
verbose_name=_("same as"),
blank=True,
null=True,
default=None,
related_name="same_as_other",
)
info_de = models.CharField(
max_length=255,
verbose_name=_("info (DE)"),
blank=True,
null=True,
default=None,
)
info_en = models.CharField(
max_length=255,
verbose_name=_("info (EN)"),
blank=True,
null=True,
default=None,
)
info_es = models.CharField(
max_length=255,
verbose_name=_("info (ES)"),
blank=True,
null=True,
default=None,
)
info_fr = models.CharField(
max_length=255,
verbose_name=_("info (FR)"),
blank=True,
null=True,
default=None,
)
info_it = models.CharField(
max_length=255,
verbose_name=_("info (IT)"),
blank=True,
null=True,
default=None,
)
info_nb = models.CharField(
max_length=255,
verbose_name=_("info (NB)"),
blank=True,
null=True,
default=None,
)
info_nl = models.CharField(
max_length=255,
verbose_name=_("info (NL)"),
blank=True,
null=True,
default=None,
)
info_cs = models.CharField(
max_length=255,
verbose_name=_("info (CS)"),
blank=True,
null=True,
default=None,
)
info_da = models.CharField(
max_length=255,
verbose_name=_("info (DA)"),
blank=True,
null=True,
default=None,
)
info_hu = models.CharField(
max_length=255,
verbose_name=_("info (HU)"),
blank=True,
null=True,
default=None,
)
info_ja = models.CharField(
max_length=255,
verbose_name=_("info (JA)"),
blank=True,
null=True,
default=None,
)
info_ko = models.CharField(
max_length=255,
verbose_name=_("info (KO)"),
blank=True,
null=True,
default=None,
)
info_pl = models.CharField(
max_length=255,
verbose_name=_("info (PL)"),
blank=True,
null=True,
default=None,
)
info_pt = models.CharField(
max_length=255,
verbose_name=_("info (PT)"),
blank=True,
null=True,
default=None,
)
info_ru = models.CharField(
max_length=255,
verbose_name=_("info (RU)"),
blank=True,
null=True,
default=None,
)
info_sv = models.CharField(
max_length=255,
verbose_name=_("info (SV)"),
blank=True,
null=True,
default=None,
)
info_tr = models.CharField(
max_length=255,
verbose_name=_("info (TR)"),
blank=True,
null=True,
default=None,
)
info_zh = models.CharField(
max_length=255,
verbose_name=_("info (ZH)"),
blank=True,
null=True,
default=None,
)
normalized_code_trainline = models.CharField(
max_length=255,
verbose_name=_("normalized code (Trainline)"),
blank=True,
null=True,
default=None,
)
iata_airport_code = models.CharField(
max_length=255,
verbose_name=_("IATA airport code"),
blank=True,
null=True,
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
class Meta:
verbose_name = _("station")
verbose_name_plural = _("stations")

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

View File

@ -88,15 +88,5 @@
"feed_url": "https://opentransportdata.swiss/fr/dataset/timetable-2024-gtfs2020/permalink",
"rt_feed_url": "https://api.opentransportdata.swiss/gtfsrt2020"
}
},
{
"model": "gtfs.gtfsfeed",
"pk": "LU-ALL",
"fields": {
"name": "CFL",
"country": "LU",
"feed_url": "https://data.public.lu/fr/datasets/r/aab2922d-27ff-4e53-a789-d990cf1ceb1e",
"rt_feed_url": ""
}
}
]

View File

@ -155,7 +155,7 @@ class Command(BaseCommand):
unique_fields=['id'])
routes.clear()
Calendar.objects.filter(gtfs_feed=gtfs_feed).delete()
# Calendar.objects.filter(gtfs_feed=gtfs_feed).delete()
calendars = {}
if "calendar.txt" in zipfile.namelist():
for calendar_dict in csv.DictReader(tqdm(read_file("calendar.txt"), desc="Calendars")):
@ -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', 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
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
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.get('min_transfer_time', 0) or 0,
min_transfer_time=transfer_dict['min_transfer_time'],
)
transfers.append(transfer)

View File

@ -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, headers=headers)
resp = requests.get(gtfs_feed.rt_feed_url, allow_redirects=True)
feed_message = FeedMessage()
feed_message.ParseFromString(resp.content)
@ -41,88 +41,87 @@ class Command(BaseCommand):
f.write(str(feed_message))
for entity in feed_message.entity:
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}"
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,
)
# 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()
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:
# 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),
)
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),
}
)
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
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()
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}"))
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))
StopTimeUpdate.objects.bulk_create(stop_times_updates,
update_conflicts=True,

View File

@ -45,7 +45,6 @@ INSTALLED_APPS = [
"rest_framework",
"trainvel.api",
"trainvel.core",
"trainvel.gtfs",
]
@ -151,8 +150,6 @@ REST_FRAMEWORK = {
'PAGE_SIZE': 20,
}
STATION_RADIUS = 300
OPENTRANSPORTDATA_SWISS_TOKEN = "CHANGE ME"

View File

@ -18,12 +18,11 @@ from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from trainvel.api.views import AgencyViewSet, StopViewSet, RouteViewSet, StationViewSet, TripViewSet, StopTimeViewSet, \
from trainvel.api.views import AgencyViewSet, StopViewSet, RouteViewSet, 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)