Skip to content

Commit 8c1712f

Browse files
committed
fix: add date filter in payments api
1 parent d91ea94 commit 8c1712f

7 files changed

Lines changed: 74 additions & 24 deletions

File tree

futurex_openedx_extensions/dashboard/details/courses.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Courses details collectors"""
22
from __future__ import annotations
33

4+
from datetime import date
45
from typing import List
56

67
from common.djangoapps.student.models import CourseEnrollment
@@ -298,7 +299,7 @@ def get_courses_feedback_queryset( # pylint: disable=too-many-arguments
298299
return queryset
299300

300301

301-
def get_courses_orders_queryset( # pylint: disable=too-many-arguments
302+
def get_courses_orders_queryset( # pylint: disable=too-many-arguments, too-many-locals
302303
fx_permission_info: dict,
303304
user_ids: list = None,
304305
course_ids: list = None,
@@ -311,6 +312,8 @@ def get_courses_orders_queryset( # pylint: disable=too-many-arguments
311312
include_user_details: bool = False,
312313
status: str | None = None,
313314
item_type: str | None = None,
315+
date_from: date | None = None,
316+
date_to: date | None = None,
314317
) -> QuerySet:
315318
"""
316319
Returns a filtered queryset of Cart Orders based on provided criteria.
@@ -356,4 +359,6 @@ def get_courses_orders_queryset( # pylint: disable=too-many-arguments
356359
item_type=item_type,
357360
include_invoice=include_invoice,
358361
include_user_details=include_user_details,
362+
date_from=date_from,
363+
date_to=date_to,
359364
)

futurex_openedx_extensions/dashboard/docs_src.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,22 @@ def get_optional_parameter(path: str) -> Any:
16941694
'Note: right now only paid_course is implemented.'
16951695
)
16961696
),
1697+
query_parameter(
1698+
'date_from',
1699+
str,
1700+
description=(
1701+
'The start date of the range for filtering results. Must be provided in `YYYY-MM-DD` format. '
1702+
'Can be used together with `date_to` to limit results within a specific date range.'
1703+
),
1704+
),
1705+
query_parameter(
1706+
'date_to',
1707+
str,
1708+
description=(
1709+
'The end date of the range for filtering results. Must be provided in `YYYY-MM-DD` format. '
1710+
'Can be used together with `date_from` to limit results within a specific date range.'
1711+
),
1712+
),
16971713
common_parameters['include_staff'],
16981714
common_parameters['download'],
16991715
],

futurex_openedx_extensions/dashboard/serializers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,3 +1601,18 @@ def create(self, validated_data: Any) -> Any:
16011601
def update(self, instance: Any, validated_data: Any) -> Any:
16021602
"""Not implemented: Update an existing object."""
16031603
raise ValueError('This serializer does not support update.')
1604+
1605+
1606+
class ReportDateFilterSerializer(ReadOnlySerializer):
1607+
"""
1608+
Serializer for date filter.
1609+
Ensures date_from and date_to follow YYYY-MM-DD format and are parsed into Python date objects.
1610+
"""
1611+
date_from = serializers.DateField(
1612+
required=False,
1613+
input_formats=['%Y-%m-%d'],
1614+
)
1615+
date_to = serializers.DateField(
1616+
required=False,
1617+
input_formats=['%Y-%m-%d'],
1618+
)

futurex_openedx_extensions/dashboard/views.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -305,16 +305,10 @@ def _load_query_params(self, request: Any) -> None:
305305

306306
self.fill_missing_periods = request.query_params.get('fill_missing_periods', '1') == '1'
307307

308-
date_from = request.query_params.get('date_from')
309-
date_to = request.query_params.get('date_to')
310-
311-
try:
312-
self.date_from = datetime.strptime(date_from, '%Y-%m-%d').date() if date_from else None
313-
self.date_to = datetime.strptime(date_to, '%Y-%m-%d').date() if date_to else None
314-
except (ValueError, TypeError) as exc:
315-
raise ParseError(
316-
'Invalid dates. You must provide a valid date_from and date_to formated as YYYY-MM-DD'
317-
) from exc
308+
date_serializer = serializers.ReportDateFilterSerializer(data=self.request.query_params)
309+
date_serializer.is_valid(raise_exception=True)
310+
self.date_from = date_serializer.validated_data.get('date_from')
311+
self.date_to = date_serializer.validated_data.get('date_to')
318312

319313
def _get_certificates_count_data(self, one_tenant_permission_info: dict) -> int:
320314
"""Get the count of certificates for the given tenant"""
@@ -1939,6 +1933,9 @@ def get_queryset(self) -> QuerySet:
19391933
message=f'Invalid item_type: {item_type}, must be one of {CatalogueItem.valid_item_types()}.'
19401934
)
19411935

1936+
date_serializer = serializers.ReportDateFilterSerializer(data=self.request.query_params)
1937+
date_serializer.is_valid(raise_exception=True)
1938+
19421939
qs = get_courses_orders_queryset(
19431940
fx_permission_info=self.fx_permission_info,
19441941
user_ids=user_ids_list,
@@ -1952,6 +1949,8 @@ def get_queryset(self) -> QuerySet:
19521949
include_user_details=self.request.query_params.get('include_user_details', '0') == '1',
19531950
status=status,
19541951
item_type=item_type,
1952+
date_from=date_serializer.validated_data.get('date_from'),
1953+
date_to=date_serializer.validated_data.get('date_to'),
19551954
)
19561955
self._cached_course_map = getattr(qs, 'courses_map', {})
19571956
return qs

test_utils/edx_platform_mocks_shared/zeitlabs_payments/querysets.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""Mock"""
2+
from datetime import date
3+
24
from django.db.models.query import QuerySet
35

46

@@ -10,6 +12,8 @@ def get_orders_queryset( # pylint: disable=too-many-arguments,unused-argument
1012
item_type: str | None = None,
1113
include_invoice: bool = False,
1214
include_user_details: bool = False,
15+
date_from: date | None = None,
16+
date_to: date | None = None,
1317
):
1418
"""
1519
Mock.

tests/test_dashboard/test_details/test_details_courses.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Tests for courses details collectors"""
2+
from datetime import datetime
23
from unittest.mock import Mock, patch
34

45
import pytest
@@ -247,6 +248,7 @@ def test_get_courses_orders_queryset( # pylint: disable=too-many-locals
247248
include_user_details = True
248249
status = 'paid'
249250
item_type = 'paid_course'
251+
date_from = datetime.strptime('2025-01-01', '%Y-%m-%d').date()
250252
mock_accessible_users = Mock(name='UsersQS')
251253
mock_accessible_courses = Mock(name='CoursesQS')
252254
mock_get_users_and_courses.return_value = (
@@ -269,6 +271,8 @@ def test_get_courses_orders_queryset( # pylint: disable=too-many-locals
269271
user_ids=user_ids,
270272
item_type=item_type,
271273
learner_search=learner_search,
274+
date_from=date_from,
275+
date_to=None,
272276
)
273277

274278
mock_get_users_and_courses.assert_called_once_with(
@@ -290,6 +294,8 @@ def test_get_courses_orders_queryset( # pylint: disable=too-many-locals
290294
item_type=item_type,
291295
include_invoice=include_invoice,
292296
include_user_details=include_user_details,
297+
date_from=date_from,
298+
date_to=None,
293299
)
294300

295301
assert result == mock_orders_qs

tests/test_dashboard/test_views.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -218,30 +218,32 @@ def test_invalid_stats(self):
218218

219219
@patch('futurex_openedx_extensions.dashboard.views.AggregatedCountsView._construct_result')
220220
@ddt.data(
221-
(None, '2024-01-01', '2024-01-01', 'Invalid aggregate_period: None'),
222-
('day', '2024-01-01', '2024-01-01', None),
223-
('day', '2024-01-01', '2024-01-02', None),
224-
('day', '2024-01-02', '2024-01-01', None),
225-
('day', None, '2024-01-01', None),
226-
('day', '2024-01-02', None, None),
227-
('day', None, None, None),
228-
('invalid', '2024-01-01', '2024-01-02', 'Invalid aggregate_period: invalid'),
221+
(None, '2024-01-01', '2024-01-01', 'Invalid aggregate_period: None', None),
222+
('day', '2024-01-01', '2024-01-01', None, None),
223+
('day', '2024-01-01', '2024-01-02', None, None),
224+
('day', '2024-01-02', '2024-01-01', None, None),
225+
('day', None, '2024-01-01', None, None),
226+
('day', '2024-01-02', None, None, None),
227+
('day', None, None, None, None),
228+
('invalid', '2024-01-01', '2024-01-02', 'Invalid aggregate_period: invalid', None),
229229
(
230230
'day',
231231
'invalid', '2024-01-02',
232-
'Invalid dates. You must provide a valid date_from and date_to formated as YYYY-MM-DD'
232+
'Date has wrong format. Use one of these formats instead: YYYY-MM-DD.',
233+
'date_from'
233234
),
234235
(
235236
'day',
236237
'2024-01-01',
237238
'invalid',
238-
'Invalid dates. You must provide a valid date_from and date_to formated as YYYY-MM-DD'
239+
'Date has wrong format. Use one of these formats instead: YYYY-MM-DD.',
240+
'date_to'
239241
),
240-
('day', '2024-01-03', '2024-01-02', None),
242+
('day', '2024-01-03', '2024-01-02', None, None),
241243
)
242244
@ddt.unpack
243245
def test_load_query_params(
244-
self, aggregate_period, date_from, date_to, error_message, mock_construct_result,
246+
self, aggregate_period, date_from, date_to, error_message, error_date_field, mock_construct_result
245247
): # pylint: disable=too-many-arguments
246248
"""Verify that _load_query_params works as expected"""
247249
mock_construct_result.return_value = {
@@ -268,7 +270,10 @@ def test_load_query_params(
268270
response = self.client.get(url)
269271
if error_message:
270272
self.assertEqual(response.status_code, http_status.HTTP_400_BAD_REQUEST)
271-
self.assertEqual(str(response.data['detail']), error_message)
273+
if error_date_field:
274+
self.assertEqual(str(response.data[error_date_field][0]), error_message)
275+
else:
276+
self.assertEqual(str(response.data['detail']), error_message)
272277
else:
273278
self.assertEqual(response.status_code, http_status.HTTP_200_OK)
274279

0 commit comments

Comments
 (0)