diff --git a/Palto/Palto/admin.py b/Palto/Palto/admin.py
index 67d2e10..ba604c8 100644
--- a/Palto/Palto/admin.py
+++ b/Palto/Palto/admin.py
@@ -24,38 +24,45 @@ class AdminUser(admin.ModelAdmin):
class AdminDepartment(admin.ModelAdmin):
list_display = ("id", "name", "email")
search_fields = ("id", "name", "email")
+ readonly_fields = ("id",)
@admin.register(models.StudentGroup)
class AdminStudentGroup(admin.ModelAdmin):
list_display = ("id", "name", "owner", "department")
search_fields = ("id", "name", "owner", "department")
+ list_filter = ("department",)
+ readonly_fields = ("id",)
@admin.register(models.TeachingUnit)
class AdminTeachingUnit(admin.ModelAdmin):
list_display = ("id", "name", "email")
search_fields = ("id", "name", "email")
+ readonly_fields = ("id",)
@admin.register(models.StudentCard)
class AdminStudentCard(admin.ModelAdmin):
- list_display = ("id", "uid", "owner")
- search_fields = ("id", "uid", "owner")
- readonly_fields = ("uid",)
+ list_display = ("id", "uid", "department", "owner")
+ search_fields = ("id", "uid", "department", "owner")
+ readonly_fields = ("id", "uid",)
+ list_filter = ("department",)
@admin.register(models.TeachingSession)
class AdminTeachingSession(admin.ModelAdmin):
- list_display = ("id", "start", "end", "duration", "teacher")
- search_fields = ("id", "start", "end", "duration", "teacher")
- list_filter = ("start", "duration")
+ list_display = ("id", "start", "end", "unit", "duration", "teacher")
+ search_fields = ("id", "start", "end", "unit", "duration", "teacher")
+ readonly_fields = ("id",)
+ list_filter = ("unit",)
@admin.register(models.Attendance)
class AdminAttendance(admin.ModelAdmin):
list_display = ("id", "date", "student")
search_fields = ("id", "date", "student")
+ readonly_fields = ("id",)
list_filter = ("date",)
@@ -63,6 +70,7 @@ class AdminAttendance(admin.ModelAdmin):
class AdminAbsence(admin.ModelAdmin):
list_display = ("id", "message", "student", "start", "end")
search_fields = ("id", "message", "student", "start", "end")
+ readonly_fields = ("id",)
list_filter = ("start", "end")
@@ -70,3 +78,4 @@ class AdminAbsence(admin.ModelAdmin):
class AdminAbsenceAttachment(admin.ModelAdmin):
list_display = ("id", "content", "absence")
search_fields = ("id", "content", "absence")
+ readonly_fields = ("id",)
diff --git a/Palto/Palto/models.py b/Palto/Palto/models.py
index 28ad2f6..d695988 100644
--- a/Palto/Palto/models.py
+++ b/Palto/Palto/models.py
@@ -39,17 +39,31 @@ class ModelPermissionHelper:
@classmethod
@abstractmethod
def all_editable_by_user(cls, user: "User") -> QuerySet:
+ """
+ Return the list of object that the user can edit
+ """
+
+ def is_editable_by_user(self, user: "User") -> bool:
"""
Return True if the user can edit this object
"""
+ return self in self.all_editable_by_user(user)
+
@classmethod
@abstractmethod
def all_visible_by_user(cls, user: "User") -> QuerySet:
+ """
+ Return the list of object that the user can see
+ """
+
+ def is_visible_by_user(self, user: "User") -> bool:
"""
Return True if the user can see this object
"""
+ return self in self.all_visible_by_user(user)
+
class User(AbstractUser, ModelPermissionHelper):
"""
@@ -522,6 +536,17 @@ class TeachingSession(models.Model, ModelPermissionHelper):
def end(self) -> datetime:
return self.start + self.duration
+ @property
+ def related_absences(self) -> QuerySet["Absence"]:
+ """
+ Return the sessions that match the user absence
+ """
+
+ return Absence.objects.filter(
+ student__in=self.group.students,
+ start__lte=self.start, end__gte=self.end
+ ).distinct()
+
# validations
def clean(self):
diff --git a/Palto/Palto/templates/Palto/absence_list.html b/Palto/Palto/templates/Palto/absence_list.html
new file mode 100644
index 0000000..9e16e6a
--- /dev/null
+++ b/Palto/Palto/templates/Palto/absence_list.html
@@ -0,0 +1,42 @@
+{% extends "Palto/base.html" %}
+
+{% block body %}
+ {# table of all the absences #}
+
+
+ {# page navigator #}
+ {# TODO(Faraphel): page navigator as template ? #}
+ {# TODO(Faraphel): new absence button #}
+
+
+{% endblock %}
diff --git a/Palto/Palto/templates/Palto/absence_view.html b/Palto/Palto/templates/Palto/absence_view.html
index 91f8e29..03e1c27 100644
--- a/Palto/Palto/templates/Palto/absence_view.html
+++ b/Palto/Palto/templates/Palto/absence_view.html
@@ -9,7 +9,7 @@
Département |
- {{ absence.department }} |
+ {{ absence.department }} |
Étudiant |
diff --git a/Palto/Palto/templates/Palto/department_view.html b/Palto/Palto/templates/Palto/department_view.html
new file mode 100644
index 0000000..906a22f
--- /dev/null
+++ b/Palto/Palto/templates/Palto/department_view.html
@@ -0,0 +1,53 @@
+{% extends "Palto/base.html" %}
+{% load dict_tags %}
+
+
+{% block body %}
+ {# department's information #}
+
+
+ Identifiant |
+ {{ department.id }} |
+
+
+ Nom |
+ {{ department.name }} |
+
+
+ Mail |
+ {% if department.email != None %}{{ department.email }}{% else %} / {% endif %} |
+
+
+
+ {# department's managers #}
+
+
+
+ Responsables |
+
+
+
+ {% for manager in department.managers.all %}
+
+ {{ manager }} |
+
+ {% endfor %}
+
+
+
+ {# department's teachers #}
+
+
+
+ Enseignants |
+
+
+
+ {% for teacher in department.teachers.all %}
+
+ {{ teacher }} |
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/Palto/Palto/templates/Palto/homepage.html b/Palto/Palto/templates/Palto/homepage.html
index bade8c3..0795085 100644
--- a/Palto/Palto/templates/Palto/homepage.html
+++ b/Palto/Palto/templates/Palto/homepage.html
@@ -1,5 +1,9 @@
{% extends "Palto/base.html" %}
{% block body %}
- Hello there.
+ Palto
+
+
+ Palto est un outil de gestion des présences d'élèves dans vos établissements scolaires.
+
{% endblock %}
diff --git a/Palto/Palto/templates/Palto/profile.html b/Palto/Palto/templates/Palto/profile.html
index d2b55a3..e879b97 100644
--- a/Palto/Palto/templates/Palto/profile.html
+++ b/Palto/Palto/templates/Palto/profile.html
@@ -11,7 +11,7 @@
{% for department, profile_department_data in profile_departments_data.items %}
{# department name #}
- {{ department.name }} |
+ {{ department.name }} |
{# relation information #}
@@ -61,7 +61,7 @@
Groupe Étudiant |
{% for student_group in student_groups %}
- {{ student_group.name }}
+ {{ student_group.name }}
{% if not forloop.last %} {% endif %}
{% endfor %}
|
diff --git a/Palto/Palto/templates/Palto/student_group.html b/Palto/Palto/templates/Palto/student_group.html
new file mode 100644
index 0000000..56de3b0
--- /dev/null
+++ b/Palto/Palto/templates/Palto/student_group.html
@@ -0,0 +1,39 @@
+{% extends "Palto/base.html" %}
+
+{% block body %}
+ {# group's information #}
+
+
+ {# group's students information #}
+
+
+
+ Étudiants |
+
+
+
+ {% for student in group.students.all %}
+
+ {{ student }} |
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/Palto/Palto/templates/Palto/teaching_session_view.html b/Palto/Palto/templates/Palto/teaching_session_view.html
index 3a26783..ada42aa 100644
--- a/Palto/Palto/templates/Palto/teaching_session_view.html
+++ b/Palto/Palto/templates/Palto/teaching_session_view.html
@@ -26,7 +26,7 @@
Groupe |
- {{ session.group }} |
+ {{ session.group }} |
diff --git a/Palto/Palto/templates/Palto/teaching_unit_view.html b/Palto/Palto/templates/Palto/teaching_unit_view.html
index 084b84e..956aa5c 100644
--- a/Palto/Palto/templates/Palto/teaching_unit_view.html
+++ b/Palto/Palto/templates/Palto/teaching_unit_view.html
@@ -14,7 +14,7 @@
|
Département |
- {{ unit.department.name }} |
+ {{ unit.department.name }} |
Mail |
diff --git a/Palto/Palto/urls.py b/Palto/Palto/urls.py
index 513d372..c664492 100644
--- a/Palto/Palto/urls.py
+++ b/Palto/Palto/urls.py
@@ -19,6 +19,12 @@ urlpatterns = [
path("profile/", views.profile_view, name="my_profile"),
path("profile//", views.profile_view, name="profile"),
+ # Student groups
+ path("student_groups/view//", views.student_group_view, name="student_group_view"),
+
+ # Departments
+ path("departments/view//", views.department_view, name="department_view"),
+
# Units
path("teaching_units/view//", views.teaching_unit_view, name="teaching_unit_view"),
@@ -27,6 +33,7 @@ urlpatterns = [
path("teaching_sessions/view//", views.teaching_session_view, name="teaching_session_view"),
# Absences
+ path("absences/", views.absence_list_view, name="absence_list"),
path("absences/view//", views.absence_view, name="absence_view"),
path("absences/new/", views.new_absence_view, name="absence_new"),
]
diff --git a/Palto/Palto/utils.py b/Palto/Palto/utils.py
index 06a968a..b7b1624 100644
--- a/Palto/Palto/utils.py
+++ b/Palto/Palto/utils.py
@@ -1,10 +1,10 @@
from typing import Optional
from django.core.exceptions import ObjectDoesNotExist
-from django.db.models import Model, Manager
+from django.db.models import Model, QuerySet
-def get_object_or_none(manager: Manager, *args, **kwargs) -> Optional[Model]:
+def get_object_or_none(manager: QuerySet, *args, **kwargs) -> Optional[Model]:
"""
Similar to the Manager.get method, but return None instead of raising an error.
"""
diff --git a/Palto/Palto/views.py b/Palto/Palto/views.py
index ca4eabe..85d67e1 100644
--- a/Palto/Palto/views.py
+++ b/Palto/Palto/views.py
@@ -8,7 +8,6 @@ from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from django.core.handlers.wsgi import WSGIRequest
from django.core.paginator import Paginator
-from django.db import IntegrityError
from django.http import HttpResponseForbidden
from django.shortcuts import render, get_object_or_404, redirect
@@ -72,7 +71,7 @@ def profile_view(request: WSGIRequest, profile_id: uuid.UUID = None):
profile = get_object_or_404(models.User, id=profile_id)
# check if the user is allowed to see this specific object
- if profile not in models.User.all_visible_by_user(request.user):
+ if not profile.is_visible_by_user(request.user):
return HttpResponseForbidden()
# prepare the data and the "complex" query for the template
@@ -124,8 +123,7 @@ def teaching_unit_view(request: WSGIRequest, unit_id: uuid.UUID):
unit = get_object_or_404(models.TeachingUnit, id=unit_id)
# check if the user is allowed to see this specific object
- if unit not in models.TeachingUnit.all_visible_by_user(request.user):
- # TODO(Faraphel): syntaxic sugar session.visible_by_user(request.user)
+ if not unit.is_visible_by_user(request.user):
return HttpResponseForbidden()
# render the page
@@ -143,23 +141,14 @@ def teaching_session_view(request: WSGIRequest, session_id: uuid.UUID):
session = get_object_or_404(models.TeachingSession, id=session_id)
# check if the user is allowed to see this specific object
- if session not in models.TeachingSession.all_visible_by_user(request.user):
- # TODO(Faraphel): syntaxic sugar session.visible_by_user(request.user)
+ if not session.is_visible_by_user(request.user):
return HttpResponseForbidden()
# prepare the data and the "complex" query for the template
session_students_data = {
student: {
- "attendance": get_object_or_none(
- models.Attendance.objects,
- session=session,
- student=student
- ),
- "absence": get_object_or_none(
- models.Absence.objects,
- student=student,
- start__lte=session.start, end__gte=session.end
- ), # TODO(Faraphel): property ?
+ "attendance": get_object_or_none(models.Attendance.objects, session=session, student=student),
+ "absence": get_object_or_none(session.related_absences, student=student)
}
for student in session.group.students.all()
@@ -181,8 +170,7 @@ def absence_view(request: WSGIRequest, absence_id: uuid.UUID):
absence = get_object_or_404(models.Absence, id=absence_id)
# check if the user is allowed to see this specific object
- if absence not in models.Absence.all_visible_by_user(request.user):
- # TODO(Faraphel): syntaxic sugar session.visible_by_user(request.user)
+ if not absence.is_visible_by_user(request.user):
return HttpResponseForbidden()
# render the page
@@ -236,3 +224,59 @@ def new_absence_view(request: WSGIRequest):
form_new_absence=form_new_absence,
)
)
+
+
+def absence_list_view(request):
+ # get all the absences that the user can see, sorted by starting date
+ raw_absences = models.Absence.all_visible_by_user(request.user).order_by("start")
+ # paginate them to avoid having too many elements at the same time
+ paginator = Paginator(raw_absences, ELEMENT_PER_PAGE)
+
+ # get only the session for the requested page
+ page = request.GET.get("page", 0)
+ absences = paginator.get_page(page)
+
+ # render the page
+ return render(
+ request,
+ "Palto/absence_list.html",
+ context=dict(
+ absences=absences
+ )
+ )
+
+
+@login_required
+def department_view(request: WSGIRequest, department_id: uuid.UUID):
+ department = get_object_or_404(models.Department, id=department_id)
+
+ # check if the user is allowed to see this specific object
+ if not department.is_visible_by_user(request.user):
+ return HttpResponseForbidden()
+
+ # render the page
+ return render(
+ request,
+ "Palto/department_view.html",
+ context=dict(
+ department=department,
+ )
+ )
+
+
+@login_required
+def student_group_view(request: WSGIRequest, group_id: uuid.UUID):
+ group = get_object_or_404(models.StudentGroup, id=group_id)
+
+ # check if the user is allowed to see this specific object
+ if not group.is_visible_by_user(request.user):
+ return HttpResponseForbidden()
+
+ # render the page
+ return render(
+ request,
+ "Palto/student_group.html",
+ context=dict(
+ group=group,
+ )
+ )