diff --git a/Palto/Palto/admin.py b/Palto/Palto/admin.py index 388aed0..86277a2 100644 --- a/Palto/Palto/admin.py +++ b/Palto/Palto/admin.py @@ -61,9 +61,9 @@ class AdminAttendance(admin.ModelAdmin): @admin.register(models.Absence) class AdminAbsence(admin.ModelAdmin): - list_display = ("id", "message", "student", "department", "start", "end") - search_fields = ("id", "message", "student", "department", "start", "end") - list_filter = ("department", "start", "end") + list_display = ("id", "message", "student", "start", "end") + search_fields = ("id", "message", "student", "start", "end") + list_filter = ("start", "end") @admin.register(models.AbsenceAttachment) diff --git a/Palto/Palto/migrations/0001_initial.py b/Palto/Palto/migrations/0001_initial.py new file mode 100644 index 0000000..56a14d7 --- /dev/null +++ b/Palto/Palto/migrations/0001_initial.py @@ -0,0 +1,166 @@ +# Generated by Django 4.2.7 on 2023-12-10 11:31 + +import Palto.Palto.models +from django.conf import settings +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Absence', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('message', models.TextField()), + ('start', models.DateTimeField()), + ('end', models.DateTimeField()), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.CreateModel( + name='Department', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64, unique=True)), + ('email', models.EmailField(max_length=254)), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.CreateModel( + name='StudentGroup', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128)), + ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_groups', to='Palto.department')), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.CreateModel( + name='User', + fields=[ + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='TeachingUnit', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teaching_units', to='Palto.department')), + ('managers', models.ManyToManyField(blank=True, related_name='managing_units', to=settings.AUTH_USER_MODEL)), + ('student_groups', models.ManyToManyField(blank=True, related_name='studying_units', to='Palto.studentgroup')), + ('teachers', models.ManyToManyField(blank=True, related_name='teaching_units', to=settings.AUTH_USER_MODEL)), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.CreateModel( + name='TeachingSession', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('start', models.DateTimeField()), + ('duration', models.DurationField()), + ('note', models.TextField(blank=True)), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teaching_sessions', to='Palto.studentgroup')), + ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teaching_sessions', to=settings.AUTH_USER_MODEL)), + ('unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sessions', to='Palto.teachingunit')), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.AddField( + model_name='studentgroup', + name='owner', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owning_groups', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='studentgroup', + name='students', + field=models.ManyToManyField(blank=True, related_name='student_groups', to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='StudentCard', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('uid', models.BinaryField(max_length=7)), + ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_cards', to='Palto.department')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_cards', to=settings.AUTH_USER_MODEL)), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.AddField( + model_name='department', + name='managers', + field=models.ManyToManyField(blank=True, related_name='managing_departments', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='department', + name='students', + field=models.ManyToManyField(blank=True, related_name='studying_departments', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='department', + name='teachers', + field=models.ManyToManyField(blank=True, related_name='teaching_departments', to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='Attendance', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('date', models.DateTimeField()), + ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attendances', to='Palto.teachingsession')), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attended_sessions', to=settings.AUTH_USER_MODEL)), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.CreateModel( + name='AbsenceAttachment', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('content', models.FileField(upload_to='absence/attachment/')), + ('absence', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='Palto.absence')), + ], + bases=(models.Model, Palto.Palto.models.ModelPermissionHelper), + ), + migrations.AddField( + model_name='absence', + name='department', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absences', to='Palto.department'), + ), + migrations.AddField( + model_name='absence', + name='student', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absences', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/Palto/Palto/models.py b/Palto/Palto/models.py index ac7f3d2..049e80b 100644 --- a/Palto/Palto/models.py +++ b/Palto/Palto/models.py @@ -63,6 +63,9 @@ class User(AbstractUser, ModelPermissionHelper): def __repr__(self): return f"<{self.__class__.__name__} id={str(self.id)[:8]} username={self.username!r}>" + def __str__(self): + return f"{self.first_name} {self.last_name.upper()}" + @staticmethod def multiple_related_departments(users: Iterable["User"]) -> QuerySet["Department"]: """ diff --git a/Palto/Palto/templates/Palto/homepage.html b/Palto/Palto/templates/Palto/homepage.html new file mode 100644 index 0000000..bade8c3 --- /dev/null +++ b/Palto/Palto/templates/Palto/homepage.html @@ -0,0 +1,5 @@ +{% extends "Palto/base.html" %} + +{% block body %} + Hello there. +{% endblock %} diff --git a/Palto/Palto/templates/Palto/navigation.html b/Palto/Palto/templates/Palto/navigation.html index bb4c86f..074a3bf 100644 --- a/Palto/Palto/templates/Palto/navigation.html +++ b/Palto/Palto/templates/Palto/navigation.html @@ -1,3 +1,11 @@ +{% load static %} + diff --git a/Palto/Palto/templates/Palto/teaching_session.html b/Palto/Palto/templates/Palto/teaching_session.html new file mode 100644 index 0000000..5a6c99c --- /dev/null +++ b/Palto/Palto/templates/Palto/teaching_session.html @@ -0,0 +1,27 @@ +{% extends "Palto/base.html" %} + +{% block body %} + {# table of all the sessions #} + + + + + + + + + + + + + + + + + + + + + +
IdentifiantDébutDuréeUnité d'EnseignementEnseignantGroupe
{{ session.id }}{{ session.start }}{{ session.duration }}{{ session.unit }}{{ session.teacher }}{{ session.group }}
+{% endblock %} diff --git a/Palto/Palto/templates/Palto/teaching_session_list.html b/Palto/Palto/templates/Palto/teaching_session_list.html new file mode 100644 index 0000000..f9ecc3e --- /dev/null +++ b/Palto/Palto/templates/Palto/teaching_session_list.html @@ -0,0 +1,36 @@ +{% extends "Palto/base.html" %} + +{% block body %} + {# table of all the sessions #} + + + + + + + + + + {% for session in sessions %} + + + + + + {% endfor %} + +
IdentifiantDébutDurée
{{ session.id }}{{ session.start }}{{ session.duration }}
+ + {# page navigator #} +
+ {% if sessions.has_previous %} + Previous + {% endif %} + + {{ sessions.number }} + + {% if sessions.has_next %} + Next + {% endif %} +
+{% endblock %} diff --git a/Palto/Palto/urls.py b/Palto/Palto/urls.py index 19c0b30..357120f 100644 --- a/Palto/Palto/urls.py +++ b/Palto/Palto/urls.py @@ -16,6 +16,8 @@ urlpatterns = [ # User path("login/", views.login_view, name="login"), path("logout/", views.logout_view, name="logout"), - path("profile/", views.profile_view, name="profile"), + path("profile/", views.profile_view, name="my_profile"), path("profile//", views.profile_view, name="profile"), + path("teaching_sessions/", views.teaching_session_list_view, name="teaching_session_list"), + path("teaching_sessions//", views.teaching_session_view, name="teaching_session"), ] diff --git a/Palto/Palto/views.py b/Palto/Palto/views.py index 535aabc..b9ac81f 100644 --- a/Palto/Palto/views.py +++ b/Palto/Palto/views.py @@ -6,20 +6,24 @@ A view is what control the content of a page, prepare the correct data, react to import uuid from django.contrib.auth import login, authenticate, logout from django.contrib.auth.decorators import login_required -from django.http import HttpRequest, HttpResponse +from django.core.handlers.wsgi import WSGIRequest +from django.core.paginator import Paginator +from django.http import HttpResponseForbidden from django.shortcuts import render, get_object_or_404, redirect +from Palto.Palto import models from Palto.Palto.forms import LoginForm -from Palto.Palto.models import User + + +ELEMENT_PER_PAGE: int = 30 # Create your views here. -def homepage_view(request: HttpRequest): - # TODO: homepage - return HttpResponse("Hello there.") +def homepage_view(request: WSGIRequest): + return render(request, "Palto/homepage.html") -def login_view(request: HttpRequest): +def login_view(request: WSGIRequest): # create a login form form_login = LoginForm(request.POST) @@ -51,7 +55,7 @@ def login_view(request: HttpRequest): @login_required -def logout_view(request: HttpRequest): +def logout_view(request: WSGIRequest): # disconnect the user from the website logout(request) # redirect him to the main page @@ -59,13 +63,13 @@ def logout_view(request: HttpRequest): @login_required -def profile_view(request: HttpRequest, profile_id: uuid.UUID = None): +def profile_view(request: WSGIRequest, profile_id: uuid.UUID = None): if profile_id is None: # if the profile id is not given, redirect to the page of the current user. return redirect("Palto:profile", request.user.id) # get the corresponding user from its id. - profile = get_object_or_404(User, id=profile_id) + profile = get_object_or_404(models.User, id=profile_id) # render the page return render( @@ -75,3 +79,42 @@ def profile_view(request: HttpRequest, profile_id: uuid.UUID = None): profile=profile ) ) + + +@login_required +def teaching_session_list_view(request: WSGIRequest): + # get all the sessions that the user can see, sorted by starting date + raw_sessions = models.TeachingSession.all_visible_by_user(request.user).order_by("start") + # paginate them to avoid having too many elements at the same time + paginator = Paginator(raw_sessions, ELEMENT_PER_PAGE) + + # get only the session for the requested page + page = request.GET.get("page", 0) + sessions = paginator.get_page(page) + + # render the page + return render( + request, + "Palto/teaching_session_list.html", + context=dict( + sessions=sessions + ) + ) + + +@login_required +def teaching_session_view(request: WSGIRequest, session_id: uuid.UUID): + session = get_object_or_404(models.TeachingSession, id=session_id) + + if session not in models.TeachingSession.all_visible_by_user(request.user): + # TODO: syntaxic sugar session.visible_by_user(request.user) + return HttpResponseForbidden() + + # render the page + return render( + request, + "Palto/teaching_session.html", + context=dict( + session=session + ) + ) \ No newline at end of file