From 02990d4b2fadef391907b9f37dc2ba206c8fe104 Mon Sep 17 00:00:00 2001 From: Emanuele Date: Fri, 21 Nov 2025 15:25:29 +0100 Subject: [PATCH] Nested polymorphic hour building legs and stops added, needs restyling --- cntmanage/cntmanage/settings.py | 3 +- cntmanage/flightslot/actions/exportweek.py | 8 +-- cntmanage/flightslot/admin.py | 1 + .../flightslot/admins/hourbuilding_adm.py | 45 ++++++-------- cntmanage/flightslot/admins/weekpred_adm.py | 4 +- ...glegbase_hourbuildinglegflight_and_more.py | 58 +++++++++++++++++++ cntmanage/flightslot/models/hourbuildings.py | 38 +++++++----- cntmanage/poetry.lock | 17 +++++- cntmanage/pyproject.toml | 1 + 9 files changed, 124 insertions(+), 51 deletions(-) create mode 100644 cntmanage/flightslot/migrations/0018_hourbuildinglegbase_hourbuildinglegflight_and_more.py diff --git a/cntmanage/cntmanage/settings.py b/cntmanage/cntmanage/settings.py index bbfd0ad..f190c78 100644 --- a/cntmanage/cntmanage/settings.py +++ b/cntmanage/cntmanage/settings.py @@ -43,6 +43,7 @@ INSTALLED_APPS = [ 'colorfield', 'import_export', 'django_admin_action_forms', + 'polymorphic' ] # Import Export plugin settings @@ -52,7 +53,7 @@ IMPORT_EXPORT_SKIP_ADMIN_LOG = True IMPORT_FORMATS = [CSV] MIDDLEWARE = [ - 'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', + 'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', # custom middleware to show "user" page to non superuser 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', diff --git a/cntmanage/flightslot/actions/exportweek.py b/cntmanage/flightslot/actions/exportweek.py index 3587ac8..7c80cc3 100644 --- a/cntmanage/flightslot/actions/exportweek.py +++ b/cntmanage/flightslot/actions/exportweek.py @@ -10,7 +10,7 @@ from typing import List from ..models.weekpref import WeekPreference from ..models.missions import Training -from ..models.hourbuildings import HourBuilding,HourBuildingLeg +from ..models.hourbuildings import HourBuilding, HourBuildingLegFlight, HourBuildingLegStop def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse: @@ -121,9 +121,9 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> hb_name if h.sunday else "" ] hb_notes = f"{h.notes}\n----\n" if h.notes else "" - hb_legs = HourBuildingLeg.objects.filter(hb_id = h.id) - for hh in hb_legs: - hb_notes += f"{hh.departure} -> {hh.destination} [{hh.time}]\n" if not hh.stop else f"STOP at {hh.departure} [{hh.time}]\n" + hb_legs = HourBuildingLegFlight.objects.filter(hb_id = h.id) + #for hh in hb_legs: + # hb_notes += f"{hh.departure} -> {hh.destination} [{hh.time}]\n" if not hh.stop else f"STOP at {hh.departure} [{hh.time}]\n" hb_notes.strip('\n') hb_data.append([str(q.week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes]) diff --git a/cntmanage/flightslot/admin.py b/cntmanage/flightslot/admin.py index 4823d35..a81539a 100644 --- a/cntmanage/flightslot/admin.py +++ b/cntmanage/flightslot/admin.py @@ -10,6 +10,7 @@ from .admins.course_adm import CourseAdmin from .admins.student_adm import StudentAdmin from .admins.mission_adm import MissionProfileAdmin from .admins.weekpred_adm import WeekPreferenceAdmin +#from .admins.hourbuilding_adm import HourBuilding, HourBuildingInLine from django.contrib.admin import AdminSite diff --git a/cntmanage/flightslot/admins/hourbuilding_adm.py b/cntmanage/flightslot/admins/hourbuilding_adm.py index 9e7fc06..186ebf6 100644 --- a/cntmanage/flightslot/admins/hourbuilding_adm.py +++ b/cntmanage/flightslot/admins/hourbuilding_adm.py @@ -7,32 +7,33 @@ from django.http import HttpRequest from durationwidget.widgets import TimeDurationWidget -from ..models.hourbuildings import HourBuilding, HourBuildingLeg +from ..models.hourbuildings import HourBuilding, HourBuildingLegBase, HourBuildingLegFlight, HourBuildingLegStop from ..models.weekpref import WeekPreference from datetime import date class HourBuildingLegForm(forms.ModelForm): class Meta: - model = HourBuildingLeg + model = HourBuildingLegFlight fields = '__all__' widgets = { 'time': TimeDurationWidget(show_days=False, - show_seconds=False - ) + show_seconds=False) } # Register your models here. -class HourBuildingLegInline(nested_admin.NestedTabularInline): - model = HourBuildingLeg - form = HourBuildingLegForm - extra = 0 +class HourBuildingLegBaseInLine(nested_admin.NestedStackedPolymorphicInline): + model = HourBuildingLegBase fk_name = 'hb' - max_num = 5 - formfield_overrides = { - models.CharField: {'widget': TextInput(attrs={'size':'20'})}, - models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':35})}, - } + verbose_name_plural = "Hour Building Legs" + + class HourBuildingLegFlightInLine(nested_admin.NestedStackedPolymorphicInline.Child): + model = HourBuildingLegFlight + form = HourBuildingLegForm + class HourBuildingLegStopInLine(nested_admin.NestedStackedPolymorphicInline.Child): + model = HourBuildingLegStop + + child_inlines = (HourBuildingLegFlightInLine, HourBuildingLegStopInLine, ) # If user is a student deny edit permission for week past the current one def has_change_permission(self, request: HttpRequest, obj: HourBuilding | None = None): @@ -45,26 +46,16 @@ class HourBuildingLegInline(nested_admin.NestedTabularInline): def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None): return self.has_change_permission(request=request, obj=obj) + class HourBuildingInLine(nested_admin.NestedTabularInline): model = HourBuilding - extra = 0 - inlines = [HourBuildingLegInline] + extra = 1 + inlines = (HourBuildingLegBaseInLine,) fk_name = 'weekpref' - verbose_name_plural = "Hour Building" + verbose_name_plural = "Hour Buildings" max_num = 7 formfield_overrides = { models.CharField: {'widget': TextInput(attrs={'size':'20'})}, models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':35})}, } - - # If user is a student deny edit permission for week past the current one - def has_change_permission(self, request: HttpRequest, obj: WeekPreference | None = None): - if hasattr(request.user, 'student') and obj: - current_week = date.today().isocalendar().week - if current_week > obj.week: - return False - return True - - def has_delete_permission(self, request: HttpRequest, obj: WeekPreference | None = None): - return self.has_change_permission(request=request, obj=obj) diff --git a/cntmanage/flightslot/admins/weekpred_adm.py b/cntmanage/flightslot/admins/weekpred_adm.py index ec17676..dd4fd06 100644 --- a/cntmanage/flightslot/admins/weekpred_adm.py +++ b/cntmanage/flightslot/admins/weekpred_adm.py @@ -18,8 +18,8 @@ from ..actions.exportweek import export_selected from datetime import date -class WeekPreferenceAdmin(nested_admin.NestedModelAdmin): - inlines = (TrainingInLIne, HourBuildingInLine,) +class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin): + inlines = (TrainingInLIne, HourBuildingInLine, ) list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",) list_filter = ("week", "student__course", "student",) actions = ("export",) diff --git a/cntmanage/flightslot/migrations/0018_hourbuildinglegbase_hourbuildinglegflight_and_more.py b/cntmanage/flightslot/migrations/0018_hourbuildinglegbase_hourbuildinglegflight_and_more.py new file mode 100644 index 0000000..6158fff --- /dev/null +++ b/cntmanage/flightslot/migrations/0018_hourbuildinglegbase_hourbuildinglegflight_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 5.2.8 on 2025-11-21 11:20 + +import datetime +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('flightslot', '0017_alter_missionprofile_mtype_alter_weekpreference_week'), + ] + + operations = [ + migrations.CreateModel( + name='HourBuildingLegBase', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('time', models.DurationField(default=datetime.timedelta(seconds=3600))), + ('hb', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='flightslot.hourbuilding')), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + ), + migrations.CreateModel( + name='HourBuildingLegFlight', + fields=[ + ('hourbuildinglegbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='flightslot.hourbuildinglegbase')), + ('departure', models.CharField(default='LILV', max_length=4)), + ('destination', models.CharField(default='LILV', max_length=4)), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + bases=('flightslot.hourbuildinglegbase',), + ), + migrations.CreateModel( + name='HourBuildingLegStop', + fields=[ + ('hourbuildinglegbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='flightslot.hourbuildinglegbase')), + ('location', models.CharField(default='LILV', max_length=4)), + ('refuel', models.BooleanField(default=False)), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + bases=('flightslot.hourbuildinglegbase',), + ), + migrations.DeleteModel( + name='HourBuildingLeg', + ), + ] diff --git a/cntmanage/flightslot/models/hourbuildings.py b/cntmanage/flightslot/models/hourbuildings.py index 33d3c4e..608f0f1 100644 --- a/cntmanage/flightslot/models/hourbuildings.py +++ b/cntmanage/flightslot/models/hourbuildings.py @@ -2,10 +2,11 @@ from django.utils.translation import gettext_lazy as _ from django.db import models from datetime import timedelta +from polymorphic.models import PolymorphicModel + from ..models.weekpref import WeekPreference from ..models.aircrafts import AircraftTypes - class HourBuilding(models.Model): id = models.BigAutoField( primary_key=True @@ -66,7 +67,7 @@ class HourBuilding(models.Model): def __str__(self): return f"Hour Building: {self.aircraft}" -class HourBuildingLeg(models.Model): +class HourBuildingLegBase(PolymorphicModel): id = models.BigAutoField( primary_key=True ) @@ -76,6 +77,12 @@ class HourBuildingLeg(models.Model): on_delete=models.CASCADE ) + time = models.DurationField( + null=False, + default = timedelta(hours=1) + ) + +class HourBuildingLegFlight(HourBuildingLegBase): departure = models.CharField( null=False, blank=False, @@ -90,18 +97,17 @@ class HourBuildingLeg(models.Model): max_length=4 ) - time = models.DurationField( - null=False, - default = timedelta(hours=1) - ) - - stop = models.BooleanField( - default=False - ) - def __str__(self): - if self.stop: - return "Refuelling Stop" - else: - return f"Flight Leg: {self.departure} -> {self.destination}" - \ No newline at end of file + return f"Flight Leg: {self.departure} -> {self.destination}" + +class HourBuildingLegStop(HourBuildingLegBase): + location = models.CharField( + null=False, + blank=False, + default="LILV", + max_length=4 + ) + + refuel = models.BooleanField( + default=False + ) \ No newline at end of file diff --git a/cntmanage/poetry.lock b/cntmanage/poetry.lock index 141d35f..da7f0b3 100644 --- a/cntmanage/poetry.lock +++ b/cntmanage/poetry.lock @@ -143,6 +143,21 @@ python-monkey-business = ">=1.0.0" dev = ["Pillow", "black", "dj-database-url", "django-selenosis", "flake8", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] test = ["Pillow", "dj-database-url", "django-selenosis", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] +[[package]] +name = "django-polymorphic" +version = "4.1.0" +description = "Seamless polymorphic inheritance for Django models" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "django_polymorphic-4.1.0-py3-none-any.whl", hash = "sha256:0ce3984999e103a0d1a434a5c5617f2c7f990dc3d5fb3585ce0fadadf9ff90ea"}, + {file = "django_polymorphic-4.1.0.tar.gz", hash = "sha256:4438d95a0aef6c4307cd6c83ead387e1142ce80b65188a931ec2f0dbdd9bfc51"}, +] + +[package.dependencies] +Django = ">=3.2" + [[package]] name = "et-xmlfile" version = "2.0.0" @@ -421,4 +436,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "6bf43236f441d8b6bf8d1928910d169d3b29cfa499bb7d09d97ea227f8115658" +content-hash = "e932d0af75c888d83fecefaaad1d018c508881a3bfde2ea640a82790e3567855" diff --git a/cntmanage/pyproject.toml b/cntmanage/pyproject.toml index 6f403fb..aa51730 100644 --- a/cntmanage/pyproject.toml +++ b/cntmanage/pyproject.toml @@ -17,6 +17,7 @@ django-import-export = "^4.3.13" django-colorfield = "^0.14.0" openpyxl = "^3.1.5" django-admin-action-forms = "^2.2.1" +django-polymorphic = "^4.1.0" [build-system]