Transport Models
Transport Models (English fallback)
Aug. 17, 2025
Posted by admin
Nhom |
Notes |
|
from django.db import models from django.conf import settings from django.utils.translation import gettext_lazy as _
|
|
✅ Completed Tasks
🏗️ System Architecture Before: Individual transit entries with fixed datetime fields After: Template-based system where:
📊 Key Features
The system now supports the practical needs of transit operators with fixed schedules while maintaining the flexibility for dynamic trip management that the original user request identified as essential for real-world transit systems.
|
|
|
Aircraft |
class Aircraft(models.Model): model = models.CharField(max_length=50, help_text="e.g. Boeing 777-300ER") manufacturer = models.CharField(max_length=50, help_text="e.g. Boeing, Airbus") capacity = models.PositiveIntegerField(default=0, help_text="Total passenger capacity")
class Meta: verbose_name = _("Aircraft") verbose_name_plural = _("Aircraft") ordering = ['manufacturer', 'model']
def __str__(self): return f"{self.manufacturer} {self.model}"
|
Airport |
class Airport(models.Model): name_en = models.CharField(max_length=200, verbose_name="English Name") name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") iata_code = models.CharField(max_length=3, unique=True, help_text="3-letter IATA code (e.g. SGN)") icao_code = models.CharField(max_length=4, unique=True, help_text="4-letter ICAO code (e.g. VVTS)") city = models.ForeignKey(City, on_delete=models.CASCADE, related_name='airports') latitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) longitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) is_active = models.BooleanField(default=True)
class Meta: verbose_name = _("Airport") verbose_name_plural = _("Airports") ordering = ['iata_code']
def __str__(self): return f"{self.name_en} ({self.iata_code})"
|
Airport Flight |
class Flight(models.Model): STATUS_CHOICES = [ ('SCHEDULED', 'Scheduled'), ('DELAYED', 'Delayed'), ('CANCELLED', 'Cancelled'), ('BOARDING', 'Boarding'), ('DEPARTED', 'Departed'), ('ARRIVED', 'Arrived'), ]
flight_number = models.CharField(max_length=20, help_text="e.g. VN151") airline = models.ForeignKey(Airline, on_delete=models.CASCADE, related_name='flights', null=True) aircraft = models.ForeignKey(Aircraft, on_delete=models.SET_NULL, null=True, blank=True)
origin_airport = models.ForeignKey(Airport, on_delete=models.CASCADE, related_name='departing_flights', null=True) destination_airport = models.ForeignKey(Airport, on_delete=models.CASCADE, related_name='arriving_flights', null=True)
departure_time = models.DateTimeField() arrival_time = models.DateTimeField() duration_minutes = models.PositiveIntegerField(default=0, help_text="Flight duration in minutes")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='SCHEDULED') gate = models.CharField(max_length=10, blank=True, help_text="Departure gate") terminal = models.CharField(max_length=10, blank=True, help_text="Departure terminal")
# Pricing economy_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) business_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) first_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True) updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta: verbose_name = _("Flight") verbose_name_plural = _("Flights") ordering = ['departure_time'] unique_together = ['flight_number', 'departure_time']
def __str__(self): return f"{self.airline.iata_code}{self.flight_number}: {self.origin_airport.iata_code} → {self.destination_airport.iata_code}"
@property def route(self): return f"{self.origin_airport.city.name_en} → {self.destination_airport.city.name_en}"
|
Country |
class Country(models.Model): name_en = models.CharField(max_length=100, verbose_name=_("English Name")) name_vi = models.CharField(max_length=100, blank=True, verbose_name=_("Vietnamese Name")) country_code = models.CharField(max_length=2, unique=True, help_text=_("ISO 3166-1 alpha-2 code"))
class Meta: verbose_name = _("Country") verbose_name_plural = _("Countries") ordering = ['name_en']
def __str__(self): return self.name_en
|
Country airline |
class Airline(models.Model): name_en = models.CharField(max_length=100, verbose_name="English Name") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") iata_code = models.CharField(max_length=2, unique=True, help_text="2-letter IATA code (e.g. VN)") icao_code = models.CharField(max_length=3, unique=True, help_text="3-letter ICAO code (e.g. HVN)") country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='airlines') logo = models.ImageField(upload_to='airlines/', blank=True, null=True) is_active = models.BooleanField(default=True)
class Meta: verbose_name = _("Airline") verbose_name_plural = _("Airlines") ordering = ['name_en']
def __str__(self): return f"{self.name_en} ({self.iata_code})"
|
Country City |
class City(models.Model): name_en = models.CharField(max_length=100, verbose_name="English Name") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='cities') timezone = models.CharField(max_length=50, blank=True, help_text="e.g. Asia/Ho_Chi_Minh")
class Meta: verbose_name = _("City") verbose_name_plural = _("Cities") ordering = ['name_en'] unique_together = ['name_en', 'country']
def __str__(self): return f"{self.name_en}, {self.country.name_en}"
|
Country Transport Operator |
class TransportOperator(models.Model): name_en = models.CharField(max_length=100, verbose_name="English Name") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=10, choices=TransportStation.STATION_TYPES) country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='transport_operators') website = models.URLField(blank=True) phone = models.CharField(max_length=20, blank=True) is_active = models.BooleanField(default=True)
class Meta: ordering = ['name_en']
def __str__(self): return self.name_en
|
Station |
class TransportStation(models.Model): STATION_TYPES = [ ('BUS', 'Bus Station'), ('TRAIN', 'Train Station'), ('FERRY', 'Ferry Terminal'), ('METRO', 'Metro Station'), ]
name_en = models.CharField(max_length=200, verbose_name="English Name") name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=10, choices=STATION_TYPES) city = models.ForeignKey(City, on_delete=models.CASCADE, related_name='transport_stations') address_en = models.TextField(blank=True, verbose_name="English Address") address_vi = models.TextField(blank=True, verbose_name="Vietnamese Address") latitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) longitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) is_active = models.BooleanField(default=True)
class Meta: ordering = ['city', 'name_en']
def __str__(self): return f"{self.name_en} ({self.get_type_display()})"
|
Station Transit
|
class Transit(models.Model): """Individual transit trip instances (generated from TransitRoute schedules)"""
STATUS_CHOICES = [ ('SCHEDULED', 'Scheduled'), ('DELAYED', 'Delayed'), ('CANCELLED', 'Cancelled'), ('BOARDING', 'Boarding'), ('DEPARTED', 'Departed'), ('ARRIVED', 'Arrived'), ]
# Link to route template (optional - for generated trips) route = models.ForeignKey(TransitRoute, on_delete=models.CASCADE, null=True, blank=True, related_name='trips')
# Legacy fields (for backward compatibility or manual entries) route_name = models.CharField(max_length=100, blank=True, help_text="e.g. Route 109, Express Line") type = models.CharField(max_length=10, choices=TransitRoute.TRANSIT_TYPES) operator = models.ForeignKey(TransportOperator, on_delete=models.CASCADE, related_name='transits', null=True)
origin_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='departing_transits', null=True) destination_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='arriving_transits', null=True)
# Specific trip timing departure_time = models.DateTimeField() arrival_time = models.DateTimeField() duration_minutes = models.PositiveIntegerField(default=0, help_text="Transit duration in minutes")
# Trip status and real-time info status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='SCHEDULED') delay_minutes = models.IntegerField(default=0, help_text="Delay in minutes (negative for early)") actual_departure_time = models.DateTimeField(null=True, blank=True) actual_arrival_time = models.DateTimeField(null=True, blank=True)
# Vehicle/service info vehicle_number = models.CharField(max_length=20, blank=True) platform = models.CharField(max_length=10, blank=True) gate = models.CharField(max_length=10, blank=True)
# Dynamic pricing (can override route pricing) standard_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) premium_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
# Capacity and booking total_seats = models.PositiveIntegerField(null=True, blank=True) available_seats = models.PositiveIntegerField(null=True, blank=True) is_bookable = models.BooleanField(default=True)
# Additional info notes_en = models.TextField(blank=True, verbose_name="English Notes") notes_vi = models.TextField(blank=True, verbose_name="Vietnamese Notes")
created_at = models.DateTimeField(auto_now_add=True, null=True) updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta: verbose_name = _("Transit") verbose_name_plural = _("Transits") ordering = ['departure_time']
def __str__(self): if self.route: return f"{self.route.route_name}: {self.departure_time.strftime('%H:%M')} - {self.get_status_display()}" else: return f"{self.get_type_display()}: {self.origin_station.city.name_en} → {self.destination_station.city.name_en}"
@property def route_display(self): if self.route: return self.route.route_name return self.route_name or f"{self.origin_station.name_en} → {self.destination_station.name_en}"
@property def effective_departure_time(self): """Return actual departure time if available, otherwise scheduled time""" return self.actual_departure_time or self.departure_time
@property def effective_arrival_time(self): """Return actual arrival time if available, otherwise scheduled time""" return self.actual_arrival_time or self.arrival_time
@property def is_delayed(self): """Check if transit is delayed""" return self.delay_minutes > 0
@property def is_cancelled(self): """Check if transit is cancelled""" return self.status == 'CANCELLED'
def get_price(self, ticket_type='standard'): """Get price for ticket type, fallback to route pricing if not set""" if ticket_type == 'premium': return self.premium_price or (self.route.premium_price if self.route else None) else: return self.standard_price or (self.route.standard_price if self.route else None)
|
Station Transit R
|
class TransitRoute(models.Model): """Base transit route information with fixed schedules""" TRANSIT_TYPES = ( ('BUS', 'Bus'), ('TRAIN', 'Train'), ('FERRY', 'Ferry'), ('METRO', 'Metro'), ('CAR', 'Car Rental'), ('TAXI', 'Taxi'), ('OTHER', 'Other'), )
SCHEDULE_TYPES = [ ('FIXED', 'Fixed Schedule'), # Specific times (e.g., 08:00, 10:30, 14:45) ('FREQUENT', 'Frequent Service'), # Every X minutes (e.g., every 15 minutes) ('DAILY', 'Daily Service'), # Same time daily ('WEEKLY', 'Weekly Service'), # Specific days of week ('ON_DEMAND', 'On Demand'), # No fixed schedule ]
FREQUENCY_UNITS = [ ('MINUTES', 'Minutes'), ('HOURS', 'Hours'), ('DAYS', 'Days'), ]
DAYS_OF_WEEK = [ ('MON', 'Monday'), ('TUE', 'Tuesday'), ('WED', 'Wednesday'), ('THU', 'Thursday'), ('FRI', 'Friday'), ('SAT', 'Saturday'), ('SUN', 'Sunday'), ]
route_name = models.CharField(max_length=100, help_text="e.g. Route 109, Express Line") type = models.CharField(max_length=10, choices=TRANSIT_TYPES) operator = models.ForeignKey(TransportOperator, on_delete=models.CASCADE, related_name='routes')
origin_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='departing_routes') destination_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='arriving_routes')
# Schedule configuration schedule_type = models.CharField(max_length=20, choices=SCHEDULE_TYPES, default='FIXED')
# For FREQUENT service (every X minutes/hours) frequency_interval = models.PositiveIntegerField(null=True, blank=True, help_text="e.g., 15 for every 15 minutes") frequency_unit = models.CharField(max_length=10, choices=FREQUENCY_UNITS, blank=True)
# Service hours (for FREQUENT service) service_start_time = models.TimeField(null=True, blank=True, help_text="First departure time") service_end_time = models.TimeField(null=True, blank=True, help_text="Last departure time")
# Operating days operates_monday = models.BooleanField(default=True) operates_tuesday = models.BooleanField(default=True) operates_wednesday = models.BooleanField(default=True) operates_thursday = models.BooleanField(default=True) operates_friday = models.BooleanField(default=True) operates_saturday = models.BooleanField(default=True) operates_sunday = models.BooleanField(default=False)
# Fixed schedule times (JSON format for multiple departure times) fixed_departure_times = models.JSONField(default=list, blank=True, help_text="List of departure times in HH:MM format, e.g. ['08:00', '10:30', '14:45']")
# Basic route info duration_minutes = models.PositiveIntegerField(help_text="Typical journey duration in minutes") distance_km = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
# Pricing standard_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) premium_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) child_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) student_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
# Route details stops_en = models.JSONField(default=list, blank=True, help_text="List of intermediate stops (English)") stops_vi = models.JSONField(default=list, blank=True, help_text="List of intermediate stops (Vietnamese)") notes_en = models.TextField(blank=True, verbose_name="English Notes") notes_vi = models.TextField(blank=True, verbose_name="Vietnamese Notes")
# Status is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: verbose_name = "Transit Route" verbose_name_plural = "Transit Routes" ordering = ['type', 'route_name'] unique_together = ['route_name', 'origin_station', 'destination_station']
def __str__(self): return f"{self.route_name}: {self.origin_station.name_en} → {self.destination_station.name_en}"
def get_operating_days(self): """Return list of operating days""" days = [] if self.operates_monday: days.append('Monday') if self.operates_tuesday: days.append('Tuesday') if self.operates_wednesday: days.append('Wednesday') if self.operates_thursday: days.append('Thursday') if self.operates_friday: days.append('Friday') if self.operates_saturday: days.append('Saturday') if self.operates_sunday: days.append('Sunday') return days
def get_departures_for_date(self, date): """Generate departure times for a specific date""" from datetime import datetime, timedelta import json
# Check if route operates on this day weekday = date.weekday() # 0 = Monday, 6 = Sunday operating_days = [ self.operates_monday, self.operates_tuesday, self.operates_wednesday, self.operates_thursday, self.operates_friday, self.operates_saturday, self.operates_sunday ]
if not operating_days[weekday]: return []
departures = []
if self.schedule_type == 'FIXED' and self.fixed_departure_times: # Fixed departure times for time_str in self.fixed_departure_times: try: hour, minute = map(int, time_str.split(':')) departure_datetime = datetime.combine(date, datetime.min.time().replace(hour=hour, minute=minute)) arrival_datetime = departure_datetime + timedelta(minutes=self.duration_minutes) departures.append({ 'departure_time': departure_datetime, 'arrival_time': arrival_datetime }) except ValueError: continue
elif self.schedule_type == 'FREQUENT' and self.service_start_time and self.service_end_time: # Frequent service (every X minutes) current_time = datetime.combine(date, self.service_start_time) end_time = datetime.combine(date, self.service_end_time)
interval_minutes = self.frequency_interval or 30 if self.frequency_unit == 'HOURS': interval_minutes *= 60 elif self.frequency_unit == 'DAYS': interval_minutes *= 1440
while current_time <= end_time: arrival_time = current_time + timedelta(minutes=self.duration_minutes) departures.append({ 'departure_time': current_time, 'arrival_time': arrival_time }) current_time += timedelta(minutes=interval_minutes)
elif self.schedule_type == 'DAILY' and self.service_start_time: # Daily service at fixed time departure_datetime = datetime.combine(date, self.service_start_time) arrival_datetime = departure_datetime + timedelta(minutes=self.duration_minutes) departures.append({ 'departure_time': departure_datetime, 'arrival_time': arrival_datetime })
return departures
|
Nhom |
Notes |
|
from django.db import models from django.conf import settings from django.utils.translation import gettext_lazy as _
|
|
✅ Completed Tasks
🏗️ System Architecture Before: Individual transit entries with fixed datetime fields After: Template-based system where:
📊 Key Features
The system now supports the practical needs of transit operators with fixed schedules while maintaining the flexibility for dynamic trip management that the original user request identified as essential for real-world transit systems.
|
|
|
Aircraft |
class Aircraft(models.Model): model = models.CharField(max_length=50, help_text="e.g. Boeing 777-300ER") manufacturer = models.CharField(max_length=50, help_text="e.g. Boeing, Airbus") capacity = models.PositiveIntegerField(default=0, help_text="Total passenger capacity")
class Meta: verbose_name = _("Aircraft") verbose_name_plural = _("Aircraft") ordering = ['manufacturer', 'model']
def __str__(self): return f"{self.manufacturer} {self.model}"
|
Airport |
class Airport(models.Model): name_en = models.CharField(max_length=200, verbose_name="English Name") name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") iata_code = models.CharField(max_length=3, unique=True, help_text="3-letter IATA code (e.g. SGN)") icao_code = models.CharField(max_length=4, unique=True, help_text="4-letter ICAO code (e.g. VVTS)") city = models.ForeignKey(City, on_delete=models.CASCADE, related_name='airports') latitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) longitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) is_active = models.BooleanField(default=True)
class Meta: verbose_name = _("Airport") verbose_name_plural = _("Airports") ordering = ['iata_code']
def __str__(self): return f"{self.name_en} ({self.iata_code})"
|
Airport Flight |
class Flight(models.Model): STATUS_CHOICES = [ ('SCHEDULED', 'Scheduled'), ('DELAYED', 'Delayed'), ('CANCELLED', 'Cancelled'), ('BOARDING', 'Boarding'), ('DEPARTED', 'Departed'), ('ARRIVED', 'Arrived'), ]
flight_number = models.CharField(max_length=20, help_text="e.g. VN151") airline = models.ForeignKey(Airline, on_delete=models.CASCADE, related_name='flights', null=True) aircraft = models.ForeignKey(Aircraft, on_delete=models.SET_NULL, null=True, blank=True)
origin_airport = models.ForeignKey(Airport, on_delete=models.CASCADE, related_name='departing_flights', null=True) destination_airport = models.ForeignKey(Airport, on_delete=models.CASCADE, related_name='arriving_flights', null=True)
departure_time = models.DateTimeField() arrival_time = models.DateTimeField() duration_minutes = models.PositiveIntegerField(default=0, help_text="Flight duration in minutes")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='SCHEDULED') gate = models.CharField(max_length=10, blank=True, help_text="Departure gate") terminal = models.CharField(max_length=10, blank=True, help_text="Departure terminal")
# Pricing economy_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) business_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) first_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True) updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta: verbose_name = _("Flight") verbose_name_plural = _("Flights") ordering = ['departure_time'] unique_together = ['flight_number', 'departure_time']
def __str__(self): return f"{self.airline.iata_code}{self.flight_number}: {self.origin_airport.iata_code} → {self.destination_airport.iata_code}"
@property def route(self): return f"{self.origin_airport.city.name_en} → {self.destination_airport.city.name_en}"
|
Country |
class Country(models.Model): name_en = models.CharField(max_length=100, verbose_name=_("English Name")) name_vi = models.CharField(max_length=100, blank=True, verbose_name=_("Vietnamese Name")) country_code = models.CharField(max_length=2, unique=True, help_text=_("ISO 3166-1 alpha-2 code"))
class Meta: verbose_name = _("Country") verbose_name_plural = _("Countries") ordering = ['name_en']
def __str__(self): return self.name_en
|
Country airline |
class Airline(models.Model): name_en = models.CharField(max_length=100, verbose_name="English Name") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") iata_code = models.CharField(max_length=2, unique=True, help_text="2-letter IATA code (e.g. VN)") icao_code = models.CharField(max_length=3, unique=True, help_text="3-letter ICAO code (e.g. HVN)") country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='airlines') logo = models.ImageField(upload_to='airlines/', blank=True, null=True) is_active = models.BooleanField(default=True)
class Meta: verbose_name = _("Airline") verbose_name_plural = _("Airlines") ordering = ['name_en']
def __str__(self): return f"{self.name_en} ({self.iata_code})"
|
Country City |
class City(models.Model): name_en = models.CharField(max_length=100, verbose_name="English Name") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='cities') timezone = models.CharField(max_length=50, blank=True, help_text="e.g. Asia/Ho_Chi_Minh")
class Meta: verbose_name = _("City") verbose_name_plural = _("Cities") ordering = ['name_en'] unique_together = ['name_en', 'country']
def __str__(self): return f"{self.name_en}, {self.country.name_en}"
|
Country Transport Operator |
class TransportOperator(models.Model): name_en = models.CharField(max_length=100, verbose_name="English Name") name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=10, choices=TransportStation.STATION_TYPES) country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='transport_operators') website = models.URLField(blank=True) phone = models.CharField(max_length=20, blank=True) is_active = models.BooleanField(default=True)
class Meta: ordering = ['name_en']
def __str__(self): return self.name_en
|
Station |
class TransportStation(models.Model): STATION_TYPES = [ ('BUS', 'Bus Station'), ('TRAIN', 'Train Station'), ('FERRY', 'Ferry Terminal'), ('METRO', 'Metro Station'), ]
name_en = models.CharField(max_length=200, verbose_name="English Name") name_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Name") type = models.CharField(max_length=10, choices=STATION_TYPES) city = models.ForeignKey(City, on_delete=models.CASCADE, related_name='transport_stations') address_en = models.TextField(blank=True, verbose_name="English Address") address_vi = models.TextField(blank=True, verbose_name="Vietnamese Address") latitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) longitude = models.DecimalField(max_digits=10, decimal_places=7, null=True, blank=True) is_active = models.BooleanField(default=True)
class Meta: ordering = ['city', 'name_en']
def __str__(self): return f"{self.name_en} ({self.get_type_display()})"
|
Station Transit
|
class Transit(models.Model): """Individual transit trip instances (generated from TransitRoute schedules)"""
STATUS_CHOICES = [ ('SCHEDULED', 'Scheduled'), ('DELAYED', 'Delayed'), ('CANCELLED', 'Cancelled'), ('BOARDING', 'Boarding'), ('DEPARTED', 'Departed'), ('ARRIVED', 'Arrived'), ]
# Link to route template (optional - for generated trips) route = models.ForeignKey(TransitRoute, on_delete=models.CASCADE, null=True, blank=True, related_name='trips')
# Legacy fields (for backward compatibility or manual entries) route_name = models.CharField(max_length=100, blank=True, help_text="e.g. Route 109, Express Line") type = models.CharField(max_length=10, choices=TransitRoute.TRANSIT_TYPES) operator = models.ForeignKey(TransportOperator, on_delete=models.CASCADE, related_name='transits', null=True)
origin_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='departing_transits', null=True) destination_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='arriving_transits', null=True)
# Specific trip timing departure_time = models.DateTimeField() arrival_time = models.DateTimeField() duration_minutes = models.PositiveIntegerField(default=0, help_text="Transit duration in minutes")
# Trip status and real-time info status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='SCHEDULED') delay_minutes = models.IntegerField(default=0, help_text="Delay in minutes (negative for early)") actual_departure_time = models.DateTimeField(null=True, blank=True) actual_arrival_time = models.DateTimeField(null=True, blank=True)
# Vehicle/service info vehicle_number = models.CharField(max_length=20, blank=True) platform = models.CharField(max_length=10, blank=True) gate = models.CharField(max_length=10, blank=True)
# Dynamic pricing (can override route pricing) standard_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) premium_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
# Capacity and booking total_seats = models.PositiveIntegerField(null=True, blank=True) available_seats = models.PositiveIntegerField(null=True, blank=True) is_bookable = models.BooleanField(default=True)
# Additional info notes_en = models.TextField(blank=True, verbose_name="English Notes") notes_vi = models.TextField(blank=True, verbose_name="Vietnamese Notes")
created_at = models.DateTimeField(auto_now_add=True, null=True) updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta: verbose_name = _("Transit") verbose_name_plural = _("Transits") ordering = ['departure_time']
def __str__(self): if self.route: return f"{self.route.route_name}: {self.departure_time.strftime('%H:%M')} - {self.get_status_display()}" else: return f"{self.get_type_display()}: {self.origin_station.city.name_en} → {self.destination_station.city.name_en}"
@property def route_display(self): if self.route: return self.route.route_name return self.route_name or f"{self.origin_station.name_en} → {self.destination_station.name_en}"
@property def effective_departure_time(self): """Return actual departure time if available, otherwise scheduled time""" return self.actual_departure_time or self.departure_time
@property def effective_arrival_time(self): """Return actual arrival time if available, otherwise scheduled time""" return self.actual_arrival_time or self.arrival_time
@property def is_delayed(self): """Check if transit is delayed""" return self.delay_minutes > 0
@property def is_cancelled(self): """Check if transit is cancelled""" return self.status == 'CANCELLED'
def get_price(self, ticket_type='standard'): """Get price for ticket type, fallback to route pricing if not set""" if ticket_type == 'premium': return self.premium_price or (self.route.premium_price if self.route else None) else: return self.standard_price or (self.route.standard_price if self.route else None)
|
Station Transit R
|
class TransitRoute(models.Model): """Base transit route information with fixed schedules""" TRANSIT_TYPES = ( ('BUS', 'Bus'), ('TRAIN', 'Train'), ('FERRY', 'Ferry'), ('METRO', 'Metro'), ('CAR', 'Car Rental'), ('TAXI', 'Taxi'), ('OTHER', 'Other'), )
SCHEDULE_TYPES = [ ('FIXED', 'Fixed Schedule'), # Specific times (e.g., 08:00, 10:30, 14:45) ('FREQUENT', 'Frequent Service'), # Every X minutes (e.g., every 15 minutes) ('DAILY', 'Daily Service'), # Same time daily ('WEEKLY', 'Weekly Service'), # Specific days of week ('ON_DEMAND', 'On Demand'), # No fixed schedule ]
FREQUENCY_UNITS = [ ('MINUTES', 'Minutes'), ('HOURS', 'Hours'), ('DAYS', 'Days'), ]
DAYS_OF_WEEK = [ ('MON', 'Monday'), ('TUE', 'Tuesday'), ('WED', 'Wednesday'), ('THU', 'Thursday'), ('FRI', 'Friday'), ('SAT', 'Saturday'), ('SUN', 'Sunday'), ]
route_name = models.CharField(max_length=100, help_text="e.g. Route 109, Express Line") type = models.CharField(max_length=10, choices=TRANSIT_TYPES) operator = models.ForeignKey(TransportOperator, on_delete=models.CASCADE, related_name='routes')
origin_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='departing_routes') destination_station = models.ForeignKey(TransportStation, on_delete=models.CASCADE, related_name='arriving_routes')
# Schedule configuration schedule_type = models.CharField(max_length=20, choices=SCHEDULE_TYPES, default='FIXED')
# For FREQUENT service (every X minutes/hours) frequency_interval = models.PositiveIntegerField(null=True, blank=True, help_text="e.g., 15 for every 15 minutes") frequency_unit = models.CharField(max_length=10, choices=FREQUENCY_UNITS, blank=True)
# Service hours (for FREQUENT service) service_start_time = models.TimeField(null=True, blank=True, help_text="First departure time") service_end_time = models.TimeField(null=True, blank=True, help_text="Last departure time")
# Operating days operates_monday = models.BooleanField(default=True) operates_tuesday = models.BooleanField(default=True) operates_wednesday = models.BooleanField(default=True) operates_thursday = models.BooleanField(default=True) operates_friday = models.BooleanField(default=True) operates_saturday = models.BooleanField(default=True) operates_sunday = models.BooleanField(default=False)
# Fixed schedule times (JSON format for multiple departure times) fixed_departure_times = models.JSONField(default=list, blank=True, help_text="List of departure times in HH:MM format, e.g. ['08:00', '10:30', '14:45']")
# Basic route info duration_minutes = models.PositiveIntegerField(help_text="Typical journey duration in minutes") distance_km = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
# Pricing standard_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) premium_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) child_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) student_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
# Route details stops_en = models.JSONField(default=list, blank=True, help_text="List of intermediate stops (English)") stops_vi = models.JSONField(default=list, blank=True, help_text="List of intermediate stops (Vietnamese)") notes_en = models.TextField(blank=True, verbose_name="English Notes") notes_vi = models.TextField(blank=True, verbose_name="Vietnamese Notes")
# Status is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: verbose_name = "Transit Route" verbose_name_plural = "Transit Routes" ordering = ['type', 'route_name'] unique_together = ['route_name', 'origin_station', 'destination_station']
def __str__(self): return f"{self.route_name}: {self.origin_station.name_en} → {self.destination_station.name_en}"
def get_operating_days(self): """Return list of operating days""" days = [] if self.operates_monday: days.append('Monday') if self.operates_tuesday: days.append('Tuesday') if self.operates_wednesday: days.append('Wednesday') if self.operates_thursday: days.append('Thursday') if self.operates_friday: days.append('Friday') if self.operates_saturday: days.append('Saturday') if self.operates_sunday: days.append('Sunday') return days
def get_departures_for_date(self, date): """Generate departure times for a specific date""" from datetime import datetime, timedelta import json
# Check if route operates on this day weekday = date.weekday() # 0 = Monday, 6 = Sunday operating_days = [ self.operates_monday, self.operates_tuesday, self.operates_wednesday, self.operates_thursday, self.operates_friday, self.operates_saturday, self.operates_sunday ]
if not operating_days[weekday]: return []
departures = []
if self.schedule_type == 'FIXED' and self.fixed_departure_times: # Fixed departure times for time_str in self.fixed_departure_times: try: hour, minute = map(int, time_str.split(':')) departure_datetime = datetime.combine(date, datetime.min.time().replace(hour=hour, minute=minute)) arrival_datetime = departure_datetime + timedelta(minutes=self.duration_minutes) departures.append({ 'departure_time': departure_datetime, 'arrival_time': arrival_datetime }) except ValueError: continue
elif self.schedule_type == 'FREQUENT' and self.service_start_time and self.service_end_time: # Frequent service (every X minutes) current_time = datetime.combine(date, self.service_start_time) end_time = datetime.combine(date, self.service_end_time)
interval_minutes = self.frequency_interval or 30 if self.frequency_unit == 'HOURS': interval_minutes *= 60 elif self.frequency_unit == 'DAYS': interval_minutes *= 1440
while current_time <= end_time: arrival_time = current_time + timedelta(minutes=self.duration_minutes) departures.append({ 'departure_time': current_time, 'arrival_time': arrival_time }) current_time += timedelta(minutes=interval_minutes)
elif self.schedule_type == 'DAILY' and self.service_start_time: # Daily service at fixed time departure_datetime = datetime.combine(date, self.service_start_time) arrival_datetime = departure_datetime + timedelta(minutes=self.duration_minutes) departures.append({ 'departure_time': departure_datetime, 'arrival_time': arrival_datetime })
return departures
|
Attached Files
You are viewing this article in public mode. Some features may be limited.