from django.forms import ModelChoiceField, TypedMultipleChoiceField from django.db.models.query import QuerySet from django.http import HttpRequest from django.contrib import admin, messages from django.utils.safestring import SafeText 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 import_export.forms import ConfirmImportForm, ImportForm from django_admin_action_forms import AdminActionFormsMixin, AdminActionForm, ActionForm, action_with_form from admin_confirm import AdminConfirmMixin, confirm_action from ..models.aircrafts import AircraftTypes from ..models.courses import Course from ..models.students import Student from ..actions.assign_aircraft import assign_aircraft from ..actions.send_email import send_mail_password from ..custom.colortag import course_color from typing import Any, Dict, List # Custom import form to select a course for student input class StudentCustomConfirmImportForm(ConfirmImportForm): course = ModelChoiceField( queryset=Course.objects.all(), required=False) # Resource Class for Student data import class StudentResource(ModelResource): surname = fields.Field(attribute="surname", column_name="surname") name = fields.Field(attribute="name", column_name="name") email = fields.Field(attribute="email", column_name="email") phone = fields.Field(attribute="phone", column_name="phone") # Cleanup fields before entering def before_import_row(self, row: Dict[str, str], **kwargs) -> None: row["name"] = SafeText("-".join(c.capitalize() for c in row["name"].split(" ")).strip()) row["surname"] = SafeText("-".join(c.capitalize() for c in row["surname"].split(" ")).strip()) row["phone"] = SafeText(row["phone"].replace(" ","")) row["email"] = SafeText(row["email"].lower().strip()) return super().before_import_row(row, **kwargs) # If course was addedd as a form kwasrg add it to the student after creation def after_init_instance(self, instance: Student, new: bool, row: Dict[str, str], **kwargs: Dict[str, Any | Course]): course = kwargs.get("course", None) if course and isinstance(course, Course): instance.course = course class Meta: model = Student skip_unchanged = True report_skipped = True fields = ("surname", "name", "email", "phone",) import_id_fields = ("email", "phone",) # Form Class for Student course change class ChangeCourseForm(AdminActionForm): course = ModelChoiceField(queryset=Course.objects.all().order_by("ctype", "-cnumber")) # Form class to assing aircrafts to students class ChangeAircraftForm(AdminActionForm): aircrafts = TypedMultipleChoiceField(choices=AircraftTypes) class StudentAdmin(ImportMixin, AdminConfirmMixin, 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", "deactivate_students", "change_aircraft", "send_mail", ) resource_classes = [StudentResource] confirm_form_class = StudentCustomConfirmImportForm tmp_storage_class = CacheStorage skip_admin_log = True @admin.display(description="Color") def course_color(self, obj: Student) -> SafeText: if not obj.course: return SafeText("") return course_color(obj.course.color) @admin.display(description="Password") def password(self, obj: Student) -> SafeText: return SafeText(obj.default_password()) @admin.display(description="Username") def username(self, obj: Student) -> SafeText: return SafeText(obj.default_username()) @admin.action(description="Deactivate Students") def deactivate_students(self, request: HttpRequest, queryset: QuerySet[Student]): for q in queryset.all(): if q.user: q.user.is_staff = False q.user.save() count: int = queryset.update(active = False) messages.success(request, f"{count} students deactivated") @action_with_form(ChangeCourseForm, description="Change Student Course") def change_course(self, request: HttpRequest, queryset: QuerySet[Student], data): course = data["course"] count: int = queryset.update(course=course) messages.success(request, f"{count} students updated to {course}") @action_with_form(ChangeAircraftForm, description="Assign Aircraft Type") def change_aircraft(self, request: HttpRequest, queryset: QuerySet[Student], data: Dict[str, List[AircraftTypes]]): i: int ac_types: List[str] i, ac_types = assign_aircraft(queryset=queryset, data=data) messages.success(request, f"{i} Students updated to {ac_types}") @confirm_action @admin.action(description="Send Access Credentials e-mail") def send_mail(self, request: HttpRequest, queryset: QuerySet[Student], *args: Any) -> None: send_mail_password(request=request, queryset=queryset) # Return the initial form for import confirmations, request course to user def get_confirm_form_initial(self, request: HttpRequest, import_form) -> Dict[str, Any]: initial: Dict[str, Any] = super().get_confirm_form_initial(request, import_form) if import_form and hasattr(import_form.cleaned_data, "course"): course: Course = import_form.cleaned_data["course"] initial["course"] = course.id return initial # Add course to import form kwargs to be used by resource to associate course with all imported students def get_import_data_kwargs(self, request: HttpRequest, *args, **kwargs) -> Dict[str, Any]: form: ImportForm | None = kwargs.get("form", None) if form and hasattr(form, "cleaned_data"): kwargs["course"] = form.cleaned_data.get("course", None) return kwargs