diff --git a/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py b/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py index e38b6de1a4..1da6bfeb09 100644 --- a/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py +++ b/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py @@ -3,7 +3,7 @@ Django admin command to send verification expiry email to learners """ import logging import time -from datetime import datetime, timedelta +from datetime import timedelta from django.conf import settings from django.contrib.auth.models import User @@ -11,9 +11,9 @@ from django.contrib.sites.models import Site from django.core.management.base import BaseCommand from django.db.models import Q from django.urls import reverse +from django.utils.timezone import now from edx_ace import ace from edx_ace.recipient import Recipient -from pytz import UTC from util.query import use_read_replica_if_available from verify_student.message_types import VerificationExpiry @@ -92,16 +92,17 @@ class Command(BaseCommand): days = options['days_range'] dry_run = options['dry_run'] + end_date = now().replace(hour=0, minute=0, second=0, microsecond=0) # If email was sent and user did not re-verify then this date will be used as the criteria for resending email - date_resend_days_ago = datetime.now(UTC) - timedelta(days=resend_days) + date_resend_days_ago = end_date - timedelta(days=resend_days) - start_date = datetime.now(UTC) - timedelta(days=days) + start_date = end_date - timedelta(days=days) # Adding an order_by() clause will override the class meta ordering as we don't need ordering here query = SoftwareSecurePhotoVerification.objects.filter(Q(status='approved') & - (Q(expiry_date__date__gte=start_date.date(), - expiry_date__date__lt=datetime.now(UTC).date()) | - Q(expiry_email_date__lt=date_resend_days_ago.date()) + (Q(expiry_date__gte=start_date, + expiry_date__lt=end_date) | + Q(expiry_email_date__lt=date_resend_days_ago) )).order_by() sspv = use_read_replica_if_available(query) @@ -109,11 +110,11 @@ class Command(BaseCommand): total_verification = sspv.count() if not total_verification: logger.info(u"No approved expired entries found in SoftwareSecurePhotoVerification for the " - u"date range {} - {}".format(start_date.date(), datetime.now(UTC).date())) + u"date range {} - {}".format(start_date.date(), now().date())) return logger.info(u"For the date range {} - {}, total Software Secure Photo verification filtered are {}" - .format(start_date.date(), datetime.now(UTC).date(), total_verification)) + .format(start_date.date(), now().date(), total_verification)) batch_verifications = [] @@ -167,4 +168,4 @@ def send_verification_expiry_email(batch_verifications, dry_run=False): ) ace.send(msg) verification_qs = SoftwareSecurePhotoVerification.objects.filter(pk=verification.pk) - verification_qs.update(expiry_email_date=datetime.now(UTC)) + verification_qs.update(expiry_email_date=now()) diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index 5ac659ca0a..9388fd1fba 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -13,10 +13,9 @@ import json import logging import os.path import uuid -from datetime import datetime, timedelta +from datetime import timedelta from email.utils import formatdate -import pytz import requests import six from django.conf import settings @@ -27,6 +26,7 @@ from django.urls import reverse from django.db import models from django.dispatch import receiver from django.utils.functional import cached_property +from django.utils.timezone import now from django.utils.translation import ugettext_lazy from model_utils import Choices from model_utils.models import StatusModel, TimeStampedModel @@ -138,7 +138,7 @@ class IDVerificationAttempt(StatusModel): """ return ( self.created_at < deadline and - self.expiration_datetime > datetime.now(pytz.UTC) + self.expiration_datetime > now() ) @@ -573,7 +573,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): status is set to `approved` expiry_date is set to one year from now """ - self.expiry_date = datetime.now(pytz.UTC) + timedelta( + self.expiry_date = now() + timedelta( days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] ) super(SoftwareSecurePhotoVerification, self).approve(user_id, service) @@ -675,7 +675,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): try: response = self.send_request(copy_id_photo_from=copy_id_photo_from) if response.ok: - self.submitted_at = datetime.now(pytz.UTC) + self.submitted_at = now() self.status = "submitted" self.save() else: diff --git a/lms/djangoapps/verify_student/tests/test_models.py b/lms/djangoapps/verify_student/tests/test_models.py index 20ce25c0b7..f03c9825f7 100644 --- a/lms/djangoapps/verify_student/tests/test_models.py +++ b/lms/djangoapps/verify_student/tests/test_models.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- import json -from datetime import datetime, timedelta +from datetime import timedelta import boto import ddt import mock -import pytz import requests.exceptions from django.conf import settings from django.test import TestCase +from django.utils.timezone import now from freezegun import freeze_time from mock import patch from opaque_keys.edx.keys import CourseKey @@ -114,7 +114,7 @@ class TestVerification(TestCase): # Not active after the expiration date attempt.created_at = attempt.created_at - timedelta(days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]) attempt.save() - self.assertFalse(attempt.active_at_datetime(datetime.now(pytz.UTC) + timedelta(days=1))) + self.assertFalse(attempt.active_at_datetime(now() + timedelta(days=1))) # Lots of patching to stub in our own settings, and HTTP posting @@ -309,15 +309,15 @@ class TestPhotoVerification(TestVerification, MockS3Mixin, ModuleStoreTestCase): self.assertEqual(second_result, first_result) # Test method 'get_initial_verification' returns None after expiration - expired_future = datetime.utcnow() + timedelta(days=(FAKE_SETTINGS['DAYS_GOOD_FOR'] + 1)) + expired_future = now() + timedelta(days=(FAKE_SETTINGS['DAYS_GOOD_FOR'] + 1)) with freeze_time(expired_future): third_result = SoftwareSecurePhotoVerification.get_initial_verification(user) self.assertIsNone(third_result) # Test method 'get_initial_verification' returns correct attempt after system expiration, # but within earliest allowed override. - expired_future = datetime.utcnow() + timedelta(days=(FAKE_SETTINGS['DAYS_GOOD_FOR'] + 1)) - earliest_allowed = datetime.utcnow() - timedelta(days=1) + expired_future = now() + timedelta(days=(FAKE_SETTINGS['DAYS_GOOD_FOR'] + 1)) + earliest_allowed = now() - timedelta(days=1) with freeze_time(expired_future): fourth_result = SoftwareSecurePhotoVerification.get_initial_verification(user, earliest_allowed) self.assertIsNotNone(fourth_result) @@ -398,8 +398,8 @@ class VerificationDeadlineTest(CacheIsolationTestCase): def test_caching(self): deadlines = { - CourseKey.from_string("edX/DemoX/Fall"): datetime.now(pytz.UTC), - CourseKey.from_string("edX/DemoX/Spring"): datetime.now(pytz.UTC) + timedelta(days=1) + CourseKey.from_string("edX/DemoX/Fall"): now(), + CourseKey.from_string("edX/DemoX/Spring"): now() + timedelta(days=1) } course_keys = deadlines.keys() diff --git a/lms/djangoapps/verify_student/tests/test_signals.py b/lms/djangoapps/verify_student/tests/test_signals.py index 96407603f2..cddce181ea 100644 --- a/lms/djangoapps/verify_student/tests/test_signals.py +++ b/lms/djangoapps/verify_student/tests/test_signals.py @@ -2,9 +2,9 @@ Unit tests for the VerificationDeadline signals """ -from datetime import datetime, timedelta +from datetime import timedelta -from pytz import UTC +from django.utils.timezone import now from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.signals import _listen_for_course_publish, _listen_for_lms_retire @@ -22,7 +22,7 @@ class VerificationDeadlineSignalTest(ModuleStoreTestCase): def setUp(self): super(VerificationDeadlineSignalTest, self).setUp() - self.end = datetime.now(tz=UTC).replace(microsecond=0) + timedelta(days=7) + self.end = now().replace(microsecond=0) + timedelta(days=7) self.course = CourseFactory.create(end=self.end) VerificationDeadline.objects.all().delete() @@ -34,7 +34,7 @@ class VerificationDeadlineSignalTest(ModuleStoreTestCase): def test_deadline(self): """ Verify deadline is set to course end date by signal when changed. """ - deadline = datetime.now(tz=UTC) - timedelta(days=7) + deadline = now() - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline) _listen_for_course_publish('store', self.course.id) @@ -42,7 +42,7 @@ class VerificationDeadlineSignalTest(ModuleStoreTestCase): def test_deadline_explicit(self): """ Verify deadline is unchanged by signal when explicitly set. """ - deadline = datetime.now(tz=UTC) - timedelta(days=7) + deadline = now() - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline, is_explicit=True) _listen_for_course_publish('store', self.course.id) diff --git a/lms/djangoapps/verify_student/tests/test_utils.py b/lms/djangoapps/verify_student/tests/test_utils.py index 12666e7830..6f17c1bf36 100644 --- a/lms/djangoapps/verify_student/tests/test_utils.py +++ b/lms/djangoapps/verify_student/tests/test_utils.py @@ -3,14 +3,14 @@ Tests for verify_student utility functions. """ -from datetime import datetime, timedelta +from datetime import timedelta import ddt import unittest -import pytz from mock import patch from pytest import mark from django.conf import settings +from django.utils import timezone from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, SSOVerification, ManualVerification from lms.djangoapps.verify_student.utils import verification_for_datetime, most_recent_verification from student.tests.factories import UserFactory @@ -30,7 +30,7 @@ class TestVerifyStudentUtils(unittest.TestCase): def test_verification_for_datetime(self): user = UserFactory.create() - now = datetime.now(pytz.UTC) + now = timezone.now() # No attempts in the query set, so should return None query = SoftwareSecurePhotoVerification.objects.filter(user=user) @@ -72,7 +72,7 @@ class TestVerifyStudentUtils(unittest.TestCase): # Immediately after the expiration date, should not get the attempt attempt.created_at = attempt.created_at - timedelta(days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]) attempt.save() - after = datetime.now(pytz.UTC) + timedelta(days=1) + after = now + timedelta(days=1) query = SoftwareSecurePhotoVerification.objects.filter(user=user) result = verification_for_datetime(after, query) self.assertIs(result, None) diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 875f2ed8e6..319554e963 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -5,7 +5,7 @@ Tests of verify_student views. import json import urllib -from datetime import datetime, timedelta +from datetime import timedelta from uuid import uuid4 import boto @@ -13,7 +13,6 @@ import ddt import httpretty import mock import moto -import pytz import requests from bs4 import BeautifulSoup from django.conf import settings @@ -22,6 +21,7 @@ from django.urls import reverse from django.test import TestCase from django.test.client import Client, RequestFactory from django.test.utils import override_settings +from django.utils.timezone import now from django.utils.translation import ugettext as _ from mock import Mock, patch from opaque_keys.edx.keys import CourseKey @@ -90,7 +90,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): USERNAME = "test_user" PASSWORD = "test_password" - NOW = datetime.now(pytz.UTC) + NOW = now() YESTERDAY = 'yesterday' TOMORROW = 'tomorrow' NEXT_YEAR = 'next_year' @@ -729,7 +729,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): @ddt.data("verify_student_start_flow", "verify_student_begin_flow") def test_verification_deadline(self, payment_flow): - deadline = datetime.now(tz=pytz.UTC) + timedelta(days=360) + deadline = now() + timedelta(days=360) course = self._create_course("verified") # Set a deadline on the course mode AND on the verification deadline model. @@ -745,7 +745,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): self.assertEqual(data['verification_deadline'], unicode(deadline)) def test_course_mode_expired(self): - deadline = datetime.now(tz=pytz.UTC) + timedelta(days=-360) + deadline = now() + timedelta(days=-360) course = self._create_course("verified") # Set the upgrade deadline (course mode expiration) and verification deadline @@ -773,13 +773,13 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): # deadline in the future. self._set_deadlines( course.id, - upgrade_deadline=datetime.now(tz=pytz.UTC) + timedelta(days=-360), + upgrade_deadline=now() + timedelta(days=-360), verification_deadline=verification_deadline, ) # Set the upgrade deadline for credit mode in future. self._set_deadlines( course.id, - upgrade_deadline=datetime.now(tz=pytz.UTC) + timedelta(days=360), + upgrade_deadline=now() + timedelta(days=360), verification_deadline=verification_deadline, mode_slug="credit" ) @@ -823,8 +823,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): # since it's a bad user experience # to purchase a verified track and then not be able to verify, # but if it happens we need to handle it gracefully. - upgrade_deadline_in_future = datetime.now(tz=pytz.UTC) + timedelta(days=360) - verification_deadline_in_past = datetime.now(tz=pytz.UTC) + timedelta(days=-360) + upgrade_deadline_in_future = now() + timedelta(days=360) + verification_deadline_in_past = now() + timedelta(days=-360) self._set_deadlines( course.id, upgrade_deadline=upgrade_deadline_in_future, @@ -910,7 +910,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): if status == "expired": days_good_for = settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] - attempt.created_at = datetime.now(pytz.UTC) - timedelta(days=(days_good_for + 1)) + attempt.created_at = now() - timedelta(days=(days_good_for + 1)) attempt.save() def _set_deadlines(self, course_key, upgrade_deadline=None, verification_deadline=None, mode_slug="verified"): @@ -1764,15 +1764,15 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): """ Test for verification passed. """ - expiry_date = datetime.now(pytz.UTC) + timedelta( + expiry_date = now() + timedelta( days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] ) verification = SoftwareSecurePhotoVerification.objects.create(user=self.user) verification.mark_ready() verification.submit() verification.approve() - verification.expiry_date = datetime.now(pytz.UTC) - verification.expiry_email_date = datetime.now(pytz.UTC) + verification.expiry_date = now() + verification.expiry_email_date = now() verification.save() data = { @@ -1807,7 +1807,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): """ Test for verification passed if the learner does not have any previous verification """ - expiry_date = datetime.now(pytz.UTC) + timedelta( + expiry_date = now() + timedelta( days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] ) @@ -1952,7 +1952,7 @@ class TestReverifyView(TestCase): attempt.approve() days_good_for = settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] - attempt.created_at = datetime.now(pytz.UTC) - timedelta(days=(days_good_for + 1)) + attempt.created_at = now() - timedelta(days=(days_good_for + 1)) attempt.save() # Allow the student to reverify diff --git a/lms/djangoapps/verify_student/utils.py b/lms/djangoapps/verify_student/utils.py index 1b32a16868..014446d80a 100644 --- a/lms/djangoapps/verify_student/utils.py +++ b/lms/djangoapps/verify_student/utils.py @@ -5,9 +5,9 @@ Common Utilities for the verify_student application. import datetime import logging -import pytz from django.conf import settings +from django.utils.timezone import now from sailthru import SailthruClient log = logging.getLogger(__name__) @@ -18,7 +18,7 @@ def is_verification_expiring_soon(expiration_datetime): Returns True if verification is expiring within EXPIRING_SOON_WINDOW. """ if expiration_datetime: - if (expiration_datetime - datetime.datetime.now(pytz.UTC)).days <= settings.VERIFY_STUDENT.get( + if (expiration_datetime - now()).days <= settings.VERIFY_STUDENT.get( "EXPIRING_SOON_WINDOW"): return True @@ -30,7 +30,7 @@ def earliest_allowed_verification_date(): Returns the earliest allowed date given the settings """ days_good_for = settings.VERIFY_STUDENT["DAYS_GOOD_FOR"] - return datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=days_good_for) + return now() - datetime.timedelta(days=days_good_for) def verification_for_datetime(deadline, candidates): diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index a5a7c901c8..abf08aadc1 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -17,6 +17,7 @@ from django.http import Http404, HttpResponse, HttpResponseBadRequest from django.shortcuts import redirect from django.urls import reverse from django.utils.decorators import method_decorator +from django.utils.timezone import now from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from django.views.decorators.csrf import csrf_exempt @@ -27,7 +28,6 @@ from eventtracking import tracker from ipware.ip import get_ip from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey -from pytz import UTC from course_modes.models import CourseMode from edxmako.shortcuts import render_to_response, render_to_string @@ -376,7 +376,7 @@ class PayAndVerifyView(View): current_step = display_steps[current_step_idx + 1]['name'] courseware_url = "" - if not course.start or course.start < datetime.datetime.today().replace(tzinfo=UTC): + if not course.start or course.start < now(): courseware_url = reverse( 'course_root', kwargs={'course_id': unicode(course_key)} @@ -716,7 +716,7 @@ class PayAndVerifyView(View): deadline_passed = ( deadline_datetime is not None and - deadline_datetime < datetime.datetime.now(UTC) + deadline_datetime < now() ) if deadline_passed: context = {