from django.forms import ModelMultipleChoiceField from django.contrib import admin, messages from django.http import HttpRequest from django.db.models.query import QuerySet, Q from django.utils.safestring import SafeText from django_admin_action_forms import AdminActionFormsMixin, AdminActionForm, action_with_form from import_export import fields from import_export.admin import ImportMixin from import_export.tmp_storages import CacheStorage from import_export.resources import ModelResource from ..models.aircrafts import Aircraft from ..models.missions import MissionProfile from datetime import timedelta from typing import Any, Dict # Resource Class for Student data import class MissionProfileResource(ModelResource): mtype = fields.Field(attribute="mtype", column_name="mtype") mnum = fields.Field(attribute="mnum", column_name="mnum") duration = fields.Field(attribute="duration", column_name="duration") # Cleanup fields before entering 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(":") row["duration"] = timedelta(hours=float(h), minutes=float(m)) super().before_import_row(row, **kwargs) class Meta: model = MissionProfile skip_unchanged = True report_skipped = True fields = ("mtype", "mnum", "duration",) import_id_fields = ("mtype", "mnum",) # Form class to assing aircrafts to students class ChangeAircraftForm(AdminActionForm): aircrafts = ModelMultipleChoiceField(queryset=Aircraft.objects.distinct('type').all()) 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"]] ac_query: Q = Q() # Build an or query to select all aircrafts of the specified types for a in ac_types: ac_query |= Q(type=a) aircrafts: QuerySet[Aircraft] = Aircraft.objects.filter(ac_query).all() # Execute query i: int = 0 for mix in queryset: mix.aircrafts.clear() for ac in aircrafts: mix.aircrafts.add(ac) mix.save() i += 1 messages.success(request, f"{i} Missions updated to {ac_types}") @admin.display(description="Assigned Aircrafts") def assigned_aircrafts(self, obj: MissionProfile) -> SafeText: if not obj.aircrafts: return SafeText("") return SafeText("/".join(ac.markings for ac in obj.aircrafts.all()))