Skip to content

Commit 6c5d9e5

Browse files
authored
feat: make 14 day trial default for feature rollout (#203)
* feat: make 14 day trial default for feature rollout * chore: nit
1 parent cf6fd2e commit 6c5d9e5

File tree

8 files changed

+33
-131
lines changed

8 files changed

+33
-131
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ Change Log
1313
1414
Unreleased
1515
**********
16+
17+
4.11.0 - 2025-08-08
18+
******************
19+
* Replaced the get_audit_trial_length_days utils.py function from with the AUDIT_TRIAL_MAX_DAYS = 14, as the
20+
audit trial length will be 14 days going forwards.
21+
1622
4.10.8 - 2025-07-31
1723
*******************
1824
* Chat history to support XPert Chat API V2 response with a list of messages.

learning_assistant/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
Plugin for a learning assistant backend, intended for use within edx-platform.
33
"""
44

5-
__version__ = '4.10.8'
5+
__version__ = '4.11.0'
66

77
default_app_config = 'learning_assistant.apps.LearningAssistantConfig' # pylint: disable=invalid-name

learning_assistant/api.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from jinja2 import BaseLoader, Environment
1313
from opaque_keys import InvalidKeyError
1414

15-
from learning_assistant.constants import ACCEPTED_CATEGORY_TYPES, CATEGORY_TYPE_MAP
15+
from learning_assistant.constants import ACCEPTED_CATEGORY_TYPES, AUDIT_TRIAL_MAX_DAYS, CATEGORY_TYPE_MAP
1616
from learning_assistant.data import LearningAssistantAuditTrialData, LearningAssistantCourseEnabledData
1717
from learning_assistant.models import (
1818
LearningAssistantAuditTrial,
@@ -29,7 +29,6 @@
2929
traverse_block_pre_order,
3030
)
3131
from learning_assistant.text_utils import html_to_text
32-
from learning_assistant.utils import get_audit_trial_length_days
3332

3433
log = logging.getLogger(__name__)
3534
User = get_user_model()
@@ -243,7 +242,7 @@ def get_message_history(courserun_key, user, message_count):
243242
return message_history
244243

245244

246-
def get_audit_trial_expiration_date_from_start_date(start_date, user_id, enrollment_mode):
245+
def get_audit_trial_expiration_date_from_start_date(start_date):
247246
"""
248247
Given a start date of an audit trial, return the expiration date of the audit trial.
249248
@@ -253,14 +252,12 @@ def get_audit_trial_expiration_date_from_start_date(start_date, user_id, enrollm
253252
254253
Arguments:
255254
* start_date (datetime): the start date of the audit trial
256-
* user_id (int): user id
257-
* enrollment_mode (str): enrollment mode of the user
258255
259256
Returns:
260257
* expiration_date (datetime): the expiration date of the audit trial
261258
"""
262-
trial_length_days = get_audit_trial_length_days(user_id, enrollment_mode)
263-
expiration_datetime = start_date + timedelta(days=trial_length_days)
259+
# Default audit trial length is 14 days
260+
expiration_datetime = start_date + timedelta(days=AUDIT_TRIAL_MAX_DAYS)
264261

265262
return expiration_datetime
266263

@@ -291,13 +288,12 @@ def get_audit_trial(user):
291288
)
292289

293290

294-
def get_or_create_audit_trial(user, enrollment_mode):
291+
def get_or_create_audit_trial(user):
295292
"""
296293
Given a user, return the associated audit trial data, creating a new audit trial for the user if one does not exist.
297294
298295
Arguments:
299296
* user (User): the user
300-
* enrollment_mode (str): enrollment mode of the user
301297
302298
Returns:
303299
* audit_trial_data (LearningAssistantAuditTrialData): the audit trial data
@@ -306,7 +302,7 @@ def get_or_create_audit_trial(user, enrollment_mode):
306302
* expiration_date (datetime): the expiration date of the audit trial
307303
"""
308304
start_date = timezone.now()
309-
expiration_date = get_audit_trial_expiration_date_from_start_date(start_date, user.id, enrollment_mode)
305+
expiration_date = get_audit_trial_expiration_date_from_start_date(start_date)
310306

311307
audit_trial, _ = LearningAssistantAuditTrial.objects.get_or_create(
312308
user=user,

learning_assistant/utils.py

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -156,42 +156,6 @@ def get_optimizely_variation(user_id, enrollment_mode):
156156
return {'enabled': enabled, 'variation_key': variation_key}
157157

158158

159-
def get_audit_trial_length_days(user_id, enrollment_mode):
160-
"""
161-
Return the length of an audit trial in days.
162-
163-
Arguments:
164-
* user_id
165-
* enrollment_mode
166-
167-
Returns:
168-
* int: the length of an audit trial in days
169-
"""
170-
variation = get_optimizely_variation(user_id, enrollment_mode)
171-
172-
# For the sake of the experiment on the backend, the only difference in behavior should be for the 28 day variation.
173-
# This is because the control group will never see the audit experience, so the value being returned here does not
174-
# matter, and the 14 day variation can use the default trial length of 14 days.
175-
if (
176-
variation['enabled']
177-
and variation['variation_key'] == getattr(settings, 'OPTIMIZELY_LEARNING_ASSISTANT_TRIAL_VARIATION_KEY_28', '')
178-
):
179-
trial_length_days = 28
180-
else:
181-
default_trial_length_days = 14
182-
trial_length_days = getattr(settings, 'LEARNING_ASSISTANT_AUDIT_TRIAL_LENGTH_DAYS', default_trial_length_days)
183-
184-
if trial_length_days is None:
185-
trial_length_days = default_trial_length_days
186-
187-
# If LEARNING_ASSISTANT_AUDIT_TRIAL_LENGTH_DAYS is set to a negative number, assume it should be 0.
188-
# pylint: disable=consider-using-max-builtin
189-
if trial_length_days < 0:
190-
trial_length_days = 0
191-
192-
return trial_length_days
193-
194-
195159
def parse_lms_datetime(datetime_string):
196160
"""
197161
Parse an LMS datetime into a timezone-aware, Python datetime object.

learning_assistant/views.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,12 @@
3333
render_prompt_template,
3434
save_chat_message,
3535
)
36+
from learning_assistant.constants import AUDIT_TRIAL_MAX_DAYS
3637
from learning_assistant.models import LearningAssistantMessage
3738
from learning_assistant.platform_imports import get_cache_course_run_data
3839
from learning_assistant.serializers import MessageSerializer
3940
from learning_assistant.toggles import chat_history_enabled
40-
from learning_assistant.utils import (
41-
extract_message_content,
42-
get_audit_trial_length_days,
43-
get_chat_response,
44-
parse_lms_datetime,
45-
user_role_is_staff,
46-
)
41+
from learning_assistant.utils import extract_message_content, get_chat_response, parse_lms_datetime, user_role_is_staff
4742

4843
log = logging.getLogger(__name__)
4944

@@ -178,7 +173,7 @@ def post(self, request, course_run_id):
178173
# If user has an audit enrollment record, get or create their trial. If the trial is not expired, return the
179174
# next message. Otherwise, return 403
180175
elif enrollment_mode in CourseMode.UPSELL_TO_VERIFIED_MODES: # AUDIT, HONOR
181-
audit_trial = get_or_create_audit_trial(request.user, enrollment_mode)
176+
audit_trial = get_or_create_audit_trial(request.user)
182177
is_user_audit_trial_expired = audit_trial_is_expired(enrollment_object, audit_trial)
183178
if is_user_audit_trial_expired:
184179
return Response(
@@ -335,6 +330,7 @@ def get(self, request, course_run_id):
335330

336331
data['audit_trial'] = trial_data
337332

338-
data['audit_trial_length_days'] = get_audit_trial_length_days(user.id, enrollment_mode)
333+
# Default audit trial length is 14 days
334+
data['audit_trial_length_days'] = AUDIT_TRIAL_MAX_DAYS
339335

340336
return Response(status=http_status.HTTP_200_OK, data=data)

tests/test_api.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -532,23 +532,15 @@ class GetAuditTrialExpirationDateFromStartTests(TestCase):
532532
"""
533533

534534
@ddt.data(
535-
(datetime(2024, 1, 1, 0, 0, 0), datetime(2024, 1, 2, 0, 0, 0), 1),
536-
(datetime(2024, 1, 18, 0, 0, 0), datetime(2024, 1, 19, 0, 0, 0), 1),
537-
(datetime(2024, 1, 1, 0, 0, 0), datetime(2024, 1, 8, 0, 0, 0), 7),
538-
(datetime(2024, 1, 18, 0, 0, 0), datetime(2024, 1, 25, 0, 0, 0), 7),
539-
(datetime(2024, 1, 1, 0, 0, 0), datetime(2024, 1, 15, 0, 0, 0), 14),
540-
(datetime(2024, 1, 18, 0, 0, 0), datetime(2024, 2, 1, 0, 0, 0), 14),
535+
(datetime(2024, 1, 1, 0, 0, 0), datetime(2024, 1, 15, 0, 0, 0)),
536+
(datetime(2024, 1, 18, 0, 0, 0), datetime(2024, 2, 1, 0, 0, 0)),
541537
)
542538
@ddt.unpack
543-
@patch('learning_assistant.api.get_audit_trial_length_days')
544539
def test_expiration_date(
545540
self, start_date,
546541
expected_expiration_date,
547-
trial_length_days,
548-
mock_get_audit_trial_length_days
549542
):
550-
mock_get_audit_trial_length_days.return_value = trial_length_days
551-
expiration_date = get_audit_trial_expiration_date_from_start_date(start_date, 1, 'verified')
543+
expiration_date = get_audit_trial_expiration_date_from_start_date(start_date)
552544
self.assertEqual(expected_expiration_date, expiration_date)
553545

554546

@@ -623,7 +615,7 @@ def test_exists_get(self, audit_trial_expiration_date, get_audit_trial_expiratio
623615
start_date=audit_trial_start_date,
624616
expiration_date=timezone.make_aware(audit_trial_expiration_date),
625617
)
626-
self.assertEqual(expected_return, get_or_create_audit_trial(self.user, 'verified'))
618+
self.assertEqual(expected_return, get_or_create_audit_trial(self.user))
627619

628620
@freeze_time('2024-01-01')
629621
@ddt.data(
@@ -644,7 +636,7 @@ def test_not_exists_create(self, audit_trial_expiration_date, get_audit_trial_ex
644636
expiration_date=timezone.make_aware(audit_trial_expiration_date)
645637
)
646638

647-
self.assertEqual(expected_return, get_or_create_audit_trial(self.user, 'verified'))
639+
self.assertEqual(expected_return, get_or_create_audit_trial(self.user))
648640

649641

650642
@ddt.ddt
@@ -715,7 +707,7 @@ def test_audit_trial_expired(self, start_date):
715707
audit_trial_data = LearningAssistantAuditTrialData(
716708
user_id=self.user.id,
717709
start_date=start_date,
718-
expiration_date=get_audit_trial_expiration_date_from_start_date(start_date, 1, 'verified'),
710+
expiration_date=get_audit_trial_expiration_date_from_start_date(start_date),
719711
)
720712

721713
self.assertEqual(audit_trial_is_expired(mock_enrollment, audit_trial_data), True)
@@ -729,7 +721,7 @@ def test_audit_trial_unexpired(self):
729721
audit_trial_data = LearningAssistantAuditTrialData(
730722
user_id=self.user.id,
731723
start_date=start_date,
732-
expiration_date=get_audit_trial_expiration_date_from_start_date(start_date, 1, 'verified'),
724+
expiration_date=get_audit_trial_expiration_date_from_start_date(start_date),
733725
)
734726

735727
self.assertEqual(audit_trial_is_expired(mock_enrollment, audit_trial_data), False)

tests/test_utils.py

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from learning_assistant.constants import LMS_DATETIME_FORMAT
1515
from learning_assistant.utils import (
1616
extract_message_content,
17-
get_audit_trial_length_days,
1817
get_chat_response,
1918
get_optimizely_variation,
2019
get_reduced_message_list,
@@ -221,44 +220,6 @@ def test_user_role_is_staff(self, role, expected_value):
221220
self.assertEqual(user_role_is_staff(role), expected_value)
222221

223222

224-
@ddt.ddt
225-
class GetAuditTrialLengthDaysTests(TestCase):
226-
"""
227-
Tests for the get_audit_trial_length_days helper function.
228-
"""
229-
@ddt.data(
230-
(None, 14),
231-
(0, 0),
232-
(-7, 0),
233-
(7, 7),
234-
(14, 14),
235-
(28, 28),
236-
)
237-
@ddt.unpack
238-
def test_get_audit_trial_length_days_with_value(self, setting_value, expected_value):
239-
with patch.object(settings, 'LEARNING_ASSISTANT_AUDIT_TRIAL_LENGTH_DAYS', setting_value):
240-
self.assertEqual(get_audit_trial_length_days(1, 'verified'), expected_value)
241-
242-
@override_settings()
243-
def test_get_audit_trial_length_days_no_setting(self):
244-
del settings.LEARNING_ASSISTANT_AUDIT_TRIAL_LENGTH_DAYS
245-
self.assertEqual(get_audit_trial_length_days(1, 'verified'), 14)
246-
247-
# mock optimizely function
248-
@ddt.data(
249-
('variation', 28),
250-
('control', 14),
251-
)
252-
@ddt.unpack
253-
@patch('learning_assistant.utils.get_optimizely_variation')
254-
def test_get_audit_trial_length_days_experiment(
255-
self, variation_key, expected_value, mock_get_optimizely_variation
256-
):
257-
mock_get_optimizely_variation.return_value = {'enabled': True, 'variation_key': variation_key}
258-
with patch.object(settings, 'OPTIMIZELY_LEARNING_ASSISTANT_TRIAL_VARIATION_KEY_28', 'variation'):
259-
self.assertEqual(get_audit_trial_length_days(1, 'verified'), expected_value)
260-
261-
262223
class GetOptimizelyVariationTests(TestCase):
263224
"""
264225
Tests for the get_optimizely_variation helper function.

0 commit comments

Comments
 (0)