Skip to content

Commit 8fe5d82

Browse files
committed
refactor: address PR review feedback
chore: cleanup chore: cleanup chore: cleanup chore: cleanup
1 parent 1a632cc commit 8fe5d82

15 files changed

Lines changed: 353 additions & 380 deletions

File tree

bc_obps/common/migrations/0131_update_dashboard_data.py

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from django.db import migrations
2+
3+
from common.utils import reset_dashboard_data
4+
5+
6+
def reset_dashboard_data_fn(_, schema_editor):
7+
reset_dashboard_data()
8+
9+
10+
class Migration(migrations.Migration):
11+
dependencies = [
12+
("common", "0131_V5_15_1"),
13+
]
14+
15+
operations = [
16+
migrations.RunPython(
17+
code=reset_dashboard_data_fn,
18+
),
19+
]

bc_obps/registration/tests/endpoints/_operations/test_reportable.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class TestOperationsPreviousReportableEndpoint(CommonTestSetup):
99
@patch("service.operation_service.OperationService.list_previous_reportable_operations")
10-
def test_list_previous_reportable_operations(
10+
def test_calls_and_returns_the_service_method_output(
1111
self,
1212
mock_list_previous_reportable_operations,
1313
):
@@ -40,11 +40,15 @@ def test_list_previous_reportable_operations(
4040

4141
assert response.status_code == 200
4242
assert len(response.json()) == 1
43-
assert response.json()[0].get("operation_name") == operation.name
44-
assert response.json()[0].get("operation_id") == str(operation.id)
45-
assert response.json()[0].get("reporting_year") == 2023
46-
assert response.json()[0].get("registration_purposes") == [
47-
Operation.Purposes.REPORTING_OPERATION,
43+
assert response.json() == [
44+
{
45+
"operation_id": str(operation.id),
46+
"operation_name": operation.name,
47+
"reporting_year": 2023,
48+
"registration_purposes": [
49+
str(Operation.Purposes.REPORTING_OPERATION),
50+
],
51+
}
4852
]
4953

5054
mock_list_previous_reportable_operations.assert_called_once()

bc_obps/reporting/tests/api/test_reports.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,4 +258,6 @@ def test_create_report_for_reporting_year(
258258

259259
# Verify that the selected registration purpose was passed to the service
260260
_, kwargs = mock_create_report_for_reporting_year.call_args
261+
assert kwargs["data"].operation_id == operation.id
262+
assert kwargs["data"].reporting_year == 2023
261263
assert kwargs["data"].registration_purpose == Operation.Purposes.REPORTING_OPERATION

bc_obps/service/operation_designated_operator_timeline_service.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,21 @@ def get_operation_designated_operator_for_reporting_year(
6767
def get_operation_designated_operators_for_reporting_years(
6868
cls,
6969
operation_ids: set[UUID],
70-
reporting_years: set[int],
70+
min_year: int,
71+
max_year: int,
7172
) -> dict[tuple[UUID, int], OperationDesignatedOperatorTimelinePlus]:
7273
"""
73-
Bulk version of get_operation_designated_operator_for_reporting_year
74+
Bulk version of get_operation_designated_operator_for_reporting_year.
7475
75-
It is intended for scenarios where many operation/reporting year
76-
combinations need to be evaluated, such as determining the list of reportable operations
76+
Returns designated operator timelines for every operation/year combination
77+
between min_year and max_year, inclusive.
7778
78-
Returns a lookup keyed by (operation_id, reporting_year)
79+
Returns a lookup keyed by (operation_id, reporting_year).
7980
"""
80-
81-
if not operation_ids or not reporting_years:
81+
if not operation_ids:
8282
return {}
8383

84-
min_year = min(reporting_years)
85-
max_year = max(reporting_years)
84+
reporting_years = range(min_year, max_year + 1)
8685

8786
timelines = (
8887
OperationDesignatedOperatorTimeline.objects.select_related(
@@ -100,24 +99,24 @@ def get_operation_designated_operators_for_reporting_years(
10099
lookup: dict[tuple[UUID, int], OperationDesignatedOperatorTimelinePlus] = {}
101100

102101
for timeline in timelines:
103-
start_date = timeline.start_date
104-
end_date = timeline.end_date
105-
106-
if start_date is None:
102+
if timeline.start_date is None:
107103
continue
108104

105+
start_year = timeline.start_date.year
106+
end_year = timeline.end_date.year if timeline.end_date else None
107+
109108
for reporting_year in reporting_years:
110-
if start_date.year > reporting_year:
109+
if start_year > reporting_year:
111110
continue
112111

113-
if end_date is not None and end_date.year <= reporting_year:
112+
if end_year is not None and end_year <= reporting_year:
114113
continue
115114

116115
lookup[(timeline.operation_id, reporting_year)] = OperationDesignatedOperatorTimelinePlus(
117116
operation=timeline.operation,
118117
operator=timeline.operator,
119-
start_date=start_date,
120-
end_date=end_date,
118+
start_date=timeline.start_date,
119+
end_date=timeline.end_date,
121120
)
122121

123122
return lookup

bc_obps/service/operation_service.py

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -816,27 +816,16 @@ def _get_registration_purposes_for_operation_type(
816816
raise ValueError(f"Unsupported operation type: {operation_type}")
817817

818818
@classmethod
819-
def _add_reportable_operation(
819+
def _build_previous_reportable_operation_row(
820820
cls,
821-
reportable_operations: list[dict],
822-
added_operation_years: set[tuple[UUID, int]],
823821
operation: Operation,
824822
reporting_year: ReportingYear,
825823
is_current_registered_fallback: bool,
826-
) -> None:
827-
"""
828-
Adds a reportable operation/reporting year combination to the result set
829-
and tracks it to prevent duplicates.
830-
"""
831-
operation_year = (operation.id, reporting_year.reporting_year)
832-
833-
reportable_operations.append(
834-
{
835-
**cls._build_reportable_operation_row(operation, reporting_year),
836-
"is_current_registered_fallback": is_current_registered_fallback,
837-
}
838-
)
839-
added_operation_years.add(operation_year)
824+
) -> dict:
825+
return {
826+
**cls._build_reportable_operation_row(operation, reporting_year),
827+
"is_current_registered_fallback": is_current_registered_fallback,
828+
}
840829

841830
@classmethod
842831
def _build_reportable_operation_row(
@@ -909,7 +898,8 @@ def list_previous_reportable_operations(
909898
designations_lookup = (
910899
OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years(
911900
operation_ids=all_operation_ids,
912-
reporting_years=year_values,
901+
min_year=min(year_values),
902+
max_year=max(year_values),
913903
)
914904
)
915905

@@ -923,7 +913,6 @@ def list_previous_reportable_operations(
923913
reporting_year.reporting_year,
924914
)
925915

926-
# Skip combinations that have already have a report
927916
if not cls._is_reportable_operation_year(
928917
operation_year,
929918
added_operation_years,
@@ -933,18 +922,21 @@ def list_previous_reportable_operations(
933922

934923
designated_operator_timeline = designations_lookup.get(operation_year)
935924

936-
# Include operations designated to the user's operator for the reporting year
937-
if cls._is_designated_to_user_operator(
925+
if not cls._is_designated_to_user_operator(
938926
designated_operator_timeline,
939927
user_operator,
940928
):
941-
cls._add_reportable_operation(
942-
reportable_operations,
943-
added_operation_years,
929+
continue
930+
931+
reportable_operations = [
932+
*reportable_operations,
933+
cls._build_previous_reportable_operation_row(
944934
timeline.operation,
945935
reporting_year,
946936
False,
947-
)
937+
),
938+
]
939+
added_operation_years = {*added_operation_years, operation_year}
948940

949941
for operation in current_registered_operations:
950942
for reporting_year in reporting_years:
@@ -953,21 +945,21 @@ def list_previous_reportable_operations(
953945
reporting_year.reporting_year,
954946
)
955947

956-
# Skip combinations that have already have a report
957948
if not cls._is_reportable_operation_year(
958949
operation_year,
959950
added_operation_years,
960951
existing_reports,
961952
):
962953
continue
963954

964-
# Fall back to the current registered operator when no historical designation applies
965-
cls._add_reportable_operation(
966-
reportable_operations,
967-
added_operation_years,
968-
operation,
969-
reporting_year,
970-
True,
971-
)
955+
reportable_operations = [
956+
*reportable_operations,
957+
cls._build_previous_reportable_operation_row(
958+
operation,
959+
reporting_year,
960+
True,
961+
),
962+
]
963+
added_operation_years = {*added_operation_years, operation_year}
972964

973965
return reportable_operations

bc_obps/service/tests/test_operation_designated_operator_timeline_service.py

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import pytest
22
from django.utils import timezone
33
from model_bakery import baker
4-
from service.operation_designated_operator_timeline_service import OperationDesignatedOperatorTimelineService
4+
from service.operation_designated_operator_timeline_service import (
5+
OperationDesignatedOperatorTimelinePlus,
6+
OperationDesignatedOperatorTimelineService,
7+
)
58

69
pytestmark = pytest.mark.django_db
710

@@ -94,59 +97,67 @@ def test_get_operation_designated_operator_for_reporting_year():
9497
assert result_none is None
9598

9699
@staticmethod
97-
def test_get_operation_designated_operators_for_reporting_years():
98-
operation = baker.make_recipe('registration.tests.utils.operation')
99-
operator1 = baker.make_recipe('registration.tests.utils.operator')
100-
operator2 = baker.make_recipe('registration.tests.utils.operator')
100+
def test_get_operation_designated_operators_for_reporting_years_returns_matching_timelines():
101+
operation = baker.make_recipe("registration.tests.utils.operation")
102+
operator1 = baker.make_recipe("registration.tests.utils.operator")
103+
operator2 = baker.make_recipe("registration.tests.utils.operator")
101104

102105
timeline1 = baker.make_recipe(
103-
'registration.tests.utils.operation_designated_operator_timeline',
106+
"registration.tests.utils.operation_designated_operator_timeline",
104107
operation=operation,
105108
operator=operator1,
106-
start_date=timezone.datetime(2024, 6, 1).date(),
107-
end_date=timezone.datetime(2025, 5, 31).date(),
109+
start_date=timezone.make_aware(timezone.datetime(2024, 6, 1)),
110+
end_date=timezone.make_aware(timezone.datetime(2025, 5, 31)),
108111
)
109112
timeline2 = baker.make_recipe(
110-
'registration.tests.utils.operation_designated_operator_timeline',
113+
"registration.tests.utils.operation_designated_operator_timeline",
111114
operation=operation,
112115
operator=operator2,
113-
start_date=timezone.datetime(2025, 5, 31).date(),
116+
start_date=timezone.make_aware(timezone.datetime(2025, 5, 31)),
114117
end_date=None,
115118
)
116119

117120
result = OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years(
118121
operation_ids={operation.id},
119-
reporting_years={2023, 2024, 2025},
122+
min_year=2023,
123+
max_year=2025,
120124
)
121125

126+
assert len(result) == 2
122127
assert (operation.id, 2023) not in result
123128

124-
result_2024 = result[(operation.id, 2024)]
125-
assert result_2024.operation == timeline1.operation
126-
assert result_2024.operator == timeline1.operator
127-
assert result_2024.start_date == timeline1.start_date
128-
assert result_2024.end_date == timeline1.end_date
129-
assert result_2024.has_been_transferred is True
130-
131-
result_2025 = result[(operation.id, 2025)]
132-
assert result_2025.operation == timeline2.operation
133-
assert result_2025.operator == timeline2.operator
134-
assert result_2025.start_date == timeline2.start_date
135-
assert result_2025.end_date == timeline2.end_date
136-
assert result_2025.has_been_transferred is False
137-
138-
empty_result = (
139-
OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years(
140-
operation_ids=set(),
141-
reporting_years={2024, 2025},
142-
)
143-
)
144-
assert empty_result == {}
145-
146-
empty_result = (
147-
OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years(
148-
operation_ids={operation.id},
149-
reporting_years=set(),
150-
)
151-
)
152-
assert empty_result == {}
129+
assert result[(operation.id, 2024)] == OperationDesignatedOperatorTimelinePlus(
130+
operation=timeline1.operation,
131+
operator=timeline1.operator,
132+
start_date=timeline1.start_date,
133+
end_date=timeline1.end_date,
134+
)
135+
136+
assert result[(operation.id, 2025)] == OperationDesignatedOperatorTimelinePlus(
137+
operation=timeline2.operation,
138+
operator=timeline2.operator,
139+
start_date=timeline2.start_date,
140+
end_date=timeline2.end_date,
141+
)
142+
143+
@staticmethod
144+
def test_get_operation_designated_operators_for_reporting_years_returns_empty_when_operation_ids_empty():
145+
result = OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years(
146+
operation_ids=set(),
147+
min_year=2024,
148+
max_year=2025,
149+
)
150+
151+
assert result == {}
152+
153+
@staticmethod
154+
def test_get_operation_designated_operators_for_reporting_years_returns_empty_when_year_range_is_empty():
155+
operation = baker.make_recipe("registration.tests.utils.operation")
156+
157+
result = OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years(
158+
operation_ids={operation.id},
159+
min_year=2025,
160+
max_year=2024,
161+
)
162+
163+
assert result == {}

0 commit comments

Comments
 (0)