Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -396,6 +396,7 @@ List of column names to request
- `NAICS`
- `PSC`
- `Primary Place of Performance`
- `recipient_id`
- `Recipient Location`
- `Recipient Name`
- `Recipient UEI`
Expand All @@ -415,6 +416,7 @@ List of column names to request
+ `internal_id`: `68856340` (required, string, nullable)
+ `generated_internal_id`: `CONT_AWD_00013U_7090_KJ88_4735` (required, string, nullable)
+ `Mod`: `P00206` (required, string, nullable)
+ `recipient_id`: `1e5032cf-11df-a3bf-4240-6dda5f6d45ff-C` (optional, string, nullable)
+ `Recipient Name`: `LEIDOS INNOVATIONS CORPORATION` (required, string, nullable)
+ `Transaction Amount`: `40000000.00` (required, string, nullable)

Expand All @@ -433,6 +435,7 @@ List of column names to request
+ `Last Date to Order` (required, string, nullable)
+ `Loan Value` (required, string, nullable)
+ `Mod` (required, string, nullable)
+ `recipient_id` (optional, string, nullable)
+ `Recipient Name` (required, string, nullable)
+ `Subsidy Cost` (required, string, nullable)
+ `Transaction Amount` (required, string, nullable)
+ `Transaction Amount` (required, string, nullable)
123 changes: 80 additions & 43 deletions usaspending_api/awards/v2/lookups/elasticsearch_lookups.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,90 @@
"""
Look ups for elasticsearch fields to be displayed for the front end
Lookups for elasticsearch fields to be displayed for the front end
"""

from copy import deepcopy
from dataclasses import dataclass
from enum import Enum

from usaspending_api.awards.v2.lookups.lookups import all_award_types_mappings

TRANSACTIONS_LOOKUP = {
"Recipient Name": "recipient_name.keyword",
"Action Date": "action_date",
"Transaction Amount": "federal_action_obligation",
"Award Type": "type_description.keyword",
"Awarding Agency": "awarding_toptier_agency_name.keyword",
"Awarding Sub Agency": "awarding_subtier_agency_name.keyword",
"Funding Agency": "funding_toptier_agency_name",
"Funding Sub Agency": "funding_subtier_agency_name",
"Issued Date": "period_of_performance_start_date",
"Loan Value": "face_value_loan_guarantee",
"Subsidy Cost": "original_loan_subsidy_cost",
"Mod": "modification_number.keyword",
"Award ID": "display_award_id",
"awarding_agency_id": "awarding_agency_id",
"internal_id": "award_id",
"generated_internal_id": "generated_unique_award_id",
"Last Date to Order": "ordering_period_end_date",
"def_codes": "disaster_emergency_fund_codes",
"Transaction Description": "transaction_description.keyword",
"Action Type": "action_type",
"Recipient UEI": "recipient_uei.keyword",
"awarding_agency_slug": "awarding_toptier_agency_name.keyword",
"funding_agency_slug": "funding_toptier_agency_name.keyword",
"recipient_location_city_name": "recipient_location_city_name.keyword",
"recipient_location_state_code": "recipient_location_state_code",
"recipient_location_country_name": "recipient_location_country_name.keyword",
"recipient_location_address_line1": "recipient_location_address_line1.keyword",
"recipient_location_address_line2": "recipient_location_address_line2.keyword",
"recipient_location_address_line3": "recipient_location_address_line3.keyword",
"pop_city_name": "pop_city_name.keyword",
"pop_state_code": "pop_state_code",
"pop_country_name": "pop_country_name.keyword",
"naics_code": "naics_code.keyword",
"naics_description": "naics_description.keyword",
"product_or_service_code": "product_or_service_code.keyword",
"product_or_service_description": "product_or_service_description.keyword",
"cfda_number": "cfda_number.keyword",
"cfda_title": "cfda_title.keyword",
}

@dataclass
class ElasticsearchField:
"""
Represents a field that is searchable by an API endpoint and pairs it with the corresponding elasticsearch field.

Args:
field_name: The name of the field provided by the user when selecting fields and returned by the API
full_path: A complete path that may include additional field types such as ".keyword"
short_path: The full_path with any additional field types removed; may be 1:1 with full_path
"""

field_name: str
full_path: str
short_path: str


class TransactionField(str, Enum):
ACTION_DATE = ("Action Date", "action_date")
ACTION_TYPE = ("Action Type", "action_type")
AWARD_ID = ("Award ID", "display_award_id")
AWARD_TYPE = ("Award Type", "type_description.keyword")
AWARDING_AGENCY = ("Awarding Agency", "awarding_toptier_agency_name.keyword")
AWARDING_AGENCY_ID = ("awarding_agency_id", "awarding_agency_id")
AWARDING_AGENCY_SLUG = ("awarding_agency_slug", "awarding_toptier_agency_name.keyword")
AWARDING_SUB_AGENCY = ("Awarding Sub Agency", "awarding_subtier_agency_name.keyword")
CFDA_NUMBER = ("cfda_number", "cfda_number.keyword")
CFDA_TITLE = ("cfda_title", "cfda_title.keyword")
DEF_CODES = ("def_codes", "disaster_emergency_fund_codes")
FUNDING_AGENCY = ("Funding Agency", "funding_toptier_agency_name.keyword")
FUNDING_AGENCY_SLUG = ("funding_agency_slug", "funding_toptier_agency_name.keyword")
FUNDING_SUB_AGENCY = ("Funding Sub Agency", "funding_subtier_agency_name.keyword")
GENERATED_INTERNAL_ID = ("generated_internal_id", "generated_unique_award_id")
INTERNAL_ID = ("internal_id", "award_id")
ISSUED_DATE = ("Issued Date", "period_of_performance_start_date")
LAST_DATE_TO_ORDER = ("Last Date to Order", "ordering_period_end_date")
LOAN_VALUE = ("Loan Value", "face_value_loan_guarantee")
MOD = ("Mod", "modification_number.keyword")
NAICS_CODE = ("naics_code", "naics_code.keyword")
NAICS_DESCRIPTION = ("naics_description", "naics_description.keyword")
POP_CITY_NAME = ("pop_city_name", "pop_city_name.keyword")
POP_COUNTRY_NAME = ("pop_country_name", "pop_country_name.keyword")
POP_STATE_CODE = ("pop_state_code", "pop_state_code")
PSC_CODE = ("product_or_service_code", "product_or_service_code.keyword")
PSC_DESCRIPTION = ("product_or_service_description", "product_or_service_description.keyword")
RECIPIENT_ID = ("recipient_id", "recipient_agg_key")
RECIPIENT_LOCATION_ADDRESS_LINE_1 = ("recipient_location_address_line1", "recipient_location_address_line1.keyword")
RECIPIENT_LOCATION_ADDRESS_LINE_2 = ("recipient_location_address_line2", "recipient_location_address_line2.keyword")
RECIPIENT_LOCATION_ADDRESS_LINE_3 = ("recipient_location_address_line3", "recipient_location_address_line3.keyword")
RECIPIENT_LOCATION_CITY_NAME = ("recipient_location_city_name", "recipient_location_city_name.keyword")
RECIPIENT_LOCATION_COUNTRY_NAME = ("recipient_location_country_name", "recipient_location_country_name.keyword")
RECIPIENT_LOCATION_STATE_CODE = ("recipient_location_state_code", "recipient_location_state_code")
RECIPIENT_NAME = ("Recipient Name", "recipient_name.keyword")
RECIPIENT_UEI = ("Recipient UEI", "recipient_uei.keyword")
SUBSIDY_COST = ("Subsidy Cost", "original_loan_subsidy_cost")
TRANSACTION_AMOUNT = ("Transaction Amount", "federal_action_obligation")
TRANSACTION_DESCRIPTION = ("Transaction Description", "transaction_description.keyword")

def __new__(cls, field_name: str, full_path: str) -> "str":
obj = str.__new__(cls, field_name)
obj._value_ = field_name
short_path = full_path.split(".")[0]
obj._es_field = ElasticsearchField(field_name, full_path, short_path)
return obj

@property
def field_name(self) -> str:
return self._es_field.field_name

@property
def full_path(self) -> str:
return self._es_field.full_path

@property
def short_path(self) -> str:
return self._es_field.short_path


base_mapping = {
"Award ID": "display_award_id",
Expand Down Expand Up @@ -145,8 +184,6 @@
},
}

TRANSACTIONS_SOURCE_LOOKUP = {key: value.replace(".keyword", "") for key, value in TRANSACTIONS_LOOKUP.items()}

CONTRACT_SOURCE_LOOKUP = {key: value.replace(".keyword", "") for key, value in contracts_mapping.items()}
IDV_SOURCE_LOOKUP = {key: value.replace(".keyword", "") for key, value in idv_mapping.items()}
NON_LOAN_ASST_SOURCE_LOOKUP = {key: value.replace(".keyword", "") for key, value in non_loan_assist_mapping.items()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def transaction_data():
recipient_location_zip5="abcde",
piid="IND12PB00323",
recipient_uei="testuei",
recipient_hash="1e5032cf-11df-a3bf-4240-6dda5f6d45ff",
recipient_levels=["C", "P", "R"],
parent_uei="test_parent_uei",
action_type="A",
legal_entity_address_line1="test address line",
Expand Down Expand Up @@ -270,6 +272,7 @@ def test_all_fields_returned(client, monkeypatch, transaction_data, elasticsearc
"NAICS",
"PSC",
"Assistance Listing",
"recipient_id",
]

request = {
Expand All @@ -284,13 +287,64 @@ def test_all_fields_returned(client, monkeypatch, transaction_data, elasticsearc
resp = client.post(ENDPOINT, content_type="application/json", data=json.dumps(request))

assert resp.status_code == status.HTTP_200_OK
assert len(resp.data["results"]) > 0
for result in resp.data["results"]:
for field in fields:
assert field in result, f"Response item is missing field {field}"

assert "Sausage" not in result
assert "A" not in result
assert len(resp.data["results"]) == 1
assert resp.data["results"] == [
{
"Action Date": "2010-10-01",
"Action Type": "A",
"Assistance Listing": {"cfda_number": "1234", "cfda_title": "cfda title 1"},
"Award ID": "IND12PB00323",
"Award Type": None,
"Awarding Agency": None,
"Awarding Sub Agency": None,
"Funding Agency": None,
"Funding Sub Agency": None,
"Issued Date": None,
"Last Date to Order": None,
"Loan Value": None,
"Mod": None,
"NAICS": {"code": "naics code 1", "description": "naics description 1"},
"PSC": {"code": "psc code 1", "description": "psc description 1"},
"Primary Place of Performance": {
"city_name": "ARLINGTON",
"congressional_code": "popcongressionalcode",
"country_name": "UNITED STATES",
"county_code": "popcountycode",
"county_name": "popcountyname",
"location_country_code": "popcountrycode",
"state_code": "TX",
"state_name": "Texas",
"zip4": "popziplast4",
"zip5": "popzip5",
},
"Recipient Location": {
"address_line1": "test address line",
"address_line2": "address2",
"address_line3": "address3",
"city_name": "ARLINGTON",
"congressional_code": "congressionalcode",
"country_name": "UNITED STATES",
"county_code": "001",
"county_name": "testcountyname",
"foreign_postal_code": "foreignpostalcode",
"foreign_province": "foreignprovince",
"location_country_code": "USA",
"state_code": "TX",
"state_name": "Texas",
"zip4": "6789",
"zip5": "abcde",
},
"Recipient Name": None,
"Recipient UEI": "testuei",
"Subsidy Cost": None,
"Transaction Amount": None,
"Transaction Description": "test",
"awarding_agency_id": None,
"generated_internal_id": None,
"internal_id": 1,
"recipient_id": "1e5032cf-11df-a3bf-4240-6dda5f6d45ff-C",
}
]


@pytest.mark.django_db
Expand Down
21 changes: 1 addition & 20 deletions usaspending_api/search/tests/unit/test_elasticsearch_helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from usaspending_api.search.v2.elasticsearch_helper import (
es_minimal_sanitize,
swap_keys,
)
from usaspending_api.search.v2.elasticsearch_helper import es_minimal_sanitize
from usaspending_api.search.v2.es_sanitization import es_sanitize


Expand All @@ -21,19 +18,3 @@ def test_es_minimal_sanitize():
test_string = "!-^~/"
processed_string = es_minimal_sanitize(test_string)
assert processed_string == r"\!\-\^\~\/"


def test_swap_keys():
test = {
"Recipient Name": "recipient_name",
"Action Date": "action_date",
"Transaction Amount": "federal_action_obligation",
}

results = swap_keys(test)

assert results == {
"recipient_name": "recipient_name",
"action_date": "action_date",
"federal_action_obligation": "federal_action_obligation",
}
18 changes: 1 addition & 17 deletions usaspending_api/search/v2/elasticsearch_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
from elasticsearch_dsl import A
from elasticsearch_dsl import Q as ES_Q

from usaspending_api.awards.v2.lookups.elasticsearch_lookups import (
INDEX_ALIASES_TO_AWARD_TYPES,
TRANSACTIONS_SOURCE_LOOKUP,
)
from usaspending_api.awards.v2.lookups.elasticsearch_lookups import INDEX_ALIASES_TO_AWARD_TYPES
from usaspending_api.common.data_classes import Pagination
from usaspending_api.common.elasticsearch.search_wrappers import AwardSearch, Search, TransactionSearch
from usaspending_api.common.query_with_filters import QueryWithFilters
Expand All @@ -19,19 +16,6 @@
logger = logging.getLogger("console")

DOWNLOAD_QUERY_SIZE = settings.MAX_DOWNLOAD_LIMIT
TRANSACTIONS_SOURCE_LOOKUP.update({v: k for k, v in TRANSACTIONS_SOURCE_LOOKUP.items()})


def swap_keys(dictionary_):
return dict(
(TRANSACTIONS_SOURCE_LOOKUP.get(old_key, old_key), new_key) for (old_key, new_key) in dictionary_.items()
)


def format_for_frontend(response):
"""calls reverse key from TRANSACTIONS_LOOKUP"""
response = [result["_source"] for result in response]
return [swap_keys(result) for result in response]


def get_total_results(keyword):
Expand Down
10 changes: 6 additions & 4 deletions usaspending_api/search/v2/urls.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from django.urls import include, re_path

from usaspending_api.search.v2.views import search_elasticsearch as es
from usaspending_api.search.v2.views.new_awards_over_time import NewAwardsOverTimeVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_award import SpendingByAwardVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_award_count import SpendingByAwardCountVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_category import SpendingByCategoryVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_geography import SpendingByGeographyVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_transaction import SpendingByTransactionVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_transaction_count import SpendingByTransactionCountVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_transaction_grouped import (
SpendingByTransactionGroupedVisualizationViewSet,
)
from usaspending_api.search.v2.views.spending_over_time import SpendingOverTimeVisualizationViewSet
from usaspending_api.search.v2.views.spending_by_subaward_grouped import SpendingBySubawardGroupedVisualizationViewSet
from usaspending_api.search.v2.views.transaction_spending_summary import TransactionSummaryVisualizationViewSet

urlpatterns = [
re_path(r"^new_awards_over_time", NewAwardsOverTimeVisualizationViewSet.as_view()),
Expand All @@ -20,9 +22,9 @@
re_path(r"^spending_by_category$", SpendingByCategoryVisualizationViewSet.as_view()),
re_path(r"^spending_by_geography", SpendingByGeographyVisualizationViewSet.as_view()),
re_path(r"^spending_by_subaward_grouped", SpendingBySubawardGroupedVisualizationViewSet.as_view()),
re_path(r"^spending_by_transaction_count", es.SpendingByTransactionCountVisualizationViewSet.as_view()),
re_path(r"^spending_by_transaction_count", SpendingByTransactionCountVisualizationViewSet.as_view()),
re_path(r"^spending_by_transaction_grouped", SpendingByTransactionGroupedVisualizationViewSet.as_view()),
re_path(r"^spending_by_transaction", es.SpendingByTransactionVisualizationViewSet.as_view()),
re_path(r"^spending_by_transaction", SpendingByTransactionVisualizationViewSet.as_view()),
re_path(r"^spending_over_time", SpendingOverTimeVisualizationViewSet.as_view()),
re_path(r"^transaction_spending_summary", es.TransactionSummaryVisualizationViewSet.as_view()),
re_path(r"^transaction_spending_summary", TransactionSummaryVisualizationViewSet.as_view()),
]
Loading