Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c17b66e
[DEV-15427] Fixed Incorrect Category Field Modification
DavidMikolaKC Jul 15, 2026
01be164
Merge branch 'qat' into bug/dev-15427-use-per-request-category-dataclass
DavidMikolaKC Jul 15, 2026
80865e7
[DEV-15427] Added New Category Instance per Request
DavidMikolaKC Jul 16, 2026
4fdf44f
[DEV-15427] - Added test case to ensure posts are cached correctly
DavidMikolaKC Jul 23, 2026
a6e0b69
[Dev-15427] testcase lint fixes
DavidMikolaKC Jul 23, 2026
0597042
Merge branch 'qat' of https://github.com/fedspendingtransparency/usas…
DavidMikolaKC Jul 24, 2026
e14a7ae
[Dev-15427] Added _category proprty
DavidMikolaKC Jul 24, 2026
18451b5
Ruff lint fix
DavidMikolaKC Jul 24, 2026
72139cc
Merge branch 'qat' into bug/dev-15427-use-per-request-category-dataclass
DavidMikolaKC Jul 24, 2026
d8f4e5a
Merge branch 'qat' of https://github.com/fedspendingtransparency/usas…
DavidMikolaKC Jul 27, 2026
d3223b8
[Dev-15427] Updated Category Declaration for Child Classes
DavidMikolaKC Jul 27, 2026
de9fe6d
Merge branch 'bug/dev-15427-use-per-request-category-dataclass' of ht…
DavidMikolaKC Jul 27, 2026
9ee057f
ruff lint fix
DavidMikolaKC Jul 27, 2026
fb2c4a9
[Dev-15427] Updated Category Declaration for other Child Classes
DavidMikolaKC Jul 27, 2026
8731e7a
Relocated Category Deepcopy
DavidMikolaKC Jul 27, 2026
6207ca0
Merge branch 'qat' into bug/dev-15427-use-per-request-category-dataclass
aguest-kc Jul 28, 2026
0ad35a6
Fixed recloning for self.category
DavidMikolaKC Jul 29, 2026
91134d1
Merge branch 'bug/dev-15427-use-per-request-category-dataclass' of ht…
DavidMikolaKC Jul 29, 2026
491fba6
Merge branch 'qat' into bug/dev-15427-use-per-request-category-dataclass
DavidMikolaKC Jul 29, 2026
8686506
Merge branch 'bug/dev-15427-use-per-request-category-dataclass' of ht…
DavidMikolaKC Jul 29, 2026
c62a47e
[Dev-15427] Updated check for validated payload
DavidMikolaKC Jul 30, 2026
cbf264e
Merge branch 'qat' into bug/dev-15427-use-per-request-category-dataclass
DavidMikolaKC Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,48 @@ def test_correct_response_of_empty_list(client, monkeypatch, elasticsearch_trans
}
assert resp.status_code == status.HTTP_200_OK, "Failed to return 200 Response"
assert resp.json() == expected_response


def test_category_dataclass_subaward_caching(client, monkeypatch, awards_and_transactions,
elasticsearch_subaward_index, elasticsearch_transaction_index):
# Tests that the Category dataclass is not being cached incorrectly
# Originally would happen when updated by subaward spending levels
# tested by making a spending_level subawards request and then requesting on transaction level
setup_elasticsearch_test(monkeypatch, elasticsearch_subaward_index)

sub_resp = client.post(
"/api/v2/search/spending_by_category/county",
content_type="application/json",
data=json.dumps({
"filters": {"time_period": [{"start_date": "2018-10-01", "end_date": "2020-09-30"}]},
"spending_level": "subawards"
}),
)

assert sub_resp.status_code == status.HTTP_200_OK, "Failed to return 200 Response"
assert sub_resp.json().get("spending_level") == "subawards"
setup_elasticsearch_test(monkeypatch, elasticsearch_transaction_index)

trn_resp = client.post(
"/api/v2/search/spending_by_category/county",
content_type="application/json",
data=json.dumps({
"filters": {"time_period": [{"start_date": "2018-10-01", "end_date": "2020-09-30"}]},
"spending_level": "transactions"
}),
)

expected_response = {
"category": "county",
"limit": 10,
"page_metadata": {"page": 1, "next": None, "previous": None, "hasNext": False, "hasPrevious": False},
"results": [
{"amount": 550005.0, "code": "001", "id": None, "name": "CHARLESTON", "total_outlays": None},
{"amount": 5500.0, "code": "005", "id": None, "name": "TEST NAME", "total_outlays": None},
{"amount": 50.0, "code": "005", "id": None, "name": "TEST NAME", "total_outlays": None},
],
"messages": _expected_messages(),
"spending_level": "transactions",
}
assert trn_resp.status_code == status.HTTP_200_OK, "Failed to return 200 Response"
assert trn_resp.json() == expected_response
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import copy
import logging
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
from dataclasses import dataclass, replace
from typing import List, Optional, Union

from django.conf import settings
Expand Down Expand Up @@ -32,7 +32,7 @@
logger = logging.getLogger(__name__)


@dataclass
@dataclass(frozen=True)
class Category:
name: str
agg_key: str
Expand Down Expand Up @@ -68,6 +68,10 @@ class AbstractSpendingByCategoryViewSet(APIView, metaclass=ABCMeta):
def post(self, request: Request, *args, **kwargs) -> Response:
original_filters = request.data.get("filters")

# Creates an instance copy for the category for each request
# Prevents modifications from previous requests affecting current requests
self.category = copy.deepcopy(self.category)
Comment thread
DavidMikolaKC marked this conversation as resolved.
Outdated

# Handles case where the request has already been validated by an implementation of the abstract class
validated_payload = kwargs.get("validated_payload", self.validate_payload(request))

Expand Down Expand Up @@ -126,8 +130,10 @@ def validate_payload(self, request: Request) -> dict:
def perform_search(self, original_filters: dict) -> dict:
if self.spending_level == SpendingLevel.SUBAWARD:
# Swap the agg_key fields for the equivalent Subaward fields, if applicable
self.category.agg_key = self.subaward_agg_key_mapper.get(self.category.agg_key, self.category.agg_key)

self.category = replace(
self.category,
agg_key=self.subaward_agg_key_mapper.get(self.category.agg_key, self.category.agg_key)
)
query_with_filters = QueryWithFilters(QueryType.SUBAWARDS)
filter_query = query_with_filters.generate_elasticsearch_query(self.filters)
results = self.query_elasticsearch(filter_query)
Expand Down
Loading