From a31798d0b0fd1874229c8dc35ab7273f20c7989e Mon Sep 17 00:00:00 2001 From: Emanuele Date: Mon, 1 Dec 2025 14:23:46 +0100 Subject: [PATCH] Improved data ordering in requests and addes ac types CAP10 --- cntmanage/flightslot/admins/course_adm.py | 14 +++++++++- .../flightslot/admins/hourbuilding_adm.py | 4 ++- cntmanage/flightslot/admins/mission_adm.py | 8 ++++-- cntmanage/flightslot/admins/student_adm.py | 21 +++++++------- cntmanage/flightslot/admins/training_adm.py | 4 ++- cntmanage/flightslot/admins/weekpref_adm.py | 12 ++++---- ...pe_alter_hourbuilding_aircraft_and_more.py | 28 +++++++++++++++++++ cntmanage/flightslot/models/aircrafts.py | 1 + 8 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 cntmanage/flightslot/migrations/0025_alter_aircraft_type_alter_hourbuilding_aircraft_and_more.py diff --git a/cntmanage/flightslot/admins/course_adm.py b/cntmanage/flightslot/admins/course_adm.py index 41694c6..191974f 100644 --- a/cntmanage/flightslot/admins/course_adm.py +++ b/cntmanage/flightslot/admins/course_adm.py @@ -1,12 +1,24 @@ from django.contrib import admin +from django.db.models.query import QuerySet +from django.http import HttpRequest from django.utils.safestring import SafeText +from ..models.students import Student from ..models.courses import Course from ..custom.colortag import course_color class CourseAdmin(admin.ModelAdmin): - list_display = ("ctype", "cnumber","color_display", "year") + list_display = ("ctype", "cnumber","color_display", "course_students", "year") list_filter = ("ctype", "year") + + def get_queryset(self, request: HttpRequest) -> QuerySet: + return super().get_queryset(request).order_by("ctype", "cnumber") + + @admin.display(description="Student Number") + def course_students(self, obj: Course) -> SafeText: + if not obj.pk: + return SafeText("") + return SafeText(f"{Student.objects.filter(course = obj.id).count()}") # Dinamically add color_display property to show a colored dot @admin.display(description="Color") diff --git a/cntmanage/flightslot/admins/hourbuilding_adm.py b/cntmanage/flightslot/admins/hourbuilding_adm.py index 133d185..08de6f6 100644 --- a/cntmanage/flightslot/admins/hourbuilding_adm.py +++ b/cntmanage/flightslot/admins/hourbuilding_adm.py @@ -9,6 +9,7 @@ from durationwidget.widgets import TimeDurationWidget from ..models.hourbuildings import HourBuilding, HourBuildingLegBase, HourBuildingLegFlight, HourBuildingLegStop from ..models.weekpref import WeekPreference +from ..models.students import Student from datetime import date @@ -75,8 +76,9 @@ class HourBuildingInLine(nested_admin.NestedTabularInline): # 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) -> bool: if hasattr(request.user, 'student') and obj: + student: Student = request.user.student current_week: int = date.today().isocalendar().week - if current_week > obj.week: + if current_week > obj.week or not student.active: return False return True diff --git a/cntmanage/flightslot/admins/mission_adm.py b/cntmanage/flightslot/admins/mission_adm.py index 9932f43..2851c53 100644 --- a/cntmanage/flightslot/admins/mission_adm.py +++ b/cntmanage/flightslot/admins/mission_adm.py @@ -27,7 +27,7 @@ class MissionProfileResource(ModelResource): def before_import_row(self, row: dict[str, str | Any], **kwargs): row["mtype"] = SafeText(row["mtype"].upper().strip()) row["mnum"] = SafeText(row["mnum"].upper().strip()) - h, m, _ = row["duration"].split(":") + h, m, _ = SafeText(row["duration"].split(":")) row["duration"] = timedelta(hours=float(h), minutes=float(m)) super().before_import_row(row, **kwargs) @@ -46,9 +46,13 @@ class MissionProfileAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): list_display = ("mtype", "mnum", "assigned_aircrafts", "duration", "notes", ) list_filter = ("mtype", ) actions = ("assign_aircraft", ) + resource_classes = [MissionProfileResource] tmp_storage_class = CacheStorage skip_admin_log = True + def get_queryset(self, request: HttpRequest) -> QuerySet[MissionProfile]: + return super().get_queryset(request).order_by("mtype", "mnum") + @action_with_form(ChangeAircraftForm, description="Assign Aircraft") def assign_aircraft(self, request: HttpRequest, queryset: QuerySet[MissionProfile], data: Dict[str, QuerySet[Aircraft]]): ac_types = [t.type for t in data["aircrafts"]] @@ -63,7 +67,7 @@ class MissionProfileAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): mix.aircrafts.add(ac) mix.save() i += 1 - messages.success(request, f"{i} Students updated to {ac_types}") + messages.success(request, f"{i} Missions updated to {ac_types}") @admin.display(description="Assigned Aircrafts") def assigned_aircrafts(self, obj: MissionProfile) -> SafeText: diff --git a/cntmanage/flightslot/admins/student_adm.py b/cntmanage/flightslot/admins/student_adm.py index 419f61c..7ce11b1 100644 --- a/cntmanage/flightslot/admins/student_adm.py +++ b/cntmanage/flightslot/admins/student_adm.py @@ -18,7 +18,7 @@ from ..models.students import Student from ..custom.colortag import course_color -from typing import Any, Dict +from typing import Any, Dict, List # Custom import form to select a course for student input class StudentCustomConfirmImportForm(ConfirmImportForm): @@ -56,18 +56,19 @@ class StudentResource(ModelResource): # Form Class for Student course change class ChangeCourseForm(AdminActionForm): - course = TypedMultipleChoiceField(choices=AircraftTypes) + course = ModelChoiceField(queryset=Course.objects.all().order_by("ctype", "-cnumber")) # Form class to assing aircrafts to students class ChangeAircraftForm(AdminActionForm): - aircrafts = ModelMultipleChoiceField(queryset=Aircraft.objects.distinct('type').all()) + #aircrafts = ModelMultipleChoiceField(queryset=Aircraft.objects.distinct("type").order_by("type")) + aircrafts = TypedMultipleChoiceField(choices=AircraftTypes) class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): model = Student list_display = ("surname", "name", "course", "course_color", "email", "phone", "username", "password", "active", ) list_filter = ("course", "active", ) search_fields = ("surname", "name", "phone", "email", ) - actions = ("change_course", "disable_students", "change_aircraft", ) + actions = ("change_course", "deactivate_students", "change_aircraft", ) resource_classes = [StudentResource] confirm_form_class = StudentCustomConfirmImportForm tmp_storage_class = CacheStorage @@ -88,7 +89,7 @@ class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): return SafeText(obj.default_username()) @admin.action(description="Deactivate Students") - def disable_students(self, request: HttpRequest, queryset: QuerySet[Student]): + def deactivate_students(self, request: HttpRequest, queryset: QuerySet[Student]): for q in queryset.all(): if q.user: q.user.is_staff = False @@ -102,13 +103,13 @@ class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): count: int = queryset.update(course=course) messages.success(request, f"{count} students updated to {course}") - @action_with_form(ChangeAircraftForm, description="Assign Aircraft") - def change_aircraft(self, request: HttpRequest, queryset: QuerySet[Student], data: Dict[str, QuerySet[Aircraft]]): - ac_types = [t.type for t in data["aircrafts"]] + @action_with_form(ChangeAircraftForm, description="Assign Aircraft Type") + def change_aircraft(self, request: HttpRequest, queryset: QuerySet[Student], data: Dict[str, List[AircraftTypes]]): + ac_types: List[AircraftTypes] = data["aircrafts"] ac_query: Q = Q() # Build an or query to select all aircrafts of the specified types - for a in ac_types: + for a in data["aircrafts"]: ac_query |= Q(type=a) - aircrafts: QuerySet[Aircraft] = Aircraft.objects.filter(ac_query).all() # Execute query + aircrafts: QuerySet[Aircraft] = Aircraft.objects.filter(ac_query).order_by("type", "markings").all() # Execute query i: int = 0 for student in queryset: student.aircrafts.clear() diff --git a/cntmanage/flightslot/admins/training_adm.py b/cntmanage/flightslot/admins/training_adm.py index 3d7d464..9f7ed07 100644 --- a/cntmanage/flightslot/admins/training_adm.py +++ b/cntmanage/flightslot/admins/training_adm.py @@ -4,6 +4,7 @@ from django.db import models from django.forms import TextInput, Textarea from django.http import HttpRequest +from ..models.students import Student from ..models.missions import Training from ..models.weekpref import WeekPreference @@ -28,8 +29,9 @@ class TrainingInLIne(nested_admin.NestedTabularInline): # 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) -> bool: if hasattr(request.user, 'student') and obj: + student: Student = request.user.student current_week: int = date.today().isocalendar().week - if current_week > obj.week: + if current_week > obj.week or not student.active: return False return True diff --git a/cntmanage/flightslot/admins/weekpref_adm.py b/cntmanage/flightslot/admins/weekpref_adm.py index 66c94b2..fa74b0b 100644 --- a/cntmanage/flightslot/admins/weekpref_adm.py +++ b/cntmanage/flightslot/admins/weekpref_adm.py @@ -2,12 +2,12 @@ import nested_admin from django.forms import Form from django.db.models.query import QuerySet -from django.contrib.auth.models import User from django.http import HttpRequest, HttpResponse from django.contrib import admin, messages from django.utils.translation import ngettext from django.utils.safestring import SafeText +from ..models.students import Student from ..models.missions import Training from ..models.weekpref import WeekPreference @@ -62,7 +62,7 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin): # If a user is registered as student show only their preferences def get_queryset(self, request: HttpRequest) -> QuerySet[WeekPreference]: - qs = super().get_queryset(request) + qs = super().get_queryset(request).order_by("-week", "-student__course", "student__surname", "student__name") if hasattr(request.user, "student"): return qs.filter(student=request.user.student) # If admin show everything @@ -79,7 +79,7 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin): # If student is current user making request if hasattr(request.user, "student"): - student = request.user.student + student: Student = request.user.student if "student" in form.base_fields: form.base_fields["student"].initial = student form.base_fields["student"].disabled = True @@ -89,8 +89,9 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin): # 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) -> bool: if hasattr(request.user, "student") and obj: + student: Student = request.user.student current_week = date.today().isocalendar().week - if current_week > obj.week: + if current_week > obj.week or not student.active: return False return True @@ -105,9 +106,10 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin): def changeform_view(self, request: HttpRequest, object_id: int | None = None, form_url: str = "", extra_context=None): extra_context = extra_context or {} if hasattr(request.user, "student") and object_id: + student: Student = request.user.student current_week = date.today().isocalendar().week weekpref = WeekPreference.objects.get(id=object_id) - if current_week > weekpref.week: + if current_week > weekpref.week or not student.active: extra_context["show_save"] = False extra_context["show_save_and_continue"] = False extra_context["show_save_and_add_another"] = False diff --git a/cntmanage/flightslot/migrations/0025_alter_aircraft_type_alter_hourbuilding_aircraft_and_more.py b/cntmanage/flightslot/migrations/0025_alter_aircraft_type_alter_hourbuilding_aircraft_and_more.py new file mode 100644 index 0000000..d7688c5 --- /dev/null +++ b/cntmanage/flightslot/migrations/0025_alter_aircraft_type_alter_hourbuilding_aircraft_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.8 on 2025-12-01 12:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0024_alter_missionprofile_mtype'), + ] + + operations = [ + migrations.AlterField( + model_name='aircraft', + name='type', + field=models.CharField(choices=[('C152', 'Cessna 152'), ('P208', 'Tecnam P2008'), ('PA28', 'Piper PA28R'), ('PA34', 'Piper PA34'), ('C182', 'Cessna 182Q'), ('TWEN', 'Tecnam P2010'), ('CP10', 'Cap 10'), ('FSTD', 'Alsim ALX40')], max_length=4), + ), + migrations.AlterField( + model_name='hourbuilding', + name='aircraft', + field=models.CharField(choices=[('C152', 'Cessna 152'), ('P208', 'Tecnam P2008'), ('PA28', 'Piper PA28R'), ('PA34', 'Piper PA34'), ('C182', 'Cessna 182Q'), ('TWEN', 'Tecnam P2010'), ('CP10', 'Cap 10'), ('FSTD', 'Alsim ALX40')]), + ), + migrations.AlterField( + model_name='weekpreference', + name='week', + field=models.PositiveSmallIntegerField(auto_created=True, db_default=49, db_index=True, verbose_name='Week Number'), + ), + ] diff --git a/cntmanage/flightslot/models/aircrafts.py b/cntmanage/flightslot/models/aircrafts.py index 53e3935..f9eb184 100644 --- a/cntmanage/flightslot/models/aircrafts.py +++ b/cntmanage/flightslot/models/aircrafts.py @@ -8,6 +8,7 @@ class AircraftTypes(models.TextChoices): PA34 = "PA34", _("Piper PA34") C182 = "C182", _("Cessna 182Q") P210 = "TWEN", _("Tecnam P2010") + CP10 = "CP10", _("Cap 10") ALX40 = "FSTD", _("Alsim ALX40") class Aircraft(models.Model):