trainvel/trainvel/core/models.py

520 lines
11 KiB
Python

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")