polymorphic #1

Merged
Obbart merged 6 commits from polymorphic into flightslot 2025-11-24 12:22:02 +01:00
9 changed files with 124 additions and 51 deletions
Showing only changes of commit 02990d4b2f - Show all commits

View File

@@ -43,6 +43,7 @@ INSTALLED_APPS = [
'colorfield', 'colorfield',
'import_export', 'import_export',
'django_admin_action_forms', 'django_admin_action_forms',
'polymorphic'
] ]
# Import Export plugin settings # Import Export plugin settings
@@ -52,7 +53,7 @@ IMPORT_EXPORT_SKIP_ADMIN_LOG = True
IMPORT_FORMATS = [CSV] IMPORT_FORMATS = [CSV]
MIDDLEWARE = [ MIDDLEWARE = [
'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', 'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', # custom middleware to show "user" page to non superuser
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',

View File

@@ -10,7 +10,7 @@ from typing import List
from ..models.weekpref import WeekPreference from ..models.weekpref import WeekPreference
from ..models.missions import Training 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: 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_name if h.sunday else ""
] ]
hb_notes = f"{h.notes}\n----\n" if h.notes else "" hb_notes = f"{h.notes}\n----\n" if h.notes else ""
hb_legs = HourBuildingLeg.objects.filter(hb_id = h.id) hb_legs = HourBuildingLegFlight.objects.filter(hb_id = h.id)
for hh in hb_legs: #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 += 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_notes.strip('\n')
hb_data.append([str(q.week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes]) hb_data.append([str(q.week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes])

View File

@@ -10,6 +10,7 @@ from .admins.course_adm import CourseAdmin
from .admins.student_adm import StudentAdmin from .admins.student_adm import StudentAdmin
from .admins.mission_adm import MissionProfileAdmin from .admins.mission_adm import MissionProfileAdmin
from .admins.weekpred_adm import WeekPreferenceAdmin from .admins.weekpred_adm import WeekPreferenceAdmin
#from .admins.hourbuilding_adm import HourBuilding, HourBuildingInLine
from django.contrib.admin import AdminSite from django.contrib.admin import AdminSite

View File

@@ -7,32 +7,33 @@ from django.http import HttpRequest
from durationwidget.widgets import TimeDurationWidget 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 ..models.weekpref import WeekPreference
from datetime import date from datetime import date
class HourBuildingLegForm(forms.ModelForm): class HourBuildingLegForm(forms.ModelForm):
class Meta: class Meta:
model = HourBuildingLeg model = HourBuildingLegFlight
fields = '__all__' fields = '__all__'
widgets = { widgets = {
'time': TimeDurationWidget(show_days=False, 'time': TimeDurationWidget(show_days=False,
show_seconds=False show_seconds=False)
)
} }
# Register your models here. # Register your models here.
class HourBuildingLegInline(nested_admin.NestedTabularInline): class HourBuildingLegBaseInLine(nested_admin.NestedStackedPolymorphicInline):
model = HourBuildingLeg model = HourBuildingLegBase
form = HourBuildingLegForm
extra = 0
fk_name = 'hb' fk_name = 'hb'
max_num = 5 verbose_name_plural = "Hour Building Legs"
formfield_overrides = {
models.CharField: {'widget': TextInput(attrs={'size':'20'})}, class HourBuildingLegFlightInLine(nested_admin.NestedStackedPolymorphicInline.Child):
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':35})}, 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 # 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): 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): def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None):
return self.has_change_permission(request=request, obj=obj) return self.has_change_permission(request=request, obj=obj)
class HourBuildingInLine(nested_admin.NestedTabularInline): class HourBuildingInLine(nested_admin.NestedTabularInline):
model = HourBuilding model = HourBuilding
extra = 0 extra = 1
inlines = [HourBuildingLegInline] inlines = (HourBuildingLegBaseInLine,)
fk_name = 'weekpref' fk_name = 'weekpref'
verbose_name_plural = "Hour Building" verbose_name_plural = "Hour Buildings"
max_num = 7 max_num = 7
formfield_overrides = { formfield_overrides = {
models.CharField: {'widget': TextInput(attrs={'size':'20'})}, models.CharField: {'widget': TextInput(attrs={'size':'20'})},
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':35})}, 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)

View File

@@ -18,8 +18,8 @@ from ..actions.exportweek import export_selected
from datetime import date from datetime import date
class WeekPreferenceAdmin(nested_admin.NestedModelAdmin): class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
inlines = (TrainingInLIne, HourBuildingInLine,) inlines = (TrainingInLIne, HourBuildingInLine, )
list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",) list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",)
list_filter = ("week", "student__course", "student",) list_filter = ("week", "student__course", "student",)
actions = ("export",) actions = ("export",)

View File

@@ -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',
),
]

View File

@@ -2,10 +2,11 @@ from django.utils.translation import gettext_lazy as _
from django.db import models from django.db import models
from datetime import timedelta from datetime import timedelta
from polymorphic.models import PolymorphicModel
from ..models.weekpref import WeekPreference from ..models.weekpref import WeekPreference
from ..models.aircrafts import AircraftTypes from ..models.aircrafts import AircraftTypes
class HourBuilding(models.Model): class HourBuilding(models.Model):
id = models.BigAutoField( id = models.BigAutoField(
primary_key=True primary_key=True
@@ -66,7 +67,7 @@ class HourBuilding(models.Model):
def __str__(self): def __str__(self):
return f"Hour Building: {self.aircraft}" return f"Hour Building: {self.aircraft}"
class HourBuildingLeg(models.Model): class HourBuildingLegBase(PolymorphicModel):
id = models.BigAutoField( id = models.BigAutoField(
primary_key=True primary_key=True
) )
@@ -76,6 +77,12 @@ class HourBuildingLeg(models.Model):
on_delete=models.CASCADE on_delete=models.CASCADE
) )
time = models.DurationField(
null=False,
default = timedelta(hours=1)
)
class HourBuildingLegFlight(HourBuildingLegBase):
departure = models.CharField( departure = models.CharField(
null=False, null=False,
blank=False, blank=False,
@@ -90,18 +97,17 @@ class HourBuildingLeg(models.Model):
max_length=4 max_length=4
) )
time = models.DurationField(
null=False,
default = timedelta(hours=1)
)
stop = models.BooleanField(
default=False
)
def __str__(self): def __str__(self):
if self.stop: return f"Flight Leg: {self.departure} -> {self.destination}"
return "Refuelling Stop"
else: class HourBuildingLegStop(HourBuildingLegBase):
return f"Flight Leg: {self.departure} -> {self.destination}" location = models.CharField(
null=False,
blank=False,
default="LILV",
max_length=4
)
refuel = models.BooleanField(
default=False
)

17
cntmanage/poetry.lock generated
View File

@@ -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"] 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"] 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]] [[package]]
name = "et-xmlfile" name = "et-xmlfile"
version = "2.0.0" version = "2.0.0"
@@ -421,4 +436,4 @@ files = [
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "6bf43236f441d8b6bf8d1928910d169d3b29cfa499bb7d09d97ea227f8115658" content-hash = "e932d0af75c888d83fecefaaad1d018c508881a3bfde2ea640a82790e3567855"

View File

@@ -17,6 +17,7 @@ django-import-export = "^4.3.13"
django-colorfield = "^0.14.0" django-colorfield = "^0.14.0"
openpyxl = "^3.1.5" openpyxl = "^3.1.5"
django-admin-action-forms = "^2.2.1" django-admin-action-forms = "^2.2.1"
django-polymorphic = "^4.1.0"
[build-system] [build-system]