Skip to content

Commit

Permalink
Add "Listen only" enrollment type and integrate it to invitations (#868)
Browse files Browse the repository at this point in the history
* Completed

* Completed
  • Loading branch information
Dmi4er4 committed Aug 24, 2024
1 parent c5446d8 commit 0d37fe4
Show file tree
Hide file tree
Showing 27 changed files with 556 additions and 343 deletions.
2 changes: 1 addition & 1 deletion apps/admission/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-12 14:53+0000\n"
"POT-Creation-Date: 2024-08-21 13:32+0000\n"
"PO-Revision-Date: 2024-07-23 17:04+0000\n"
"Last-Translator: Дмитрий Чернушевич <dmi4er4@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down
13 changes: 9 additions & 4 deletions apps/courses/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
from files.response import XAccelRedirectFileResponse
from files.views import ProtectedFileDownloadView
from learning.models import Enrollment
from learning.settings import Branches, GradeTypes, StudentStatuses
from learning.settings import Branches, GradeTypes, StudentStatuses, EnrollmentTypes
from learning.invitation.views import complete_student_profile
from learning.tests.factories import EnrollmentFactory, CourseInvitationFactory
from users.constants import Roles
from users.models import StudentProfile
from users.services import update_student_status
from users.tests.factories import (
CuratorFactory, StudentFactory, StudentProfileFactory, TeacherFactory, UserFactory
Expand Down Expand Up @@ -325,8 +324,14 @@ def test_view_course_detail_enroll_by_invitation(client):
assert course_invitation.enrolled_students.count() == 0

url = course_invitation.get_absolute_url()
response = client.post(url, follow=True)
assert response.redirect_chain[-1][0] == course.get_absolute_url()
response = client.get(url)
assert response.status_code == 200
html = response.content.decode('utf-8')
assert 'Как вы хотите записаться на курс?' in html
response = client.post(url, data={
"type": EnrollmentTypes.REGULAR
})
assert response.status_code == 302
assert Enrollment.objects.filter(invitation=course_invitation.invitation).exists()
assert course_invitation.enrolled_students.count() == 1
assert current_profile == course_invitation.enrolled_students.all().first()
Expand Down
2 changes: 1 addition & 1 deletion apps/htmlpages/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-12 14:53+0000\n"
"POT-Creation-Date: 2024-08-21 13:32+0000\n"
"PO-Revision-Date: 2015-03-18 08:34+0000\n"
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/django/language/"
Expand Down
29 changes: 29 additions & 0 deletions apps/learning/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
)

from .models import AssignmentComment
from .settings import EnrollmentTypes, InvitationEnrollmentTypes


class SubmitLink(BaseInput):
Expand Down Expand Up @@ -284,13 +285,41 @@ def clean_testimonial(self):


class CourseEnrollmentForm(forms.Form):
type = forms.ChoiceField(
label=_("Как вы хотите записаться на курс?"),
choices=EnrollmentTypes.choices,
initial=EnrollmentTypes.REGULAR,
required=True
)
reason = forms.CharField(
label=_("Почему вы выбрали этот курс?"),
widget=forms.Textarea(),
required=False)

def __init__(self, **kwargs):
ask_enrollment_reason = kwargs.pop('ask_enrollment_reason', None)
super().__init__(**kwargs)
if not ask_enrollment_reason:
self.fields.pop('reason')

self.helper = FormHelper(self)
self.helper.layout.append(Submit('enroll', 'Записаться на курс'))

class CourseInvitationEnrollmentForm(forms.Form):
type = forms.ChoiceField(
label=_("Как вы хотите записаться на курс?"),
choices=EnrollmentTypes.choices,
initial=EnrollmentTypes.REGULAR,
required=True
)

def __init__(self, **kwargs):
enrollment_type = kwargs.pop('enrollment_type', None)
super().__init__(**kwargs)
if enrollment_type != InvitationEnrollmentTypes.ANY:
assert enrollment_type in EnrollmentTypes.values
self.fields["type"].initial = enrollment_type
self.fields["type"].disabled = True
self.helper = FormHelper(self)
self.helper.layout.append(Submit('enroll', 'Записаться на курс'))

Expand Down
5 changes: 3 additions & 2 deletions apps/learning/gradebook/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from courses.constants import AssignmentStatus
from courses.models import Assignment, Course
from learning.models import Enrollment, StudentAssignment, StudentGroup
from learning.settings import GradeTypes
from learning.settings import GradeTypes, EnrollmentTypes

__all__ = ('GradebookStudent', 'GradeBookData', 'gradebook_data',
'get_student_assignment_state')
Expand Down Expand Up @@ -164,7 +164,8 @@ def gradebook_data(course: Course, student_group: Optional[int] = None) -> Grade
# Collect active enrollments
enrolled_students = OrderedDict()
course_enrollments = (Enrollment.active
.filter(course=course))
.filter(course=course)
.exclude(type=EnrollmentTypes.LECTIONS_ONLY))
if student_group is not None:
course_enrollments = course_enrollments.filter(student_group=student_group)
enrollments = (course_enrollments
Expand Down
9 changes: 7 additions & 2 deletions apps/learning/gradebook/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
get_personal_assignments_by_stepik_id
)
from learning.settings import (
AssignmentScoreUpdateSource, Branches, GradeTypes, StudentStatuses, EnrollmentGradeUpdateSource
AssignmentScoreUpdateSource, Branches, GradeTypes, StudentStatuses, EnrollmentGradeUpdateSource, EnrollmentTypes
)
from learning.tests.factories import (
AssignmentCommentFactory, EnrollmentFactory, StudentAssignmentFactory,
Expand Down Expand Up @@ -219,11 +219,16 @@ def test_save_gradebook(client, assert_redirect):
@pytest.mark.django_db
def test_gradebook_data(settings):
co = CourseFactory()
e1, e2, e3, e4, e5 = EnrollmentFactory.create_batch(5, course=co)
e1, e2, e3, e4, e5, e6 = EnrollmentFactory.create_batch(6, course=co)
a1, a2, a3 = AssignmentFactory.create_batch(3, course=co,
passing_score=1, maximum_score=10)
data = gradebook_data(co)
assert len(data.assignments) == 3
assert len(data.students) == 6
e6.type = EnrollmentTypes.LECTIONS_ONLY
e6.save()
data = gradebook_data(co)
assert len(data.assignments) == 3
assert len(data.students) == 5
e1.is_deleted = True
e1.save()
Expand Down
23 changes: 23 additions & 0 deletions apps/learning/migrations/0052_auto_20240821_1331.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.18 on 2024-08-21 13:31

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('learning', '0051_auto_20240802_2000'),
]

operations = [
migrations.AddField(
model_name='courseinvitation',
name='enrollment_type',
field=models.CharField(choices=[('Regular', 'InvitationEnrollmentTypes|Regular'), ('lections', 'InvitationEnrollmentTypes|Lections'), ('Any', 'InvitationEnrollmentTypes|Any')], default='Any', max_length=100, verbose_name='Enrollment|type'),
),
migrations.AddField(
model_name='enrollment',
name='type',
field=models.CharField(choices=[('Regular', 'EnrollmentTypes|Regular'), ('lections', 'EnrollmentTypes|Lections')], default='Regular', max_length=100, verbose_name='Enrollment|type'),
),
]
12 changes: 11 additions & 1 deletion apps/learning/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
)
from learning.settings import (
ENROLLMENT_DURATION, AssignmentScoreUpdateSource, GradeTypes, GradingSystems, EnrollmentGradeUpdateSource,
InvitationCategories
InvitationCategories, EnrollmentTypes, InvitationEnrollmentTypes
)
from learning.utils import humanize_duration, grade_to_base_system
from users.constants import ThumbnailSizes
Expand Down Expand Up @@ -358,6 +358,11 @@ class Enrollment(TimezoneAwareMixin, TimeStampedModel):
Course,
verbose_name=_("Course offering"),
on_delete=models.CASCADE)
type = models.CharField(
verbose_name=_("Enrollment|type"),
max_length=100,
choices=EnrollmentTypes.choices,
default=EnrollmentTypes.REGULAR)
grade = models.CharField(
verbose_name=_("Enrollment|grade"),
max_length=100,
Expand Down Expand Up @@ -480,6 +485,11 @@ class CourseInvitation(models.Model):
verbose_name=_("CourseOffering|capacity"),
default=0,
help_text=_("0 - unlimited"))
enrollment_type = models.CharField(
verbose_name=_("Enrollment|type"),
max_length=100,
choices=InvitationEnrollmentTypes.choices,
default=InvitationEnrollmentTypes.ANY)
enrolled_students = models.ManyToManyField(
StudentProfile,
verbose_name=_("Enrolled students"),
Expand Down
7 changes: 4 additions & 3 deletions apps/learning/services/enrollment_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from learning.services.notification_service import (
remove_course_notifications_for_student
)
from learning.settings import GradeTypes, EnrollmentGradeUpdateSource
from learning.settings import GradeTypes, EnrollmentGradeUpdateSource, EnrollmentTypes
from users.models import StudentProfile, User


Expand All @@ -42,7 +42,7 @@ def _format_reason_record(reason_text: str, course: Course) -> str:

@classmethod
def enroll(cls, student_profile: StudentProfile, course: Course,
reason_entry: str = '',
reason_entry: str = '', type: EnrollmentTypes = EnrollmentTypes.REGULAR,
student_group: Optional[StudentGroup] = None, **attrs: Any) -> Enrollment:
if course.group_mode != CourseGroupModes.NO_GROUPS and not student_group:
raise ValidationError("Provide student group value")
Expand Down Expand Up @@ -81,7 +81,8 @@ def enroll(cls, student_profile: StudentProfile, course: Course,
"is_deleted": False,
"student_profile": student_profile,
"student_group": student_group,
"reason_entry": reason_entry
"reason_entry": reason_entry,
"type": type
})
updated = (Enrollment.objects
.filter(*filters)
Expand Down
9 changes: 9 additions & 0 deletions apps/learning/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ class AcademicDegreeLevels(DjangoChoices):
ACADEMIC_LEAVE = C("10", _('academic_leave'))
OTHER = C("other", _('Other'))

class EnrollmentTypes(DjangoChoices):
REGULAR = C("Regular", _("EnrollmentTypes|Regular"))
LECTIONS_ONLY = C("lections", _("EnrollmentTypes|Lections"))

class InvitationEnrollmentTypes(DjangoChoices):
REGULAR = C("Regular", _("InvitationEnrollmentTypes|Regular"))
LECTIONS_ONLY = C("lections", _("InvitationEnrollmentTypes|Lections"))
ANY = C("Any", _("InvitationEnrollmentTypes|Any"))

class InvitationCategories(DjangoChoices):
UNIVERSITY = C("university", _("Invitation|University"))
STAFF = C("staff", _("Invitation|Staff"))
Expand Down
6 changes: 4 additions & 2 deletions apps/learning/study/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
StudentAssignment
)
from learning.permissions import ViewCourses, ViewOwnStudentAssignment
from learning.settings import Branches, StudentStatuses, GradeTypes
from learning.settings import Branches, StudentStatuses, GradeTypes, EnrollmentTypes
from learning.tests.factories import (
AssignmentCommentFactory, EnrollmentFactory, StudentAssignmentFactory, CourseInvitationFactory
)
Expand Down Expand Up @@ -551,7 +551,9 @@ def test_view_student_courses_list_as_invited(client):
assert len(response.context_data['ongoing_rest']) == 1
assert len(response.context_data['archive']) == 1

client.post(course_invitation.get_absolute_url(), follow=True)
client.post(course_invitation.get_absolute_url(), data={
"type": EnrollmentTypes.REGULAR
})
response = client.get(url)
assert len(response.context_data['ongoing_enrolled']) == 2
assert len(response.context_data['ongoing_rest']) == 0
Expand Down
13 changes: 8 additions & 5 deletions apps/learning/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from core.urls import reverse
from courses.tests.factories import AssignmentFactory, CourseFactory
from learning.models import AssignmentScoreAuditLog, AssignmentStatus, StudentAssignment, EnrollmentGradeLog
from learning.settings import AssignmentScoreUpdateSource, GradeTypes
from learning.settings import AssignmentScoreUpdateSource, GradeTypes, EnrollmentTypes
from learning.tests.factories import EnrollmentFactory
from users.tests.factories import CuratorFactory

Expand Down Expand Up @@ -87,10 +87,13 @@ def test_admin_enrollment_grade_log(client):
assert not EnrollmentGradeLog.objects.exists()

enrollment_url = reverse('admin:learning_enrollment_change', args=[enrollment.pk])
form_data = {'student_profile': enrollment.student_profile.pk,
'grade': GradeTypes.GOOD,
'grade_history-TOTAL_FORMS': 0,
'grade_history-INITIAL_FORMS': 0}
form_data = {
'type': EnrollmentTypes.REGULAR,
'student_profile': enrollment.student_profile.pk,
'grade': GradeTypes.GOOD,
'grade_history-TOTAL_FORMS': 0,
'grade_history-INITIAL_FORMS': 0
}
client.login(curator)
response = client.post(enrollment_url, form_data)
assert response.status_code == 302
Expand Down
Loading

0 comments on commit 0d37fe4

Please sign in to comment.