from django.db import models from django.utils.translation import gettext_lazy as _ class TransportType(models.TextChoices): TGV = "TGV", _("TGV") TER = "TER", _("TER") INTERCITES = "IC", _("Intercités") TRANSILIEN = "TN", _("Transilien") class LocationType(models.IntegerChoices): STOP_PLATFORM = 0, _("Stop/platform") STATION = 1, _("Station") ENTRANCE_EXIT = 2, _("Entrance/exit") GENERIC_NODE = 3, _("Generic node") BOARDING_AREA = 4, _("Boarding area") class AccessInformation(models.IntegerChoices): NO_INFORMATION = 0, _("No information") POSSIBLE = 1, _("Possible") NOT_POSSIBLE = 2, _("Not possible") class PickupType(models.IntegerChoices): REGULAR = 0, _("Regular") NONE = 1, _("None") MUST_PHONE_AGENCY = 2, _("Must phone agency") MUST_COORDINATE_WITH_DRIVER = 3, _("Must coordinate with driver") class RouteType(models.IntegerChoices): TRAM = 0, _("Tram") METRO = 1, _("Metro") RAIL = 2, _("Rail") BUS = 3, _("Bus") FERRY = 4, _("Ferry") CABLE_CAR = 5, _("Cable car") GONDOLA = 6, _("Gondola") FUNICULAR = 7, _("Funicular") class Direction(models.IntegerChoices): OUTBOUND = 0, _("Outbound") INBOUND = 1, _("Inbound") class TransferType(models.IntegerChoices): RECOMMENDED = 0, _("Recommended") TIMED = 1, _("Timed") MINIMUM_TIME = 2, _("Minimum time") NOT_POSSIBLE = 3, _("Not possible") class ExceptionType(models.IntegerChoices): ADDED = 1, _("Added") REMOVED = 2, _("Removed") class Agency(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("Agency ID"), ) name = models.CharField( max_length=255, unique=True, verbose_name=_("Agency name"), ) url = models.URLField( verbose_name=_("Agency URL"), ) timezone = models.CharField( max_length=255, verbose_name=_("Agency timezone"), ) lang = models.CharField( max_length=255, verbose_name=_("Agency language"), blank=True, ) phone = models.CharField( max_length=255, verbose_name=_("Agency phone"), blank=True, ) email = models.EmailField( verbose_name=_("Agency email"), blank=True, ) def __str__(self): return self.name class Meta: verbose_name = _("Agency") verbose_name_plural = _("Agencies") class Stop(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("Stop ID"), ) code = models.CharField( max_length=255, verbose_name=_("Stop code"), blank=True, ) name = models.CharField( max_length=255, verbose_name=_("Stop name"), ) desc = models.CharField( max_length=255, verbose_name=_("Stop description"), blank=True, ) lon = models.FloatField( verbose_name=_("Stop longitude"), ) lat = models.FloatField( verbose_name=_("Stop latitude"), ) zone_id = models.CharField( max_length=255, verbose_name=_("Zone ID"), ) url = models.URLField( verbose_name=_("Stop URL"), blank=True, ) location_type = models.IntegerField( verbose_name=_("Location type"), blank=True, choices=LocationType, default=LocationType.STOP_PLATFORM, ) parent_station = models.ForeignKey( to="Stop", on_delete=models.PROTECT, verbose_name=_("Parent station"), related_name="children", blank=True, null=True, ) timezone = models.CharField( max_length=255, verbose_name=_("Stop timezone"), blank=True, ) level_id = models.CharField( max_length=255, verbose_name=_("Level ID"), blank=True, ) wheelchair_boarding = models.IntegerField( verbose_name=_("Wheelchair boarding"), blank=True, choices=AccessInformation, default=AccessInformation.NO_INFORMATION, ) platform_code = models.CharField( max_length=255, verbose_name=_("Platform code"), blank=True, ) def __str__(self): return f"{self.name} ({self.id})" class Meta: verbose_name = _("Stop") verbose_name_plural = _("Stops") class Route(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("ID"), ) agency = models.ForeignKey( to="Agency", on_delete=models.CASCADE, verbose_name=_("Agency ID"), related_name="routes", ) short_name = models.CharField( max_length=255, verbose_name=_("Route short name"), ) long_name = models.CharField( max_length=255, verbose_name=_("Route long name"), ) desc = models.CharField( max_length=255, verbose_name=_("Route description"), blank=True, ) type = models.IntegerField( verbose_name=_("Route type"), choices=RouteType, ) url = models.URLField( verbose_name=_("Route URL"), blank=True, ) color = models.CharField( max_length=255, verbose_name=_("Route color"), blank=True, ) text_color = models.CharField( max_length=255, verbose_name=_("Route text color"), blank=True, ) def __str__(self): return f"{self.long_name}" class Meta: verbose_name = _("Route") verbose_name_plural = _("Routes") class Trip(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("Trip ID"), ) route = models.ForeignKey( to="Route", on_delete=models.CASCADE, verbose_name=_("Route"), related_name="trips", ) service = models.ForeignKey( to="Calendar", on_delete=models.CASCADE, verbose_name=_("Service"), related_name="trips", ) headsign = models.CharField( max_length=255, verbose_name=_("Trip headsign"), blank=True, ) short_name = models.CharField( max_length=255, verbose_name=_("Trip short name"), blank=True, ) direction_id = models.IntegerField( verbose_name=_("Direction"), choices=Direction, null=True, ) block_id = models.CharField( max_length=255, verbose_name=_("Block ID"), blank=True, ) shape_id = models.CharField( max_length=255, verbose_name=_("Shape ID"), blank=True, ) wheelchair_accessible = models.IntegerField( verbose_name=_("Wheelchair accessible"), choices=AccessInformation, default=AccessInformation.NO_INFORMATION, null=True, ) bikes_allowed = models.IntegerField( verbose_name=_("Bikes allowed"), choices=AccessInformation, default=AccessInformation.NO_INFORMATION, null=True, ) @property def origin(self): return self.stop_times.order_by('stop_sequence').first().stop @property def destination(self): return self.stop_times.order_by('-stop_sequence').first().stop @property def train_type(self): if self.service.transport_type == TransportType.TRANSILIEN: return self.route.short_name else: train_type = self.origin.id.split('StopPoint:OCE')[1].split('-')[0] if train_type == "Train TER": train_type = "TER" elif train_type == "INTERCITES": train_type = "INTER-CITÉS" elif train_type == "INTERCITES de nuit": train_type = "INTER-CITÉS de nuit" return train_type @property def train_number(self): if self.service.transport_type == TransportType.TRANSILIEN: return self.short_name else: return self.headsign @property def color(self): if self.route.color: return self.route.color elif self.train_type == "OUIGO": return "E60075" return "FFFFFF" @property def text_color(self): if self.route.text_color: return self.route.text_color elif self.train_type == "OUIGO": return "FFFFFF" elif self.train_type == "TGV INOUI": return "9B2743" elif self.train_type == "INTER-CITÉS" or self.train_type == "INTER-CITÉS de nuit": return "404042" return "000000" def __str__(self): return f"{self.route.long_name} - {self.id}" class Meta: verbose_name = _("Trip") verbose_name_plural = _("Trips") class StopTime(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("ID"), ) trip = models.ForeignKey( to="Trip", on_delete=models.CASCADE, verbose_name=_("Trip"), related_name="stop_times", ) arrival_time = models.DurationField( verbose_name=_("Arrival time"), ) departure_time = models.DurationField( verbose_name=_("Departure time"), ) stop = models.ForeignKey( to="Stop", on_delete=models.CASCADE, verbose_name=_("Stop ID"), related_name="stop_times", ) stop_sequence = models.IntegerField( verbose_name=_("Stop sequence"), ) stop_headsign = models.CharField( max_length=255, verbose_name=_("Stop headsign"), blank=True, ) pickup_type = models.IntegerField( verbose_name=_("Pickup type"), choices=PickupType, default=PickupType.REGULAR, null=True, ) drop_off_type = models.IntegerField( verbose_name=_("Drop off type"), choices=PickupType, default=PickupType.REGULAR, null=True, ) timepoint = models.BooleanField( verbose_name=_("Timepoint"), default=True, null=True, ) @property def pretty_arrival_time(self): seconds = self.arrival_time.total_seconds() hours = int(seconds // 3600) % 24 minutes = int((seconds % 3600) // 60) return f"{hours:02}:{minutes:02}" @property def pretty_departure_time(self): seconds = self.departure_time.total_seconds() hours = int(seconds // 3600) % 24 minutes = int((seconds % 3600) // 60) return f"{hours:02}:{minutes:02}" def __str__(self): return f"{self.trip.route.long_name} - {self.trip_id} - {self.stop.name}" class Meta: verbose_name = _("Stop time") verbose_name_plural = _("Stop times") class Calendar(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("Service ID"), ) monday = models.BooleanField( verbose_name=_("Monday"), ) tuesday = models.BooleanField( verbose_name=_("Tuesday"), ) wednesday = models.BooleanField( verbose_name=_("Wednesday"), ) thursday = models.BooleanField( verbose_name=_("Thursday"), ) friday = models.BooleanField( verbose_name=_("Friday"), ) saturday = models.BooleanField( verbose_name=_("Saturday"), ) sunday = models.BooleanField( verbose_name=_("Sunday"), ) start_date = models.DateField( verbose_name=_("Start date"), ) end_date = models.DateField( verbose_name=_("End date"), ) transport_type = models.CharField( max_length=255, verbose_name=_("Transport type"), choices=TransportType, ) def __str__(self): return self.id class Meta: verbose_name = _("Calendar") verbose_name_plural = _("Calendars") class CalendarDate(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("ID"), ) service = models.ForeignKey( to="Calendar", on_delete=models.CASCADE, verbose_name=_("Service"), related_name="dates", ) date = models.DateField( verbose_name=_("Date"), ) exception_type = models.IntegerField( verbose_name=_("Exception type"), choices=ExceptionType, ) transport_type = models.CharField( max_length=255, verbose_name=_("Transport type"), choices=TransportType, ) def __str__(self): return f"{self.service.id} - {self.date} - {self.exception_type}" class Meta: verbose_name = _("Calendar date") verbose_name_plural = _("Calendar dates") class Transfer(models.Model): id = models.CharField( max_length=255, primary_key=True, verbose_name=_("ID"), ) from_stop = models.ForeignKey( to="Stop", on_delete=models.CASCADE, verbose_name=_("From stop"), related_name="transfers_from", ) to_stop = models.ForeignKey( to="Stop", on_delete=models.CASCADE, verbose_name=_("To stop"), related_name="transfers_to", ) transfer_type = models.IntegerField( verbose_name=_("Transfer type"), choices=TransferType, default=TransferType.RECOMMENDED, ) min_transfer_time = models.IntegerField( verbose_name=_("Minimum transfer time"), blank=True, ) class Meta: verbose_name = _("Transfer") verbose_name_plural = _("Transfers") class FeedInfo(models.Model): feed_publisher_name = models.CharField( max_length=255, verbose_name=_("Feed publisher name"), ) feed_publisher_url = models.URLField( verbose_name=_("Feed publisher URL"), ) feed_lang = models.CharField( max_length=255, verbose_name=_("Feed language"), ) feed_start_date = models.DateField( verbose_name=_("Feed start date"), ) feed_end_date = models.DateField( verbose_name=_("Feed end date"), ) feed_version = models.CharField( max_length=255, verbose_name=_("Feed version"), ) class Meta: verbose_name = _("Feed info") verbose_name_plural = _("Feed infos")