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 .models.courses import Course from .models.hourbuildings import HourBuilding, HourBuildingLeg from .models.missions import Training, MissionProfile from .models.students import Student 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",) admin.site.register(Course, CourseAdmin) admin.site.register(MissionProfile, MissionProfileAdmin) admin.site.register(Student, StudentAdmin) admin.site.register(WeekPreference, WeekPreferenceAdmin)