Skip to content
Snippets Groups Projects
Unverified Commit 097807dc authored by Jeremy Bowman's avatar Jeremy Bowman Committed by GitHub
Browse files

Fix time zone bug in send_verification_expiry_email (#20350)

parent 77255210
Branches
Tags
No related merge requests found
......@@ -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())
......@@ -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:
......
# -*- 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()
......
......@@ -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)
......
......@@ -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)
......
......@@ -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
......
......@@ -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):
......
......@@ -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 = {
......
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