From 44c45a2bfbfc4a98a53e00881686ad11cb32e7e3 Mon Sep 17 00:00:00 2001 From: dimitri-hoareau Date: Thu, 8 Jan 2026 19:43:46 +0100 Subject: [PATCH 1/2] create BustStop model and admin interface --- padam_django/apps/geography/admin.py | 4 ++++ .../apps/geography/migrations/0002_busstop.py | 22 +++++++++++++++++++ padam_django/apps/geography/models.py | 8 +++++++ 3 files changed, 34 insertions(+) create mode 100644 padam_django/apps/geography/migrations/0002_busstop.py diff --git a/padam_django/apps/geography/admin.py b/padam_django/apps/geography/admin.py index e0334458..22cacab6 100644 --- a/padam_django/apps/geography/admin.py +++ b/padam_django/apps/geography/admin.py @@ -6,3 +6,7 @@ @admin.register(models.Place) class PlaceAdmin(admin.ModelAdmin): pass + +@admin.register(models.BusStop) +class BusStopAdmin(admin.ModelAdmin): + pass \ No newline at end of file diff --git a/padam_django/apps/geography/migrations/0002_busstop.py b/padam_django/apps/geography/migrations/0002_busstop.py new file mode 100644 index 00000000..8e0ef1ba --- /dev/null +++ b/padam_django/apps/geography/migrations/0002_busstop.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.16 on 2026-01-08 18:33 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('geography', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='BusStop', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, verbose_name='Name of the bus stop')), + ('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='geography.place')), + ], + ), + ] diff --git a/padam_django/apps/geography/models.py b/padam_django/apps/geography/models.py index e566ee2b..d803c381 100644 --- a/padam_django/apps/geography/models.py +++ b/padam_django/apps/geography/models.py @@ -13,3 +13,11 @@ class Meta: def __str__(self): return f"Place: {self.name} (id: {self.pk})" + + +class BusStop(models.Model): + name = models.CharField("Name of the bus stop", max_length=50) + place = models.ForeignKey(Place, on_delete=models.CASCADE) + + def __str__(self): + return f"Bus stop: {self.name} (id: {self.pk})" \ No newline at end of file From b0a1721e4a3828a5aac362e6b77a5b9f042912f4 Mon Sep 17 00:00:00 2001 From: dimitri-hoareau Date: Thu, 8 Jan 2026 20:22:43 +0100 Subject: [PATCH 2/2] create BusShift model and admin interface --- padam_django/apps/geography/admin.py | 13 ++++- ...03_busshift_busshiftstop_busshift_stops.py | 42 ++++++++++++++++ padam_django/apps/geography/models.py | 48 ++++++++++++++++++- 3 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 padam_django/apps/geography/migrations/0003_busshift_busshiftstop_busshift_stops.py diff --git a/padam_django/apps/geography/admin.py b/padam_django/apps/geography/admin.py index 22cacab6..83e618e7 100644 --- a/padam_django/apps/geography/admin.py +++ b/padam_django/apps/geography/admin.py @@ -9,4 +9,15 @@ class PlaceAdmin(admin.ModelAdmin): @admin.register(models.BusStop) class BusStopAdmin(admin.ModelAdmin): - pass \ No newline at end of file + pass + + +class BusShiftStopInline(admin.TabularInline): + model = models.BusShiftStop + fields = ['bus_stop', 'order', 'scheduled_time'] + + +@admin.register(models.BusShift) +class BusShiftAdmin(admin.ModelAdmin): + list_display = ['id', 'bus', 'driver', 'departure_time', 'arrival_time', 'duration'] + inlines = [BusShiftStopInline] \ No newline at end of file diff --git a/padam_django/apps/geography/migrations/0003_busshift_busshiftstop_busshift_stops.py b/padam_django/apps/geography/migrations/0003_busshift_busshiftstop_busshift_stops.py new file mode 100644 index 00000000..6b449cb5 --- /dev/null +++ b/padam_django/apps/geography/migrations/0003_busshift_busshiftstop_busshift_stops.py @@ -0,0 +1,42 @@ +# Generated by Django 4.2.16 on 2026-01-08 19:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0002_auto_20211109_1456'), + ('geography', '0002_busstop'), + ] + + operations = [ + migrations.CreateModel( + name='BusShift', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('bus', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.bus')), + ('driver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.driver')), + ], + ), + migrations.CreateModel( + name='BusShiftStop', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveIntegerField(verbose_name='Stop order in the shift')), + ('scheduled_time', models.DateTimeField(verbose_name='Scheduled time at this stop')), + ('bus_shift', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='geography.busshift')), + ('bus_stop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='geography.busstop')), + ], + options={ + 'ordering': ['order'], + 'unique_together': {('bus_shift', 'order'), ('bus_shift', 'bus_stop')}, + }, + ), + migrations.AddField( + model_name='busshift', + name='stops', + field=models.ManyToManyField(related_name='shifts', through='geography.BusShiftStop', to='geography.busstop'), + ), + ] diff --git a/padam_django/apps/geography/models.py b/padam_django/apps/geography/models.py index d803c381..9363f83d 100644 --- a/padam_django/apps/geography/models.py +++ b/padam_django/apps/geography/models.py @@ -18,6 +18,50 @@ def __str__(self): class BusStop(models.Model): name = models.CharField("Name of the bus stop", max_length=50) place = models.ForeignKey(Place, on_delete=models.CASCADE) - + + def __str__(self): + return f"Bus stop: {self.name} (id: {self.pk})" + + +class BusShift(models.Model): + """Represents a bus shift (trajet de bus)""" + bus = models.ForeignKey('fleet.Bus', on_delete=models.CASCADE, related_name='shifts') + driver = models.ForeignKey('fleet.Driver', on_delete=models.CASCADE, related_name='shifts') + stops = models.ManyToManyField(BusStop, through='BusShiftStop', related_name='shifts') + + def __str__(self): + return f"Shift {self.pk}: Bus {self.bus.licence_plate} - Driver {self.driver.user.username}" + + @property + def departure_time(self): + """Returns the time of the first stop""" + first_stop = self.busshiftstop_set.first() + return first_stop.scheduled_time if first_stop else None + + @property + def arrival_time(self): + """Returns the time of the last stop""" + last_stop = self.busshiftstop_set.last() + return last_stop.scheduled_time if last_stop else None + + @property + def duration(self): + """Returns the total duration of the shift""" + if self.departure_time and self.arrival_time: + return self.arrival_time - self.departure_time + return None + + +class BusShiftStop(models.Model): + """Represents a stop during a bus shift with order and time""" + bus_shift = models.ForeignKey(BusShift, on_delete=models.CASCADE) + bus_stop = models.ForeignKey(BusStop, on_delete=models.CASCADE) + order = models.PositiveIntegerField("Stop order in the shift") + scheduled_time = models.DateTimeField("Scheduled time at this stop") + + class Meta: + ordering = ['order'] + unique_together = (('bus_shift', 'order'), ('bus_shift', 'bus_stop')) + def __str__(self): - return f"Bus stop: {self.name} (id: {self.pk})" \ No newline at end of file + return f"Shift {self.bus_shift.pk} - Stop {self.order}: {self.bus_stop.name} at {self.scheduled_time}" \ No newline at end of file