From 05996a71dff52274bac2a9dc83070924add4522c Mon Sep 17 00:00:00 2001 From: Julia Hansbrough <julia@edx.org> Date: Wed, 6 Nov 2013 19:29:50 +0000 Subject: [PATCH] Added expiration_datetime field to CourseModes Added an expiration_datetime field to CourseModes, intended to eventually replace expiration_date. Included relevant schema and data migration file. LMS-1454 --- ...dd_field_coursemode_expiration_datetime.py | 37 +++++++++++++++++ .../0006_expiration_date_to_datetime.py | 40 +++++++++++++++++++ common/djangoapps/course_modes/models.py | 10 +++-- .../course_modes/tests/factories.py | 2 +- .../course_modes/tests/test_models.py | 8 ++-- common/djangoapps/student/tests/tests.py | 8 ++-- common/djangoapps/student/views.py | 4 +- .../shoppingcart/tests/test_models.py | 6 +-- 8 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 common/djangoapps/course_modes/migrations/0005_auto__add_field_coursemode_expiration_datetime.py create mode 100644 common/djangoapps/course_modes/migrations/0006_expiration_date_to_datetime.py diff --git a/common/djangoapps/course_modes/migrations/0005_auto__add_field_coursemode_expiration_datetime.py b/common/djangoapps/course_modes/migrations/0005_auto__add_field_coursemode_expiration_datetime.py new file mode 100644 index 00000000000..b293efa23f6 --- /dev/null +++ b/common/djangoapps/course_modes/migrations/0005_auto__add_field_coursemode_expiration_datetime.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'CourseMode.expiration_datetime' + db.add_column('course_modes_coursemode', 'expiration_datetime', + self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'CourseMode.expiration_datetime' + db.delete_column('course_modes_coursemode', 'expiration_datetime') + + + models = { + 'course_modes.coursemode': { + 'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'}, + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), + 'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}) + } + } + + complete_apps = ['course_modes'] \ No newline at end of file diff --git a/common/djangoapps/course_modes/migrations/0006_expiration_date_to_datetime.py b/common/djangoapps/course_modes/migrations/0006_expiration_date_to_datetime.py new file mode 100644 index 00000000000..319c1b02b82 --- /dev/null +++ b/common/djangoapps/course_modes/migrations/0006_expiration_date_to_datetime.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + from datetime import datetime + for course_mode in orm.CourseMode.objects.all(): + if course_mode.expiration_date is None: + course_mode.expiration_datetime = None + course_mode.save() + else: + course_mode.expiration_datetime = datetime.combine(course_mode.expiration_date, datetime.min.time()) + course_mode.save() + + def backwards(self, orm): + for course_mode in orm.CourseMode.objects.all(): + course_mode.expiration_datetime = None + course_mode.save() + + models = { + 'course_modes.coursemode': { + 'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'}, + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), + 'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}) + } + } + + complete_apps = ['course_modes'] + symmetrical = True diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index ec2cbf1245f..4cf60c0e1eb 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -9,7 +9,7 @@ from collections import namedtuple from django.utils.translation import ugettext as _ from django.db.models import Q -Mode = namedtuple('Mode', ['slug', 'name', 'min_price', 'suggested_prices', 'currency', 'expiration_date']) +Mode = namedtuple('Mode', ['slug', 'name', 'min_price', 'suggested_prices', 'currency', 'expiration_datetime']) class CourseMode(models.Model): """ @@ -38,6 +38,8 @@ class CourseMode(models.Model): # turn this mode off after the given expiration date expiration_date = models.DateField(default=None, null=True, blank=True) + expiration_datetime = models.DateTimeField(default=None, null=True, blank=True) + DEFAULT_MODE = Mode('honor', _('Honor Code Certificate'), 0, '', 'usd', None) DEFAULT_MODE_SLUG = 'honor' @@ -54,15 +56,15 @@ class CourseMode(models.Model): """ now = datetime.now(pytz.UTC) found_course_modes = cls.objects.filter(Q(course_id=course_id) & - (Q(expiration_date__isnull=True) | - Q(expiration_date__gte=now))) + (Q(expiration_datetime__isnull=True) | + Q(expiration_datetime__gte=now))) modes = ([Mode( mode.mode_slug, mode.mode_display_name, mode.min_price, mode.suggested_prices, mode.currency, - mode.expiration_date + mode.expiration_datetime ) for mode in found_course_modes]) if not modes: modes = [cls.DEFAULT_MODE] diff --git a/common/djangoapps/course_modes/tests/factories.py b/common/djangoapps/course_modes/tests/factories.py index 0d519b3ad5f..945858fb1f2 100644 --- a/common/djangoapps/course_modes/tests/factories.py +++ b/common/djangoapps/course_modes/tests/factories.py @@ -11,4 +11,4 @@ class CourseModeFactory(DjangoModelFactory): mode_display_name = 'audit course' min_price = 0 currency = 'usd' - expiration_date = None + expiration_datetime = None diff --git a/common/djangoapps/course_modes/tests/test_models.py b/common/djangoapps/course_modes/tests/test_models.py index 7f09fbf7ccd..5336b3e5fab 100644 --- a/common/djangoapps/course_modes/tests/test_models.py +++ b/common/djangoapps/course_modes/tests/test_models.py @@ -93,7 +93,7 @@ class CourseModeModelTest(TestCase): def test_modes_for_course_expired(self): expired_mode, _status = self.create_mode('verified', 'Verified Certificate') - expired_mode.expiration_date = datetime.now(pytz.UTC) + timedelta(days=-1) + expired_mode.expiration_datetime = datetime.now(pytz.UTC) + timedelta(days=-1) expired_mode.save() modes = CourseMode.modes_for_course(self.course_id) self.assertEqual([CourseMode.DEFAULT_MODE], modes) @@ -103,10 +103,10 @@ class CourseModeModelTest(TestCase): modes = CourseMode.modes_for_course(self.course_id) self.assertEqual([mode1], modes) - expiration_date = datetime.now(pytz.UTC) + timedelta(days=1) - expired_mode.expiration_date = expiration_date + expiration_datetime = datetime.now(pytz.UTC) + timedelta(days=1) + expired_mode.expiration_datetime = expiration_datetime expired_mode.save() - expired_mode_value = Mode(u'verified', u'Verified Certificate', 0, '', 'usd', expiration_date.date()) + expired_mode_value = Mode(u'verified', u'Verified Certificate', 0, '', 'usd', expiration_datetime) modes = CourseMode.modes_for_course(self.course_id) self.assertEqual([expired_mode_value, mode1], modes) diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index 634996fd55c..06d61c0425c 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -243,14 +243,14 @@ class DashboardTest(TestCase): course_id=self.course.id, mode_slug='verified', mode_display_name='Verified', - expiration_date=(datetime.now(pytz.UTC) + timedelta(days=1)).date() + expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=1) ) enrollment = CourseEnrollment.enroll(self.user, self.course.id) course_mode_info = complete_course_mode_info(self.course.id, enrollment) self.assertTrue(course_mode_info['show_upsell']) self.assertEquals(course_mode_info['days_for_upsell'], 1) - verified_mode.expiration_date = datetime.now(pytz.UTC) + timedelta(days=-1) + verified_mode.expiration_datetime = datetime.now(pytz.UTC) + timedelta(days=-1) verified_mode.save() course_mode_info = complete_course_mode_info(self.course.id, enrollment) self.assertFalse(course_mode_info['show_upsell']) @@ -261,13 +261,13 @@ class DashboardTest(TestCase): course_id=self.course.id, mode_slug='verified', mode_display_name='Verified', - expiration_date=(datetime.now(pytz.UTC) + timedelta(days=1)).date() + expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=1) ) enrollment = CourseEnrollment.enroll(self.user, self.course.id, mode='verified') self.assertTrue(enrollment.refundable()) - verified_mode.expiration_date = (datetime.now(pytz.UTC) - timedelta(days=1)).date() + verified_mode.expiration_datetime = datetime.now(pytz.UTC) - timedelta(days=1) verified_mode.save() self.assertFalse(enrollment.refundable()) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index b6904f2fb7f..f92ffe9d3ea 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -284,9 +284,9 @@ def complete_course_mode_info(course_id, enrollment): if 'verified' in modes and enrollment.mode != 'verified': mode_info['show_upsell'] = True # if there is an expiration date, find out how long from now it is - if modes['verified'].expiration_date: + if modes['verified'].expiration_datetime: today = datetime.datetime.now(UTC).date() - mode_info['days_for_upsell'] = (modes['verified'].expiration_date - today).days + mode_info['days_for_upsell'] = (modes['verified'].expiration_datetime.date() - today).days return mode_info diff --git a/lms/djangoapps/shoppingcart/tests/test_models.py b/lms/djangoapps/shoppingcart/tests/test_models.py index f158efe2b39..ecb76ac9416 100644 --- a/lms/djangoapps/shoppingcart/tests/test_models.py +++ b/lms/djangoapps/shoppingcart/tests/test_models.py @@ -384,7 +384,7 @@ class CertificateItemTest(ModuleStoreTestCase): mode_slug="verified", mode_display_name="verified cert", min_price=self.cost, - expiration_date=(datetime.datetime.now(pytz.utc).date() + many_days)) + expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days)) course_mode.save() CourseEnrollment.enroll(self.user, course_id, 'verified') @@ -407,7 +407,7 @@ class CertificateItemTest(ModuleStoreTestCase): mode_slug="verified", mode_display_name="verified cert", min_price=self.cost, - expiration_date=(datetime.datetime.now(pytz.utc).date() + many_days)) + expiration_datetime=datetime.datetime.now(pytz.utc) + many_days) course_mode.save() CourseEnrollment.enroll(self.user, course_id, 'verified') @@ -436,7 +436,7 @@ class CertificateItemTest(ModuleStoreTestCase): CertificateItem.add_to_order(cart, course_id, self.cost, 'verified') cart.purchase() - course_mode.expiration_date = (datetime.datetime.now(pytz.utc).date() - many_days) + course_mode.expiration_datetime = (datetime.datetime.now(pytz.utc) - many_days) course_mode.save() CourseEnrollment.unenroll(self.user, course_id) -- GitLab