feat: Implement sharing functionality for CidUserExam attempts, including user management and sharing settings
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -1681,6 +1681,92 @@ class CidUserExam(models.Model):
|
||||
|
||||
share_with_supervisor = models.BooleanField(default=False, help_text="If true the exam status and results will be available to a users supervisor")
|
||||
|
||||
shared_with_users = models.ManyToManyField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
blank=True,
|
||||
related_name="shared_cid_user_exams",
|
||||
help_text="Users with whom this attempt has been manually shared.",
|
||||
)
|
||||
|
||||
def get_sharing_info(self):
|
||||
"""Return a structured dict describing who can view this attempt and why.
|
||||
|
||||
Returns a dict with keys:
|
||||
always_visible_to – list of User objects (authors + markers of the exam)
|
||||
supervisor – User or None (the supervisor's Django account, if applicable)
|
||||
supervisor_enabled – bool (CidUserExam.share_with_supervisor value)
|
||||
exam_supervisor_visible – bool (exam-level results_supervisor_visible flag)
|
||||
manual_shares – QuerySet of User objects (shared_with_users)
|
||||
"""
|
||||
exam = self.exam
|
||||
|
||||
always_visible = []
|
||||
seen_pks = set()
|
||||
for attr in ("author", "markers"):
|
||||
if hasattr(exam, attr):
|
||||
for u in getattr(exam, attr).all():
|
||||
if u.pk not in seen_pks:
|
||||
seen_pks.add(u.pk)
|
||||
always_visible.append(u)
|
||||
|
||||
exam_supervisor_visible = getattr(exam, "results_supervisor_visible", False)
|
||||
supervisor_user = None
|
||||
if self.share_with_supervisor or exam_supervisor_visible:
|
||||
if self.user_user_id:
|
||||
try:
|
||||
sup = self.user_user.userprofile.supervisor
|
||||
if sup and sup.user_id:
|
||||
supervisor_user = sup.user
|
||||
except Exception:
|
||||
pass
|
||||
elif self.cid_user_id:
|
||||
sup = self.cid_user.supervisor
|
||||
if sup and sup.user_id:
|
||||
supervisor_user = sup.user
|
||||
|
||||
return {
|
||||
"always_visible_to": always_visible,
|
||||
"supervisor": supervisor_user,
|
||||
"supervisor_enabled": self.share_with_supervisor,
|
||||
"exam_supervisor_visible": exam_supervisor_visible,
|
||||
"manual_shares": self.shared_with_users.all(),
|
||||
}
|
||||
|
||||
def can_user_view(self, user):
|
||||
"""Check whether a given authenticated User may view this attempt.
|
||||
|
||||
Permitted if the user is:
|
||||
- The attempt owner (user_user)
|
||||
- An author or marker of the related exam/collection
|
||||
- The supervisor of the attempt owner (when share_with_supervisor=True or
|
||||
the exam-level results_supervisor_visible flag is set)
|
||||
- Manually listed in shared_with_users
|
||||
"""
|
||||
if not user or not user.is_authenticated:
|
||||
return False
|
||||
if self.user_user_id and self.user_user_id == user.pk:
|
||||
return True
|
||||
exam = self.exam
|
||||
for attr in ("author", "markers"):
|
||||
if hasattr(exam, attr) and getattr(exam, attr).filter(pk=user.pk).exists():
|
||||
return True
|
||||
exam_supervisor_visible = getattr(exam, "results_supervisor_visible", False)
|
||||
if self.share_with_supervisor or exam_supervisor_visible:
|
||||
if self.user_user_id:
|
||||
try:
|
||||
sup = self.user_user.userprofile.supervisor
|
||||
if sup and sup.user_id == user.pk:
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
elif self.cid_user_id:
|
||||
sup = self.cid_user.supervisor
|
||||
if sup and sup.user_id == user.pk:
|
||||
return True
|
||||
if self.shared_with_users.filter(pk=user.pk).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.cid_user is None:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user