From 5c0b361e197df83ef574f2379068ebd1973a3b7e Mon Sep 17 00:00:00 2001 From: abarreaux Date: Sat, 10 Jun 2023 14:59:11 +0200 Subject: [PATCH 01/10] Setting venv up and bringing migrate command from branch --- .gitignore | 7 +++++++ Makefile | 3 +++ 2 files changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index d7d26693..016292d1 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,10 @@ ENV/ # Editors stuff .idea .vscode + +# Python venv +/lib +lib64 +pyvenv.cfg +/bin +/share \ No newline at end of file diff --git a/Makefile b/Makefile index 4062f4c4..b921d523 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,6 @@ run: ## Run the test server. install: ## Install the python requirements. pip install -r requirements.txt + +migrate: ## Make migrations + python manage.py migrate \ No newline at end of file From 36145f200d0c10a606b184b0b9d7879315cb5101 Mon Sep 17 00:00:00 2001 From: abarreaux Date: Sat, 10 Jun 2023 15:02:43 +0200 Subject: [PATCH 02/10] Adding formatter and linter --- requirements.txt | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 89c9f0c8..0f1b9c94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,36 @@ +asgiref==3.7.2 +backcall==0.2.0 +black==23.3.0 +click==8.1.3 +decorator==5.1.1 Django==3.2.5 - django-extensions==3.1.3 -Werkzeug==2.0.1 -ipython==7.25.0 - factory-boy==3.2.0 Faker==8.10.1 +flake8==6.0.0 +ipython==7.25.0 +jedi==0.18.2 +matplotlib-inline==0.1.6 +mccabe==0.7.0 +mypy-extensions==1.0.0 +packaging==23.1 +parso==0.8.3 +pathspec==0.11.1 +pexpect==4.8.0 +pickleshare==0.7.5 +platformdirs==3.5.3 +prompt-toolkit==3.0.38 +ptyprocess==0.7.0 +pycodestyle==2.10.0 +pyflakes==3.0.1 +Pygments==2.15.1 +python-dateutil==2.8.2 +pytz==2023.3 +six==1.16.0 +sqlparse==0.4.4 +text-unidecode==1.3 +tomli==2.0.1 +traitlets==5.9.0 +typing_extensions==4.6.3 +wcwidth==0.2.6 +Werkzeug==2.0.1 From 66d03970e8cd256bb0f198d3948bcb259e863fa2 Mon Sep 17 00:00:00 2001 From: abarreaux Date: Sat, 10 Jun 2023 15:11:58 +0200 Subject: [PATCH 03/10] Updating gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 016292d1..43d2d9c5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ ENV/ lib64 pyvenv.cfg /bin -/share \ No newline at end of file +/share +notes_perso.txt \ No newline at end of file From c56e69284891938fd8aeedda08a92994efb30e83 Mon Sep 17 00:00:00 2001 From: alexisbarreaux <67143881+alexisbarreaux@users.noreply.github.com> Date: Sat, 10 Jun 2023 15:31:58 +0200 Subject: [PATCH 04/10] Initial setup (#3) * Setting venv up and bringing migrate command from branch * Adding formatter and linter * Updating gitignore --- .gitignore | 8 ++++++++ Makefile | 3 +++ requirements.txt | 36 ++++++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d7d26693..43d2d9c5 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,11 @@ ENV/ # Editors stuff .idea .vscode + +# Python venv +/lib +lib64 +pyvenv.cfg +/bin +/share +notes_perso.txt \ No newline at end of file diff --git a/Makefile b/Makefile index 4062f4c4..b921d523 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,6 @@ run: ## Run the test server. install: ## Install the python requirements. pip install -r requirements.txt + +migrate: ## Make migrations + python manage.py migrate \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 89c9f0c8..0f1b9c94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,36 @@ +asgiref==3.7.2 +backcall==0.2.0 +black==23.3.0 +click==8.1.3 +decorator==5.1.1 Django==3.2.5 - django-extensions==3.1.3 -Werkzeug==2.0.1 -ipython==7.25.0 - factory-boy==3.2.0 Faker==8.10.1 +flake8==6.0.0 +ipython==7.25.0 +jedi==0.18.2 +matplotlib-inline==0.1.6 +mccabe==0.7.0 +mypy-extensions==1.0.0 +packaging==23.1 +parso==0.8.3 +pathspec==0.11.1 +pexpect==4.8.0 +pickleshare==0.7.5 +platformdirs==3.5.3 +prompt-toolkit==3.0.38 +ptyprocess==0.7.0 +pycodestyle==2.10.0 +pyflakes==3.0.1 +Pygments==2.15.1 +python-dateutil==2.8.2 +pytz==2023.3 +six==1.16.0 +sqlparse==0.4.4 +text-unidecode==1.3 +tomli==2.0.1 +traitlets==5.9.0 +typing_extensions==4.6.3 +wcwidth==0.2.6 +Werkzeug==2.0.1 From 7f0a746e0c7d38376efb317961cc1c99432c104a Mon Sep 17 00:00:00 2001 From: alexisbarreaux <67143881+alexisbarreaux@users.noreply.github.com> Date: Sat, 10 Jun 2023 16:26:37 +0200 Subject: [PATCH 05/10] 2 implement busstop (#4) * Splitting fleet models * Creating base BusStop class * Adding factory for bus stops * Modifying bus stop display style --- Makefile | 5 +++- .../common/management/commands/create_data.py | 12 +++++----- padam_django/apps/fleet/admin.py | 5 ++++ padam_django/apps/fleet/factories.py | 13 +++++++++-- .../management/commands/create_busstops.py | 12 ++++++++++ .../apps/fleet/migrations/0003_busstop.py | 23 +++++++++++++++++++ padam_django/apps/fleet/models/__init__.py | 3 +++ .../fleet/{models.py => models/bus_model.py} | 7 ------ .../apps/fleet/models/bus_stop_model.py | 11 +++++++++ .../apps/fleet/models/driver_model.py | 10 ++++++++ 10 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 padam_django/apps/fleet/management/commands/create_busstops.py create mode 100644 padam_django/apps/fleet/migrations/0003_busstop.py create mode 100644 padam_django/apps/fleet/models/__init__.py rename padam_django/apps/fleet/{models.py => models/bus_model.py} (55%) create mode 100644 padam_django/apps/fleet/models/bus_stop_model.py create mode 100644 padam_django/apps/fleet/models/driver_model.py diff --git a/Makefile b/Makefile index b921d523..e4c08087 100644 --- a/Makefile +++ b/Makefile @@ -5,4 +5,7 @@ install: ## Install the python requirements. pip install -r requirements.txt migrate: ## Make migrations - python manage.py migrate \ No newline at end of file + python manage.py migrate + +makemigrations: ## Generate database migrations from models state + python manage.py makemigrations \ No newline at end of file diff --git a/padam_django/apps/common/management/commands/create_data.py b/padam_django/apps/common/management/commands/create_data.py index a149a937..bd4768b5 100644 --- a/padam_django/apps/common/management/commands/create_data.py +++ b/padam_django/apps/common/management/commands/create_data.py @@ -4,11 +4,11 @@ class Command(BaseCommand): - - help = 'Create test data' + help = "Create test data" def handle(self, *args, **options): - management.call_command('create_users', number=5) - management.call_command('create_drivers', number=5) - management.call_command('create_buses', number=10) - management.call_command('create_places', number=30) + management.call_command("create_users", number=5) + management.call_command("create_drivers", number=5) + management.call_command("create_buses", number=10) + management.call_command("create_places", number=30) + management.call_command("create_busstops", number=10) diff --git a/padam_django/apps/fleet/admin.py b/padam_django/apps/fleet/admin.py index 3fba5023..10e7bf5e 100644 --- a/padam_django/apps/fleet/admin.py +++ b/padam_django/apps/fleet/admin.py @@ -11,3 +11,8 @@ class BusAdmin(admin.ModelAdmin): @admin.register(models.Driver) class DriverAdmin(admin.ModelAdmin): pass + + +@admin.register(models.BusStop) +class BusStopAdmin(admin.ModelAdmin): + pass diff --git a/padam_django/apps/fleet/factories.py b/padam_django/apps/fleet/factories.py index c78c832e..cdb1035c 100644 --- a/padam_django/apps/fleet/factories.py +++ b/padam_django/apps/fleet/factories.py @@ -1,14 +1,15 @@ import factory from faker import Faker +from django.utils import timezone from . import models -fake = Faker(['fr']) +fake = Faker(["fr"]) class DriverFactory(factory.django.DjangoModelFactory): - user = factory.SubFactory('padam_django.apps.users.factories.UserFactory') + user = factory.SubFactory("padam_django.apps.users.factories.UserFactory") class Meta: model = models.Driver @@ -19,3 +20,11 @@ class BusFactory(factory.django.DjangoModelFactory): class Meta: model = models.Bus + + +class BusStopFactory(factory.django.DjangoModelFactory): + place = factory.SubFactory("padam_django.apps.geography.factories.PlaceFactory") + stop_time = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) + + class Meta: + model = models.BusStop diff --git a/padam_django/apps/fleet/management/commands/create_busstops.py b/padam_django/apps/fleet/management/commands/create_busstops.py new file mode 100644 index 00000000..c2fe47d0 --- /dev/null +++ b/padam_django/apps/fleet/management/commands/create_busstops.py @@ -0,0 +1,12 @@ +from padam_django.apps.common.management.base import CreateDataBaseCommand + +from padam_django.apps.fleet.factories import BusStopFactory + + +class Command(CreateDataBaseCommand): + help = "Create few bus stops" + + def handle(self, *args, **options): + super().handle(*args, **options) + self.stdout.write(f"Creating {self.number} bus stops ...") + BusStopFactory.create_batch(size=self.number) diff --git a/padam_django/apps/fleet/migrations/0003_busstop.py b/padam_django/apps/fleet/migrations/0003_busstop.py new file mode 100644 index 00000000..52c9991c --- /dev/null +++ b/padam_django/apps/fleet/migrations/0003_busstop.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.5 on 2023-06-10 13:52 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('geography', '0001_initial'), + ('fleet', '0002_auto_20211109_1456'), + ] + + operations = [ + migrations.CreateModel( + name='BusStop', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('stop_time', models.DateTimeField(verbose_name='Bus stop datetime')), + ('place', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='place', to='geography.place')), + ], + ), + ] diff --git a/padam_django/apps/fleet/models/__init__.py b/padam_django/apps/fleet/models/__init__.py new file mode 100644 index 00000000..a3053ce4 --- /dev/null +++ b/padam_django/apps/fleet/models/__init__.py @@ -0,0 +1,3 @@ +from .bus_model import Bus +from .bus_stop_model import BusStop +from .driver_model import Driver diff --git a/padam_django/apps/fleet/models.py b/padam_django/apps/fleet/models/bus_model.py similarity index 55% rename from padam_django/apps/fleet/models.py rename to padam_django/apps/fleet/models/bus_model.py index 4cd3f19d..4019f7b9 100644 --- a/padam_django/apps/fleet/models.py +++ b/padam_django/apps/fleet/models/bus_model.py @@ -1,13 +1,6 @@ from django.db import models -class Driver(models.Model): - user = models.OneToOneField('users.User', on_delete=models.CASCADE, related_name='driver') - - def __str__(self): - return f"Driver: {self.user.username} (id: {self.pk})" - - class Bus(models.Model): licence_plate = models.CharField("Name of the bus", max_length=10) diff --git a/padam_django/apps/fleet/models/bus_stop_model.py b/padam_django/apps/fleet/models/bus_stop_model.py new file mode 100644 index 00000000..fee9efaf --- /dev/null +++ b/padam_django/apps/fleet/models/bus_stop_model.py @@ -0,0 +1,11 @@ +from django.db import models + + +class BusStop(models.Model): + place = models.OneToOneField( + "geography.Place", on_delete=models.CASCADE, related_name="place" + ) + stop_time = models.DateTimeField(verbose_name="Bus stop datetime") + + def __str__(self): + return f"BusStop: {self.place} the {self.stop_time.date()} at {self.stop_time.time()} (id: {self.pk})" diff --git a/padam_django/apps/fleet/models/driver_model.py b/padam_django/apps/fleet/models/driver_model.py new file mode 100644 index 00000000..337a5465 --- /dev/null +++ b/padam_django/apps/fleet/models/driver_model.py @@ -0,0 +1,10 @@ +from django.db import models + + +class Driver(models.Model): + user = models.OneToOneField( + "users.User", on_delete=models.CASCADE, related_name="driver" + ) + + def __str__(self): + return f"Driver: {self.user.username} (id: {self.pk})" From df751bb0a7850a2189fd43ed3811d31c5f89f46f Mon Sep 17 00:00:00 2001 From: alexisbarreaux <67143881+alexisbarreaux@users.noreply.github.com> Date: Sun, 11 Jun 2023 17:40:06 +0200 Subject: [PATCH 06/10] 1 implement busshift (#5) * Adding simple shift and modifying stop * Corrected factories * Adding admin inline for shifts * Working on total time definition * Making automatic shift update on stop change * Working on drivers and buses not overlapping * Making separate simpler save on stop update --- .../common/management/commands/create_data.py | 3 +- padam_django/apps/fleet/admin.py | 33 ++++- padam_django/apps/fleet/apps.py | 5 +- padam_django/apps/fleet/exceptions.py | 6 + padam_django/apps/fleet/factories.py | 13 +- .../management/commands/create_bus_shifts.py | 12 ++ ...create_busstops.py => create_bus_stops.py} | 0 .../0004_rename_stop_time_busstop_datetime.py | 18 +++ .../apps/fleet/migrations/0005_busshift.py | 24 ++++ .../migrations/0006_auto_20230610_1457.py | 24 ++++ .../migrations/0007_alter_busstop_shift.py | 19 +++ .../migrations/0008_auto_20230610_1509.py | 23 +++ .../migrations/0009_alter_busstop_shift.py | 19 +++ .../migrations/0010_alter_busstop_datetime.py | 18 +++ .../0011_busshift_total_duration.py | 20 +++ .../0012_alter_busshift_total_duration.py | 18 +++ .../migrations/0013_alter_busstop_shift.py | 19 +++ .../0014_busshift_has_enough_stops.py | 19 +++ .../migrations/0015_alter_busstop_place.py | 20 +++ .../migrations/0016_auto_20230611_1507.py | 41 ++++++ .../0017_alter_busshift_total_duration.py | 19 +++ .../0018_alter_busshift_has_enough_stops.py | 18 +++ padam_django/apps/fleet/models/__init__.py | 1 + .../apps/fleet/models/bus_shift_model.py | 134 ++++++++++++++++++ .../apps/fleet/models/bus_stop_model.py | 11 +- padam_django/apps/fleet/signals/__init__.py | 1 + .../apps/fleet/signals/bus_stop_signals.py | 10 ++ 27 files changed, 540 insertions(+), 8 deletions(-) create mode 100644 padam_django/apps/fleet/exceptions.py create mode 100644 padam_django/apps/fleet/management/commands/create_bus_shifts.py rename padam_django/apps/fleet/management/commands/{create_busstops.py => create_bus_stops.py} (100%) create mode 100644 padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py create mode 100644 padam_django/apps/fleet/migrations/0005_busshift.py create mode 100644 padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py create mode 100644 padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py create mode 100644 padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py create mode 100644 padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py create mode 100644 padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py create mode 100644 padam_django/apps/fleet/migrations/0011_busshift_total_duration.py create mode 100644 padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py create mode 100644 padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py create mode 100644 padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py create mode 100644 padam_django/apps/fleet/migrations/0015_alter_busstop_place.py create mode 100644 padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py create mode 100644 padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py create mode 100644 padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py create mode 100644 padam_django/apps/fleet/models/bus_shift_model.py create mode 100644 padam_django/apps/fleet/signals/__init__.py create mode 100644 padam_django/apps/fleet/signals/bus_stop_signals.py diff --git a/padam_django/apps/common/management/commands/create_data.py b/padam_django/apps/common/management/commands/create_data.py index bd4768b5..ecf87bd0 100644 --- a/padam_django/apps/common/management/commands/create_data.py +++ b/padam_django/apps/common/management/commands/create_data.py @@ -11,4 +11,5 @@ def handle(self, *args, **options): management.call_command("create_drivers", number=5) management.call_command("create_buses", number=10) management.call_command("create_places", number=30) - management.call_command("create_busstops", number=10) + management.call_command("create_bus_shifts", number=10) + management.call_command("create_bus_stops", number=10) diff --git a/padam_django/apps/fleet/admin.py b/padam_django/apps/fleet/admin.py index 10e7bf5e..0fb5c9b3 100644 --- a/padam_django/apps/fleet/admin.py +++ b/padam_django/apps/fleet/admin.py @@ -8,6 +8,37 @@ class BusAdmin(admin.ModelAdmin): pass +class BusStopsInline(admin.TabularInline): + model = models.BusStop + ordering = ("datetime",) + + +@admin.register(models.BusShift) +class BusShiftAdmin(admin.ModelAdmin): + readonly_fields = ( + "total_duration", + "start_datetime", + "end_datetime", + "has_enough_stops", + ) + fieldsets = [ + ( + "Main", + { + "fields": [ + "bus", + "driver", + "start_datetime", + "end_datetime", + "total_duration", + ] + }, + ), + ("Flags", {"fields": ["has_enough_stops"]}), + ] + inlines = [BusStopsInline] + + @admin.register(models.Driver) class DriverAdmin(admin.ModelAdmin): pass @@ -15,4 +46,4 @@ class DriverAdmin(admin.ModelAdmin): @admin.register(models.BusStop) class BusStopAdmin(admin.ModelAdmin): - pass + readonly_fields = ("shift",) diff --git a/padam_django/apps/fleet/apps.py b/padam_django/apps/fleet/apps.py index 71378675..95129c6e 100644 --- a/padam_django/apps/fleet/apps.py +++ b/padam_django/apps/fleet/apps.py @@ -2,4 +2,7 @@ class FleetConfig(AppConfig): - name = 'padam_django.apps.fleet' + name = "padam_django.apps.fleet" + + def ready(self): + import padam_django.apps.fleet.signals diff --git a/padam_django/apps/fleet/exceptions.py b/padam_django/apps/fleet/exceptions.py new file mode 100644 index 00000000..558118b1 --- /dev/null +++ b/padam_django/apps/fleet/exceptions.py @@ -0,0 +1,6 @@ +class BusOtherShiftsOverlapException(Exception): + pass + + +class DriverOtherShiftsOverlapException(Exception): + pass diff --git a/padam_django/apps/fleet/factories.py b/padam_django/apps/fleet/factories.py index cdb1035c..a3caf0b4 100644 --- a/padam_django/apps/fleet/factories.py +++ b/padam_django/apps/fleet/factories.py @@ -22,9 +22,20 @@ class Meta: model = models.Bus +class BusShiftFactory(factory.django.DjangoModelFactory): + bus = factory.SubFactory("padam_django.apps.fleet.factories.BusFactory") + driver = factory.SubFactory("padam_django.apps.fleet.factories.DriverFactory") + start_time = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) + end_time = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) + + class Meta: + model = models.BusShift + + class BusStopFactory(factory.django.DjangoModelFactory): place = factory.SubFactory("padam_django.apps.geography.factories.PlaceFactory") - stop_time = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) + datetime = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) + shift = factory.SubFactory("padam_django.apps.fleet.factories.BusShiftFactory") class Meta: model = models.BusStop diff --git a/padam_django/apps/fleet/management/commands/create_bus_shifts.py b/padam_django/apps/fleet/management/commands/create_bus_shifts.py new file mode 100644 index 00000000..1178d096 --- /dev/null +++ b/padam_django/apps/fleet/management/commands/create_bus_shifts.py @@ -0,0 +1,12 @@ +from padam_django.apps.common.management.base import CreateDataBaseCommand + +from padam_django.apps.fleet.factories import BusShiftFactory + + +class Command(CreateDataBaseCommand): + help = "Create few bus shifts" + + def handle(self, *args, **options): + super().handle(*args, **options) + self.stdout.write(f"Creating {self.number} bus shifts ...") + BusShiftFactory.create_batch(size=self.number) diff --git a/padam_django/apps/fleet/management/commands/create_busstops.py b/padam_django/apps/fleet/management/commands/create_bus_stops.py similarity index 100% rename from padam_django/apps/fleet/management/commands/create_busstops.py rename to padam_django/apps/fleet/management/commands/create_bus_stops.py diff --git a/padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py b/padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py new file mode 100644 index 00000000..75030caa --- /dev/null +++ b/padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2023-06-10 14:31 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0003_busstop'), + ] + + operations = [ + migrations.RenameField( + model_name='busstop', + old_name='stop_time', + new_name='datetime', + ), + ] diff --git a/padam_django/apps/fleet/migrations/0005_busshift.py b/padam_django/apps/fleet/migrations/0005_busshift.py new file mode 100644 index 00000000..6bbef7b8 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0005_busshift.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.5 on 2023-06-10 14:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0004_rename_stop_time_busstop_datetime'), + ] + + operations = [ + migrations.CreateModel( + name='BusShift', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_time', models.DateTimeField(verbose_name='Shift start datetime')), + ('end_time', models.DateTimeField(verbose_name='Shift end datetime')), + ('bus', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='bus', to='fleet.bus')), + ('driver', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='driver', to='fleet.driver')), + ], + ), + ] diff --git a/padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py b/padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py new file mode 100644 index 00000000..e574bed3 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.5 on 2023-06-10 14:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0005_busshift'), + ] + + operations = [ + migrations.AddField( + model_name='busstop', + name='shift', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bus_shift', to='fleet.busshift'), + ), + migrations.AlterField( + model_name='busstop', + name='datetime', + field=models.DateTimeField(auto_now_add=True, verbose_name='Bus stop datetime'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py b/padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py new file mode 100644 index 00000000..d44024cc --- /dev/null +++ b/padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2023-06-10 14:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0006_auto_20230610_1457'), + ] + + operations = [ + migrations.AlterField( + model_name='busstop', + name='shift', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='bus_shift', to='fleet.busshift'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py b/padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py new file mode 100644 index 00000000..00ad4868 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.5 on 2023-06-10 15:09 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0007_alter_busstop_shift'), + ] + + operations = [ + migrations.RenameField( + model_name='busshift', + old_name='end_time', + new_name='end_datetime', + ), + migrations.RenameField( + model_name='busshift', + old_name='start_time', + new_name='start_datetime', + ), + ] diff --git a/padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py b/padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py new file mode 100644 index 00000000..f609d1cc --- /dev/null +++ b/padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2023-06-10 15:26 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0008_auto_20230610_1509'), + ] + + operations = [ + migrations.AlterField( + model_name='busstop', + name='shift', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bus_shift', to='fleet.busshift'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py b/padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py new file mode 100644 index 00000000..47a3d79d --- /dev/null +++ b/padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2023-06-10 15:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0009_alter_busstop_shift'), + ] + + operations = [ + migrations.AlterField( + model_name='busstop', + name='datetime', + field=models.DateTimeField(verbose_name='Bus stop datetime'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0011_busshift_total_duration.py b/padam_django/apps/fleet/migrations/0011_busshift_total_duration.py new file mode 100644 index 00000000..32238604 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0011_busshift_total_duration.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.5 on 2023-06-10 15:42 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0010_alter_busstop_datetime'), + ] + + operations = [ + migrations.AddField( + model_name='busshift', + name='total_duration', + field=models.DurationField(default=datetime.timedelta(0), editable=False, verbose_name='Total shift duration'), + preserve_default=False, + ), + ] diff --git a/padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py b/padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py new file mode 100644 index 00000000..27ec816e --- /dev/null +++ b/padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2023-06-10 15:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0011_busshift_total_duration'), + ] + + operations = [ + migrations.AlterField( + model_name='busshift', + name='total_duration', + field=models.DurationField(verbose_name='Total shift duration'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py b/padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py new file mode 100644 index 00000000..b90c6356 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2023-06-11 13:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0012_alter_busshift_total_duration'), + ] + + operations = [ + migrations.AlterField( + model_name='busstop', + name='shift', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stops', to='fleet.busshift'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py b/padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py new file mode 100644 index 00000000..2af43fc4 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2023-06-11 13:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0013_alter_busstop_shift'), + ] + + operations = [ + migrations.AddField( + model_name='busshift', + name='has_enough_stops', + field=models.BooleanField(default=False, verbose_name='Has enough stops to be valid'), + preserve_default=False, + ), + ] diff --git a/padam_django/apps/fleet/migrations/0015_alter_busstop_place.py b/padam_django/apps/fleet/migrations/0015_alter_busstop_place.py new file mode 100644 index 00000000..8d9c3eb2 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0015_alter_busstop_place.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.5 on 2023-06-11 14:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('geography', '0001_initial'), + ('fleet', '0014_busshift_has_enough_stops'), + ] + + operations = [ + migrations.AlterField( + model_name='busstop', + name='place', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stops', to='geography.place'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py b/padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py new file mode 100644 index 00000000..4086c53e --- /dev/null +++ b/padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py @@ -0,0 +1,41 @@ +# Generated by Django 3.2.5 on 2023-06-11 15:07 + +import datetime +from django.db import migrations, models +import django.db.models.deletion +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0015_alter_busstop_place'), + ] + + operations = [ + migrations.AlterField( + model_name='busshift', + name='bus', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.bus'), + ), + migrations.AlterField( + model_name='busshift', + name='driver', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.driver'), + ), + migrations.AlterField( + model_name='busshift', + name='end_datetime', + field=models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc), verbose_name='Shift end datetime'), + ), + migrations.AlterField( + model_name='busshift', + name='start_datetime', + field=models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc), verbose_name='Shift start datetime'), + ), + migrations.AlterField( + model_name='busshift', + name='total_duration', + field=models.DurationField(default=0, verbose_name='Total shift duration'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py b/padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py new file mode 100644 index 00000000..a466d7ad --- /dev/null +++ b/padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2023-06-11 15:08 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0016_auto_20230611_1507'), + ] + + operations = [ + migrations.AlterField( + model_name='busshift', + name='total_duration', + field=models.DurationField(default=datetime.timedelta(0), verbose_name='Total shift duration'), + ), + ] diff --git a/padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py b/padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py new file mode 100644 index 00000000..65e292b8 --- /dev/null +++ b/padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2023-06-11 15:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fleet', '0017_alter_busshift_total_duration'), + ] + + operations = [ + migrations.AlterField( + model_name='busshift', + name='has_enough_stops', + field=models.BooleanField(default=False, verbose_name='Has enough stops to be valid'), + ), + ] diff --git a/padam_django/apps/fleet/models/__init__.py b/padam_django/apps/fleet/models/__init__.py index a3053ce4..f09ecc7c 100644 --- a/padam_django/apps/fleet/models/__init__.py +++ b/padam_django/apps/fleet/models/__init__.py @@ -1,3 +1,4 @@ from .bus_model import Bus +from .bus_shift_model import BusShift from .bus_stop_model import BusStop from .driver_model import Driver diff --git a/padam_django/apps/fleet/models/bus_shift_model.py b/padam_django/apps/fleet/models/bus_shift_model.py new file mode 100644 index 00000000..7ea344a3 --- /dev/null +++ b/padam_django/apps/fleet/models/bus_shift_model.py @@ -0,0 +1,134 @@ +from datetime import datetime, MINYEAR, timedelta + +from django.db import models +from django.db.models.query import QuerySet +from django.utils import timezone + +from padam_django.apps.fleet.models.bus_stop_model import BusStop +from padam_django.apps.fleet.models.bus_model import Bus +from padam_django.apps.fleet.models.driver_model import Driver +from padam_django.apps.fleet.exceptions import ( + DriverOtherShiftsOverlapException, + BusOtherShiftsOverlapException, +) + +DEFAULT_DATETIME_FOR_MISSING_STOPS = datetime( + year=MINYEAR, month=1, day=1, tzinfo=timezone.get_current_timezone() +) + + +class BusShift(models.Model): + bus = models.ForeignKey( + "fleet.Bus", on_delete=models.CASCADE, related_name="shifts" + ) + driver = models.ForeignKey( + "fleet.Driver", on_delete=models.CASCADE, related_name="shifts" + ) + start_datetime = models.DateTimeField( + verbose_name="Shift start datetime", default=DEFAULT_DATETIME_FOR_MISSING_STOPS + ) + end_datetime = models.DateTimeField( + verbose_name="Shift end datetime", default=DEFAULT_DATETIME_FOR_MISSING_STOPS + ) + total_duration = models.DurationField( + verbose_name="Total shift duration", default=timedelta(days=0) + ) + has_enough_stops = models.BooleanField( + verbose_name="Has enough stops to be valid", default=False + ) + + def save(self, *args, **kwargs): + if self.bus_has_overlapping_shifts(): + raise BusOtherShiftsOverlapException( + "Chosen bus can't be assigned to shift." + ) + elif self.driver_has_overlapping_shifts(): + raise DriverOtherShiftsOverlapException( + "Choosen driver can't be assigned to shift." + ) + else: + super().save(*args, **kwargs) + return + + def bus_has_overlapping_shifts(self) -> bool: + chosen_bus_shifts: QuerySet[BusShift] = self.bus.shifts + return self.shifts_overlap_with_self(chosen_bus_shifts) + + def driver_has_overlapping_shifts(self) -> bool: + driver_shifts: QuerySet[BusShift] = self.driver.shifts + return self.shifts_overlap_with_self(driver_shifts) + + def shifts_overlap_with_self(self, shifts: QuerySet) -> bool: + return self.shifts_start_overlap_with_self( + shifts + ) or self.shifts_end_overlap_with_self(shifts) + + def shifts_start_overlap_with_self(self, shifts: QuerySet) -> bool: + return ( + shifts.filter( + start_datetime__gt=self.start_datetime, + start_datetime__lt=self.end_datetime, + ) + .exclude(pk=self.pk) + .exists() + ) + + def shifts_end_overlap_with_self(self, shifts: QuerySet) -> bool: + return ( + shifts.filter( + end_datetime__gt=self.start_datetime, + end_datetime__lt=self.end_datetime, + ) + .exclude(pk=self.pk) + .exists() + ) + + def update_on_linked_stop_change(self): + ordered_stops = self.get_ascending_linked_stops() + self.update_stops_related_fields(ordered_stops) + + self.update_total_duration() + + self.linked_stops_modifications_only_save() + return + + def update_total_duration(self) -> None: + self.total_duration = self.end_datetime - self.start_datetime + return + + def get_ascending_linked_stops(self) -> QuerySet[BusStop]: + stops: QuerySet[BusStop] = self.stops.all() + return stops.order_by("datetime") + + def update_stops_related_fields(self, ordered_stops: QuerySet[BusStop]) -> None: + self.update_has_enough_stops(ordered_stops) + self.update_start_datetime(ordered_stops) + self.update_end_datetime(ordered_stops) + return + + def update_has_enough_stops(self, stops: QuerySet[BusStop]) -> bool: + self.has_enough_stops = len(stops) >= 2 + return + + def update_start_datetime(self, ordered_stops: QuerySet[BusStop]) -> None: + first_stop: BusStop = ordered_stops.first() + if first_stop is not None: + self.start_datetime = first_stop.datetime + else: + self.start_datetime = DEFAULT_DATETIME_FOR_MISSING_STOPS + return + + def update_end_datetime(self, ordered_stops: QuerySet[BusStop]) -> None: + last_stop: BusStop = ordered_stops.last() + if last_stop is not None: + self.end_datetime = last_stop.datetime + else: + self.end_datetime = DEFAULT_DATETIME_FOR_MISSING_STOPS + return + + def linked_stops_modifications_only_save(self): + super().save() + return + + def __str__(self): + return f"BusShift: {self.bus} by {self.driver} from {self.start_datetime} to {self.end_datetime} (id: {self.pk})" diff --git a/padam_django/apps/fleet/models/bus_stop_model.py b/padam_django/apps/fleet/models/bus_stop_model.py index fee9efaf..a15d98cd 100644 --- a/padam_django/apps/fleet/models/bus_stop_model.py +++ b/padam_django/apps/fleet/models/bus_stop_model.py @@ -2,10 +2,13 @@ class BusStop(models.Model): - place = models.OneToOneField( - "geography.Place", on_delete=models.CASCADE, related_name="place" + place = models.ForeignKey( + "geography.Place", on_delete=models.CASCADE, related_name="stops" + ) + datetime = models.DateTimeField(verbose_name="Bus stop datetime") + shift = models.ForeignKey( + "fleet.BusShift", on_delete=models.CASCADE, related_name="stops" ) - stop_time = models.DateTimeField(verbose_name="Bus stop datetime") def __str__(self): - return f"BusStop: {self.place} the {self.stop_time.date()} at {self.stop_time.time()} (id: {self.pk})" + return f"BusStop: {self.place} the {self.datetime.date()} at {self.datetime.time()} (id: {self.pk})" diff --git a/padam_django/apps/fleet/signals/__init__.py b/padam_django/apps/fleet/signals/__init__.py new file mode 100644 index 00000000..0cebe4bb --- /dev/null +++ b/padam_django/apps/fleet/signals/__init__.py @@ -0,0 +1 @@ +from .bus_stop_signals import * diff --git a/padam_django/apps/fleet/signals/bus_stop_signals.py b/padam_django/apps/fleet/signals/bus_stop_signals.py new file mode 100644 index 00000000..a11d2e25 --- /dev/null +++ b/padam_django/apps/fleet/signals/bus_stop_signals.py @@ -0,0 +1,10 @@ +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver + +from padam_django.apps.fleet.models import BusStop + + +@receiver(post_delete, sender=BusStop) +@receiver(post_save, sender=BusStop) +def update_linked_shift(sender, instance: BusStop, using, **kwargs): + instance.shift.update_on_linked_stop_change() From 09a18a6cda894bfb7d25ee80069e7a63b813c7d3 Mon Sep 17 00:00:00 2001 From: abarreaux Date: Sun, 11 Jun 2023 17:46:37 +0200 Subject: [PATCH 07/10] Squashing migrations --- .../apps/fleet/migrations/0003_busstop.py | 23 ----------- ...ed_0018_alter_busshift_has_enough_stops.py | 40 ++++++++++++++++++ .../0004_rename_stop_time_busstop_datetime.py | 18 -------- .../apps/fleet/migrations/0005_busshift.py | 24 ----------- .../migrations/0006_auto_20230610_1457.py | 24 ----------- .../migrations/0007_alter_busstop_shift.py | 19 --------- .../migrations/0008_auto_20230610_1509.py | 23 ----------- .../migrations/0009_alter_busstop_shift.py | 19 --------- .../migrations/0010_alter_busstop_datetime.py | 18 -------- .../0011_busshift_total_duration.py | 20 --------- .../0012_alter_busshift_total_duration.py | 18 -------- .../migrations/0013_alter_busstop_shift.py | 19 --------- .../0014_busshift_has_enough_stops.py | 19 --------- .../migrations/0015_alter_busstop_place.py | 20 --------- .../migrations/0016_auto_20230611_1507.py | 41 ------------------- .../0017_alter_busshift_total_duration.py | 19 --------- .../0018_alter_busshift_has_enough_stops.py | 18 -------- 17 files changed, 40 insertions(+), 342 deletions(-) delete mode 100644 padam_django/apps/fleet/migrations/0003_busstop.py create mode 100644 padam_django/apps/fleet/migrations/0003_busstop_squashed_0018_alter_busshift_has_enough_stops.py delete mode 100644 padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py delete mode 100644 padam_django/apps/fleet/migrations/0005_busshift.py delete mode 100644 padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py delete mode 100644 padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py delete mode 100644 padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py delete mode 100644 padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py delete mode 100644 padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py delete mode 100644 padam_django/apps/fleet/migrations/0011_busshift_total_duration.py delete mode 100644 padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py delete mode 100644 padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py delete mode 100644 padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py delete mode 100644 padam_django/apps/fleet/migrations/0015_alter_busstop_place.py delete mode 100644 padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py delete mode 100644 padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py delete mode 100644 padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py diff --git a/padam_django/apps/fleet/migrations/0003_busstop.py b/padam_django/apps/fleet/migrations/0003_busstop.py deleted file mode 100644 index 52c9991c..00000000 --- a/padam_django/apps/fleet/migrations/0003_busstop.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 13:52 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('geography', '0001_initial'), - ('fleet', '0002_auto_20211109_1456'), - ] - - operations = [ - migrations.CreateModel( - name='BusStop', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('stop_time', models.DateTimeField(verbose_name='Bus stop datetime')), - ('place', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='place', to='geography.place')), - ], - ), - ] diff --git a/padam_django/apps/fleet/migrations/0003_busstop_squashed_0018_alter_busshift_has_enough_stops.py b/padam_django/apps/fleet/migrations/0003_busstop_squashed_0018_alter_busshift_has_enough_stops.py new file mode 100644 index 00000000..d6f3b55f --- /dev/null +++ b/padam_django/apps/fleet/migrations/0003_busstop_squashed_0018_alter_busshift_has_enough_stops.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.5 on 2023-06-11 15:42 + +import datetime +from django.db import migrations, models +import django.db.models.deletion +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + replaces = [('fleet', '0003_busstop'), ('fleet', '0004_rename_stop_time_busstop_datetime'), ('fleet', '0005_busshift'), ('fleet', '0006_auto_20230610_1457'), ('fleet', '0007_alter_busstop_shift'), ('fleet', '0008_auto_20230610_1509'), ('fleet', '0009_alter_busstop_shift'), ('fleet', '0010_alter_busstop_datetime'), ('fleet', '0011_busshift_total_duration'), ('fleet', '0012_alter_busshift_total_duration'), ('fleet', '0013_alter_busstop_shift'), ('fleet', '0014_busshift_has_enough_stops'), ('fleet', '0015_alter_busstop_place'), ('fleet', '0016_auto_20230611_1507'), ('fleet', '0017_alter_busshift_total_duration'), ('fleet', '0018_alter_busshift_has_enough_stops')] + + dependencies = [ + ('fleet', '0002_auto_20211109_1456'), + ('geography', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='BusShift', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_datetime', models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc), verbose_name='Shift start datetime')), + ('end_datetime', models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc), verbose_name='Shift end datetime')), + ('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')), + ('total_duration', models.DurationField(default=datetime.timedelta(0), verbose_name='Total shift duration')), + ('has_enough_stops', models.BooleanField(default=False, verbose_name='Has enough stops to be valid')), + ], + ), + migrations.CreateModel( + name='BusStop', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('datetime', models.DateTimeField(verbose_name='Bus stop datetime')), + ('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stops', to='geography.place')), + ('shift', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stops', to='fleet.busshift')), + ], + ), + ] diff --git a/padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py b/padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py deleted file mode 100644 index 75030caa..00000000 --- a/padam_django/apps/fleet/migrations/0004_rename_stop_time_busstop_datetime.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 14:31 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0003_busstop'), - ] - - operations = [ - migrations.RenameField( - model_name='busstop', - old_name='stop_time', - new_name='datetime', - ), - ] diff --git a/padam_django/apps/fleet/migrations/0005_busshift.py b/padam_django/apps/fleet/migrations/0005_busshift.py deleted file mode 100644 index 6bbef7b8..00000000 --- a/padam_django/apps/fleet/migrations/0005_busshift.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 14:45 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0004_rename_stop_time_busstop_datetime'), - ] - - operations = [ - migrations.CreateModel( - name='BusShift', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start_time', models.DateTimeField(verbose_name='Shift start datetime')), - ('end_time', models.DateTimeField(verbose_name='Shift end datetime')), - ('bus', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='bus', to='fleet.bus')), - ('driver', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='driver', to='fleet.driver')), - ], - ), - ] diff --git a/padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py b/padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py deleted file mode 100644 index e574bed3..00000000 --- a/padam_django/apps/fleet/migrations/0006_auto_20230610_1457.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 14:57 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0005_busshift'), - ] - - operations = [ - migrations.AddField( - model_name='busstop', - name='shift', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bus_shift', to='fleet.busshift'), - ), - migrations.AlterField( - model_name='busstop', - name='datetime', - field=models.DateTimeField(auto_now_add=True, verbose_name='Bus stop datetime'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py b/padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py deleted file mode 100644 index d44024cc..00000000 --- a/padam_django/apps/fleet/migrations/0007_alter_busstop_shift.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 14:57 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0006_auto_20230610_1457'), - ] - - operations = [ - migrations.AlterField( - model_name='busstop', - name='shift', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='bus_shift', to='fleet.busshift'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py b/padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py deleted file mode 100644 index 00ad4868..00000000 --- a/padam_django/apps/fleet/migrations/0008_auto_20230610_1509.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 15:09 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0007_alter_busstop_shift'), - ] - - operations = [ - migrations.RenameField( - model_name='busshift', - old_name='end_time', - new_name='end_datetime', - ), - migrations.RenameField( - model_name='busshift', - old_name='start_time', - new_name='start_datetime', - ), - ] diff --git a/padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py b/padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py deleted file mode 100644 index f609d1cc..00000000 --- a/padam_django/apps/fleet/migrations/0009_alter_busstop_shift.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 15:26 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0008_auto_20230610_1509'), - ] - - operations = [ - migrations.AlterField( - model_name='busstop', - name='shift', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bus_shift', to='fleet.busshift'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py b/padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py deleted file mode 100644 index 47a3d79d..00000000 --- a/padam_django/apps/fleet/migrations/0010_alter_busstop_datetime.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 15:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0009_alter_busstop_shift'), - ] - - operations = [ - migrations.AlterField( - model_name='busstop', - name='datetime', - field=models.DateTimeField(verbose_name='Bus stop datetime'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0011_busshift_total_duration.py b/padam_django/apps/fleet/migrations/0011_busshift_total_duration.py deleted file mode 100644 index 32238604..00000000 --- a/padam_django/apps/fleet/migrations/0011_busshift_total_duration.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 15:42 - -import datetime -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0010_alter_busstop_datetime'), - ] - - operations = [ - migrations.AddField( - model_name='busshift', - name='total_duration', - field=models.DurationField(default=datetime.timedelta(0), editable=False, verbose_name='Total shift duration'), - preserve_default=False, - ), - ] diff --git a/padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py b/padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py deleted file mode 100644 index 27ec816e..00000000 --- a/padam_django/apps/fleet/migrations/0012_alter_busshift_total_duration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-10 15:44 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0011_busshift_total_duration'), - ] - - operations = [ - migrations.AlterField( - model_name='busshift', - name='total_duration', - field=models.DurationField(verbose_name='Total shift duration'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py b/padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py deleted file mode 100644 index b90c6356..00000000 --- a/padam_django/apps/fleet/migrations/0013_alter_busstop_shift.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-11 13:15 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0012_alter_busshift_total_duration'), - ] - - operations = [ - migrations.AlterField( - model_name='busstop', - name='shift', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stops', to='fleet.busshift'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py b/padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py deleted file mode 100644 index 2af43fc4..00000000 --- a/padam_django/apps/fleet/migrations/0014_busshift_has_enough_stops.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-11 13:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0013_alter_busstop_shift'), - ] - - operations = [ - migrations.AddField( - model_name='busshift', - name='has_enough_stops', - field=models.BooleanField(default=False, verbose_name='Has enough stops to be valid'), - preserve_default=False, - ), - ] diff --git a/padam_django/apps/fleet/migrations/0015_alter_busstop_place.py b/padam_django/apps/fleet/migrations/0015_alter_busstop_place.py deleted file mode 100644 index 8d9c3eb2..00000000 --- a/padam_django/apps/fleet/migrations/0015_alter_busstop_place.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-11 14:22 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('geography', '0001_initial'), - ('fleet', '0014_busshift_has_enough_stops'), - ] - - operations = [ - migrations.AlterField( - model_name='busstop', - name='place', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stops', to='geography.place'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py b/padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py deleted file mode 100644 index 4086c53e..00000000 --- a/padam_django/apps/fleet/migrations/0016_auto_20230611_1507.py +++ /dev/null @@ -1,41 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-11 15:07 - -import datetime -from django.db import migrations, models -import django.db.models.deletion -from django.utils.timezone import utc - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0015_alter_busstop_place'), - ] - - operations = [ - migrations.AlterField( - model_name='busshift', - name='bus', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.bus'), - ), - migrations.AlterField( - model_name='busshift', - name='driver', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.driver'), - ), - migrations.AlterField( - model_name='busshift', - name='end_datetime', - field=models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc), verbose_name='Shift end datetime'), - ), - migrations.AlterField( - model_name='busshift', - name='start_datetime', - field=models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc), verbose_name='Shift start datetime'), - ), - migrations.AlterField( - model_name='busshift', - name='total_duration', - field=models.DurationField(default=0, verbose_name='Total shift duration'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py b/padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py deleted file mode 100644 index a466d7ad..00000000 --- a/padam_django/apps/fleet/migrations/0017_alter_busshift_total_duration.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-11 15:08 - -import datetime -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0016_auto_20230611_1507'), - ] - - operations = [ - migrations.AlterField( - model_name='busshift', - name='total_duration', - field=models.DurationField(default=datetime.timedelta(0), verbose_name='Total shift duration'), - ), - ] diff --git a/padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py b/padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py deleted file mode 100644 index 65e292b8..00000000 --- a/padam_django/apps/fleet/migrations/0018_alter_busshift_has_enough_stops.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2023-06-11 15:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('fleet', '0017_alter_busshift_total_duration'), - ] - - operations = [ - migrations.AlterField( - model_name='busshift', - name='has_enough_stops', - field=models.BooleanField(default=False, verbose_name='Has enough stops to be valid'), - ), - ] From 2aad5af89b5297618a1644ee4f6ba0398b71a2b0 Mon Sep 17 00:00:00 2001 From: alexisbarreaux <67143881+alexisbarreaux@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:40:25 +0200 Subject: [PATCH 08/10] Corrected bus shift factory (#8) --- padam_django/apps/fleet/factories.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/padam_django/apps/fleet/factories.py b/padam_django/apps/fleet/factories.py index a3caf0b4..b6f6d9fc 100644 --- a/padam_django/apps/fleet/factories.py +++ b/padam_django/apps/fleet/factories.py @@ -25,8 +25,6 @@ class Meta: class BusShiftFactory(factory.django.DjangoModelFactory): bus = factory.SubFactory("padam_django.apps.fleet.factories.BusFactory") driver = factory.SubFactory("padam_django.apps.fleet.factories.DriverFactory") - start_time = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) - end_time = factory.Faker("date_time", tzinfo=timezone.get_current_timezone()) class Meta: model = models.BusShift From 315a3ed0a41892bc6b26853d20ff1d0b964181f5 Mon Sep 17 00:00:00 2001 From: abarreaux Date: Mon, 12 Jun 2023 12:01:35 +0200 Subject: [PATCH 09/10] Minor changes --- padam_django/apps/fleet/models/bus_shift_model.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/padam_django/apps/fleet/models/bus_shift_model.py b/padam_django/apps/fleet/models/bus_shift_model.py index 7ea344a3..e7e615e4 100644 --- a/padam_django/apps/fleet/models/bus_shift_model.py +++ b/padam_django/apps/fleet/models/bus_shift_model.py @@ -47,8 +47,7 @@ def save(self, *args, **kwargs): "Choosen driver can't be assigned to shift." ) else: - super().save(*args, **kwargs) - return + return super().save(*args, **kwargs) def bus_has_overlapping_shifts(self) -> bool: chosen_bus_shifts: QuerySet[BusShift] = self.bus.shifts @@ -87,6 +86,7 @@ def update_on_linked_stop_change(self): ordered_stops = self.get_ascending_linked_stops() self.update_stops_related_fields(ordered_stops) + # Must be done after update_stops_related_fields self.update_total_duration() self.linked_stops_modifications_only_save() @@ -127,8 +127,7 @@ def update_end_datetime(self, ordered_stops: QuerySet[BusStop]) -> None: return def linked_stops_modifications_only_save(self): - super().save() - return + return super().save() def __str__(self): return f"BusShift: {self.bus} by {self.driver} from {self.start_datetime} to {self.end_datetime} (id: {self.pk})" From e3f80dadd222326ba6f02613b50a5aff238b3de9 Mon Sep 17 00:00:00 2001 From: abarreaux Date: Mon, 12 Jun 2023 15:37:23 +0200 Subject: [PATCH 10/10] Correcting exceptions through signals --- padam_django/apps/fleet/admin.py | 2 +- padam_django/apps/fleet/exceptions.py | 8 +++++ .../apps/fleet/models/bus_shift_model.py | 32 ++++--------------- padam_django/apps/fleet/signals/__init__.py | 1 + .../apps/fleet/signals/bus_shift_signals.py | 24 ++++++++++++++ .../apps/fleet/signals/bus_stop_signals.py | 18 ++++++++++- 6 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 padam_django/apps/fleet/signals/bus_shift_signals.py diff --git a/padam_django/apps/fleet/admin.py b/padam_django/apps/fleet/admin.py index 0fb5c9b3..efff2975 100644 --- a/padam_django/apps/fleet/admin.py +++ b/padam_django/apps/fleet/admin.py @@ -46,4 +46,4 @@ class DriverAdmin(admin.ModelAdmin): @admin.register(models.BusStop) class BusStopAdmin(admin.ModelAdmin): - readonly_fields = ("shift",) + pass diff --git a/padam_django/apps/fleet/exceptions.py b/padam_django/apps/fleet/exceptions.py index 558118b1..24d5bec4 100644 --- a/padam_django/apps/fleet/exceptions.py +++ b/padam_django/apps/fleet/exceptions.py @@ -4,3 +4,11 @@ class BusOtherShiftsOverlapException(Exception): class DriverOtherShiftsOverlapException(Exception): pass + + +class StopWouldOverlapBusOtherShifts(Exception): + pass + + +class StopWouldOverlapDriverOtherShifts(Exception): + pass diff --git a/padam_django/apps/fleet/models/bus_shift_model.py b/padam_django/apps/fleet/models/bus_shift_model.py index e7e615e4..ad942999 100644 --- a/padam_django/apps/fleet/models/bus_shift_model.py +++ b/padam_django/apps/fleet/models/bus_shift_model.py @@ -5,8 +5,6 @@ from django.utils import timezone from padam_django.apps.fleet.models.bus_stop_model import BusStop -from padam_django.apps.fleet.models.bus_model import Bus -from padam_django.apps.fleet.models.driver_model import Driver from padam_django.apps.fleet.exceptions import ( DriverOtherShiftsOverlapException, BusOtherShiftsOverlapException, @@ -38,16 +36,13 @@ class BusShift(models.Model): ) def save(self, *args, **kwargs): - if self.bus_has_overlapping_shifts(): - raise BusOtherShiftsOverlapException( - "Chosen bus can't be assigned to shift." - ) - elif self.driver_has_overlapping_shifts(): - raise DriverOtherShiftsOverlapException( - "Choosen driver can't be assigned to shift." - ) - else: - return super().save(*args, **kwargs) + ordered_stops = self.get_ascending_linked_stops() + self.update_stops_related_fields(ordered_stops) + + # Must be done after update_stops_related_fields + self.update_total_duration() + + return super().save(*args, **kwargs) def bus_has_overlapping_shifts(self) -> bool: chosen_bus_shifts: QuerySet[BusShift] = self.bus.shifts @@ -82,16 +77,6 @@ def shifts_end_overlap_with_self(self, shifts: QuerySet) -> bool: .exists() ) - def update_on_linked_stop_change(self): - ordered_stops = self.get_ascending_linked_stops() - self.update_stops_related_fields(ordered_stops) - - # Must be done after update_stops_related_fields - self.update_total_duration() - - self.linked_stops_modifications_only_save() - return - def update_total_duration(self) -> None: self.total_duration = self.end_datetime - self.start_datetime return @@ -126,8 +111,5 @@ def update_end_datetime(self, ordered_stops: QuerySet[BusStop]) -> None: self.end_datetime = DEFAULT_DATETIME_FOR_MISSING_STOPS return - def linked_stops_modifications_only_save(self): - return super().save() - def __str__(self): return f"BusShift: {self.bus} by {self.driver} from {self.start_datetime} to {self.end_datetime} (id: {self.pk})" diff --git a/padam_django/apps/fleet/signals/__init__.py b/padam_django/apps/fleet/signals/__init__.py index 0cebe4bb..7a831011 100644 --- a/padam_django/apps/fleet/signals/__init__.py +++ b/padam_django/apps/fleet/signals/__init__.py @@ -1 +1,2 @@ from .bus_stop_signals import * +from .bus_shift_signals import * diff --git a/padam_django/apps/fleet/signals/bus_shift_signals.py b/padam_django/apps/fleet/signals/bus_shift_signals.py new file mode 100644 index 00000000..74f30521 --- /dev/null +++ b/padam_django/apps/fleet/signals/bus_shift_signals.py @@ -0,0 +1,24 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from padam_django.apps.fleet.models import BusShift +from padam_django.apps.fleet.exceptions import ( + DriverOtherShiftsOverlapException, + BusOtherShiftsOverlapException, +) + + +@receiver(post_save, sender=BusShift) +def ensure_overlapping_is_fine( + sender, instance: BusShift, using, update_fields, **kwargs +): + if instance.bus_has_overlapping_shifts(): + raise BusOtherShiftsOverlapException( + f"{instance.bus} can't be assigned to shift." + ) + elif instance.driver_has_overlapping_shifts(): + raise DriverOtherShiftsOverlapException( + f"{instance.driver} can't be assigned to shift." + ) + else: + return diff --git a/padam_django/apps/fleet/signals/bus_stop_signals.py b/padam_django/apps/fleet/signals/bus_stop_signals.py index a11d2e25..2803cf6d 100644 --- a/padam_django/apps/fleet/signals/bus_stop_signals.py +++ b/padam_django/apps/fleet/signals/bus_stop_signals.py @@ -2,9 +2,25 @@ from django.dispatch import receiver from padam_django.apps.fleet.models import BusStop +from padam_django.apps.fleet.exceptions import ( + DriverOtherShiftsOverlapException, + BusOtherShiftsOverlapException, + StopWouldOverlapBusOtherShifts, + StopWouldOverlapDriverOtherShifts, +) @receiver(post_delete, sender=BusStop) @receiver(post_save, sender=BusStop) def update_linked_shift(sender, instance: BusStop, using, **kwargs): - instance.shift.update_on_linked_stop_change() + try: + instance.shift.save() + except DriverOtherShiftsOverlapException: + raise StopWouldOverlapDriverOtherShifts( + f"Can't set stop {instance.pk} to {instance.datetime}. Would overlap driver's other shifts." + ) + except BusOtherShiftsOverlapException: + raise StopWouldOverlapBusOtherShifts( + f"Can't set stop {instance.pk} to {instance.datetime}. Would overlap bus's other shifts." + ) + return