diff --git a/cntmanage/cntmanage/urls.py b/cntmanage/cntmanage/urls.py index c485b3c..9b63692 100644 --- a/cntmanage/cntmanage/urls.py +++ b/cntmanage/cntmanage/urls.py @@ -2,10 +2,12 @@ from django.contrib import admin from django.urls import path from django.shortcuts import redirect from flightslot.admin import flightslot_user +from flightslot.admin import flightslot_staff urlpatterns = [ #path('', RedirectView.as_view(url='/admin/', permanent=False)), path('admin/', admin.site.urls), path('user/', flightslot_user.urls), + path('staff/', flightslot_staff.urls), path("", lambda r: redirect("/user/")), # la root porta gli utenti nella pagina giusta ] diff --git a/cntmanage/flightslot/admin.py b/cntmanage/flightslot/admin.py index 4d3cb92..f6dd88b 100644 --- a/cntmanage/flightslot/admin.py +++ b/cntmanage/flightslot/admin.py @@ -6,7 +6,8 @@ from .models.courses import Course from .models.students import Student from .models.missions import MissionProfile from .models.weekpref import WeekPreference -from.models.instructors import Instructor +from .models.instructors import Instructor +from .models.availabilities import Availability from .admins.aircraft_adm import AircraftAdmin from .admins.course_adm import CourseAdmin @@ -14,16 +15,19 @@ from .admins.student_adm import StudentAdmin from .admins.mission_adm import MissionProfileAdmin from .admins.weekpref_adm import WeekPreferenceAdmin from .admins.instructor_admin import InstructorAdmin +from .admins.availability_adm import AvailabilityAdmin from django.contrib.admin import AdminSite from os import environ -# User website under /user/ URL +################################## +# User website under /user/ URL # +################################## class FlightSlotUserSite(AdminSite): site_header = "Flight Scheduler 🛫" site_title = "Flight Scheduler 🛫" - index_title = "Welcome to CantorAir Flight Scheduler Portal" + index_title = "Welcome to CantorAir Flight Scheduler Student Portal" def get_app_list(self, request: HttpRequest, *args, **kwargs): app_list = super().get_app_list(request) @@ -37,6 +41,26 @@ class FlightSlotUserSite(AdminSite): flightslot_user = FlightSlotUserSite(name="user_site") flightslot_user.register(WeekPreference, WeekPreferenceAdmin) +################################## +# User website under /staff/ URL # +################################## +class FlightSlotStaffSite(AdminSite): + site_header = "Flight Scheduler Staff 🛫" + site_title = "Flight Scheduler Staff 🛫" + index_title = "Welcome to CantorAir Flight Scheduler Staff Portal" + + def get_app_list(self, request: HttpRequest, *args, **kwargs): + app_list = super().get_app_list(request) + + if not request.user.is_superuser: + self.enable_nav_sidebar = False + + return app_list + +# Register only user visible models +flightslot_staff = FlightSlotUserSite(name="staff_site") +flightslot_staff.register(Availability, AvailabilityAdmin) + # Get version for debug purposes ver: str = environ.get("VERSION", "dev") @@ -51,3 +75,4 @@ admin.site.register(MissionProfile, MissionProfileAdmin) admin.site.register(Student, StudentAdmin) admin.site.register(WeekPreference, WeekPreferenceAdmin) admin.site.register(Instructor, InstructorAdmin) +admin.site.register(Availability, AvailabilityAdmin) diff --git a/cntmanage/flightslot/admins/availability_adm.py b/cntmanage/flightslot/admins/availability_adm.py new file mode 100644 index 0000000..e28da7f --- /dev/null +++ b/cntmanage/flightslot/admins/availability_adm.py @@ -0,0 +1,66 @@ +from django import forms +from django.db.models.query import QuerySet +from django.http import HttpRequest +from django.contrib import admin +from django.utils.safestring import SafeText + +from ..models.instructors import Instructor +from ..models.availabilities import Availability + +from datetime import date +from typing import Any, List + +class AvailabilityForm(forms.ModelForm): + model=Availability + +class AvailabilityAdmin(admin.ModelAdmin): + model = Availability + list_display = ("week", "instructor__surname", "instructor__name", "days_available", "hours") + list_filter = ("week", ) + search_fields = ("instructor__surname","instructor__name", ) + #actions = ("export", ) + + @admin.display(description="Days Available") + def days_available(self, obj: Availability) -> SafeText: + if not obj: + return SafeText("") + days: List[str | None] = [ + "Mon" if obj.monday else None, + "Tue" if obj.tuesday else None, + "Wed" if obj.wednesday else None, + "Thu" if obj.thursday else None, + "Fri" if obj.friday else None, + "Sat" if obj.saturday else None, + "Sun" if obj.sunday else None, + ] + return SafeText("/".join(d if d else "" for d in days)) + + def get_queryset(self, request: HttpRequest) -> QuerySet: + return super().get_queryset(request).order_by("-week", "instructor__surname", "instructor__name") + + def get_form(self, request: HttpRequest, obj: Availability | None = None, change: bool = False, **kwargs: Any) -> AvailabilityForm: + form: AvailabilityForm = super().get_form(request, obj, change, **kwargs) + + if change: # if is only a form change do not set default values and return form + return form + + # If form contains the week field + current_week = date.today().isocalendar().week + 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, "instructor"): + instructor: Instructor = request.user.instructor + if "instructor" in form.base_fields: + form.base_fields["instructor"].initial = instructor + form.base_fields["instructor"].disabled = True + return form + + # Imposta automaticamente l'istruttore se non è già valorizzato + def save_model(self, request: HttpRequest, obj: Availability, form: AvailabilityForm, change: bool): + if hasattr(request.user, "instructor") and not obj.instructor_id: + obj.instructor = request.user.instructor + super().save_model(request, obj, form, change) + \ No newline at end of file diff --git a/cntmanage/flightslot/admins/weekpref_adm.py b/cntmanage/flightslot/admins/weekpref_adm.py index 123670a..bdd70f5 100644 --- a/cntmanage/flightslot/admins/weekpref_adm.py +++ b/cntmanage/flightslot/admins/weekpref_adm.py @@ -93,13 +93,13 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin): # If user is a student deny edit permission for week past the current one def has_add_permission(self, request: HttpRequest, obj: WeekPreference | None = None) -> bool: - return self.has_change_permission(request, obj) + return not obj and self.has_change_permission(request, obj) # If user is a student deny edit permission for week past the current one def has_delete_permission(self, request: HttpRequest, obj: WeekPreference | None = None)-> bool: return self.has_change_permission(request, obj) - 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 {} if hasattr(request.user, "student") and object_id: weekpref = WeekPreference.objects.get(id=object_id) diff --git a/cntmanage/flightslot/custom/student_permissions.py b/cntmanage/flightslot/custom/student_permissions.py index d995526..12b1b72 100644 --- a/cntmanage/flightslot/custom/student_permissions.py +++ b/cntmanage/flightslot/custom/student_permissions.py @@ -12,6 +12,6 @@ def has_edit_permission(request: HttpRequest, obj: WeekPreference | None = None) if not student.active: return False current_week: int = date.today().isocalendar().week - if obj and current_week > obj.week or not student.active: + if obj and (current_week > obj.week or not student.active): return False return True diff --git a/cntmanage/flightslot/middleware.py b/cntmanage/flightslot/middleware.py index e61654b..81387de 100644 --- a/cntmanage/flightslot/middleware.py +++ b/cntmanage/flightslot/middleware.py @@ -7,8 +7,10 @@ class RedirectNonSuperuserFromAdminMiddleware: self.get_response = get_response def __call__(self, request: HttpRequest): - # Se l'utente è loggato, non è superuser e prova ad andare in /admin/... + # Se l'utente è loggato, non è superuser e prova ad andare in /admin/... o qualsiasi altro path if hasattr(request, "user") and not request.user.is_superuser: - if "/admin/" in request.path: + if hasattr(request.user, "student") and not "/user/" in request.path: return redirect("/user/") # redirect automatico + elif hasattr(request.user, "instructor") and not "/staff/" in request.path: + return redirect("/staff/") # redirect automatico return self.get_response(request) diff --git a/cntmanage/flightslot/migrations/0027_alter_weekpreference_student_availability.py b/cntmanage/flightslot/migrations/0027_alter_weekpreference_student_availability.py new file mode 100644 index 0000000..a28ec07 --- /dev/null +++ b/cntmanage/flightslot/migrations/0027_alter_weekpreference_student_availability.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.8 on 2025-12-02 10:02 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0026_alter_student_email_alter_student_phone_instructor'), + ] + + operations = [ + migrations.AlterField( + model_name='weekpreference', + name='student', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='flightslot.student', verbose_name='Student Selection'), + ), + migrations.CreateModel( + name='Availability', + fields=[ + ('week', models.PositiveSmallIntegerField(auto_created=True, db_default=49, db_index=True, verbose_name='Week Number')), + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('monday', models.BooleanField(default=True)), + ('tuesday', models.BooleanField(default=True)), + ('wednesday', models.BooleanField(default=True)), + ('thursday', models.BooleanField(default=True)), + ('friday', models.BooleanField(default=True)), + ('saturday', models.BooleanField(default=True)), + ('sunday', models.BooleanField(default=True)), + ('hours', models.DurationField(null=True, verbose_name='Available hours')), + ('notes', models.TextField(blank=True, max_length=140, null=True)), + ('instructor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='flightslot.instructor', verbose_name='Instructor Selection')), + ], + ), + ] diff --git a/cntmanage/flightslot/migrations/0028_alter_availability_options.py b/cntmanage/flightslot/migrations/0028_alter_availability_options.py new file mode 100644 index 0000000..3238a02 --- /dev/null +++ b/cntmanage/flightslot/migrations/0028_alter_availability_options.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.8 on 2025-12-02 10:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0027_alter_weekpreference_student_availability'), + ] + + operations = [ + migrations.AlterModelOptions( + name='availability', + options={'verbose_name': 'Instructor Availability', 'verbose_name_plural': 'Instructor Availabilities'}, + ), + ] diff --git a/cntmanage/flightslot/models/availabilities.py b/cntmanage/flightslot/models/availabilities.py new file mode 100644 index 0000000..db1cda9 --- /dev/null +++ b/cntmanage/flightslot/models/availabilities.py @@ -0,0 +1,79 @@ +from django.db import models +from datetime import date + +from ..models.instructors import Instructor + +class Availability(models.Model): + id = models.BigAutoField( + primary_key=True + ) + + week = models.PositiveSmallIntegerField( + null=False, + db_index=True, + db_default=date.today().isocalendar().week, + auto_created=True, + verbose_name="Week Number" + ) + + instructor = models.ForeignKey( + Instructor, + null=False, + db_index=True, + on_delete=models.CASCADE, + verbose_name="Instructor Selection" + ) + + monday = models.BooleanField( + default=True, + null=False + ) + + tuesday = models.BooleanField( + default=True, + null=False + ) + + wednesday = models.BooleanField( + default=True, + null=False + ) + + thursday = models.BooleanField( + default=True, + null=False + ) + + friday = models.BooleanField( + default=True, + null=False + ) + + saturday = models.BooleanField( + default=True, + null=False + ) + + sunday = models.BooleanField( + default=True, + null=False + ) + + hours = models.DurationField( + null=True, + verbose_name="Available hours" + ) + + notes = models.TextField( + max_length=140, + null=True, + blank=True + ) + + class Meta(): + verbose_name = "Instructor Availability" + verbose_name_plural = "Instructor Availabilities" + + def __str__(self): + return f"Week {self.week} - {self.instructor.surname} {self.instructor.name[0]}." + \ No newline at end of file diff --git a/cntmanage/flightslot/models/weekpref.py b/cntmanage/flightslot/models/weekpref.py index ac7fad5..1d9d7d6 100644 --- a/cntmanage/flightslot/models/weekpref.py +++ b/cntmanage/flightslot/models/weekpref.py @@ -20,7 +20,7 @@ class WeekPreference(models.Model): Student, null=False, db_index=True, - on_delete=models.DO_NOTHING, + on_delete=models.CASCADE, verbose_name="Student Selection" )