-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: default stale start_dates to today in braze assignment task (#560)
- Loading branch information
1 parent
b79810d
commit 3613a5e
Showing
5 changed files
with
288 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
209 changes: 209 additions & 0 deletions
209
enterprise_access/apps/content_assignments/tests/test_utils.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
""" | ||
Tests for Enterprise Access content_assignments utils. | ||
""" | ||
from datetime import datetime, timedelta | ||
|
||
import ddt | ||
from django.test import TestCase | ||
from pytz import UTC | ||
|
||
from enterprise_access.apps.content_assignments.constants import BRAZE_ACTION_REQUIRED_BY_TIMESTAMP_FORMAT | ||
from enterprise_access.apps.content_assignments.utils import ( | ||
get_self_paced_normalized_start_date, | ||
has_time_to_complete, | ||
is_within_minimum_start_date_threshold | ||
) | ||
|
||
|
||
def _curr_date(date_format=None): | ||
curr_date = datetime.now() | ||
if not date_format: | ||
return curr_date | ||
return curr_date.strftime(date_format) | ||
|
||
|
||
def _days_from_now(days_from_now=0, date_format=None): | ||
date = datetime.now().replace(tzinfo=UTC) + timedelta(days=days_from_now) | ||
if not date_format: | ||
return date | ||
return date.strftime(date_format) | ||
|
||
|
||
@ddt.ddt | ||
class UtilsTests(TestCase): | ||
""" | ||
Tests related to utility functions for content assignments | ||
""" | ||
|
||
@ddt.data( | ||
# Start after is before the curr_date - START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _days_from_now(5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"expected_output": False | ||
}, | ||
# Start after is before the curr_date - START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _days_from_now(-5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"expected_output": False | ||
}, | ||
# Start after is before the curr_date - START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _days_from_now(15, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"expected_output": False | ||
}, | ||
# Start date is before the curr_date - START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _days_from_now(-15, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"expected_output": True | ||
}, | ||
# Start after is before the curr_date - START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _curr_date('%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"expected_output": False | ||
} | ||
) | ||
@ddt.unpack | ||
def test_is_within_minimum_start_date_threshold(self, start_date, curr_date, expected_output): | ||
assert is_within_minimum_start_date_threshold(curr_date, start_date) == expected_output | ||
|
||
@ddt.data( | ||
# endDate is the exact day as weeks to complete offset | ||
{ | ||
"end_date": _days_from_now(49, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"weeks_to_complete": 7, | ||
"expected_output": True | ||
}, | ||
# weeks to complete is within endDate | ||
{ | ||
"end_date": _days_from_now(49, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"weeks_to_complete": 4, | ||
"expected_output": True | ||
}, | ||
# weeks to complete is beyond end date | ||
{ | ||
"end_date": _days_from_now(49, '%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"weeks_to_complete": 8, | ||
"expected_output": False | ||
}, | ||
# end date is current date | ||
{ | ||
"end_date": _curr_date('%Y-%m-%dT%H:%M:%SZ'), | ||
"curr_date": _curr_date(), | ||
"weeks_to_complete": 1, | ||
"expected_output": False | ||
}, | ||
) | ||
@ddt.unpack | ||
def test_has_time_to_complete(self, end_date, curr_date, weeks_to_complete, expected_output): | ||
assert has_time_to_complete(curr_date, end_date, weeks_to_complete) == expected_output | ||
|
||
@ddt.data( | ||
{ | ||
"start_date": None, | ||
"end_date": _days_from_now(10, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": "self_paced", | ||
"weeks_to_complete": 8, | ||
}, | ||
}, | ||
{ | ||
"start_date": _days_from_now(5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": None, | ||
"course_metadata": { | ||
"pacing_type": "self_paced", | ||
"weeks_to_complete": 8, | ||
}, | ||
}, | ||
{ | ||
"start_date": _days_from_now(5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": _days_from_now(10, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": None, | ||
"weeks_to_complete": 8, | ||
}, | ||
}, | ||
{ | ||
"start_date": _days_from_now(5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": _days_from_now(10, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": "self_paced", | ||
"weeks_to_complete": None, | ||
}, | ||
}, | ||
{ | ||
"start_date": None, | ||
"end_date": None, | ||
"course_metadata": { | ||
"pacing_type": None, | ||
"weeks_to_complete": None, | ||
}, | ||
}, | ||
) | ||
@ddt.unpack | ||
def test_get_self_paced_normalized_start_date_empty_data(self, start_date, end_date, course_metadata): | ||
assert get_self_paced_normalized_start_date(start_date, end_date, course_metadata) == \ | ||
_curr_date(BRAZE_ACTION_REQUIRED_BY_TIMESTAMP_FORMAT) | ||
|
||
@ddt.data( | ||
# self-paced, has time to complete | ||
{ | ||
"start_date": _days_from_now(5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": _days_from_now(28, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": "self_paced", | ||
"weeks_to_complete": 3, | ||
}, | ||
}, | ||
# self-paced, does not have time to complete, but start date older than | ||
# START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _days_from_now(-15, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": _days_from_now(10, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": "self_paced", | ||
"weeks_to_complete": 300, | ||
}, | ||
}, | ||
# self-paced, does not have time to complete, start date within | ||
# START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
{ | ||
"start_date": _days_from_now(-5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": _days_from_now(10, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": "self_paced", | ||
"weeks_to_complete": 300, | ||
}, | ||
}, | ||
# instructor paced | ||
{ | ||
"start_date": _days_from_now(5, '%Y-%m-%dT%H:%M:%SZ'), | ||
"end_date": _days_from_now(10, '%Y-%m-%dT%H:%M:%SZ'), | ||
"course_metadata": { | ||
"pacing_type": "instructor_paced", | ||
"weeks_to_complete": 8, | ||
}, | ||
}, | ||
) | ||
@ddt.unpack | ||
def test_get_self_paced_normalized_start_date_self_paced(self, start_date, end_date, course_metadata): | ||
pacing_type = course_metadata.get('pacing_type') | ||
weeks_to_complete = course_metadata.get('weeks_to_complete') | ||
|
||
can_complete_in_time = has_time_to_complete(_curr_date(), end_date, weeks_to_complete) | ||
within_start_date_threshold = is_within_minimum_start_date_threshold(_curr_date(), start_date) | ||
|
||
if pacing_type == 'self_paced' and (can_complete_in_time or within_start_date_threshold): | ||
assert get_self_paced_normalized_start_date(start_date, end_date, course_metadata) == \ | ||
_curr_date(BRAZE_ACTION_REQUIRED_BY_TIMESTAMP_FORMAT) | ||
else: | ||
assert get_self_paced_normalized_start_date(start_date, end_date, course_metadata) == \ | ||
start_date |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
""" | ||
Utils for content_assignments | ||
""" | ||
from datetime import datetime, timedelta | ||
|
||
from dateutil import parser | ||
from pytz import UTC | ||
|
||
from enterprise_access.apps.content_assignments.constants import ( | ||
BRAZE_ACTION_REQUIRED_BY_TIMESTAMP_FORMAT, | ||
START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS | ||
) | ||
|
||
|
||
def is_within_minimum_start_date_threshold(curr_date, start_date): | ||
""" | ||
Checks if today's date were set to a certain number of days in the past, | ||
offset_date_from_today, is the start_date before offset_date_from_today. | ||
""" | ||
start_date_datetime = parser.parse(start_date) | ||
offset_date_from_today = curr_date - timedelta(days=START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS) | ||
return start_date_datetime < offset_date_from_today.replace(tzinfo=UTC) | ||
|
||
|
||
def has_time_to_complete(curr_date, end_date, weeks_to_complete): | ||
""" | ||
Checks if today's date were set to a certain number of weeks_to_complete in the future, | ||
offset_now_by_weeks_to_complete, is offset_now_by_weeks_to_complete date before the end_date | ||
""" | ||
end_date_datetime = parser.parse(end_date) | ||
offset_now_by_weeks_to_complete = curr_date + timedelta(weeks=weeks_to_complete) | ||
print(offset_now_by_weeks_to_complete, end_date_datetime) | ||
return offset_now_by_weeks_to_complete.replace(tzinfo=UTC).strftime("%Y-%m-%d") <= \ | ||
end_date_datetime.strftime("%Y-%m-%d") | ||
|
||
|
||
def get_self_paced_normalized_start_date(start_date, end_date, course_metadata): | ||
""" | ||
Normalizes courses start_date far in the past based on a heuristic for the purpose of displaying a | ||
reasonable start_date in content assignment related emails. | ||
Heuristic: | ||
For self-paced courses with a weeks_to_complete field too close to the end date to complete the course | ||
or a start_date that is before today offset by the START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS should | ||
default to today's date. | ||
Otherwise, return the current start_date | ||
""" | ||
curr_date = datetime.now() | ||
pacing_type = course_metadata.get('pacing_type', {}) or None | ||
weeks_to_complete = course_metadata.get('weeks_to_complete', {}) or None | ||
if not (start_date and end_date and pacing_type and weeks_to_complete): | ||
return curr_date.strftime(BRAZE_ACTION_REQUIRED_BY_TIMESTAMP_FORMAT) | ||
print(has_time_to_complete(curr_date, end_date, weeks_to_complete), | ||
is_within_minimum_start_date_threshold(curr_date, start_date)) | ||
if pacing_type == "self_paced": | ||
if has_time_to_complete(curr_date, end_date, weeks_to_complete) or \ | ||
is_within_minimum_start_date_threshold(curr_date, start_date): | ||
return curr_date.strftime(BRAZE_ACTION_REQUIRED_BY_TIMESTAMP_FORMAT) | ||
return start_date |