Skip to content
Snippets Groups Projects
Commit 04fe4af9 authored by bmedx's avatar bmedx
Browse files

Adding user_api call to get retirements by date range and current state

- Also supports PLAT-2186
parent 74b07c3f
No related merge requests found
......@@ -810,6 +810,155 @@ class TestAccountRetirementList(RetirementTestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
class TestAccountRetirementsByStatusAndDate(RetirementTestCase):
"""
Tests the retirements_by_status_and_date endpoint
"""
def setUp(self):
super(TestAccountRetirementsByStatusAndDate, self).setUp()
self.test_superuser = SuperuserFactory()
self.headers = build_jwt_headers(self.test_superuser)
self.url = reverse('accounts_retirements_by_status_and_date')
self.maxDiff = None
def assert_status_and_user_list(
self,
expected_data,
expected_status=status.HTTP_200_OK,
state_to_request=None,
start_date=None,
end_date=None
):
"""
Helper function for making a request to the endpoint, asserting the status, and
optionally asserting data returned. Will try to convert datetime start and end dates
to the correct string formatting.
"""
if state_to_request is None:
state_to_request = 'COMPLETE'
if start_date is None:
start_date = datetime.datetime.now().date().strftime('%Y-%m-%d')
else:
start_date = start_date.date().strftime('%Y-%m-%d')
if end_date is None:
end_date = datetime.datetime.now().date().strftime('%Y-%m-%d')
else:
end_date = end_date.date().strftime('%Y-%m-%d')
data = {'start_date': start_date, 'end_date': end_date, 'state': state_to_request}
response = self.client.get(self.url, data, **self.headers)
print(response.status_code)
print(response)
self.assertEqual(response.status_code, expected_status)
response_data = response.json()
if expected_data:
# These datetimes won't match up due to serialization, but they're inherited fields tested elsewhere
for data in (response_data, expected_data):
for retirement in data:
# These may have been deleted in a previous pass
try:
del retirement['created']
del retirement['modified']
except KeyError:
pass
self.assertItemsEqual(response_data, expected_data)
def test_empty(self):
"""
Verify that an empty array is returned if no users are awaiting retirement
"""
self.assert_status_and_user_list([])
def test_users_exist_none_in_correct_state(self):
"""
Verify that users in non-requested states are not returned
"""
state = RetirementState.objects.get(state_name='PENDING')
self._create_retirement(state=state)
self.assert_status_and_user_list([])
def test_users_exist(self):
"""
Verify correct user is returned when users in different states exist
"""
# Stores the user we expect to get back
retirement_values = None
for retirement in self._create_users_all_states():
if retirement.current_state == 'COMPLETE':
retirement_values.append(self._retirement_to_dict(retirement))
self.assert_status_and_user_list(retirement_values)
def test_bad_states(self):
"""
Check some bad inputs to make sure we get back the expected status
"""
self.assert_status_and_user_list(None, expected_status=status.HTTP_400_BAD_REQUEST, state_to_request='TACO')
def test_date_filter(self):
"""
Verifies the functionality of the start and end date filters
"""
retirements = []
complete_state = RetirementState.objects.get(state_name='COMPLETE')
# Create retirements for the last 10 days
for days_back in range(0, 10):
create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=days_back)
ret = self._create_retirement(state=complete_state, create_datetime=create_datetime)
retirements.append(self._retirement_to_dict(ret))
# Go back in time adding days to the query, assert the correct retirements are present
end_date = datetime.datetime.now(pytz.UTC)
for days_back in range(1, 11):
retirement_dicts = retirements[:days_back]
start_date = end_date - datetime.timedelta(days=days_back - 1)
self.assert_status_and_user_list(
retirement_dicts,
start_date=start_date,
end_date=end_date
)
def test_bad_dates(self):
"""
Check some bad inputs to make sure we get back the expected status
"""
good_date = '2018-01-01'
for bad_param, good_param in (('start_date', 'end_date'), ('end_date', 'start_date')):
for bad_date in ('10/21/2001', '2118-01-01', '2018-14-25', 'toast', 5):
data = {
bad_param: bad_date,
good_param: good_date,
'state': 'COMPLETE'
}
response = self.client.get(self.url, data, **self.headers)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@ddt.data(
{},
{'start_date': '2018-01-01'},
{'end_date': '2018-01-01'},
{'state': 'PENDING'},
{'start_date': '2018-01-01', 'state': 'PENDING'},
{'end_date': '2018-01-01', 'state': 'PENDING'},
)
def test_missing_params(self, request_data):
"""
All params are required, make sure that is enforced
"""
response = self.client.get(self.url, request_data, **self.headers)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
class TestAccountRetirementRetrieve(RetirementTestCase):
"""
......
......@@ -683,6 +683,49 @@ class AccountRetirementStatusView(ViewSet):
except RetirementStateError as exc:
return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
def retirements_by_status_and_date(self, request):
"""
GET /api/user/v1/accounts/retirements_by_status_and_date/
?start_date=2018-09-05&end_date=2018-09-07&state=COMPLETE
Returns a list of UserRetirementStatusSerializer serialized
RetirementStatus rows in the given state that were created in the
retirement queue between the dates given. Date range is inclusive,
so to get one day you would set both dates to that day.
"""
try:
start_date = datetime.datetime.strptime(request.GET['start_date'], '%Y-%m-%d')
end_date = datetime.datetime.strptime(request.GET['end_date'], '%Y-%m-%d')
now = datetime.datetime.now()
if start_date > now or end_date > now or start_date > end_date:
raise RetirementStateError('Dates must be today or earlier, and start must be earlier than end.')
# Add a day to make sure we get all the way to 23:59:59.999, this is compared "lt" in the query
# not "lte".
end_date += datetime.timedelta(days=1)
state = request.GET['state']
state_obj = RetirementState.objects.get(state_name=state)
retirements = UserRetirementStatus.objects.select_related(
'user', 'current_state', 'last_state'
).filter(
current_state=state_obj, created__lt=end_date, created__gte=start_date
).order_by(
'id'
)
serializer = UserRetirementStatusSerializer(retirements, many=True)
return Response(serializer.data)
# This should only occur on the datetime conversion of the start / end dates.
except ValueError as exc:
return Response('Invalid start or end date: {}'.format(text_type(exc)), status=status.HTTP_400_BAD_REQUEST)
except KeyError as exc:
return Response('Missing required parameter: {}'.format(text_type(exc)), status=status.HTTP_400_BAD_REQUEST)
except RetirementState.DoesNotExist:
return Response('Unknown retirement state.', status=status.HTTP_400_BAD_REQUEST)
except RetirementStateError as exc:
return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
def retrieve(self, request, username): # pylint: disable=unused-argument
"""
GET /api/user/v1/accounts/{username}/retirement_status/
......
......@@ -43,6 +43,10 @@ RETIREMENT_QUEUE = AccountRetirementStatusView.as_view({
'get': 'retirement_queue'
})
RETIREMENT_LIST_BY_STATUS_AND_DATE = AccountRetirementStatusView.as_view({
'get': 'retirements_by_status_and_date'
})
RETIREMENT_RETRIEVE = AccountRetirementStatusView.as_view({
'get': 'retrieve'
})
......@@ -115,6 +119,11 @@ urlpatterns = [
RETIREMENT_QUEUE,
name='accounts_retirement_queue'
),
url(
r'^v1/accounts/retirements_by_status_and_date/$',
RETIREMENT_LIST_BY_STATUS_AND_DATE,
name='accounts_retirements_by_status_and_date'
),
url(
r'^v1/accounts/retire/$',
RETIREMENT_POST,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment