diff --git a/Palto/Palto/models.py b/Palto/Palto/models.py
index 049e80b..761ef10 100644
--- a/Palto/Palto/models.py
+++ b/Palto/Palto/models.py
@@ -61,11 +61,15 @@ class User(AbstractUser, ModelPermissionHelper):
id: uuid.UUID = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False, max_length=36)
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} username={self.username!r}>"
+ return f"<{self.__class__.__name__} id={self.short_id} username={self.username!r}>"
def __str__(self):
return f"{self.first_name} {self.last_name.upper()}"
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
+
@staticmethod
def multiple_related_departments(users: Iterable["User"]) -> QuerySet["Department"]:
"""
@@ -141,11 +145,15 @@ class Department(models.Model, ModelPermissionHelper):
students = models.ManyToManyField(to=User, blank=True, related_name="studying_departments")
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} name={self.name!r}>"
+ return f"<{self.__class__.__name__} id={self.short_id} name={self.name!r}>"
def __str__(self):
return self.name
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
+
@staticmethod
def multiple_related_users(departments: Iterable["Department"]) -> QuerySet["User"]:
"""
@@ -212,22 +220,26 @@ class StudentGroup(models.Model, ModelPermissionHelper):
students = models.ManyToManyField(to=User, blank=True, related_name="student_groups")
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} name={self.name!r}>"
+ return f"<{self.__class__.__name__} id={self.short_id} name={self.name!r}>"
def __str__(self):
return self.name
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
+
# validators
def clean(self):
super().clean()
# owner check
- if self.department not in self.owner.teaching_departments:
+ if self.department not in self.owner.teaching_departments.all():
raise ValidationError("The owner is not related to the department.")
# students check
- if not all(self.department in student.studying_departments for student in self.students.all()):
+ if not all(self.department in student.studying_departments.all() for student in self.students.all()):
raise ValidationError("A student is not related to the department.")
# permissions
@@ -254,9 +266,9 @@ class StudentGroup(models.Model, ModelPermissionHelper):
return {
# the user can only interact with a related departments
- "department": lambda data: user.managing_departments | user.teaching_departments,
+ "department": lambda data: (user.managing_departments | user.teaching_departments).all(),
# the owner must be a teacher or a manager of this department
- "owner": lambda data: data["department"].managers | data["department"].teachers,
+ "owner": lambda data: (data["department"].managers | data["department"].teachers).all(),
}
@classmethod
@@ -314,26 +326,30 @@ class TeachingUnit(models.Model, ModelPermissionHelper):
student_groups = models.ManyToManyField(to=StudentGroup, blank=True, related_name="studying_units")
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} name={self.name!r}>"
+ return f"<{self.__class__.__name__} id={self.short_id} name={self.name!r}>"
def __str__(self):
return self.name
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
+
# validations
def clean(self):
super().clean()
# managers check
- if not all(self.department in manager.managing_departments for manager in self.managers.all()):
+ if not all(self.department in manager.managing_departments.all() for manager in self.managers.all()):
raise ValidationError("A manager is not related to the department.")
# teachers check
- if not all(self.department in teacher.teaching_departments for teacher in self.teachers.all()):
+ if not all(self.department in teacher.teaching_departments.all() for teacher in self.teachers.all()):
raise ValidationError("A teacher is not related to the department.")
# student groups check
- if not all(self.department in student_group.department for student_group in self.student_groups.all()):
+ if not all(self.department in student_group.department.all() for student_group in self.student_groups.all()):
raise ValidationError("A student group is not related to the department.")
# permissions
@@ -356,7 +372,7 @@ class TeachingUnit(models.Model, ModelPermissionHelper):
return {
# a user can only interact with a related departments
- "department": lambda data: user.managing_departments | user.teaching_departments
+ "department": lambda data: (user.managing_departments | user.teaching_departments).all()
}
@classmethod
@@ -406,7 +422,11 @@ class StudentCard(models.Model, ModelPermissionHelper):
owner: User = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name="student_cards")
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} owner={self.owner.username!r}>"
+ return f"<{self.__class__.__name__} id={self.short_id} owner={self.owner.username!r}>"
+
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
# validations
@@ -414,7 +434,7 @@ class StudentCard(models.Model, ModelPermissionHelper):
super().clean()
# owner check
- if self.department not in self.owner.studying_departments:
+ if self.department not in self.owner.studying_departments.all():
raise ValidationError("The student is not related to the department.")
# permissions
@@ -436,7 +456,7 @@ class StudentCard(models.Model, ModelPermissionHelper):
return {
# a user can only interact with a related departments
- "department": lambda field, data: field in user.managing_departments,
+ "department": lambda field, data: field in user.managing_departments.all(),
}
@classmethod
@@ -488,11 +508,15 @@ class TeachingSession(models.Model, ModelPermissionHelper):
teacher = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name="teaching_sessions")
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} unit={self.unit.name!r} start={self.start}>"
+ return f"<{self.__class__.__name__} id={self.short_id} unit={self.unit.name!r} start={self.start}>"
def __str__(self):
return f"{self.unit.name} ({self.start})"
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
+
@property
def end(self) -> datetime:
return self.start + self.duration
@@ -502,12 +526,12 @@ class TeachingSession(models.Model, ModelPermissionHelper):
def clean(self):
super().clean()
- # group check
- if self.unit.department not in self.group.department:
+ # department check
+ if self.unit.department != self.group.department:
raise ValidationError("The group is not related to the unit department.")
# teacher check
- if self.unit not in self.teacher.teaching_units:
+ if self.unit not in self.teacher.teaching_units.all():
raise ValidationError("The teacher is not related to the unit.")
# permissions
@@ -545,7 +569,7 @@ class TeachingSession(models.Model, ModelPermissionHelper):
user.teaching_units |
# all the units of the department the user is managing
TeachingUnit.objects.filter(pk__in=user.managing_departments.values("teaching_units"))
- )
+ ).all()
}
@classmethod
@@ -609,19 +633,23 @@ class Attendance(models.Model, ModelPermissionHelper):
def __repr__(self):
return (
f"<{self.__class__.__name__} "
- f"id={str(self.id)[:8]} "
+ f"id={self.short_id} "
f"student={self.student.username} "
- f"session={str(self.session.id)[:8]}"
+ f"session={self.session.short_id}"
f">"
)
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
+
# validations
def clean(self):
super().clean()
# student check
- if self.student not in self.session.group.students:
+ if self.student not in self.session.group.students.all():
raise ValidationError("The student is not related to the student group.")
# permissions
@@ -662,7 +690,7 @@ class Attendance(models.Model, ModelPermissionHelper):
pk__in=user.managing_departments.values("teaching_units")
).values("sessions")
)
- )
+ ).all()
}
@classmethod
@@ -721,7 +749,7 @@ class Absence(models.Model, ModelPermissionHelper):
def __repr__(self):
return (
f"<{self.__class__.__name__} "
- f"id={str(self.id)[:8]} "
+ f"id={self.short_id} "
f"department={self.department} "
f"student={self.student.username} "
f"start={self.start} "
@@ -730,7 +758,11 @@ class Absence(models.Model, ModelPermissionHelper):
)
def __str__(self):
- return f"[{str(self.id)[:8]}] {self.student}"
+ return f"[{self.short_id}] {self.student}"
+
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
# validations
@@ -738,7 +770,7 @@ class Absence(models.Model, ModelPermissionHelper):
super().clean()
# student check
- if self.department not in self.student.studying_departments:
+ if self.department not in self.student.studying_departments.all():
raise ValidationError("The student is not related to the department.")
# properties
@@ -775,7 +807,7 @@ class Absence(models.Model, ModelPermissionHelper):
return {
# all the departments the user is studying in
- "department": lambda data: user.studying_departments,
+ "department": lambda data: user.studying_departments.all(),
}
@classmethod
@@ -831,7 +863,11 @@ class AbsenceAttachment(models.Model, ModelPermissionHelper):
absence = models.ForeignKey(to=Absence, on_delete=models.CASCADE, related_name="attachments")
def __repr__(self):
- return f"<{self.__class__.__name__} id={str(self.id)[:8]} content={self.content!r}>"
+ return f"<{self.__class__.__name__} id={self.short_id} content={self.content!r}>"
+
+ @property
+ def short_id(self) -> str:
+ return str(self.id)[:8]
# permissions
@@ -853,7 +889,7 @@ class AbsenceAttachment(models.Model, ModelPermissionHelper):
return {
# all the departments the user is studying in
- "absence": lambda data: user.absences,
+ "absence": lambda data: user.absences.all(),
}
@classmethod
diff --git a/Palto/Palto/templates/Palto/profile.html b/Palto/Palto/templates/Palto/profile.html
index 177ba14..6ed7d5f 100644
--- a/Palto/Palto/templates/Palto/profile.html
+++ b/Palto/Palto/templates/Palto/profile.html
@@ -1,9 +1,74 @@
{% extends "Palto/base.html" %}
+{% load dict_tags %}
{% block body %}
{{ profile.username }}
{{ profile.email }}
{% if profile.is_superuser %}Administrator{% endif %}
+
+ {# user related departments table #}
+
+ {% for department, profile_department_data in profile_departments_data.items %}
+
+ {# department name #}
+ {{ department.name }} |
+ {# relation information #}
+
+
+ {# user managing the department #}
+ {% if profile_department_data|dict_get:"is_manager" %}
+
+ Responsable de Département |
+ / |
+
+ {% endif %}
+ {# user managing units #}
+ {% with managing_units=profile_department_data|dict_get:"managing_units" %}
+ {% if managing_units|length > 0 %}
+
+ Responsable d'UE |
+
+ {% for managing_unit in managing_units %}
+ {{ managing_unit.name }}
+ {% if not forloop.last %} {% endif %}
+ {% endfor %}
+ |
+
+ {% endif %}
+ {% endwith %}
+ {# user teaching units #}
+ {% with teaching_units=profile_department_data|dict_get:"teaching_units" %}
+ {% if teaching_units|length > 0 %}
+
+ Enseignant |
+
+ {% for teaching_unit in teaching_units %}
+ {{ teaching_unit.name }}
+ {% if not forloop.last %} {% endif %}
+ {% endfor %}
+ |
+
+ {% endif %}
+ {% endwith %}
+ {# user studying groups #}
+ {% with student_groups=profile_department_data|dict_get:"student_groups" %}
+ {% if student_groups|length > 0 %}
+
+ Groupe Étudiant |
+
+ {% for student_group in student_groups %}
+ {{ student_group.name }}
+ {% if not forloop.last %} {% endif %}
+ {% endfor %}
+ |
+
+ {% endif %}
+ {% endwith %}
+
+ |
+
+ {% endfor %}
+
{% endblock %}
diff --git a/Palto/Palto/templates/Palto/teaching_session.html b/Palto/Palto/templates/Palto/teaching_session.html
index 5a6c99c..c71e13d 100644
--- a/Palto/Palto/templates/Palto/teaching_session.html
+++ b/Palto/Palto/templates/Palto/teaching_session.html
@@ -1,27 +1,66 @@
{% extends "Palto/base.html" %}
+{% load dict_tags %}
{% block body %}
- {# table of all the sessions #}
+ {# session's information #}
+
+
+ Identifiant |
+ {{ session.id }} |
+
+
+ Début |
+ {{ session.start }} |
+
+
+ Durée |
+ {{ session.duration }} |
+
+
+ Unité d'Enseignement |
+ {{ session.unit }} |
+
+
+ Enseignant |
+ {{ session.teacher }} |
+
+
+ Groupe |
+ {{ session.group }} |
+
+
+
+ {# session's students information #}
- Identifiant |
- Début |
- Durée |
- Unité d'Enseignement |
- Enseignant |
- Groupe |
+ Elève |
+ Présence |
+ Absence |
-
- {{ session.id }} |
- {{ session.start }} |
- {{ session.duration }} |
- {{ session.unit }} |
- {{ session.teacher }} |
- {{ session.group }} |
-
+ {% for student, session_student_data in session_students_data.items %}
+
+ {{ student }} |
+
+ {% with attendance=session_student_data|dict_get:"attendance" %}
+ {% if attendance != None %}
+ {{ attendance.date }}
+ {% endif %}
+ {% endwith %}
+ |
+
+ {% with absence=session_student_data|dict_get:"attendance" %}
+ {% if absence != None %}
+ ...
+ {% endif %}
+ {% endwith %}
+ |
+
+ {% endfor %}
+
+ {# TODO(Raphaël): export boutton #}
{% endblock %}
diff --git a/Palto/Palto/templates/Palto/teaching_session_list.html b/Palto/Palto/templates/Palto/teaching_session_list.html
index f9ecc3e..6cbf08c 100644
--- a/Palto/Palto/templates/Palto/teaching_session_list.html
+++ b/Palto/Palto/templates/Palto/teaching_session_list.html
@@ -6,16 +6,21 @@
Identifiant |
- Début |
- Durée |
+ UE |
+ Horaire |
+ Enseignant |
+ Effectif |
+ {# show the information for every session #}
{% for session in sessions %}
- {{ session.id }} |
- {{ session.start }} |
- {{ session.duration }} |
+ {{ session.short_id }} |
+ {{ session.unit.name }} |
+ {{ session.start }} {{ session.end }} |
+ {{ session.teacher }} |
+ {{ session.attendances.all|length }} / {{ session.group.students.all|length }} |
{% endfor %}
diff --git a/Palto/Palto/templatetags/__init__.py b/Palto/Palto/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Palto/Palto/templatetags/dict_tags.py b/Palto/Palto/templatetags/dict_tags.py
new file mode 100644
index 0000000..887312f
--- /dev/null
+++ b/Palto/Palto/templatetags/dict_tags.py
@@ -0,0 +1,6 @@
+from django.template.defaulttags import register
+
+
+@register.filter
+def dict_get(d: dict, key: str):
+ return d.get(key)
diff --git a/Palto/Palto/utils.py b/Palto/Palto/utils.py
new file mode 100644
index 0000000..06a968a
--- /dev/null
+++ b/Palto/Palto/utils.py
@@ -0,0 +1,15 @@
+from typing import Optional
+
+from django.core.exceptions import ObjectDoesNotExist
+from django.db.models import Model, Manager
+
+
+def get_object_or_none(manager: Manager, *args, **kwargs) -> Optional[Model]:
+ """
+ Similar to the Manager.get method, but return None instead of raising an error.
+ """
+
+ try:
+ return manager.get(*args, **kwargs)
+ except ObjectDoesNotExist:
+ return None
diff --git a/Palto/Palto/views.py b/Palto/Palto/views.py
index b9ac81f..ead12c6 100644
--- a/Palto/Palto/views.py
+++ b/Palto/Palto/views.py
@@ -13,7 +13,7 @@ from django.shortcuts import render, get_object_or_404, redirect
from Palto.Palto import models
from Palto.Palto.forms import LoginForm
-
+from Palto.Palto.utils import get_object_or_none
ELEMENT_PER_PAGE: int = 30
@@ -71,12 +71,25 @@ def profile_view(request: WSGIRequest, profile_id: uuid.UUID = None):
# get the corresponding user from its id.
profile = get_object_or_404(models.User, id=profile_id)
+ # prepare the data and the "complex" query for the template
+ profile_departments_data = {
+ department: {
+ "is_manager": profile in department.managers.all(),
+ "managing_units": models.TeachingUnit.objects.filter(department=department, managers=profile).all(),
+ "teaching_units": models.TeachingUnit.objects.filter(department=department, teachers=profile).all(),
+ "student_groups": models.StudentGroup.objects.filter(department=department, students=profile).all(),
+ }
+
+ for department in profile.related_departments
+ }
+
# render the page
return render(
request,
"Palto/profile.html",
context=dict(
- profile=profile
+ profile=profile,
+ profile_departments_data=profile_departments_data,
)
)
@@ -110,11 +123,30 @@ def teaching_session_view(request: WSGIRequest, session_id: uuid.UUID):
# TODO: syntaxic sugar session.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__gte=session.start, end__lte=session.end
+ ),
+ }
+
+ for student in session.group.students.all()
+ }
+
# render the page
return render(
request,
"Palto/teaching_session.html",
context=dict(
- session=session
+ session=session,
+ session_students_data=session_students_data,
)
- )
\ No newline at end of file
+ )