From 79d7333ca0f17ea6cdc6bf9d92e836c9fad8b855 Mon Sep 17 00:00:00 2001 From: Emanuele Date: Tue, 18 Nov 2025 22:44:16 +0100 Subject: [PATCH] Admin files refactoring --- techdb/flightslot/admin.py | 280 +------------------ techdb/flightslot/admins/course_adm.py | 16 ++ techdb/flightslot/admins/hourbuilding_adm.py | 59 ++++ techdb/flightslot/admins/mission_adm.py | 5 + techdb/flightslot/admins/student_adm.py | 75 +++++ techdb/flightslot/admins/training_adm.py | 30 ++ techdb/flightslot/admins/weekpred_adm.py | 126 +++++++++ techdb/techdb/settings.py | 4 +- 8 files changed, 319 insertions(+), 276 deletions(-) create mode 100644 techdb/flightslot/admins/course_adm.py create mode 100644 techdb/flightslot/admins/hourbuilding_adm.py create mode 100644 techdb/flightslot/admins/mission_adm.py create mode 100644 techdb/flightslot/admins/student_adm.py create mode 100644 techdb/flightslot/admins/training_adm.py create mode 100644 techdb/flightslot/admins/weekpred_adm.py diff --git a/techdb/flightslot/admin.py b/techdb/flightslot/admin.py index f60549c..5f6b87a 100644 --- a/techdb/flightslot/admin.py +++ b/techdb/flightslot/admin.py @@ -1,282 +1,14 @@ -from django import forms -from django.db.models.query import QuerySet -from django.http import HttpRequest, HttpResponse -from django.contrib import admin, messages -from django.utils.translation import ngettext -from django.utils.safestring import SafeText - -import nested_admin - -from durationwidget.widgets import TimeDurationWidget - -from import_export import fields -from import_export.admin import ImportMixin -from import_export.resources import ModelResource -from import_export.widgets import CharWidget - -from django_admin_action_forms import AdminActionFormsMixin, AdminActionForm, action_with_form +from django.contrib import admin from .models.courses import Course -from .models.hourbuildings import HourBuilding, HourBuildingLeg -from .models.missions import Training, MissionProfile from .models.students import Student +from .models.missions import MissionProfile from .models.weekpref import WeekPreference -from .custom.colortag import course_color -from .actions.exportweek import export_selected - -from datetime import date - -class TrainingForm(forms.ModelForm): - model=Training - -class HourBuildingLegForm(forms.ModelForm): - class Meta: - model = HourBuildingLeg - fields = '__all__' - widgets = { - 'time': TimeDurationWidget(show_days=False, - show_seconds=False - ) - } - -# Register your models here. -class HourBuildingLegInline(nested_admin.NestedTabularInline): - model = HourBuildingLeg - form = HourBuildingLegForm - extra = 0 - fk_name = 'hb' - max_num = 5 - - # 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): - if hasattr(request.user, 'student') and obj: - current_week = date.today().isocalendar().week - if not obj.DoesNotExist and current_week > obj.weekpref.week: - return False - return True - - def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None): - return self.has_change_permission(request=request, obj=obj) - -class HourBuildingInLine(nested_admin.NestedTabularInline): - model = HourBuilding - extra = 0 - inlines = [HourBuildingLegInline] - fk_name = 'weekpref' - verbose_name_plural = "Hour Building" - max_num = 7 - - # 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) - -class TrainingInLIne(nested_admin.NestedTabularInline): - model = Training - form = TrainingForm - extra = 0 - fk_name = 'weekpref' - verbose_name_plural = "Training Missions" - max_num = 7 - - # 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) - -class WeekPreferenceAdmin(nested_admin.NestedModelAdmin): - inlines = (TrainingInLIne, HourBuildingInLine,) - list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",) - list_filter = ("week", "student__course", "student",) - actions = ("export",) - - @admin.action(description="Export Selected Preferences") - def export(weekpreferenceadmin, request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse | None: - if queryset.count() == 0: - return None - weekpreferenceadmin.message_user(request, ngettext("Exporting %d row", "Exporting %d rows", queryset.count()) % queryset.count(), messages.SUCCESS) - return export_selected(request=request, queryset=queryset) - - @admin.display(description="Mission Count") - def student_brief_mix(self, obj: WeekPreference) -> SafeText: - if not obj.student.course: - return SafeText("") - return SafeText(f"{Training.objects.filter(weekpref = obj.id).count()}") - - @admin.display(description="Color") - def course_color(self, obj: WeekPreference) -> SafeText: - if not obj.student.course: - return SafeText("") - return course_color(obj.student.course.color) - - # If a user is registered as student hide filters - def get_list_filter(self, request): - list_filter = super().get_list_filter(request) - if hasattr(request.user, 'student'): - return [] - return list_filter - - # If a user is registered as student do not show actions - def get_actions(self, request): - actions = super().get_actions(request) - if hasattr(request.user, 'student'): - return [] - return actions - - # If a user is registered as student show only their preferences - def get_queryset(self, request): - qs = super().get_queryset(request) - if hasattr(request.user, 'student'): - return qs.filter(student=request.user.student) - # If admin show everything - return qs - - def get_form(self, request, obj=None, **kwargs): - form: forms.Form = super().get_form(request, obj, **kwargs) - current_week = date.today().isocalendar().week - - # If form contains the week field - if 'week' in form.base_fields: - # Set default value as current week - form.base_fields['week'].initial = current_week - - # If student is current user making request - if hasattr(request.user, 'student'): - student = request.user.student - if 'student' in form.base_fields: - form.base_fields['student'].initial = student - form.base_fields['student'].disabled = True - form.base_fields['week'].disabled = True # student cannot change week - return form - - # If user is a student deny edit permission for week past the current one - def has_change_permission(self, request, 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 - - # If user is a student deny edit permission for week past the current one - def has_add_permission(self, request, 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 - - # If user is a student deny edit permission for week past the current one - def has_delete_permission(self, request, 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 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: - current_week = date.today().isocalendar().week - weekpref = WeekPreference.objects.get(id=object_id) - if current_week > weekpref.week: - extra_context['show_save'] = False - extra_context['show_save_and_continue'] = False - extra_context['show_save_and_add_another'] = False - extra_context['show_delete'] = False - return super().changeform_view(request, object_id, form_url, extra_context) - - def save_model(self, request, obj, form, change): - # Imposta automaticamente lo studente se non è già valorizzato - if hasattr(request.user, 'student') and not obj.student_id: - obj.student = request.user.student - super().save_model(request, obj, form, change) - - -# Resource Class for Student data import -class StudentResource(ModelResource): - surname = fields.Field(attribute="surname", column_name="surname", widget=CharWidget()) - name = fields.Field(attribute="name", column_name="name", widget=CharWidget()) - email = fields.Field(attribute="email", column_name="email", widget=CharWidget()) - phone = fields.Field(attribute="phone", column_name="phone", widget=CharWidget()) - - # Cleanup fields before entering - def before_import_row(self, row: dict[str, str], **kwargs) -> None: - row['name'] = SafeText(row['name'].capitalize().strip()) - row['surname'] = SafeText(row['surname'].capitalize().strip()) - row['phone'] = SafeText(row['phone'].replace(' ','')) - row['email'] = SafeText(row['email'].lower().strip()) - return super().before_import_row(row, **kwargs) - - 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 = forms.ModelChoiceField(queryset=Course.objects.all()) - -class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): - list_display = ("surname", "name", "course", "course_color", "email", "phone", "password", "active") - list_filter = ("course", "active") - actions = ("change_course", "disable_students") - resource_classes = [StudentResource] - - @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.action(description="Disable Students") - def disable_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") - pass - - @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}") - -class CourseAdmin(admin.ModelAdmin): - list_display = ("ctype", "cnumber","color_display", "year") - list_filter = ("ctype", "year") - - # Dinamically add color_display property to show a colored dot - @admin.display(description="Color") - def color_display(self, obj: Course) -> SafeText: - if not obj.pk: - return SafeText("") - return course_color(obj.color) - -class MissionProfileAdmin(admin.ModelAdmin): - list_display = ("mtype", "mnum",) - list_filter = ("mtype",) +from .admins.course_adm import CourseAdmin +from .admins.student_adm import StudentAdmin +from .admins.mission_adm import MissionProfileAdmin +from .admins.weekpred_adm import WeekPreferenceAdmin admin.site.register(Course, CourseAdmin) admin.site.register(MissionProfile, MissionProfileAdmin) diff --git a/techdb/flightslot/admins/course_adm.py b/techdb/flightslot/admins/course_adm.py new file mode 100644 index 0000000..41694c6 --- /dev/null +++ b/techdb/flightslot/admins/course_adm.py @@ -0,0 +1,16 @@ +from django.contrib import admin +from django.utils.safestring import SafeText + +from ..models.courses import Course +from ..custom.colortag import course_color + +class CourseAdmin(admin.ModelAdmin): + list_display = ("ctype", "cnumber","color_display", "year") + list_filter = ("ctype", "year") + + # Dinamically add color_display property to show a colored dot + @admin.display(description="Color") + def color_display(self, obj: Course) -> SafeText: + if not obj.pk: + return SafeText("") + return course_color(obj.color) diff --git a/techdb/flightslot/admins/hourbuilding_adm.py b/techdb/flightslot/admins/hourbuilding_adm.py new file mode 100644 index 0000000..d41e7e7 --- /dev/null +++ b/techdb/flightslot/admins/hourbuilding_adm.py @@ -0,0 +1,59 @@ +import nested_admin + +from django import forms +from django.http import HttpRequest + +from durationwidget.widgets import TimeDurationWidget + +from ..models.hourbuildings import HourBuilding, HourBuildingLeg +from ..models.weekpref import WeekPreference + +from datetime import date + +class HourBuildingLegForm(forms.ModelForm): + class Meta: + model = HourBuildingLeg + fields = '__all__' + widgets = { + 'time': TimeDurationWidget(show_days=False, + show_seconds=False + ) + } + +# Register your models here. +class HourBuildingLegInline(nested_admin.NestedTabularInline): + model = HourBuildingLeg + form = HourBuildingLegForm + extra = 0 + fk_name = 'hb' + max_num = 5 + + # 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): + if hasattr(request.user, 'student') and obj: + current_week = date.today().isocalendar().week + if not obj.DoesNotExist and current_week > obj.weekpref.week: + return False + return True + + def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None): + return self.has_change_permission(request=request, obj=obj) + +class HourBuildingInLine(nested_admin.NestedTabularInline): + model = HourBuilding + extra = 0 + inlines = [HourBuildingLegInline] + fk_name = 'weekpref' + verbose_name_plural = "Hour Building" + max_num = 7 + + # 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) diff --git a/techdb/flightslot/admins/mission_adm.py b/techdb/flightslot/admins/mission_adm.py new file mode 100644 index 0000000..fa85fc1 --- /dev/null +++ b/techdb/flightslot/admins/mission_adm.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +class MissionProfileAdmin(admin.ModelAdmin): + list_display = ("mtype", "mnum",) + list_filter = ("mtype",) diff --git a/techdb/flightslot/admins/student_adm.py b/techdb/flightslot/admins/student_adm.py new file mode 100644 index 0000000..15db717 --- /dev/null +++ b/techdb/flightslot/admins/student_adm.py @@ -0,0 +1,75 @@ +from django import forms +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.resources import ModelResource +from import_export.widgets import CharWidget + +from django_admin_action_forms import AdminActionFormsMixin, AdminActionForm, action_with_form + +from ..models.courses import Course +from ..models.students import Student + +from ..custom.colortag import course_color + +# Resource Class for Student data import +class StudentResource(ModelResource): + surname = fields.Field(attribute="surname", column_name="surname", widget=CharWidget()) + name = fields.Field(attribute="name", column_name="name", widget=CharWidget()) + email = fields.Field(attribute="email", column_name="email", widget=CharWidget()) + phone = fields.Field(attribute="phone", column_name="phone", widget=CharWidget()) + + # Cleanup fields before entering + def before_import_row(self, row: dict[str, str], **kwargs) -> None: + row["name"] = SafeText(row["name"].capitalize().strip()) + row["surname"] = SafeText(row["surname"].capitalize().strip()) + row["phone"] = SafeText(row["phone"].replace(" ","")) + row["email"] = SafeText(row["email"].lower().strip()) + return super().before_import_row(row, **kwargs) + + 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 = forms.ModelChoiceField(queryset=Course.objects.all()) + +class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin): + list_display = ("surname", "name", "course", "course_color", "email", "phone", "password", "active") + list_filter = ("course", "active") + actions = ("change_course", "disable_students") + resource_classes = [StudentResource] + + @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.action(description="Disable Students") + def disable_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") + pass + + @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}") diff --git a/techdb/flightslot/admins/training_adm.py b/techdb/flightslot/admins/training_adm.py new file mode 100644 index 0000000..04a3b54 --- /dev/null +++ b/techdb/flightslot/admins/training_adm.py @@ -0,0 +1,30 @@ +import nested_admin +from django import forms +from django.http import HttpRequest + +from ..models.missions import Training +from ..models.weekpref import WeekPreference + +from datetime import date + +class TrainingForm(forms.ModelForm): + model=Training + +class TrainingInLIne(nested_admin.NestedTabularInline): + model = Training + form = TrainingForm + extra = 0 + fk_name = 'weekpref' + verbose_name_plural = "Training Missions" + max_num = 7 + + # 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: int = 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) diff --git a/techdb/flightslot/admins/weekpred_adm.py b/techdb/flightslot/admins/weekpred_adm.py new file mode 100644 index 0000000..ec17676 --- /dev/null +++ b/techdb/flightslot/admins/weekpred_adm.py @@ -0,0 +1,126 @@ +import nested_admin + +from django import forms +from django.db.models.query import QuerySet +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.missions import Training +from ..models.weekpref import WeekPreference + +from .training_adm import TrainingInLIne +from .hourbuilding_adm import HourBuildingInLine + +from ..custom.colortag import course_color +from ..actions.exportweek import export_selected + +from datetime import date + +class WeekPreferenceAdmin(nested_admin.NestedModelAdmin): + inlines = (TrainingInLIne, HourBuildingInLine,) + list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",) + list_filter = ("week", "student__course", "student",) + actions = ("export",) + + @admin.action(description="Export Selected Preferences") + def export(self, request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse | None: + if queryset.count() == 0: + return None + self.message_user(request, ngettext("Exporting %d row", "Exporting %d rows", queryset.count()) % queryset.count(), messages.SUCCESS) + return export_selected(request=request, queryset=queryset) + + @admin.display(description="Mission Count") + def student_brief_mix(self, obj: WeekPreference) -> SafeText: + if not obj.student.course: + return SafeText("") + return SafeText(f"{Training.objects.filter(weekpref = obj.id).count()}") + + @admin.display(description="Color") + def course_color(self, obj: WeekPreference) -> SafeText: + if not obj.student.course: + return SafeText("") + return course_color(obj.student.course.color) + + # If a user is registered as student hide filters + def get_list_filter(self, request): + list_filter = super().get_list_filter(request) + if hasattr(request.user, 'student'): + return [] + return list_filter + + # If a user is registered as student do not show actions + def get_actions(self, request): + actions = super().get_actions(request) + if hasattr(request.user, 'student'): + return [] + return actions + + # If a user is registered as student show only their preferences + def get_queryset(self, request): + qs = super().get_queryset(request) + if hasattr(request.user, 'student'): + return qs.filter(student=request.user.student) + # If admin show everything + return qs + + def get_form(self, request, obj=None, **kwargs): + form: forms.Form = super().get_form(request, obj, **kwargs) + current_week = date.today().isocalendar().week + + # If form contains the week field + if 'week' in form.base_fields: + # Set default value as current week + form.base_fields['week'].initial = current_week + + # If student is current user making request + if hasattr(request.user, 'student'): + student = request.user.student + if 'student' in form.base_fields: + form.base_fields['student'].initial = student + form.base_fields['student'].disabled = True + form.base_fields['week'].disabled = True # student cannot change week + return form + + # If user is a student deny edit permission for week past the current one + def has_change_permission(self, request, 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 + + # If user is a student deny edit permission for week past the current one + def has_add_permission(self, request, 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 + + # If user is a student deny edit permission for week past the current one + def has_delete_permission(self, request, 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 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: + current_week = date.today().isocalendar().week + weekpref = WeekPreference.objects.get(id=object_id) + if current_week > weekpref.week: + extra_context['show_save'] = False + extra_context['show_save_and_continue'] = False + extra_context['show_save_and_add_another'] = False + extra_context['show_delete'] = False + return super().changeform_view(request, object_id, form_url, extra_context) + + def save_model(self, request, obj, form, change): + # Imposta automaticamente lo studente se non è già valorizzato + if hasattr(request.user, 'student') and not obj.student_id: + obj.student = request.user.student + super().save_model(request, obj, form, change) diff --git a/techdb/techdb/settings.py b/techdb/techdb/settings.py index bedb4b9..caeb22d 100644 --- a/techdb/techdb/settings.py +++ b/techdb/techdb/settings.py @@ -47,10 +47,10 @@ INSTALLED_APPS = [ ] # Import Export plugin settings +from import_export.formats.base_formats import CSV IMPORT_EXPORT_USE_TRANSACTIONS = True IMPORT_EXPORT_SKIP_ADMIN_LOG = True -from import_export.formats.base_formats import CSV, XLSX -IMPORT_FORMATS = [CSV, XLSX] +IMPORT_FORMATS = [CSV] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware',