Commit 4ef6ee98 by Sanford Student

use LMS date for course start/end

parent 28f54cfa
...@@ -335,6 +335,7 @@ class CourseRunWithProgramsSerializerTests(TestCase): ...@@ -335,6 +335,7 @@ class CourseRunWithProgramsSerializerTests(TestCase):
""" """
ProgramFactory(courses=[self.course_run.course], status=ProgramStatus.Deleted) ProgramFactory(courses=[self.course_run.course], status=ProgramStatus.Deleted)
serializer = CourseRunWithProgramsSerializer(self.course_run, context=self.serializer_context) serializer = CourseRunWithProgramsSerializer(self.course_run, context=self.serializer_context)
self.assertEqual(serializer.data['programs'], []) self.assertEqual(serializer.data['programs'], [])
def test_include_deleted_programs(self): def test_include_deleted_programs(self):
......
...@@ -87,3 +87,46 @@ class LMSAPIClient(object): ...@@ -87,3 +87,46 @@ class LMSAPIClient(object):
exception.__class__.__name__, user.username) exception.__class__.__name__, user.username)
return api_access_request return api_access_request
def get_course_details(self, course_key):
"""
Get details for a course.
Arguments:
course_key (string): LMS identifier for the course.
Returns:
Body consists of the following fields:
* effort: A textual description of the weekly hours of effort expected
in the course.
* end: Date the course ends, in ISO 8601 notation
* enrollment_end: Date enrollment ends, in ISO 8601 notation
* enrollment_start: Date enrollment begins, in ISO 8601 notation
* id: A unique identifier of the course; a serialized representation
of the opaque key identifying the course.
* media: An object that contains named media items. Included here:
* course_image: An image to show for the course. Represented
as an object with the following fields:
* uri: The location of the image
* name: Name of the course
* number: Catalog number of the course
* org: Name of the organization that owns the course
* overview: A possibly verbose HTML textual description of the course.
Note: this field is only included in the Course Detail view, not
the Course List view.
* short_description: A textual description of the course
* start: Date the course begins, in ISO 8601 notation
* start_display: Readably formatted start of the course
* start_type: Hint describing how `start_display` is set. One of:
* `"string"`: manually set by the course author
* `"timestamp"`: generated from the `start` timestamp
* `"empty"`: no start date is specified
* pacing: Course pacing. Possible values: instructor, self
"""
resource = '/api/courses/v1/courses/{}'.format(course_key)
try:
return getattr(self.client, resource).get()
except (SlumberBaseException, ConnectionError, Timeout, KeyError) as exception:
logger.exception('%s: Failed to fetch CourseDetails from LMS for course [%s].',
exception.__class__.__name__, course_key)
...@@ -8,7 +8,9 @@ from django.db import DatabaseError, connection, transaction ...@@ -8,7 +8,9 @@ from django.db import DatabaseError, connection, transaction
from django.http import Http404, JsonResponse from django.http import Http404, JsonResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.generic import View from django.views.generic import View
from course_discovery.apps.core.constants import Status from course_discovery.apps.core.constants import Status
try: try:
import newrelic.agent import newrelic.agent
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
......
...@@ -21,6 +21,7 @@ from course_discovery.apps.publisher.tests.factories import ( ...@@ -21,6 +21,7 @@ from course_discovery.apps.publisher.tests.factories import (
CourseFactory, CourseRunFactory, CourseRunStateFactory, CourseStateFactory, CourseUserRoleFactory, CourseFactory, CourseRunFactory, CourseRunStateFactory, CourseStateFactory, CourseUserRoleFactory,
OrganizationExtensionFactory, SeatFactory OrganizationExtensionFactory, SeatFactory
) )
from course_discovery.apps.publisher.tests.utils import MockedStartEndDateTestCase
class CourseUserRoleSerializerTests(SiteMixin, TestCase): class CourseUserRoleSerializerTests(SiteMixin, TestCase):
...@@ -292,7 +293,7 @@ class CourseStateSerializerTests(SiteMixin, TestCase): ...@@ -292,7 +293,7 @@ class CourseStateSerializerTests(SiteMixin, TestCase):
serializer.update(self.course_state, data) serializer.update(self.course_state, data)
class CourseRunStateSerializerTests(SiteMixin, TestCase): class CourseRunStateSerializerTests(SiteMixin, MockedStartEndDateTestCase):
serializer_class = CourseRunStateSerializer serializer_class = CourseRunStateSerializer
def setUp(self): def setUp(self):
......
...@@ -26,6 +26,7 @@ from course_discovery.apps.publisher.models import ( ...@@ -26,6 +26,7 @@ from course_discovery.apps.publisher.models import (
Course, CourseRun, CourseRunState, CourseState, OrganizationExtension, Seat Course, CourseRun, CourseRunState, CourseState, OrganizationExtension, Seat
) )
from course_discovery.apps.publisher.tests import JSON_CONTENT_TYPE, factories from course_discovery.apps.publisher.tests import JSON_CONTENT_TYPE, factories
from course_discovery.apps.publisher.tests.utils import MockedStartEndDateTestCase
@ddt.ddt @ddt.ddt
...@@ -588,7 +589,7 @@ class ChangeCourseStateViewTests(SiteMixin, TestCase): ...@@ -588,7 +589,7 @@ class ChangeCourseStateViewTests(SiteMixin, TestCase):
self._assert_email_sent(course_team_user, subject) self._assert_email_sent(course_team_user, subject)
class ChangeCourseRunStateViewTests(SiteMixin, TestCase): class ChangeCourseRunStateViewTests(SiteMixin, MockedStartEndDateTestCase):
def setUp(self): def setUp(self):
super(ChangeCourseRunStateViewTests, self).setUp() super(ChangeCourseRunStateViewTests, self).setUp()
......
...@@ -121,7 +121,7 @@ class CourseRunViewSetTests(APITestCase): ...@@ -121,7 +121,7 @@ class CourseRunViewSetTests(APITestCase):
log.check((LOGGER_NAME, 'INFO', log.check((LOGGER_NAME, 'INFO',
'Published course run with id: [{}] lms_course_id: [{}], user: [{}], date: [{}]'.format( 'Published course run with id: [{}] lms_course_id: [{}], user: [{}], date: [{}]'.format(
publisher_course_run.id, publisher_course_run.lms_course_id, self.user, date.today()))) publisher_course_run.id, publisher_course_run.lms_course_id, self.user, date.today())))
assert len(responses.calls) == 3 assert len(responses.calls) == 5
expected = { expected = {
'discovery': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS, 'discovery': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS,
'ecommerce': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS, 'ecommerce': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS,
...@@ -130,7 +130,7 @@ class CourseRunViewSetTests(APITestCase): ...@@ -130,7 +130,7 @@ class CourseRunViewSetTests(APITestCase):
assert response.data == expected assert response.data == expected
# Verify the correct deadlines were sent to the E-Commerce API # Verify the correct deadlines were sent to the E-Commerce API
ecommerce_body = json.loads(responses.calls[2].request.body) ecommerce_body = json.loads(responses.calls[4].request.body)
expected = [ expected = [
serialize_seat_for_ecommerce_api(audit_seat), serialize_seat_for_ecommerce_api(audit_seat),
serialize_seat_for_ecommerce_api(professional_seat), serialize_seat_for_ecommerce_api(professional_seat),
...@@ -289,13 +289,16 @@ class CourseRunViewSetTests(APITestCase): ...@@ -289,13 +289,16 @@ class CourseRunViewSetTests(APITestCase):
root=partner.studio_url.strip('/'), root=partner.studio_url.strip('/'),
key=publisher_course_run.lms_course_id key=publisher_course_run.lms_course_id
) )
responses.add(responses.PATCH, url, json=expected_error, status=500) responses.add(responses.PATCH, url, json=expected_error, status=500)
self._mock_ecommerce_api(publisher_course_run) self._mock_ecommerce_api(publisher_course_run)
url = reverse('publisher:api:v1:course_run-publish', kwargs={'pk': publisher_course_run.pk}) url = reverse('publisher:api:v1:course_run-publish', kwargs={'pk': publisher_course_run.pk})
response = self.client.post(url, {}) response = self.client.post(url, {})
assert response.status_code == 502 assert response.status_code == 502
assert len(responses.calls) == 2
assert len(responses.calls) == 4
expected = { expected = {
'discovery': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS, 'discovery': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS,
'ecommerce': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS, 'ecommerce': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS,
...@@ -317,7 +320,7 @@ class CourseRunViewSetTests(APITestCase): ...@@ -317,7 +320,7 @@ class CourseRunViewSetTests(APITestCase):
url = reverse('publisher:api:v1:course_run-publish', kwargs={'pk': publisher_course_run.pk}) url = reverse('publisher:api:v1:course_run-publish', kwargs={'pk': publisher_course_run.pk})
response = self.client.post(url, {}) response = self.client.post(url, {})
assert response.status_code == 502 assert response.status_code == 502
assert len(responses.calls) == 3 assert len(responses.calls) == 5
expected = { expected = {
'discovery': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS, 'discovery': CourseRunViewSet.PUBLICATION_SUCCESS_STATUS,
'ecommerce': 'FAILED: ' + json.dumps(expected_error), 'ecommerce': 'FAILED: ' + json.dumps(expected_error),
......
...@@ -95,7 +95,7 @@ class CourseRunViewSet(viewsets.GenericViewSet): ...@@ -95,7 +95,7 @@ class CourseRunViewSet(viewsets.GenericViewSet):
'id': course_run.lms_course_id, 'id': course_run.lms_course_id,
'uuid': str(discovery_course.uuid), 'uuid': str(discovery_course.uuid),
'name': course_run.title_override or course_run.course.title, 'name': course_run.title_override or course_run.course.title,
'verification_deadline': serialize_datetime(course_run.end), 'verification_deadline': serialize_datetime(course_run.lms_end),
} }
# NOTE: We only order here to aid testing. The E-Commerce API does NOT care about ordering. # NOTE: We only order here to aid testing. The E-Commerce API does NOT care about ordering.
...@@ -146,7 +146,7 @@ class CourseRunViewSet(viewsets.GenericViewSet): ...@@ -146,7 +146,7 @@ class CourseRunViewSet(viewsets.GenericViewSet):
defaults = { defaults = {
'start': course_run.start, 'start': course_run.start,
'end': course_run.end, 'end': course_run.lms_end,
'pacing_type': course_run.pacing_type, 'pacing_type': course_run.pacing_type,
'title_override': course_run.title_override, 'title_override': course_run.title_override,
'min_effort': course_run.min_effort, 'min_effort': course_run.min_effort,
......
...@@ -2,6 +2,7 @@ import datetime ...@@ -2,6 +2,7 @@ import datetime
import logging import logging
from urllib.parse import urljoin from urllib.parse import urljoin
import pytz
import waffle import waffle
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
...@@ -18,6 +19,7 @@ from sortedm2m.fields import SortedManyToManyField ...@@ -18,6 +19,7 @@ from sortedm2m.fields import SortedManyToManyField
from stdimage.models import StdImageField from stdimage.models import StdImageField
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from course_discovery.apps.core.api_client.lms import LMSAPIClient
from course_discovery.apps.core.models import Currency, User from course_discovery.apps.core.models import Currency, User
from course_discovery.apps.course_metadata.choices import CourseRunPacing from course_discovery.apps.course_metadata.choices import CourseRunPacing
from course_discovery.apps.course_metadata.models import Course as DiscoveryCourse from course_discovery.apps.course_metadata.models import Course as DiscoveryCourse
...@@ -28,7 +30,9 @@ from course_discovery.apps.publisher import emails ...@@ -28,7 +30,9 @@ from course_discovery.apps.publisher import emails
from course_discovery.apps.publisher.choices import ( from course_discovery.apps.publisher.choices import (
CourseRunStateChoices, CourseStateChoices, InternalUserRole, PublisherUserRole CourseRunStateChoices, CourseStateChoices, InternalUserRole, PublisherUserRole
) )
from course_discovery.apps.publisher.utils import is_email_notification_enabled, is_internal_user, is_publisher_admin from course_discovery.apps.publisher.utils import (
is_email_notification_enabled, is_internal_user, is_publisher_admin, parse_datetime_field
)
from course_discovery.apps.publisher.validators import ImageMultiSizeValidator from course_discovery.apps.publisher.validators import ImageMultiSizeValidator
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -355,13 +359,33 @@ class CourseRun(TimeStampedModel, ChangedByMixin): ...@@ -355,13 +359,33 @@ class CourseRun(TimeStampedModel, ChangedByMixin):
history = HistoricalRecords() history = HistoricalRecords()
def __str__(self): def __str__(self):
return '{course}: {start_date}'.format(course=self.course.title, start_date=self.start) return '{course}: {lms_course_id}'.format(course=self.course.title, lms_course_id=self.lms_course_id)
@property @property
def post_back_url(self): def post_back_url(self):
return reverse('publisher:publisher_course_runs_edit', kwargs={'pk': self.id}) return reverse('publisher:publisher_course_runs_edit', kwargs={'pk': self.id})
@property @property
def lms_start(self):
if self.course.partner and self.course.partner.site:
lms = LMSAPIClient(self.course.partner.site)
details = lms.get_course_details(self.lms_course_id)
if details and details['start']:
return pytz.utc.localize(parse_datetime_field(details['start']))
return self.start
@property
def lms_end(self):
if self.course.partner and self.course.partner.site:
lms = LMSAPIClient(self.course.partner.site)
details = lms.get_course_details(self.lms_course_id)
if details and details['end']:
return pytz.utc.localize(parse_datetime_field(details['end']))
return self.end
@property
def created_by(self): def created_by(self):
history_user = self.history.order_by('history_date').first().history_user # pylint: disable=no-member history_user = self.history.order_by('history_date').first().history_user # pylint: disable=no-member
if history_user: if history_user:
...@@ -497,7 +521,7 @@ class Seat(TimeStampedModel, ChangedByMixin): ...@@ -497,7 +521,7 @@ class Seat(TimeStampedModel, ChangedByMixin):
if self.upgrade_deadline: if self.upgrade_deadline:
return self.upgrade_deadline return self.upgrade_deadline
deadline = self.course_run.end - datetime.timedelta(days=settings.PUBLISHER_UPGRADE_DEADLINE_DAYS) deadline = self.course_run.lms_end - datetime.timedelta(days=settings.PUBLISHER_UPGRADE_DEADLINE_DAYS)
deadline = deadline.replace(hour=23, minute=59, second=59, microsecond=99999) deadline = deadline.replace(hour=23, minute=59, second=59, microsecond=99999)
return deadline return deadline
...@@ -800,8 +824,8 @@ class CourseRunState(TimeStampedModel, ChangedByMixin): ...@@ -800,8 +824,8 @@ class CourseRunState(TimeStampedModel, ChangedByMixin):
""" """
course_run = self.course_run course_run = self.course_run
return all([ return all([
course_run.course.course_state.is_approved, course_run.has_valid_seats, course_run.start, course_run.end, course_run.course.course_state.is_approved, course_run.has_valid_seats, course_run.lms_start,
course_run.pacing_type, course_run.has_valid_staff, course_run.is_valid_micromasters, course_run.lms_end, course_run.pacing_type, course_run.has_valid_staff, course_run.is_valid_micromasters,
course_run.is_valid_professional_certificate, course_run.is_valid_xseries, course_run.language, course_run.is_valid_professional_certificate, course_run.is_valid_xseries, course_run.language,
course_run.transcript_languages.all(), course_run.lms_course_id, course_run.min_effort, course_run.transcript_languages.all(), course_run.lms_course_id, course_run.min_effort,
course_run.video_language, course_run.length course_run.video_language, course_run.length
......
...@@ -20,21 +20,22 @@ from course_discovery.apps.publisher.models import ( ...@@ -20,21 +20,22 @@ from course_discovery.apps.publisher.models import (
Course, CourseUserRole, OrganizationExtension, OrganizationUserRole, Seat Course, CourseUserRole, OrganizationExtension, OrganizationUserRole, Seat
) )
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.tests.utils import MockedStartEndDateTestCase
@ddt.ddt @ddt.ddt
class CourseRunTests(TestCase): class CourseRunTests(MockedStartEndDateTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(CourseRunTests, cls).setUpClass() super(CourseRunTests, cls).setUpClass()
cls.course_run = factories.CourseRunFactory() cls.course_run = factories.CourseRunFactory()
def test_str(self): def test_str(self):
""" Verify casting an instance to a string returns a string containing the course title and start date. """ """ Verify casting an instance to a string returns a string containing the course title and LMS ID. """
self.assertEqual( self.assertEqual(
str(self.course_run), str(self.course_run),
'{title}: {date}'.format( '{title}: {lms_course_id}'.format(
title=self.course_run.course.title, date=self.course_run.start title=self.course_run.course.title, lms_course_id=self.course_run.lms_course_id
) )
) )
...@@ -368,7 +369,7 @@ class CourseTests(TestCase): ...@@ -368,7 +369,7 @@ class CourseTests(TestCase):
@pytest.mark.django_db @pytest.mark.django_db
class TestSeatModel: class TestSeatModel():
def test_str(self): def test_str(self):
seat = factories.SeatFactory() seat = factories.SeatFactory()
assert str(seat) == '{course}: {type}'.format(course=seat.course_run.course.title, type=seat.type) assert str(seat) == '{course}: {type}'.format(course=seat.course_run.course.title, type=seat.type)
...@@ -383,6 +384,7 @@ class TestSeatModel: ...@@ -383,6 +384,7 @@ class TestSeatModel:
settings.PUBLISHER_UPGRADE_DEADLINE_DAYS = random.randint(1, 21) settings.PUBLISHER_UPGRADE_DEADLINE_DAYS = random.randint(1, 21)
now = datetime.datetime.utcnow() now = datetime.datetime.utcnow()
seat = factories.SeatFactory(type=Seat.VERIFIED, upgrade_deadline=None, course_run__end=now) seat = factories.SeatFactory(type=Seat.VERIFIED, upgrade_deadline=None, course_run__end=now)
expected = now - datetime.timedelta(days=settings.PUBLISHER_UPGRADE_DEADLINE_DAYS) expected = now - datetime.timedelta(days=settings.PUBLISHER_UPGRADE_DEADLINE_DAYS)
expected = expected.replace(hour=23, minute=59, second=59, microsecond=99999) expected = expected.replace(hour=23, minute=59, second=59, microsecond=99999)
assert seat.calculated_upgrade_deadline == expected assert seat.calculated_upgrade_deadline == expected
...@@ -626,7 +628,7 @@ class CourseStateTests(TestCase): ...@@ -626,7 +628,7 @@ class CourseStateTests(TestCase):
@ddt.ddt @ddt.ddt
class CourseRunStateTests(TestCase): class CourseRunStateTests:
""" Tests for the publisher `CourseRunState` model. """ """ Tests for the publisher `CourseRunState` model. """
@classmethod @classmethod
......
...@@ -43,7 +43,7 @@ from course_discovery.apps.publisher.models import ( ...@@ -43,7 +43,7 @@ from course_discovery.apps.publisher.models import (
Course, CourseEntitlement, CourseRun, CourseRunState, CourseState, OrganizationExtension, Seat Course, CourseEntitlement, CourseRun, CourseRunState, CourseState, OrganizationExtension, Seat
) )
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.tests.utils import create_non_staff_user_and_login from course_discovery.apps.publisher.tests.utils import MockedStartEndDateTestCase, create_non_staff_user_and_login
from course_discovery.apps.publisher.utils import is_email_notification_enabled from course_discovery.apps.publisher.utils import is_email_notification_enabled
from course_discovery.apps.publisher.views import logger as publisher_views_logger from course_discovery.apps.publisher.views import logger as publisher_views_logger
from course_discovery.apps.publisher.views import ( from course_discovery.apps.publisher.views import (
...@@ -55,7 +55,7 @@ from course_discovery.apps.publisher_comments.tests.factories import CommentFact ...@@ -55,7 +55,7 @@ from course_discovery.apps.publisher_comments.tests.factories import CommentFact
@ddt.ddt @ddt.ddt
class CreateCourseViewTests(SiteMixin, TestCase): class CreateCourseViewTests(SiteMixin, MockedStartEndDateTestCase):
""" Tests for the publisher `CreateCourseView`. """ """ Tests for the publisher `CreateCourseView`. """
def setUp(self): def setUp(self):
...@@ -359,7 +359,7 @@ class CreateCourseViewTests(SiteMixin, TestCase): ...@@ -359,7 +359,7 @@ class CreateCourseViewTests(SiteMixin, TestCase):
@ddt.ddt @ddt.ddt
class CreateCourseRunViewTests(SiteMixin, TestCase): class CreateCourseRunViewTests(SiteMixin, MockedStartEndDateTestCase):
""" Tests for the publisher `CreateCourseRunView`. """ """ Tests for the publisher `CreateCourseRunView`. """
def setUp(self): def setUp(self):
...@@ -840,7 +840,7 @@ class CreateCourseRunViewTests(SiteMixin, TestCase): ...@@ -840,7 +840,7 @@ class CreateCourseRunViewTests(SiteMixin, TestCase):
@ddt.ddt @ddt.ddt
class CourseRunDetailTests(SiteMixin, TestCase): class CourseRunDetailTests(SiteMixin, MockedStartEndDateTestCase):
""" Tests for the course-run detail view. """ """ Tests for the course-run detail view. """
def setUp(self): def setUp(self):
...@@ -1185,7 +1185,7 @@ class CourseRunDetailTests(SiteMixin, TestCase): ...@@ -1185,7 +1185,7 @@ class CourseRunDetailTests(SiteMixin, TestCase):
self.assertContains( self.assertContains(
response, '{type}: {start}'.format( response, '{type}: {start}'.format(
type=course_run.get_pacing_type_display(), type=course_run.get_pacing_type_display(),
start=course_run.start.strftime("%B %d, %Y") start=course_run.lms_start.strftime("%B %d, %Y")
) )
) )
...@@ -1534,7 +1534,7 @@ class CourseRunDetailTests(SiteMixin, TestCase): ...@@ -1534,7 +1534,7 @@ class CourseRunDetailTests(SiteMixin, TestCase):
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
@ddt.ddt @ddt.ddt
class CourseRunListViewTests(SiteMixin, TestCase): class CourseRunListViewTests(SiteMixin, MockedStartEndDateTestCase):
def setUp(self): def setUp(self):
super(CourseRunListViewTests, self).setUp() super(CourseRunListViewTests, self).setUp()
Site.objects.exclude(id=self.site.id).delete() Site.objects.exclude(id=self.site.id).delete()
...@@ -2671,7 +2671,7 @@ class CourseDetailViewTests(TestCase): ...@@ -2671,7 +2671,7 @@ class CourseDetailViewTests(TestCase):
@ddt.ddt @ddt.ddt
class CourseEditViewTests(SiteMixin, TestCase): class CourseEditViewTests(SiteMixin, MockedStartEndDateTestCase):
""" Tests for the course edit view. """ """ Tests for the course edit view. """
def setUp(self): def setUp(self):
...@@ -3357,7 +3357,7 @@ class CourseEditViewTests(SiteMixin, TestCase): ...@@ -3357,7 +3357,7 @@ class CourseEditViewTests(SiteMixin, TestCase):
@ddt.ddt @ddt.ddt
class CourseRunEditViewTests(SiteMixin, TestCase): class CourseRunEditViewTests(SiteMixin, MockedStartEndDateTestCase):
""" Tests for the course run edit view. """ """ Tests for the course run edit view. """
def setUp(self): def setUp(self):
...@@ -4075,7 +4075,7 @@ class CourseRevisionViewTests(SiteMixin, TestCase): ...@@ -4075,7 +4075,7 @@ class CourseRevisionViewTests(SiteMixin, TestCase):
@ddt.ddt @ddt.ddt
class CreateRunFromDashboardViewTests(SiteMixin, TestCase): class CreateRunFromDashboardViewTests(SiteMixin, MockedStartEndDateTestCase):
""" Tests for the publisher `CreateRunFromDashboardView`. """ """ Tests for the publisher `CreateRunFromDashboardView`. """
def setUp(self): def setUp(self):
......
import datetime
import mock
from django.test import TestCase
from course_discovery.apps.core.tests.factories import USER_PASSWORD, UserFactory from course_discovery.apps.core.tests.factories import USER_PASSWORD, UserFactory
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
class MockedStartEndDateTestCase(TestCase):
def setUp(self):
super(MockedStartEndDateTestCase, self).setUp()
start_date_patcher = mock.patch(
'course_discovery.apps.publisher.models.CourseRun.lms_start', new_callable=mock.PropertyMock
)
self.addCleanup(start_date_patcher.stop)
self.start_date_mock = start_date_patcher.start()
self.start_date_mock.return_value = datetime.datetime.utcnow()
end_date_patcher = mock.patch(
'course_discovery.apps.publisher.models.CourseRun.lms_end', new_callable=mock.PropertyMock
)
self.addCleanup(end_date_patcher.stop)
self.end_date_mock = end_date_patcher.start()
self.end_date_mock.return_value = datetime.datetime.utcnow()
def create_non_staff_user_and_login(test_class): def create_non_staff_user_and_login(test_class):
""" Create non staff user and login and return user and group. """ """ Create non staff user and login and return user and group. """
non_staff_user = UserFactory() non_staff_user = UserFactory()
......
...@@ -195,7 +195,7 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM ...@@ -195,7 +195,7 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
if history_object: if history_object:
context['publish_date'] = history_object.modified context['publish_date'] = history_object.modified
start_date = course_run.start.strftime("%B %d, %Y") if course_run.start else None start_date = course_run.lms_start.strftime("%B %d, %Y") if course_run.lms_start else None
context['breadcrumbs'] = make_bread_crumbs( context['breadcrumbs'] = make_bread_crumbs(
[ [
(reverse('publisher:publisher_courses'), _('Courses')), (reverse('publisher:publisher_courses'), _('Courses')),
...@@ -430,7 +430,7 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -430,7 +430,7 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
published_runs = set() published_runs = set()
for course_run in self._get_active_course_runs(course): for course_run in self._get_active_course_runs(course):
if course_run.course_run_state.is_published: if course_run.course_run_state.is_published:
start_date = course_run.start.strftime("%B %d, %Y") if course_run.start else None start_date = course_run.lms_start.strftime("%B %d, %Y") if course_run.lms_start else None
published_runs.add('{type} - {start}'.format( published_runs.add('{type} - {start}'.format(
type=course_run.get_pacing_type_display(), type=course_run.get_pacing_type_display(),
start=start_date start=start_date
...@@ -460,12 +460,12 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -460,12 +460,12 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
if not type_is_valid: if not type_is_valid:
misconfigured_seat_type_runs.add('{type} - {start}'.format( misconfigured_seat_type_runs.add('{type} - {start}'.format(
type=course_run.get_pacing_type_display(), type=course_run.get_pacing_type_display(),
start=course_run.start.strftime("%B %d, %Y") start=course_run.lms_start.strftime("%B %d, %Y")
)) ))
if not price_is_valid: if not price_is_valid:
misconfigured_price_runs.add('{type} - {start}'.format( misconfigured_price_runs.add('{type} - {start}'.format(
type=course_run.get_pacing_type_display(), type=course_run.get_pacing_type_display(),
start=course_run.start.strftime("%B %d, %Y") start=course_run.lms_start.strftime("%B %d, %Y")
)) ))
return misconfigured_price_runs, misconfigured_seat_type_runs return misconfigured_price_runs, misconfigured_seat_type_runs
...@@ -1005,7 +1005,7 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix ...@@ -1005,7 +1005,7 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
course_run_seat = self.get_latest_course_run_seat(course_run) course_run_seat = self.get_latest_course_run_seat(course_run)
context['seat_form'] = self.seat_form(instance=course_run_seat) context['seat_form'] = self.seat_form(instance=course_run_seat)
start_date = course_run.start.strftime("%B %d, %Y") if course_run.start else None start_date = course_run.lms_start.strftime("%B %d, %Y") if course_run.lms_start else None
context['breadcrumbs'] = make_bread_crumbs( context['breadcrumbs'] = make_bread_crumbs(
[ [
(reverse('publisher:publisher_courses'), 'Courses'), (reverse('publisher:publisher_courses'), 'Courses'),
......
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