Improved data ordering in requests and addes ac types CAP10

This commit is contained in:
2025-12-01 14:23:46 +01:00
parent af62bf843c
commit a31798d0b0
8 changed files with 72 additions and 20 deletions

View File

@@ -1,12 +1,24 @@
from django.contrib import admin from django.contrib import admin
from django.db.models.query import QuerySet
from django.http import HttpRequest
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
from ..models.students import Student
from ..models.courses import Course from ..models.courses import Course
from ..custom.colortag import course_color from ..custom.colortag import course_color
class CourseAdmin(admin.ModelAdmin): class CourseAdmin(admin.ModelAdmin):
list_display = ("ctype", "cnumber","color_display", "year") list_display = ("ctype", "cnumber","color_display", "course_students", "year")
list_filter = ("ctype", "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 # Dinamically add color_display property to show a colored dot
@admin.display(description="Color") @admin.display(description="Color")

View File

@@ -9,6 +9,7 @@ from durationwidget.widgets import TimeDurationWidget
from ..models.hourbuildings import HourBuilding, HourBuildingLegBase, HourBuildingLegFlight, HourBuildingLegStop from ..models.hourbuildings import HourBuilding, HourBuildingLegBase, HourBuildingLegFlight, HourBuildingLegStop
from ..models.weekpref import WeekPreference from ..models.weekpref import WeekPreference
from ..models.students import Student
from datetime import date 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 # 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: def has_change_permission(self, request: HttpRequest, obj: WeekPreference | None = None) -> bool:
if hasattr(request.user, 'student') and obj: if hasattr(request.user, 'student') and obj:
student: Student = request.user.student
current_week: int = date.today().isocalendar().week current_week: int = date.today().isocalendar().week
if current_week > obj.week: if current_week > obj.week or not student.active:
return False return False
return True return True

View File

@@ -27,7 +27,7 @@ class MissionProfileResource(ModelResource):
def before_import_row(self, row: dict[str, str | Any], **kwargs): def before_import_row(self, row: dict[str, str | Any], **kwargs):
row["mtype"] = SafeText(row["mtype"].upper().strip()) row["mtype"] = SafeText(row["mtype"].upper().strip())
row["mnum"] = SafeText(row["mnum"].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)) row["duration"] = timedelta(hours=float(h), minutes=float(m))
super().before_import_row(row, **kwargs) 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_display = ("mtype", "mnum", "assigned_aircrafts", "duration", "notes", )
list_filter = ("mtype", ) list_filter = ("mtype", )
actions = ("assign_aircraft", ) actions = ("assign_aircraft", )
resource_classes = [MissionProfileResource]
tmp_storage_class = CacheStorage tmp_storage_class = CacheStorage
skip_admin_log = True 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") @action_with_form(ChangeAircraftForm, description="Assign Aircraft")
def assign_aircraft(self, request: HttpRequest, queryset: QuerySet[MissionProfile], data: Dict[str, QuerySet[Aircraft]]): def assign_aircraft(self, request: HttpRequest, queryset: QuerySet[MissionProfile], data: Dict[str, QuerySet[Aircraft]]):
ac_types = [t.type for t in data["aircrafts"]] ac_types = [t.type for t in data["aircrafts"]]
@@ -63,7 +67,7 @@ class MissionProfileAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
mix.aircrafts.add(ac) mix.aircrafts.add(ac)
mix.save() mix.save()
i += 1 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") @admin.display(description="Assigned Aircrafts")
def assigned_aircrafts(self, obj: MissionProfile) -> SafeText: def assigned_aircrafts(self, obj: MissionProfile) -> SafeText:

View File

@@ -18,7 +18,7 @@ from ..models.students import Student
from ..custom.colortag import course_color 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 # Custom import form to select a course for student input
class StudentCustomConfirmImportForm(ConfirmImportForm): class StudentCustomConfirmImportForm(ConfirmImportForm):
@@ -56,18 +56,19 @@ class StudentResource(ModelResource):
# Form Class for Student course change # Form Class for Student course change
class ChangeCourseForm(AdminActionForm): class ChangeCourseForm(AdminActionForm):
course = TypedMultipleChoiceField(choices=AircraftTypes) course = ModelChoiceField(queryset=Course.objects.all().order_by("ctype", "-cnumber"))
# Form class to assing aircrafts to students # Form class to assing aircrafts to students
class ChangeAircraftForm(AdminActionForm): 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): class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
model = Student model = Student
list_display = ("surname", "name", "course", "course_color", "email", "phone", "username", "password", "active", ) list_display = ("surname", "name", "course", "course_color", "email", "phone", "username", "password", "active", )
list_filter = ("course", "active", ) list_filter = ("course", "active", )
search_fields = ("surname", "name", "phone", "email", ) search_fields = ("surname", "name", "phone", "email", )
actions = ("change_course", "disable_students", "change_aircraft", ) actions = ("change_course", "deactivate_students", "change_aircraft", )
resource_classes = [StudentResource] resource_classes = [StudentResource]
confirm_form_class = StudentCustomConfirmImportForm confirm_form_class = StudentCustomConfirmImportForm
tmp_storage_class = CacheStorage tmp_storage_class = CacheStorage
@@ -88,7 +89,7 @@ class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
return SafeText(obj.default_username()) return SafeText(obj.default_username())
@admin.action(description="Deactivate Students") @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(): for q in queryset.all():
if q.user: if q.user:
q.user.is_staff = False q.user.is_staff = False
@@ -102,13 +103,13 @@ class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
count: int = queryset.update(course=course) count: int = queryset.update(course=course)
messages.success(request, f"{count} students updated to {course}") messages.success(request, f"{count} students updated to {course}")
@action_with_form(ChangeAircraftForm, description="Assign Aircraft") @action_with_form(ChangeAircraftForm, description="Assign Aircraft Type")
def change_aircraft(self, request: HttpRequest, queryset: QuerySet[Student], data: Dict[str, QuerySet[Aircraft]]): def change_aircraft(self, request: HttpRequest, queryset: QuerySet[Student], data: Dict[str, List[AircraftTypes]]):
ac_types = [t.type for t in data["aircrafts"]] ac_types: List[AircraftTypes] = data["aircrafts"]
ac_query: Q = Q() # Build an or query to select all aircrafts of the specified types 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) 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 i: int = 0
for student in queryset: for student in queryset:
student.aircrafts.clear() student.aircrafts.clear()

View File

@@ -4,6 +4,7 @@ from django.db import models
from django.forms import TextInput, Textarea from django.forms import TextInput, Textarea
from django.http import HttpRequest from django.http import HttpRequest
from ..models.students import Student
from ..models.missions import Training from ..models.missions import Training
from ..models.weekpref import WeekPreference 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 # 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: def has_change_permission(self, request: HttpRequest, obj: WeekPreference | None = None) -> bool:
if hasattr(request.user, 'student') and obj: if hasattr(request.user, 'student') and obj:
student: Student = request.user.student
current_week: int = date.today().isocalendar().week current_week: int = date.today().isocalendar().week
if current_week > obj.week: if current_week > obj.week or not student.active:
return False return False
return True return True

View File

@@ -2,12 +2,12 @@ import nested_admin
from django.forms import Form from django.forms import Form
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.contrib.auth.models import User
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.contrib import admin, messages from django.contrib import admin, messages
from django.utils.translation import ngettext from django.utils.translation import ngettext
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
from ..models.students import Student
from ..models.missions import Training from ..models.missions import Training
from ..models.weekpref import WeekPreference 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 # If a user is registered as student show only their preferences
def get_queryset(self, request: HttpRequest) -> QuerySet[WeekPreference]: 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"): if hasattr(request.user, "student"):
return qs.filter(student=request.user.student) return qs.filter(student=request.user.student)
# If admin show everything # If admin show everything
@@ -79,7 +79,7 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
# If student is current user making request # If student is current user making request
if hasattr(request.user, "student"): if hasattr(request.user, "student"):
student = request.user.student student: Student = request.user.student
if "student" in form.base_fields: if "student" in form.base_fields:
form.base_fields["student"].initial = student form.base_fields["student"].initial = student
form.base_fields["student"].disabled = True 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 # 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: def has_change_permission(self, request: HttpRequest, obj: WeekPreference | None = None) -> bool:
if hasattr(request.user, "student") and obj: if hasattr(request.user, "student") and obj:
student: Student = request.user.student
current_week = date.today().isocalendar().week current_week = date.today().isocalendar().week
if current_week > obj.week: if current_week > obj.week or not student.active:
return False return False
return True 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): def changeform_view(self, request: HttpRequest, object_id: int | None = None, form_url: str = "", extra_context=None):
extra_context = extra_context or {} extra_context = extra_context or {}
if hasattr(request.user, "student") and object_id: if hasattr(request.user, "student") and object_id:
student: Student = request.user.student
current_week = date.today().isocalendar().week current_week = date.today().isocalendar().week
weekpref = WeekPreference.objects.get(id=object_id) 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"] = False
extra_context["show_save_and_continue"] = False extra_context["show_save_and_continue"] = False
extra_context["show_save_and_add_another"] = False extra_context["show_save_and_add_another"] = False

View File

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

View File

@@ -8,6 +8,7 @@ class AircraftTypes(models.TextChoices):
PA34 = "PA34", _("Piper PA34") PA34 = "PA34", _("Piper PA34")
C182 = "C182", _("Cessna 182Q") C182 = "C182", _("Cessna 182Q")
P210 = "TWEN", _("Tecnam P2010") P210 = "TWEN", _("Tecnam P2010")
CP10 = "CP10", _("Cap 10")
ALX40 = "FSTD", _("Alsim ALX40") ALX40 = "FSTD", _("Alsim ALX40")
class Aircraft(models.Model): class Aircraft(models.Model):