Skip to content

Commit

Permalink
chore: prevent creation of retired product types from admin (#4533)
Browse files Browse the repository at this point in the history
  • Loading branch information
zawan-ila authored Jan 7, 2025
1 parent 3bae84c commit 34494e7
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 2 deletions.
10 changes: 9 additions & 1 deletion course_discovery/apps/course_metadata/forms.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.forms.utils import ErrorList
from django.utils.translation import gettext_lazy as _

from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.models import Course, CourseRun, Pathway, Program
from course_discovery.apps.course_metadata.models import Course, CourseRun, CourseRunType, CourseType, Pathway, Program
from course_discovery.apps.course_metadata.widgets import SortedModelSelect2Multiple


Expand Down Expand Up @@ -121,6 +122,8 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.fields.get('product_source'):
self.fields['product_source'].required = True
if not self.instance.pk:
self.fields['type'].queryset = CourseType.objects.exclude(slug__in=settings.RETIRED_COURSE_TYPES)


class CourseRunAdminForm(forms.ModelForm):
Expand Down Expand Up @@ -151,6 +154,11 @@ class Meta:
),
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.instance.pk:
self.fields['type'].queryset = CourseRunType.objects.exclude(slug__in=settings.RETIRED_RUN_TYPES)


class PathwayAdminForm(forms.ModelForm):
class Meta:
Expand Down
62 changes: 61 additions & 1 deletion course_discovery/apps/course_metadata/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from django.contrib.contenttypes.models import ContentType
from django.http import HttpRequest
from django.test import LiveServerTestCase, TestCase
from django.test.utils import override_settings
from django.urls import reverse
from rest_framework.status import HTTP_200_OK
from selenium import webdriver
Expand All @@ -25,7 +26,9 @@
from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.constants import PathwayType
from course_discovery.apps.course_metadata.forms import PathwayAdminForm, ProgramAdminForm
from course_discovery.apps.course_metadata.models import Degree, Person, Position, Program, ProgramType, Source
from course_discovery.apps.course_metadata.models import (
CourseRunType, CourseType, Degree, Person, Position, Program, ProgramType, Source
)
from course_discovery.apps.course_metadata.tests import factories


Expand Down Expand Up @@ -222,6 +225,63 @@ def test_new_program_without_courses(self):
response = self.client.get(reverse('admin:course_metadata_program_change', args=(program.id,)))
assert response.status_code == 200

@ddt.data(
[{'RETIRED_COURSE_TYPES': ['audit']}, False, CourseType],
[{'RETIRED_RUN_TYPES': ['audit']}, False, CourseRunType],
[{}, True, CourseType],
[{}, True, CourseRunType]
)
@ddt.unpack
def test_retired_product_types_not_in_options(self, custom_settings, audit_in_options, type_model):
""" Verify that new objects (courses/courseruns) can not have a retired type"""
audit_type = type_model.objects.get(slug='audit')
url_name = (
"admin:course_metadata_course_add"
if type_model is CourseType
else "admin:course_metadata_courserun_add"
)
url = reverse(url_name)
with override_settings(**custom_settings):
response = self.client.get(url)
assert response.status_code == 200

soup = BeautifulSoup(response.content)
type_options = soup.find('select', {'name': 'type'}).find_all('option')
type_option_names = map(lambda opt: opt.get_text(), type_options)
assert (audit_type.name in type_option_names) == audit_in_options

@ddt.data(
[{'RETIRED_COURSE_TYPES': ['audit']}, CourseType],
[{'RETIRED_RUN_TYPES': ['audit']}, CourseRunType],
[{}, CourseType],
[{}, CourseRunType]
)
@ddt.unpack
def test_retired_product_types_in_options(self, custom_settings, type_model):
""" Verify that objects associated to retired types keep showing it in the type dropdown """
audit_type = type_model.objects.get(slug='audit')

url_name = (
"admin:course_metadata_course_change"
if type_model is CourseType
else "admin:course_metadata_courserun_change"
)
product = (
factories.CourseFactory(type=audit_type)
if type_model is CourseType
else factories.CourseRunFactory(type=audit_type)
)

url = reverse(url_name, args=(product.id,))
with override_settings(**custom_settings):
response = self.client.get(url)
assert response.status_code == 200

soup = BeautifulSoup(response.content)
type_options = soup.find('select', {'name': 'type'}).find_all('option')
type_option_names = map(lambda opt: opt.get_text(), type_options)
assert audit_type.name in type_option_names


class ProgramAdminFunctionalTests(SiteMixin, LiveServerTestCase):
""" Functional Tests for Admin page."""
Expand Down

0 comments on commit 34494e7

Please sign in to comment.