Commit e5490b2e by Clinton Blackburn Committed by Clinton Blackburn

Updated Publisher to always create an audit seat for verified course runs

The SeatForm implements the logic to create an audit seat for verified
and credit course runs. It also removes audit seats for professional
course runs.

In the longterm, this should be implemented on the UI and exposed to the
end user to avoid future confusion.

LEARNER-2876
parent cd9df78c
import html import html
import logging
import waffle
from dal import autocomplete from dal import autocomplete
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
...@@ -18,6 +20,8 @@ from course_discovery.apps.publisher.models import (Course, CourseRun, CourseUse ...@@ -18,6 +20,8 @@ from course_discovery.apps.publisher.models import (Course, CourseRun, CourseUse
from course_discovery.apps.publisher.utils import is_internal_user from course_discovery.apps.publisher.utils import is_internal_user
from course_discovery.apps.publisher.validators import validate_text_count from course_discovery.apps.publisher.validators import validate_text_count
logger = logging.getLogger(__name__)
class UserModelChoiceField(forms.ModelChoiceField): class UserModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj): def label_from_instance(self, obj):
...@@ -408,6 +412,21 @@ class SeatForm(BaseForm): ...@@ -408,6 +412,21 @@ class SeatForm(BaseForm):
if commit: if commit:
seat.save() seat.save()
if waffle.switch_is_active('publisher_create_audit_seats_for_verified_course_runs'):
course_run = seat.course_run
audit_seats = course_run.seats.filter(type=Seat.AUDIT)
# Ensure that course runs with a verified seat always have an audit seat
if seat.type in (Seat.CREDIT, Seat.VERIFIED,):
if not audit_seats.exists():
course_run.seats.create(type=Seat.AUDIT, price=0, upgrade_deadline=None)
logger.info('Created audit seat for course run [%d]', course_run.id)
elif seat.type != Seat.AUDIT:
# Ensure that professional course runs do NOT have an audit seat
count = audit_seats.count()
audit_seats.delete()
logger.info('Removed [%d] audit seat for course run [%d]', count, course_run.id)
return seat return seat
def clean(self): def clean(self):
......
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pytest
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.test import TestCase from django.test import TestCase
from pytz import timezone from pytz import timezone
from waffle.testutils import override_switch
from course_discovery.apps.core.models import User from course_discovery.apps.core.models import User
from course_discovery.apps.core.tests.factories import UserFactory from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.course_metadata.models import Person from course_discovery.apps.course_metadata.models import Person
from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory, PersonFactory from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory, PersonFactory
from course_discovery.apps.publisher.forms import CourseForm, CourseRunForm, PublisherUserCreationForm from course_discovery.apps.publisher.forms import CourseForm, CourseRunForm, PublisherUserCreationForm, SeatForm
from course_discovery.apps.publisher.tests.factories import CourseFactory, OrganizationExtensionFactory from course_discovery.apps.publisher.models import Seat
from course_discovery.apps.publisher.tests.factories import CourseFactory, OrganizationExtensionFactory, SeatFactory
class UserModelChoiceFieldTests(TestCase): class UserModelChoiceFieldTests(TestCase):
...@@ -190,6 +193,7 @@ class PublisherCustomCourseFormTests(TestCase): ...@@ -190,6 +193,7 @@ class PublisherCustomCourseFormTests(TestCase):
""" """
Tests for publisher 'CourseForm' Tests for publisher 'CourseForm'
""" """
def setUp(self): def setUp(self):
super(PublisherCustomCourseFormTests, self).setUp() super(PublisherCustomCourseFormTests, self).setUp()
self.course_form = CourseForm() self.course_form = CourseForm()
...@@ -265,3 +269,32 @@ class PublisherCustomCourseFormTests(TestCase): ...@@ -265,3 +269,32 @@ class PublisherCustomCourseFormTests(TestCase):
course_form.save() course_form.save()
course.refresh_from_db() course.refresh_from_db()
assert course.title == 'áçã' assert course.title == 'áçã'
@pytest.mark.django_db
class TestSeatForm:
@override_switch('publisher_create_audit_seats_for_verified_course_runs', active=True)
@pytest.mark.parametrize('seat_type', (Seat.NO_ID_PROFESSIONAL, Seat.PROFESSIONAL,))
def test_remove_audit_seat_for_professional_course_runs(self, seat_type):
seat = SeatFactory(type=seat_type)
audit_seat = SeatFactory(type=Seat.AUDIT, course_run=seat.course_run)
form = SeatForm(instance=seat)
form.save()
assert list(seat.course_run.seats.all()) == [seat]
assert not Seat.objects.filter(pk=audit_seat.pk).exists()
@override_switch('publisher_create_audit_seats_for_verified_course_runs', active=True)
def test_audit_only_seat_not_modified(self):
seat = SeatFactory(type=Seat.AUDIT)
form = SeatForm(instance=seat)
form.save()
assert list(seat.course_run.seats.all()) == [seat]
@override_switch('publisher_create_audit_seats_for_verified_course_runs', active=True)
@pytest.mark.parametrize('seat_type', (Seat.CREDIT, Seat.VERIFIED,))
def test_create_audit_seat_for_credit_and_verified_course_runs(self, seat_type):
seat = SeatFactory(type=seat_type)
form = SeatForm(instance=seat)
form.save()
assert seat.course_run.seats.count() == 2
assert seat.course_run.seats.filter(type=Seat.AUDIT, price=0).exists()
...@@ -17,7 +17,7 @@ from course_discovery.apps.publisher.tests.factories import CourseRunFactory, Or ...@@ -17,7 +17,7 @@ from course_discovery.apps.publisher.tests.factories import CourseRunFactory, Or
@freeze_time('2017-01-01T00:00:00Z') @freeze_time('2017-01-01T00:00:00Z')
@pytest.mark.django_db @pytest.mark.django_db
class TestSignals: class TestCreateCourseRunInStudio:
@override_switch('enable_publisher_create_course_run_in_studio', active=True) @override_switch('enable_publisher_create_course_run_in_studio', active=True)
def test_create_course_run_in_studio_without_partner(self): def test_create_course_run_in_studio_without_partner(self):
with mock.patch('course_discovery.apps.publisher.signals.logger.error') as mock_logger: with mock.patch('course_discovery.apps.publisher.signals.logger.error') as mock_logger:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment