Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions courses/models/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
77 changes: 76 additions & 1 deletion courses/tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,79 @@ def test_project_submission_with_404_url(self):
commit_id="abcd1234",
)
with self.assertRaises(ValidationError):
self.project_submission.full_clean()
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 = "[email protected]"
self.user.save()
self.enrollment.certificate_name = "Student One"
self.enrollment.save()

other_user = CustomUser.objects.create(
username="student2", email="[email protected]", 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)
5 changes: 5 additions & 0 deletions courses/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,9 @@
data.project_data_view,
name="data_project",
),
path(
"data/<slug:course_slug>/graduates",
data.graduates_data_view,
name="data_graduates",
),
]
51 changes: 51 additions & 0 deletions courses/views/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)