Commit a51d35ff by Matthew Piatetsky

Add upgradeable filter to course runs

ECOM-7336
parent 49038df3
...@@ -542,7 +542,7 @@ class CourseWithProgramsSerializer(CourseSerializer): ...@@ -542,7 +542,7 @@ class CourseWithProgramsSerializer(CourseSerializer):
if self.context.get('marketable_enrollable_course_runs_with_archived'): if self.context.get('marketable_enrollable_course_runs_with_archived'):
# Same as "marketable_course_runs_only", but includes courses with an end date in the past # Same as "marketable_course_runs_only", but includes courses with an end date in the past
course_runs = course_runs.marketable().enrollable() course_runs = course_runs.marketable().enrollable().upgradeable()
if self.context.get('published_course_runs_only'): if self.context.get('published_course_runs_only'):
course_runs = course_runs.filter(status=CourseRunStatus.Published) course_runs = course_runs.filter(status=CourseRunStatus.Published)
......
...@@ -26,7 +26,7 @@ from course_discovery.apps.core.tests.factories import UserFactory ...@@ -26,7 +26,7 @@ from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.core.tests.helpers import make_image_file from course_discovery.apps.core.tests.helpers import make_image_file
from course_discovery.apps.core.tests.mixins import ElasticsearchTestMixin from course_discovery.apps.core.tests.mixins import ElasticsearchTestMixin
from course_discovery.apps.course_metadata.choices import CourseRunStatus, ProgramStatus from course_discovery.apps.course_metadata.choices import CourseRunStatus, ProgramStatus
from course_discovery.apps.course_metadata.models import Course, CourseRun, Program from course_discovery.apps.course_metadata.models import Course, CourseRun, Program, Seat
from course_discovery.apps.course_metadata.tests.factories import ( from course_discovery.apps.course_metadata.tests.factories import (
CorporateEndorsementFactory, CourseFactory, CourseRunFactory, EndorsementFactory, ExpectedLearningItemFactory, CorporateEndorsementFactory, CourseFactory, CourseRunFactory, EndorsementFactory, ExpectedLearningItemFactory,
ImageFactory, JobOutlookItemFactory, OrganizationFactory, PersonFactory, PositionFactory, PrerequisiteFactory, ImageFactory, JobOutlookItemFactory, OrganizationFactory, PersonFactory, PositionFactory, PrerequisiteFactory,
...@@ -260,9 +260,9 @@ class CourseWithProgramsSerializerTests(CourseSerializerTests): ...@@ -260,9 +260,9 @@ class CourseWithProgramsSerializerTests(CourseSerializerTests):
enrollment_end=None, enrollment_end=None,
course=self.course course=self.course
) )
SeatFactory(course_run=unpublished_course_run) SeatFactory(course_run=unpublished_course_run, upgrade_deadline=None, type=Seat.VERIFIED)
SeatFactory(course_run=enrollable_course_run) SeatFactory(course_run=enrollable_course_run, upgrade_deadline=None, type=Seat.VERIFIED)
SeatFactory(course_run=archived_course_run) SeatFactory(course_run=archived_course_run, upgrade_deadline=None, type=Seat.VERIFIED)
context = { context = {
'request': self.request, 'request': self.request,
......
...@@ -76,7 +76,7 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -76,7 +76,7 @@ class CourseViewSet(viewsets.ReadOnlyModelViewSet):
mulitple: false mulitple: false
- name: marketable_enrollable_course_runs_with_archived - name: marketable_enrollable_course_runs_with_archived
description: Restrict returned course runs to those that are published, have seats, description: Restrict returned course runs to those that are published, have seats,
and can be enrolled in now. Includes archived courses. can be enrolled in now and can be upgraded. Includes archived courses.
required: false required: false
type: integer type: integer
paramType: query paramType: query
......
...@@ -83,6 +83,22 @@ class CourseRunQuerySet(models.QuerySet): ...@@ -83,6 +83,22 @@ class CourseRunQuerySet(models.QuerySet):
status=CourseRunStatus.Published status=CourseRunStatus.Published
) )
def upgradeable(self):
""" Returns course runs which have a verified or professional seat and do not have
an expired upgrade deadline.
Returns:
QuerySet
"""
now = datetime.datetime.now(pytz.UTC)
# Nested to avoid circular import.
from course_discovery.apps.course_metadata.models import Seat
return self.filter(
Q(seats__type__contains=Seat.VERIFIED) | Q(seats__type__contains=Seat.PROFESSIONAL)
).exclude(
Q(seats__upgrade_deadline__lt=now)
)
class ProgramQuerySet(models.QuerySet): class ProgramQuerySet(models.QuerySet):
def marketable(self): def marketable(self):
......
...@@ -5,7 +5,7 @@ import pytz ...@@ -5,7 +5,7 @@ import pytz
from django.test import TestCase from django.test import TestCase
from course_discovery.apps.course_metadata.choices import CourseRunStatus, ProgramStatus from course_discovery.apps.course_metadata.choices import CourseRunStatus, ProgramStatus
from course_discovery.apps.course_metadata.models import Course, CourseRun, Program from course_discovery.apps.course_metadata.models import Course, CourseRun, Program, Seat
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, ProgramFactory, SeatFactory from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, ProgramFactory, SeatFactory
...@@ -120,6 +120,33 @@ class CourseRunQuerySetTests(TestCase): ...@@ -120,6 +120,33 @@ class CourseRunQuerySetTests(TestCase):
self.assertEqual(CourseRun.objects.marketable().exists(), is_published) self.assertEqual(CourseRun.objects.marketable().exists(), is_published)
def test_upgradeable(self):
""" Verify the method returns only course runs whose upgrade deadline has not passed
or is not defined for verified/professional modes. """
past = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=2)
future = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2)
audit_mode = CourseRunFactory()
SeatFactory(upgrade_deadline=future, course_run=audit_mode, type=Seat.AUDIT)
upgrade_deadline_passed = CourseRunFactory()
SeatFactory(upgrade_deadline=past, course_run=upgrade_deadline_passed, type=Seat.VERIFIED)
upgrade_deadline_not_passed = CourseRunFactory()
SeatFactory(upgrade_deadline=future, course_run=upgrade_deadline_not_passed, type=Seat.VERIFIED)
upgrade_deadline_not_defined = CourseRunFactory()
SeatFactory(upgrade_deadline=None, course_run=upgrade_deadline_not_defined, type=Seat.VERIFIED)
SeatFactory(upgrade_deadline=future, course_run=upgrade_deadline_not_defined, type=Seat.AUDIT)
upgrade_deadline_not_defined_professional = CourseRunFactory()
SeatFactory(upgrade_deadline=None, course_run=upgrade_deadline_not_defined_professional, type=Seat.PROFESSIONAL)
# order doesn't matter
assert sorted(CourseRun.objects.upgradeable(), key=lambda x: x.id) == \
sorted([upgrade_deadline_not_passed, upgrade_deadline_not_defined,
upgrade_deadline_not_defined_professional], key=lambda x: x.id)
@ddt.ddt @ddt.ddt
class ProgramQuerySetTests(TestCase): class ProgramQuerySetTests(TestCase):
......
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