diff --git a/courses/models/course.py b/courses/models/course.py index 75cf90e..e05eaf4 100644 --- a/courses/models/course.py +++ b/courses/models/course.py @@ -42,6 +42,12 @@ class Course(models.Model): help_text="The URL of the FAQ document for the course.", ) + min_projects_to_pass = models.IntegerField( + default=1, + blank=False, + help_text="The minimum number of projects to pass the course.", + ) + homework_problems_comments_field = models.BooleanField( default=False, help_text="Include field for problems and comments in homework", diff --git a/courses/tests/test_data.py b/courses/tests/test_data.py index 2fe00ec..ac91e36 100644 --- a/courses/tests/test_data.py +++ b/courses/tests/test_data.py @@ -377,4 +377,79 @@ def test_project_submission_with_404_url(self): commit_id="abcd1234", ) with self.assertRaises(ValidationError): - self.project_submission.full_clean() \ No newline at end of file + self.project_submission.full_clean() + + def test_graduate_data_view(self): + """Test that only students who passed enough projects are returned.""" + + self.course.min_projects_to_pass = 2 + self.course.save() + + self.user.email = "student1@example.com" + self.user.save() + self.enrollment.certificate_name = "Student One" + self.enrollment.save() + + other_user = CustomUser.objects.create( + username="student2", email="student2@example.com", password="pass" + ) + other_enrollment = Enrollment.objects.create( + student=other_user, + course=self.course, + certificate_name="Student Two", + ) + + project1 = Project.objects.create( + course=self.course, + slug="project1", + title="Project 1", + description="Description", + submission_due_date=timezone.now() + timezone.timedelta(days=7), + peer_review_due_date=timezone.now() + timezone.timedelta(days=14), + ) + project2 = Project.objects.create( + course=self.course, + slug="project2", + title="Project 2", + description="Description", + submission_due_date=timezone.now() + timezone.timedelta(days=7), + peer_review_due_date=timezone.now() + timezone.timedelta(days=14), + ) + + ProjectSubmission.objects.create( + project=project1, + student=self.user, + enrollment=self.enrollment, + github_link="https://httpbin.org/status/200", + commit_id="1111", + passed=True, + ) + ProjectSubmission.objects.create( + project=project2, + student=self.user, + enrollment=self.enrollment, + github_link="https://httpbin.org/status/200", + commit_id="2222", + passed=True, + ) + ProjectSubmission.objects.create( + project=project1, + student=other_user, + enrollment=other_enrollment, + github_link="https://httpbin.org/status/200", + commit_id="3333", + passed=True, + ) + + url = reverse( + "data_graduates", + kwargs={"course_slug": self.course.slug}, + ) + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + actual_result = response.json() + + self.assertEqual(len(actual_result), 1) + self.assertEqual(actual_result[0]["email"], self.user.email) + self.assertEqual(actual_result[0]["name"], self.enrollment.certificate_name) diff --git a/courses/urls.py b/courses/urls.py index e470a9f..8226855 100644 --- a/courses/urls.py +++ b/courses/urls.py @@ -94,4 +94,9 @@ data.project_data_view, name="data_project", ), + path( + "data//graduates", + data.graduates_data_view, + name="data_graduates", + ), ] diff --git a/courses/views/data.py b/courses/views/data.py index dd1621b..6007aaa 100644 --- a/courses/views/data.py +++ b/courses/views/data.py @@ -3,6 +3,8 @@ from django.shortcuts import get_object_or_404 from django.db.models import Prefetch +from collections import Counter + from courses.models import ( Answer, Course, @@ -115,3 +117,52 @@ def project_data_view(request, course_slug: str, project_slug: str): } return JsonResponse(result) + +@token_required +def graduates_data_view(request, course_slug: str): + + # Fetch course + try: + course = get_object_or_404(Course, slug=course_slug) + #course = Course.objects.get(id=course_id) + except Course.DoesNotExist: + return JsonResponse({"error": "Course not found"}, status=404) + + # Get passed students + submissions = ProjectSubmission.objects \ + .filter(project__course=course, passed=True) \ + .prefetch_related("enrollment") + + # Count projects per student + cnt = Counter() + ids_mapping = {} + + for s in submissions: + e = s.enrollment + eid = e.id + cnt[eid] += 1 + ids_mapping[eid] = e + + passed = [] + + # Get mimimum number of projects to pass + min_projects = course.min_projects_to_pass + + for eid, c in cnt.items(): + if c >= min_projects: + passed.append(ids_mapping[eid]) + + # Prepare results + results = [] + for enrollment in passed: + student = enrollment.student + email = student.email + name = enrollment.certificate_name or enrollment.display_name + + results.append({ + "email": email, + "name": name, + }) + + + return JsonResponse(results, safe=False)