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