From 8fa1285bc40bc994529f073f360155568112cd32 Mon Sep 17 00:00:00 2001 From: infinityplusone Date: Tue, 27 Oct 2026 08:30:00 -0400 Subject: [PATCH 1/2] chore(contracts): vendor the filter/shape contract at Tango API 5.12.1 Adds the four financial-assistance award resources (assistance/grants, loans, direct_payments, other). Co-Authored-By: Claude Opus 5.5 --- contracts/filter_shape_contract.json | 2234 +++++++++++++++++++++++++- 1 file changed, 2233 insertions(+), 1 deletion(-) diff --git a/contracts/filter_shape_contract.json b/contracts/filter_shape_contract.json index fed5961..6fdd5e1 100644 --- a/contracts/filter_shape_contract.json +++ b/contracts/filter_shape_contract.json @@ -1,6 +1,6 @@ { "meta": { - "api_version": "5.11.0", + "api_version": "5.12.1", "description": "Canonical API filter/shape contract. Downstream consumers (SDK, MCP) should validate their conformance against this manifest.", "generated_from": "scripts/filter_shape_conformance.py", "schema_version": 2 @@ -1596,6 +1596,2230 @@ "swagger_has_key": false, "swagger_params": [] }, + "assistance/direct_payments": { + "basename": "assistance-direct_payments", + "docs_file": null, + "docs_params": [], + "prefix": "assistance/direct_payments", + "resource_key": "assistance/direct_payments", + "runtime": { + "filter_params": [ + "assistance_type", + "award_date", + "award_date_gte", + "award_date_lte", + "awarding_agency", + "business_funds_indicator", + "cfda_number", + "disaster_emergency_fund_code", + "fain", + "fiscal_year", + "fiscal_year_gte", + "fiscal_year_lte", + "funding_agency", + "funding_opportunity_number", + "key", + "obligated_gte", + "obligated_lte", + "ordering", + "pop_country", + "pop_end_date_gte", + "pop_end_date_lte", + "pop_start_date_gte", + "pop_start_date_lte", + "pop_state", + "pop_zip", + "recipient", + "record_type", + "search", + "uei", + "uri" + ], + "filter_params_detail": { + "assistance_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "award_date": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_gte": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_lte": { + "filter_class": "DateFilter", + "type": "date" + }, + "awarding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "business_funds_indicator": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "cfda_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "disaster_emergency_fund_code": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "fain": { + "filter_class": "IdListFilter", + "type": "string" + }, + "fiscal_year": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_gte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_lte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "funding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "funding_opportunity_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "key": { + "filter_class": "IdListFilter", + "type": "string" + }, + "obligated_gte": { + "filter_class": "NumberFilter", + "lookup": "gte", + "type": "number" + }, + "obligated_lte": { + "filter_class": "NumberFilter", + "lookup": "lte", + "type": "number" + }, + "ordering": { + "choices": [ + "-award_date", + "-obligated", + "-pragmatic_obligations", + "award_date", + "obligated", + "pragmatic_obligations" + ], + "choices_count": 6, + "filter_class": "OrderingFilter", + "type": "ordering" + }, + "pop_country": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_end_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_end_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_start_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_start_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_state": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_zip": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "recipient": { + "filter_class": "CharFilter", + "type": "string" + }, + "record_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "search": { + "filter_class": "CharFilter", + "type": "string" + }, + "uei": { + "filter_class": "UeiToEntityUuidFilter", + "type": "string" + }, + "uri": { + "filter_class": "IdListFilter", + "type": "string" + } + }, + "ordering_aliases": [], + "ordering_fields": [ + "award_date", + "obligated", + "pragmatic_obligations" + ], + "pagination": { + "class": "AssistanceKeysetPagination", + "max_page_size": 100 + }, + "shape": { + "expands": { + "assistance_listing": { + "expands": {}, + "fields": [ + "number", + "title" + ] + }, + "assistance_type": { + "expands": {}, + "fields": [ + "code", + "description" + ] + }, + "awarding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "funding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "listings": { + "expands": {}, + "fields": [ + "cfda_number", + "face_value_of_loan", + "first_action_date", + "indirect_cost_federal_share_amount", + "is_primary", + "last_action_date", + "loan_subsidy_cost", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transactions_count" + ] + }, + "obligations_by_fiscal_year": { + "expands": {}, + "fields": [ + "fiscal_year", + "obligated" + ] + }, + "officers": { + "expands": {}, + "fields": [ + "highly_compensated_officer_1_amount", + "highly_compensated_officer_1_name", + "highly_compensated_officer_2_amount", + "highly_compensated_officer_2_name", + "highly_compensated_officer_3_amount", + "highly_compensated_officer_3_name", + "highly_compensated_officer_4_amount", + "highly_compensated_officer_4_name", + "highly_compensated_officer_5_amount", + "highly_compensated_officer_5_name" + ] + }, + "period_of_performance": { + "expands": {}, + "fields": [ + "current_end_date", + "start_date" + ] + }, + "place_of_performance": { + "expands": {}, + "fields": [ + "city_name", + "code", + "country", + "county_fips", + "county_name", + "foreign_location", + "scope", + "state_code", + "state_fips", + "state_name", + "zip_code", + "zip_plus_four" + ] + }, + "recipient": { + "expands": {}, + "fields": [ + "business_types", + "congressional_district", + "congressional_district_original", + "county", + "duns", + "highest_owner", + "legal_business_name", + "legal_business_name_raw", + "uei", + "uuid" + ] + }, + "recipient_address": { + "expands": {}, + "fields": [ + "address_line1", + "address_line2", + "city", + "congressional_district_current", + "congressional_district_original", + "country_code", + "county_fips", + "foreign_city_name", + "foreign_postal_code", + "foreign_province_name", + "state_fips", + "state_or_province_code", + "zip_code", + "zip_code_plus4" + ] + }, + "transactions": { + "expands": {}, + "fields": [ + "action_date_fiscal_year", + "action_type", + "assistance_type_code", + "award_amount_basis", + "award_recipient_basis", + "cfda_number", + "correction_delete_indicator", + "face_value_of_loan", + "indirect_cost_federal_share_amount", + "initial_report_date", + "key", + "last_modified_date", + "loan_subsidy_cost", + "modification_number", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transaction_date" + ] + } + }, + "fields": [ + "assistance_type", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ] + }, + "shape_flat_paths": [ + "assistance_listing", + "assistance_listing.number", + "assistance_listing.title", + "assistance_type", + "assistance_type.code", + "assistance_type.description", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "awarding_office", + "awarding_office.agency_code", + "awarding_office.agency_name", + "awarding_office.department_code", + "awarding_office.department_name", + "awarding_office.office_code", + "awarding_office.office_name", + "awarding_office.organization_id", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_office", + "funding_office.agency_code", + "funding_office.agency_name", + "funding_office.department_code", + "funding_office.department_name", + "funding_office.office_code", + "funding_office.office_name", + "funding_office.organization_id", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings", + "listings.cfda_number", + "listings.face_value_of_loan", + "listings.first_action_date", + "listings.indirect_cost_federal_share_amount", + "listings.is_primary", + "listings.last_action_date", + "listings.loan_subsidy_cost", + "listings.non_federal_funding_amount", + "listings.obligated", + "listings.pragmatic_obligations", + "listings.transactions_count", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "obligations_by_fiscal_year", + "obligations_by_fiscal_year.fiscal_year", + "obligations_by_fiscal_year.obligated", + "officers", + "officers.highly_compensated_officer_1_amount", + "officers.highly_compensated_officer_1_name", + "officers.highly_compensated_officer_2_amount", + "officers.highly_compensated_officer_2_name", + "officers.highly_compensated_officer_3_amount", + "officers.highly_compensated_officer_3_name", + "officers.highly_compensated_officer_4_amount", + "officers.highly_compensated_officer_4_name", + "officers.highly_compensated_officer_5_amount", + "officers.highly_compensated_officer_5_name", + "period_of_performance", + "period_of_performance.current_end_date", + "period_of_performance.start_date", + "place_of_performance", + "place_of_performance.city_name", + "place_of_performance.code", + "place_of_performance.country", + "place_of_performance.county_fips", + "place_of_performance.county_name", + "place_of_performance.foreign_location", + "place_of_performance.scope", + "place_of_performance.state_code", + "place_of_performance.state_fips", + "place_of_performance.state_name", + "place_of_performance.zip_code", + "place_of_performance.zip_plus_four", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "recipient", + "recipient.business_types", + "recipient.congressional_district", + "recipient.congressional_district_original", + "recipient.county", + "recipient.duns", + "recipient.highest_owner", + "recipient.legal_business_name", + "recipient.legal_business_name_raw", + "recipient.uei", + "recipient.uuid", + "recipient_address", + "recipient_address.address_line1", + "recipient_address.address_line2", + "recipient_address.city", + "recipient_address.congressional_district_current", + "recipient_address.congressional_district_original", + "recipient_address.country_code", + "recipient_address.county_fips", + "recipient_address.foreign_city_name", + "recipient_address.foreign_postal_code", + "recipient_address.foreign_province_name", + "recipient_address.state_fips", + "recipient_address.state_or_province_code", + "recipient_address.zip_code", + "recipient_address.zip_code_plus4", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions.action_date_fiscal_year", + "transactions.action_type", + "transactions.assistance_type_code", + "transactions.award_amount_basis", + "transactions.award_recipient_basis", + "transactions.cfda_number", + "transactions.correction_delete_indicator", + "transactions.face_value_of_loan", + "transactions.indirect_cost_federal_share_amount", + "transactions.initial_report_date", + "transactions.key", + "transactions.last_modified_date", + "transactions.loan_subsidy_cost", + "transactions.modification_number", + "transactions.non_federal_funding_amount", + "transactions.obligated", + "transactions.pragmatic_obligations", + "transactions.transaction_date", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ], + "shape_gated_by_level": {}, + "shape_supported": true, + "shape_tier_required": null, + "viewset": "assistance.views.DirectPaymentAwardViewSet" + }, + "swagger_has_key": false, + "swagger_params": [] + }, + "assistance/grants": { + "basename": "assistance-grants", + "docs_file": null, + "docs_params": [], + "prefix": "assistance/grants", + "resource_key": "assistance/grants", + "runtime": { + "filter_params": [ + "assistance_type", + "award_date", + "award_date_gte", + "award_date_lte", + "awarding_agency", + "business_funds_indicator", + "cfda_number", + "disaster_emergency_fund_code", + "fain", + "fiscal_year", + "fiscal_year_gte", + "fiscal_year_lte", + "funding_agency", + "funding_opportunity_number", + "key", + "obligated_gte", + "obligated_lte", + "ordering", + "pop_country", + "pop_end_date_gte", + "pop_end_date_lte", + "pop_start_date_gte", + "pop_start_date_lte", + "pop_state", + "pop_zip", + "recipient", + "record_type", + "search", + "uei", + "uri" + ], + "filter_params_detail": { + "assistance_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "award_date": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_gte": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_lte": { + "filter_class": "DateFilter", + "type": "date" + }, + "awarding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "business_funds_indicator": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "cfda_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "disaster_emergency_fund_code": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "fain": { + "filter_class": "IdListFilter", + "type": "string" + }, + "fiscal_year": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_gte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_lte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "funding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "funding_opportunity_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "key": { + "filter_class": "IdListFilter", + "type": "string" + }, + "obligated_gte": { + "filter_class": "NumberFilter", + "lookup": "gte", + "type": "number" + }, + "obligated_lte": { + "filter_class": "NumberFilter", + "lookup": "lte", + "type": "number" + }, + "ordering": { + "choices": [ + "-award_date", + "-obligated", + "-pragmatic_obligations", + "award_date", + "obligated", + "pragmatic_obligations" + ], + "choices_count": 6, + "filter_class": "OrderingFilter", + "type": "ordering" + }, + "pop_country": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_end_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_end_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_start_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_start_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_state": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_zip": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "recipient": { + "filter_class": "CharFilter", + "type": "string" + }, + "record_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "search": { + "filter_class": "CharFilter", + "type": "string" + }, + "uei": { + "filter_class": "UeiToEntityUuidFilter", + "type": "string" + }, + "uri": { + "filter_class": "IdListFilter", + "type": "string" + } + }, + "ordering_aliases": [], + "ordering_fields": [ + "award_date", + "obligated", + "pragmatic_obligations" + ], + "pagination": { + "class": "AssistanceKeysetPagination", + "max_page_size": 100 + }, + "shape": { + "expands": { + "assistance_listing": { + "expands": {}, + "fields": [ + "number", + "title" + ] + }, + "assistance_type": { + "expands": {}, + "fields": [ + "code", + "description" + ] + }, + "awarding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "funding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "listings": { + "expands": {}, + "fields": [ + "cfda_number", + "face_value_of_loan", + "first_action_date", + "indirect_cost_federal_share_amount", + "is_primary", + "last_action_date", + "loan_subsidy_cost", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transactions_count" + ] + }, + "obligations_by_fiscal_year": { + "expands": {}, + "fields": [ + "fiscal_year", + "obligated" + ] + }, + "officers": { + "expands": {}, + "fields": [ + "highly_compensated_officer_1_amount", + "highly_compensated_officer_1_name", + "highly_compensated_officer_2_amount", + "highly_compensated_officer_2_name", + "highly_compensated_officer_3_amount", + "highly_compensated_officer_3_name", + "highly_compensated_officer_4_amount", + "highly_compensated_officer_4_name", + "highly_compensated_officer_5_amount", + "highly_compensated_officer_5_name" + ] + }, + "period_of_performance": { + "expands": {}, + "fields": [ + "current_end_date", + "start_date" + ] + }, + "place_of_performance": { + "expands": {}, + "fields": [ + "city_name", + "code", + "country", + "county_fips", + "county_name", + "foreign_location", + "scope", + "state_code", + "state_fips", + "state_name", + "zip_code", + "zip_plus_four" + ] + }, + "recipient": { + "expands": {}, + "fields": [ + "business_types", + "congressional_district", + "congressional_district_original", + "county", + "duns", + "highest_owner", + "legal_business_name", + "legal_business_name_raw", + "uei", + "uuid" + ] + }, + "recipient_address": { + "expands": {}, + "fields": [ + "address_line1", + "address_line2", + "city", + "congressional_district_current", + "congressional_district_original", + "country_code", + "county_fips", + "foreign_city_name", + "foreign_postal_code", + "foreign_province_name", + "state_fips", + "state_or_province_code", + "zip_code", + "zip_code_plus4" + ] + }, + "transactions": { + "expands": {}, + "fields": [ + "action_date_fiscal_year", + "action_type", + "assistance_type_code", + "award_amount_basis", + "award_recipient_basis", + "cfda_number", + "correction_delete_indicator", + "face_value_of_loan", + "indirect_cost_federal_share_amount", + "initial_report_date", + "key", + "last_modified_date", + "loan_subsidy_cost", + "modification_number", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transaction_date" + ] + } + }, + "fields": [ + "assistance_type", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ] + }, + "shape_flat_paths": [ + "assistance_listing", + "assistance_listing.number", + "assistance_listing.title", + "assistance_type", + "assistance_type.code", + "assistance_type.description", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "awarding_office", + "awarding_office.agency_code", + "awarding_office.agency_name", + "awarding_office.department_code", + "awarding_office.department_name", + "awarding_office.office_code", + "awarding_office.office_name", + "awarding_office.organization_id", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_office", + "funding_office.agency_code", + "funding_office.agency_name", + "funding_office.department_code", + "funding_office.department_name", + "funding_office.office_code", + "funding_office.office_name", + "funding_office.organization_id", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings", + "listings.cfda_number", + "listings.face_value_of_loan", + "listings.first_action_date", + "listings.indirect_cost_federal_share_amount", + "listings.is_primary", + "listings.last_action_date", + "listings.loan_subsidy_cost", + "listings.non_federal_funding_amount", + "listings.obligated", + "listings.pragmatic_obligations", + "listings.transactions_count", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "obligations_by_fiscal_year", + "obligations_by_fiscal_year.fiscal_year", + "obligations_by_fiscal_year.obligated", + "officers", + "officers.highly_compensated_officer_1_amount", + "officers.highly_compensated_officer_1_name", + "officers.highly_compensated_officer_2_amount", + "officers.highly_compensated_officer_2_name", + "officers.highly_compensated_officer_3_amount", + "officers.highly_compensated_officer_3_name", + "officers.highly_compensated_officer_4_amount", + "officers.highly_compensated_officer_4_name", + "officers.highly_compensated_officer_5_amount", + "officers.highly_compensated_officer_5_name", + "period_of_performance", + "period_of_performance.current_end_date", + "period_of_performance.start_date", + "place_of_performance", + "place_of_performance.city_name", + "place_of_performance.code", + "place_of_performance.country", + "place_of_performance.county_fips", + "place_of_performance.county_name", + "place_of_performance.foreign_location", + "place_of_performance.scope", + "place_of_performance.state_code", + "place_of_performance.state_fips", + "place_of_performance.state_name", + "place_of_performance.zip_code", + "place_of_performance.zip_plus_four", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "recipient", + "recipient.business_types", + "recipient.congressional_district", + "recipient.congressional_district_original", + "recipient.county", + "recipient.duns", + "recipient.highest_owner", + "recipient.legal_business_name", + "recipient.legal_business_name_raw", + "recipient.uei", + "recipient.uuid", + "recipient_address", + "recipient_address.address_line1", + "recipient_address.address_line2", + "recipient_address.city", + "recipient_address.congressional_district_current", + "recipient_address.congressional_district_original", + "recipient_address.country_code", + "recipient_address.county_fips", + "recipient_address.foreign_city_name", + "recipient_address.foreign_postal_code", + "recipient_address.foreign_province_name", + "recipient_address.state_fips", + "recipient_address.state_or_province_code", + "recipient_address.zip_code", + "recipient_address.zip_code_plus4", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions.action_date_fiscal_year", + "transactions.action_type", + "transactions.assistance_type_code", + "transactions.award_amount_basis", + "transactions.award_recipient_basis", + "transactions.cfda_number", + "transactions.correction_delete_indicator", + "transactions.face_value_of_loan", + "transactions.indirect_cost_federal_share_amount", + "transactions.initial_report_date", + "transactions.key", + "transactions.last_modified_date", + "transactions.loan_subsidy_cost", + "transactions.modification_number", + "transactions.non_federal_funding_amount", + "transactions.obligated", + "transactions.pragmatic_obligations", + "transactions.transaction_date", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ], + "shape_gated_by_level": {}, + "shape_supported": true, + "shape_tier_required": null, + "viewset": "assistance.views.GrantAwardViewSet" + }, + "swagger_has_key": false, + "swagger_params": [] + }, + "assistance/loans": { + "basename": "assistance-loans", + "docs_file": null, + "docs_params": [], + "prefix": "assistance/loans", + "resource_key": "assistance/loans", + "runtime": { + "filter_params": [ + "assistance_type", + "award_date", + "award_date_gte", + "award_date_lte", + "awarding_agency", + "business_funds_indicator", + "cfda_number", + "disaster_emergency_fund_code", + "fain", + "fiscal_year", + "fiscal_year_gte", + "fiscal_year_lte", + "funding_agency", + "funding_opportunity_number", + "key", + "obligated_gte", + "obligated_lte", + "ordering", + "pop_country", + "pop_end_date_gte", + "pop_end_date_lte", + "pop_start_date_gte", + "pop_start_date_lte", + "pop_state", + "pop_zip", + "recipient", + "record_type", + "search", + "uei", + "uri" + ], + "filter_params_detail": { + "assistance_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "award_date": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_gte": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_lte": { + "filter_class": "DateFilter", + "type": "date" + }, + "awarding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "business_funds_indicator": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "cfda_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "disaster_emergency_fund_code": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "fain": { + "filter_class": "IdListFilter", + "type": "string" + }, + "fiscal_year": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_gte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_lte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "funding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "funding_opportunity_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "key": { + "filter_class": "IdListFilter", + "type": "string" + }, + "obligated_gte": { + "filter_class": "NumberFilter", + "lookup": "gte", + "type": "number" + }, + "obligated_lte": { + "filter_class": "NumberFilter", + "lookup": "lte", + "type": "number" + }, + "ordering": { + "choices": [ + "-award_date", + "-obligated", + "-pragmatic_obligations", + "award_date", + "obligated", + "pragmatic_obligations" + ], + "choices_count": 6, + "filter_class": "OrderingFilter", + "type": "ordering" + }, + "pop_country": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_end_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_end_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_start_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_start_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_state": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_zip": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "recipient": { + "filter_class": "CharFilter", + "type": "string" + }, + "record_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "search": { + "filter_class": "CharFilter", + "type": "string" + }, + "uei": { + "filter_class": "UeiToEntityUuidFilter", + "type": "string" + }, + "uri": { + "filter_class": "IdListFilter", + "type": "string" + } + }, + "ordering_aliases": [], + "ordering_fields": [ + "award_date", + "obligated", + "pragmatic_obligations" + ], + "pagination": { + "class": "AssistanceKeysetPagination", + "max_page_size": 100 + }, + "shape": { + "expands": { + "assistance_listing": { + "expands": {}, + "fields": [ + "number", + "title" + ] + }, + "assistance_type": { + "expands": {}, + "fields": [ + "code", + "description" + ] + }, + "awarding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "funding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "listings": { + "expands": {}, + "fields": [ + "cfda_number", + "face_value_of_loan", + "first_action_date", + "indirect_cost_federal_share_amount", + "is_primary", + "last_action_date", + "loan_subsidy_cost", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transactions_count" + ] + }, + "obligations_by_fiscal_year": { + "expands": {}, + "fields": [ + "fiscal_year", + "obligated" + ] + }, + "officers": { + "expands": {}, + "fields": [ + "highly_compensated_officer_1_amount", + "highly_compensated_officer_1_name", + "highly_compensated_officer_2_amount", + "highly_compensated_officer_2_name", + "highly_compensated_officer_3_amount", + "highly_compensated_officer_3_name", + "highly_compensated_officer_4_amount", + "highly_compensated_officer_4_name", + "highly_compensated_officer_5_amount", + "highly_compensated_officer_5_name" + ] + }, + "period_of_performance": { + "expands": {}, + "fields": [ + "current_end_date", + "start_date" + ] + }, + "place_of_performance": { + "expands": {}, + "fields": [ + "city_name", + "code", + "country", + "county_fips", + "county_name", + "foreign_location", + "scope", + "state_code", + "state_fips", + "state_name", + "zip_code", + "zip_plus_four" + ] + }, + "recipient": { + "expands": {}, + "fields": [ + "business_types", + "congressional_district", + "congressional_district_original", + "county", + "duns", + "highest_owner", + "legal_business_name", + "legal_business_name_raw", + "uei", + "uuid" + ] + }, + "recipient_address": { + "expands": {}, + "fields": [ + "address_line1", + "address_line2", + "city", + "congressional_district_current", + "congressional_district_original", + "country_code", + "county_fips", + "foreign_city_name", + "foreign_postal_code", + "foreign_province_name", + "state_fips", + "state_or_province_code", + "zip_code", + "zip_code_plus4" + ] + }, + "transactions": { + "expands": {}, + "fields": [ + "action_date_fiscal_year", + "action_type", + "assistance_type_code", + "award_amount_basis", + "award_recipient_basis", + "cfda_number", + "correction_delete_indicator", + "face_value_of_loan", + "indirect_cost_federal_share_amount", + "initial_report_date", + "key", + "last_modified_date", + "loan_subsidy_cost", + "modification_number", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transaction_date" + ] + } + }, + "fields": [ + "assistance_type", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ] + }, + "shape_flat_paths": [ + "assistance_listing", + "assistance_listing.number", + "assistance_listing.title", + "assistance_type", + "assistance_type.code", + "assistance_type.description", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "awarding_office", + "awarding_office.agency_code", + "awarding_office.agency_name", + "awarding_office.department_code", + "awarding_office.department_name", + "awarding_office.office_code", + "awarding_office.office_name", + "awarding_office.organization_id", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_office", + "funding_office.agency_code", + "funding_office.agency_name", + "funding_office.department_code", + "funding_office.department_name", + "funding_office.office_code", + "funding_office.office_name", + "funding_office.organization_id", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings", + "listings.cfda_number", + "listings.face_value_of_loan", + "listings.first_action_date", + "listings.indirect_cost_federal_share_amount", + "listings.is_primary", + "listings.last_action_date", + "listings.loan_subsidy_cost", + "listings.non_federal_funding_amount", + "listings.obligated", + "listings.pragmatic_obligations", + "listings.transactions_count", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "obligations_by_fiscal_year", + "obligations_by_fiscal_year.fiscal_year", + "obligations_by_fiscal_year.obligated", + "officers", + "officers.highly_compensated_officer_1_amount", + "officers.highly_compensated_officer_1_name", + "officers.highly_compensated_officer_2_amount", + "officers.highly_compensated_officer_2_name", + "officers.highly_compensated_officer_3_amount", + "officers.highly_compensated_officer_3_name", + "officers.highly_compensated_officer_4_amount", + "officers.highly_compensated_officer_4_name", + "officers.highly_compensated_officer_5_amount", + "officers.highly_compensated_officer_5_name", + "period_of_performance", + "period_of_performance.current_end_date", + "period_of_performance.start_date", + "place_of_performance", + "place_of_performance.city_name", + "place_of_performance.code", + "place_of_performance.country", + "place_of_performance.county_fips", + "place_of_performance.county_name", + "place_of_performance.foreign_location", + "place_of_performance.scope", + "place_of_performance.state_code", + "place_of_performance.state_fips", + "place_of_performance.state_name", + "place_of_performance.zip_code", + "place_of_performance.zip_plus_four", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "recipient", + "recipient.business_types", + "recipient.congressional_district", + "recipient.congressional_district_original", + "recipient.county", + "recipient.duns", + "recipient.highest_owner", + "recipient.legal_business_name", + "recipient.legal_business_name_raw", + "recipient.uei", + "recipient.uuid", + "recipient_address", + "recipient_address.address_line1", + "recipient_address.address_line2", + "recipient_address.city", + "recipient_address.congressional_district_current", + "recipient_address.congressional_district_original", + "recipient_address.country_code", + "recipient_address.county_fips", + "recipient_address.foreign_city_name", + "recipient_address.foreign_postal_code", + "recipient_address.foreign_province_name", + "recipient_address.state_fips", + "recipient_address.state_or_province_code", + "recipient_address.zip_code", + "recipient_address.zip_code_plus4", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions.action_date_fiscal_year", + "transactions.action_type", + "transactions.assistance_type_code", + "transactions.award_amount_basis", + "transactions.award_recipient_basis", + "transactions.cfda_number", + "transactions.correction_delete_indicator", + "transactions.face_value_of_loan", + "transactions.indirect_cost_federal_share_amount", + "transactions.initial_report_date", + "transactions.key", + "transactions.last_modified_date", + "transactions.loan_subsidy_cost", + "transactions.modification_number", + "transactions.non_federal_funding_amount", + "transactions.obligated", + "transactions.pragmatic_obligations", + "transactions.transaction_date", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ], + "shape_gated_by_level": {}, + "shape_supported": true, + "shape_tier_required": null, + "viewset": "assistance.views.LoanAwardViewSet" + }, + "swagger_has_key": false, + "swagger_params": [] + }, + "assistance/other": { + "basename": "assistance-other", + "docs_file": null, + "docs_params": [], + "prefix": "assistance/other", + "resource_key": "assistance/other", + "runtime": { + "filter_params": [ + "assistance_type", + "award_date", + "award_date_gte", + "award_date_lte", + "awarding_agency", + "business_funds_indicator", + "cfda_number", + "disaster_emergency_fund_code", + "fain", + "fiscal_year", + "fiscal_year_gte", + "fiscal_year_lte", + "funding_agency", + "funding_opportunity_number", + "key", + "obligated_gte", + "obligated_lte", + "ordering", + "pop_country", + "pop_end_date_gte", + "pop_end_date_lte", + "pop_start_date_gte", + "pop_start_date_lte", + "pop_state", + "pop_zip", + "recipient", + "record_type", + "search", + "uei", + "uri" + ], + "filter_params_detail": { + "assistance_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "award_date": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_gte": { + "filter_class": "DateFilter", + "type": "date" + }, + "award_date_lte": { + "filter_class": "DateFilter", + "type": "date" + }, + "awarding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "business_funds_indicator": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "cfda_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "disaster_emergency_fund_code": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "fain": { + "filter_class": "IdListFilter", + "type": "string" + }, + "fiscal_year": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_gte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "fiscal_year_lte": { + "filter_class": "NumberFilter", + "type": "number" + }, + "funding_agency": { + "filter_class": "PerformantAgencyFilter", + "type": "string" + }, + "funding_opportunity_number": { + "filter_class": "IdListFilter", + "type": "string" + }, + "key": { + "filter_class": "IdListFilter", + "type": "string" + }, + "obligated_gte": { + "filter_class": "NumberFilter", + "lookup": "gte", + "type": "number" + }, + "obligated_lte": { + "filter_class": "NumberFilter", + "lookup": "lte", + "type": "number" + }, + "ordering": { + "choices": [ + "-award_date", + "-obligated", + "-pragmatic_obligations", + "award_date", + "obligated", + "pragmatic_obligations" + ], + "choices_count": 6, + "filter_class": "OrderingFilter", + "type": "ordering" + }, + "pop_country": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_end_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_end_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_start_date_gte": { + "filter_class": "DateFilter", + "lookup": "gte", + "type": "date" + }, + "pop_start_date_lte": { + "filter_class": "DateFilter", + "lookup": "lte", + "type": "date" + }, + "pop_state": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "pop_zip": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "recipient": { + "filter_class": "CharFilter", + "type": "string" + }, + "record_type": { + "filter_class": "BaseSmartFilter", + "type": "string" + }, + "search": { + "filter_class": "CharFilter", + "type": "string" + }, + "uei": { + "filter_class": "UeiToEntityUuidFilter", + "type": "string" + }, + "uri": { + "filter_class": "IdListFilter", + "type": "string" + } + }, + "ordering_aliases": [], + "ordering_fields": [ + "award_date", + "obligated", + "pragmatic_obligations" + ], + "pagination": { + "class": "AssistanceKeysetPagination", + "max_page_size": 100 + }, + "shape": { + "expands": { + "assistance_listing": { + "expands": {}, + "fields": [ + "number", + "title" + ] + }, + "assistance_type": { + "expands": {}, + "fields": [ + "code", + "description" + ] + }, + "awarding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "funding_office": { + "expands": {}, + "fields": [ + "agency_code", + "agency_name", + "department_code", + "department_name", + "office_code", + "office_name", + "organization_id" + ] + }, + "listings": { + "expands": {}, + "fields": [ + "cfda_number", + "face_value_of_loan", + "first_action_date", + "indirect_cost_federal_share_amount", + "is_primary", + "last_action_date", + "loan_subsidy_cost", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transactions_count" + ] + }, + "obligations_by_fiscal_year": { + "expands": {}, + "fields": [ + "fiscal_year", + "obligated" + ] + }, + "officers": { + "expands": {}, + "fields": [ + "highly_compensated_officer_1_amount", + "highly_compensated_officer_1_name", + "highly_compensated_officer_2_amount", + "highly_compensated_officer_2_name", + "highly_compensated_officer_3_amount", + "highly_compensated_officer_3_name", + "highly_compensated_officer_4_amount", + "highly_compensated_officer_4_name", + "highly_compensated_officer_5_amount", + "highly_compensated_officer_5_name" + ] + }, + "period_of_performance": { + "expands": {}, + "fields": [ + "current_end_date", + "start_date" + ] + }, + "place_of_performance": { + "expands": {}, + "fields": [ + "city_name", + "code", + "country", + "county_fips", + "county_name", + "foreign_location", + "scope", + "state_code", + "state_fips", + "state_name", + "zip_code", + "zip_plus_four" + ] + }, + "recipient": { + "expands": {}, + "fields": [ + "business_types", + "congressional_district", + "congressional_district_original", + "county", + "duns", + "highest_owner", + "legal_business_name", + "legal_business_name_raw", + "uei", + "uuid" + ] + }, + "recipient_address": { + "expands": {}, + "fields": [ + "address_line1", + "address_line2", + "city", + "congressional_district_current", + "congressional_district_original", + "country_code", + "county_fips", + "foreign_city_name", + "foreign_postal_code", + "foreign_province_name", + "state_fips", + "state_or_province_code", + "zip_code", + "zip_code_plus4" + ] + }, + "transactions": { + "expands": {}, + "fields": [ + "action_date_fiscal_year", + "action_type", + "assistance_type_code", + "award_amount_basis", + "award_recipient_basis", + "cfda_number", + "correction_delete_indicator", + "face_value_of_loan", + "indirect_cost_federal_share_amount", + "initial_report_date", + "key", + "last_modified_date", + "loan_subsidy_cost", + "modification_number", + "non_federal_funding_amount", + "obligated", + "pragmatic_obligations", + "transaction_date" + ] + } + }, + "fields": [ + "assistance_type", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ] + }, + "shape_flat_paths": [ + "assistance_listing", + "assistance_listing.number", + "assistance_listing.title", + "assistance_type", + "assistance_type.code", + "assistance_type.description", + "assistance_types_count", + "award_amount_basis", + "award_date", + "award_recipient_basis", + "award_total_face_value_of_loan", + "award_total_loan_subsidy_cost", + "award_total_non_federal_funding", + "award_total_obligated", + "award_total_outlayed", + "awarding_office", + "awarding_office.agency_code", + "awarding_office.agency_name", + "awarding_office.department_code", + "awarding_office.department_name", + "awarding_office.office_code", + "awarding_office.office_name", + "awarding_office.organization_id", + "business_funds_indicator", + "cfda_number", + "covid19_obligated", + "covid19_outlayed", + "disaster_emergency_fund_codes", + "face_value_of_loan", + "fain", + "federal_accounts_funding_this_award", + "fiscal_year", + "former_award_keys", + "funding_office", + "funding_office.agency_code", + "funding_office.agency_name", + "funding_office.department_code", + "funding_office.department_name", + "funding_office.office_code", + "funding_office.office_name", + "funding_office.organization_id", + "funding_opportunity_goals_text", + "funding_opportunity_number", + "iija_obligated", + "iija_outlayed", + "indirect_cost_federal_share_amount", + "insurance_coverage_amount", + "key", + "last_action_date", + "last_modified_date", + "listings", + "listings.cfda_number", + "listings.face_value_of_loan", + "listings.first_action_date", + "listings.indirect_cost_federal_share_amount", + "listings.is_primary", + "listings.last_action_date", + "listings.loan_subsidy_cost", + "listings.non_federal_funding_amount", + "listings.obligated", + "listings.pragmatic_obligations", + "listings.transactions_count", + "listings_count", + "loan_subsidy_cost", + "non_federal_funding_amount", + "object_classes_funding_this_award", + "obligated", + "obligations_by_fiscal_year", + "obligations_by_fiscal_year.fiscal_year", + "obligations_by_fiscal_year.obligated", + "officers", + "officers.highly_compensated_officer_1_amount", + "officers.highly_compensated_officer_1_name", + "officers.highly_compensated_officer_2_amount", + "officers.highly_compensated_officer_2_name", + "officers.highly_compensated_officer_3_amount", + "officers.highly_compensated_officer_3_name", + "officers.highly_compensated_officer_4_amount", + "officers.highly_compensated_officer_4_name", + "officers.highly_compensated_officer_5_amount", + "officers.highly_compensated_officer_5_name", + "period_of_performance", + "period_of_performance.current_end_date", + "period_of_performance.start_date", + "place_of_performance", + "place_of_performance.city_name", + "place_of_performance.code", + "place_of_performance.country", + "place_of_performance.county_fips", + "place_of_performance.county_name", + "place_of_performance.foreign_location", + "place_of_performance.scope", + "place_of_performance.state_code", + "place_of_performance.state_fips", + "place_of_performance.state_name", + "place_of_performance.zip_code", + "place_of_performance.zip_plus_four", + "pragmatic_obligations", + "program_activities_funding_this_award", + "purposes_differ", + "recipient", + "recipient.business_types", + "recipient.congressional_district", + "recipient.congressional_district_original", + "recipient.county", + "recipient.duns", + "recipient.highest_owner", + "recipient.legal_business_name", + "recipient.legal_business_name_raw", + "recipient.uei", + "recipient.uuid", + "recipient_address", + "recipient_address.address_line1", + "recipient_address.address_line2", + "recipient_address.city", + "recipient_address.congressional_district_current", + "recipient_address.congressional_district_original", + "recipient_address.country_code", + "recipient_address.county_fips", + "recipient_address.foreign_city_name", + "recipient_address.foreign_postal_code", + "recipient_address.foreign_province_name", + "recipient_address.state_fips", + "recipient_address.state_or_province_code", + "recipient_address.zip_code", + "recipient_address.zip_code_plus4", + "record_type", + "sai_number", + "summary", + "title", + "transactions", + "transactions.action_date_fiscal_year", + "transactions.action_type", + "transactions.assistance_type_code", + "transactions.award_amount_basis", + "transactions.award_recipient_basis", + "transactions.cfda_number", + "transactions.correction_delete_indicator", + "transactions.face_value_of_loan", + "transactions.indirect_cost_federal_share_amount", + "transactions.initial_report_date", + "transactions.key", + "transactions.last_modified_date", + "transactions.loan_subsidy_cost", + "transactions.modification_number", + "transactions.non_federal_funding_amount", + "transactions.obligated", + "transactions.pragmatic_obligations", + "transactions.transaction_date", + "transactions_count", + "treasury_accounts_funding_this_award", + "uri" + ], + "shape_gated_by_level": {}, + "shape_supported": true, + "shape_tier_required": null, + "viewset": "assistance.views.OtherAssistanceAwardViewSet" + }, + "swagger_has_key": false, + "swagger_params": [] + }, "assistance_listings": { "basename": "assistancelisting", "docs_file": null, @@ -17665,6 +19889,10 @@ "agencies", "agencies_contracts_awarding", "agencies_contracts_funding", + "assistance/direct_payments", + "assistance/grants", + "assistance/loans", + "assistance/other", "assistance_listings", "budget/accounts", "business_types", @@ -17717,6 +19945,10 @@ "no_swagger": [ "agencies_contracts_awarding", "agencies_contracts_funding", + "assistance/direct_payments", + "assistance/grants", + "assistance/loans", + "assistance/other", "budget/accounts", "contracts_subawards", "dibbs/awards", From 9a66e7424a48d156670ec2e7bce1b38584ec1611 Mon Sep 17 00:00:00 2001 From: infinityplusone Date: Tue, 27 Oct 2026 08:30:14 -0400 Subject: [PATCH 2/2] feat(assistance): financial-assistance award endpoints Adds list, get-by-key, transactions and listings methods for /api/assistance/{grants,loans,direct_payments,other}/, mirroring how the SDK exposes contracts: every API filter as an explicit argument, shaping with the API's own default shape, and cursor pagination with exact=True. The sub-routes page by number and return plain dicts. One AssistanceAward schema backs all four resources, covering every field and expansion in the contract's shape tree, so the shape-coverage gate reports no new gaps. Unit tests mock HTTP; integration tests replay cassettes recorded against prod. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 10 + README.md | 12 +- docs/API_REFERENCE.md | 132 ++++ scripts/check_filter_shape_conformance.py | 6 + scripts/check_shape_coverage.py | 4 + tango/__init__.py | 8 + tango/client.py | 655 ++++++++++++++++++ tango/models.py | 143 ++++ tango/shapes/explicit_schemas.py | 355 ++++++++++ ...dsIntegration.test_filters_narrow_the_list | 82 +++ ...n.test_get_and_sub_routes[direct_payments] | 331 +++++++++ ...ntegration.test_get_and_sub_routes[grants] | 329 +++++++++ ...Integration.test_get_and_sub_routes[loans] | 328 +++++++++ ...Integration.test_get_and_sub_routes[other] | 330 +++++++++ ...test_list_pages_by_cursor[direct_payments] | 209 ++++++ ...egration.test_list_pages_by_cursor[grants] | 205 ++++++ ...tegration.test_list_pages_by_cursor[loans] | 193 ++++++ ...tegration.test_list_pages_by_cursor[other] | 213 ++++++ .../test_assistance_integration.py | 117 ++++ tests/test_assistance.py | 436 ++++++++++++ 20 files changed, 4097 insertions(+), 1 deletion(-) create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_filters_narrow_the_list create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[direct_payments] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[grants] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[loans] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[other] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[direct_payments] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[grants] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[loans] create mode 100644 tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[other] create mode 100644 tests/integration/test_assistance_integration.py create mode 100644 tests/test_assistance.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c8284..0676a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **Financial-assistance award methods** for `/api/assistance/{grants,loans,direct_payments,other}/` (Tango API 5.12.1): `list_assistance_*` with every API filter and cursor pagination, and `get_assistance_*` by key (see `docs/API_REFERENCE.md#financial-assistance-awards`). +- **Assistance award sub-routes**: `list_assistance_*_transactions()` and `list_assistance_*_listings()` page through an award's transactions and its money by assistance listing. +- **`AssistanceAward` shape schema and `ShapeConfig.ASSISTANCE_AWARDS_MINIMAL`**, the API's own default shape, so every assistance field and expansion validates client-side. + +### Internal / Infra + +- **Vendored filter/shape contract refreshed to Tango API 5.12.1**, which adds the four assistance resources; the conformance and shape-coverage gates now map them. + ## [1.20.0] - 2026-10-24 ### Added diff --git a/README.md b/README.md index d8c0ebc..13c6945 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A modern Python SDK for the [Tango API](https://tango.makegov.com) by MakeGov, f - **Dynamic Response Shaping** - Request only the fields you need, reducing payload sizes by 60-80% - **Full Type Safety** - Runtime-generated TypedDict types with accurate type hints for IDE autocomplete -- **Comprehensive API Coverage** - All major Tango API endpoints (contracts, IDVs, OTAs, entities, forecasts, opportunities, notices, grants, protests, webhooks, and more) +- **Comprehensive API Coverage** - All major Tango API endpoints (contracts, IDVs, OTAs, financial-assistance awards, entities, forecasts, opportunities, notices, grants, protests, webhooks, and more) - **Flexible Data Access** - Dictionary-based response objects with validation - **Modern Python** - Built for Python 3.12+ using modern async-ready patterns - **Production-Ready** - Comprehensive test suite with VCR.py-based integration tests @@ -256,6 +256,16 @@ notices = client.list_notices(agency="DOD", notice_type="Presolicitation", limit grants = client.list_grants(agency="HHS", status="F", limit=25) # F = Forecasted ``` +### Financial Assistance Awards + +```python +# Grants, loans, direct payments and other assistance from USAspending, one method family per endpoint +grants = client.list_assistance_grants(cfda_number="93.778", ordering="-obligated", limit=25) +award = client.get_assistance_grant(grants.results[0]["key"]) +transactions = client.list_assistance_grant_transactions(award["key"]) +loans = client.list_assistance_loans(pop_state="VA", fiscal_year=2026) +``` + ### Protests ```python diff --git a/docs/API_REFERENCE.md b/docs/API_REFERENCE.md index 7e216cb..7553141 100644 --- a/docs/API_REFERENCE.md +++ b/docs/API_REFERENCE.md @@ -20,6 +20,7 @@ Complete reference for all Tango Python SDK methods and functionality. - [Notices](#notices) - [State \& Local (SLED)](#state--local-sled) - [Grants](#grants) +- [Financial Assistance Awards](#financial-assistance-awards) - [GSA eLibrary Contracts](#gsa-elibrary-contracts) - [Protests](#protests) - [Contract Appeals](#contract-appeals) @@ -1214,6 +1215,136 @@ for grant in grants.results: --- +## Financial Assistance Awards + +Federal financial-assistance awards from USAspending, split across four endpoints by GSDM assistance type: + +| Endpoint | Assistance types | Methods | +|----------|------------------|---------| +| `/api/assistance/grants/` | `F001` grant, `F002` cooperative agreement | `list_assistance_grants`, `get_assistance_grant`, `list_assistance_grant_transactions`, `list_assistance_grant_listings` | +| `/api/assistance/loans/` | `F003` direct loan, `F004` loan guarantee | `list_assistance_loans`, `get_assistance_loan`, `list_assistance_loan_transactions`, `list_assistance_loan_listings` | +| `/api/assistance/direct_payments/` | `F006`, `F007` direct payments | `list_assistance_direct_payments`, `get_assistance_direct_payment`, `list_assistance_direct_payment_transactions`, `list_assistance_direct_payment_listings` | +| `/api/assistance/other/` | `F005` insurance, `F008`-`F010` other | `list_assistance_other`, `get_assistance_other`, `list_assistance_other_transactions`, `list_assistance_other_listings` | + +The four endpoints take the same filters and shapes, and return the same fields (`AssistanceAward`). An award lives under the endpoint of its latest action's assistance type. These are awards: Grants.gov funding opportunities are [`list_grants()`](#grants). + +### list_assistance_grants() / list_assistance_loans() / list_assistance_direct_payments() / list_assistance_other() + +List awards with filtering, shaping and cursor pagination. + +```python +grants = client.list_assistance_grants( + cursor=None, + limit=25, + shape=ShapeConfig.ASSISTANCE_AWARDS_MINIMAL, + # Filter parameters (all optional) + key=None, + fain=None, + uri=None, + uei=None, + recipient=None, + search=None, + awarding_agency=None, + funding_agency=None, + cfda_number=None, + assistance_type=None, + record_type=None, + business_funds_indicator=None, + disaster_emergency_fund_code=None, + funding_opportunity_number=None, + award_date=None, + award_date_gte=None, + award_date_lte=None, + fiscal_year=None, + fiscal_year_gte=None, + fiscal_year_lte=None, + pop_start_date_gte=None, + pop_start_date_lte=None, + pop_end_date_gte=None, + pop_end_date_lte=None, + pop_state=None, + pop_country=None, + pop_zip=None, + obligated_gte=None, + obligated_lte=None, + ordering=None, + exact=False, +) +``` + +**Filter Parameters:** +- `key` / `fain` / `uri` - Award key, Federal Award Identification Number, or Unique Record Identifier (aggregate records), exact; OR several with `|` +- `uei` - Recipient UEI, exact; OR several with `|` +- `recipient` - Recipient name or UEI. Each term matches the recipients closest to it by name; a term under three characters is a 400. OR several with `|` +- `search` - Full-text search over identifiers, recipient names, assistance listing titles and descriptions, web-search style (`broadband or fiber`, `housing -veterans`) +- `awarding_agency` / `funding_agency` - Agency code, name or abbreviation; OR several with `|` +- `cfda_number` - The award's primary assistance listing number, e.g. `"93.778"`; OR several with `|`. Other programs on the award are in its listings +- `assistance_type` - GSDM code `F001`-`F010`, case-insensitive; OR several with `|` +- `record_type` - `"1"` aggregate, `"2"` non-aggregate, `"3"` non-aggregate to an individual (PII-redacted); OR several with `|` +- `business_funds_indicator` - `"NON"` or `"REC"` (Recovery Act); OR several with `|` +- `disaster_emergency_fund_code` - DEFC, e.g. `"L"`, matched as a whole code; OR several with `|` +- `funding_opportunity_number` - The Grants.gov opportunity number the award was made under; OR several with `|` +- `award_date`, `award_date_gte`, `award_date_lte` - Award date, exact or range (`YYYY-MM-DD`) +- `fiscal_year`, `fiscal_year_gte`, `fiscal_year_lte` - Fiscal year, exact or range +- `pop_start_date_gte` / `pop_start_date_lte`, `pop_end_date_gte` / `pop_end_date_lte` - Period of performance start and current end date ranges +- `pop_state`, `pop_country` (ISO alpha-3), `pop_zip` (five digits) - Place of performance; OR several with `|` +- `obligated_gte` / `obligated_lte` - Obligated amount range (USD) +- `ordering` - `award_date`, `obligated` or `pragmatic_obligations`, `-` prefix for descending; defaults to `-award_date` +- `exact` - `True` for an exact `count`; past 10,000 matches a filtered list otherwise reports an estimate (`count_type == "approximate"`) + +Any other parameter is rejected by the API with a 400. + +**Returns:** [PaginatedResponse](#paginatedresponse) with award dictionaries; pass `response.cursor` back as `cursor` for the next page. + +**Example:** +```python +grants = client.list_assistance_grants( + cfda_number="93.778", + fiscal_year=2026, + ordering="-obligated", + limit=10, +) + +for award in grants.results: + print(award["key"], award["obligated"], award["recipient"]["legal_business_name"]) + +if grants.cursor: + more = client.list_assistance_grants(cfda_number="93.778", fiscal_year=2026, cursor=grants.cursor) +``` + +To compare dollars across the four endpoints, read `pragmatic_obligations`: the obligation for grants and payments, the subsidy cost for loans. + +### get_assistance_grant() / get_assistance_loan() / get_assistance_direct_payment() / get_assistance_other() + +Get one award by `key`. + +```python +award = client.get_assistance_grant( + "ASST_NON_693JF72640002_069", + shape="key,title,summary,obligated,listings(*),obligations_by_fiscal_year(*)", +) +``` + +A retired key (one in an award's `former_award_keys`) returns the award that carries it now, with its current `key`. A key that names no award on that endpoint raises `TangoNotFoundError`. + +### list_assistance_*_transactions() / list_assistance_*_listings() + +An award's transactions (latest first) and its money by assistance listing (primary listing first): `list_assistance_grant_transactions`, `list_assistance_grant_listings`, and the same pair for `loan`, `direct_payment` and `other`. + +```python +transactions = client.list_assistance_grant_transactions("ASST_NON_693JF72640002_069", page=1, limit=100) +listings = client.list_assistance_grant_listings("ASST_NON_693JF72640002_069") +``` + +These sub-routes take no shape and page by number (`page`, `limit`): transactions default to 100 per page (max 500), listings to 10 (max 100). Rows come back as plain dictionaries. A key that names no award returns an empty page rather than a 404. + +**Shape notes:** +- `recipient` is Tango's shared recipient record, shaped like an entity; it exists for recipients without a UEI too, and is `None` on aggregate and PII-redacted records (`record_type` `"1"` and `"3"`). `business_types` is a list of `{code, description}` in USAspending's recipient-type codes. +- `cfda_number` is always a plain field; `assistance_listing(number,title)` adds the program title. +- `transactions` works as a leaf (every key of every transaction) or an expansion (`transactions(key,obligated)`); for long histories, page through the transactions sub-route instead. + +--- + ## GSA eLibrary Contracts GSA Schedule contracts from the GSA eLibrary. @@ -2660,6 +2791,7 @@ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE) | `OPPORTUNITIES_MINIMAL` | `list_opportunities` | opportunity_id, title, solicitation_number, response_deadline, active | | `NOTICES_MINIMAL` | `list_notices` | notice_id, title, solicitation_number, posted_date | | `GRANTS_MINIMAL` | `list_grants` | grant_id, opportunity_number, title, status(*), agency_code | +| `ASSISTANCE_AWARDS_MINIMAL` | `list_assistance_*`, `get_assistance_*` | The API's own default: key, fain, uri, award_date, fiscal_year, assistance_type(code,description), record_type, cfda_number, assistance_listing(number,title), title, obligated, pragmatic_obligations, face_value_of_loan, loan_subsidy_cost, awarding_office(*), funding_office(*), recipient(*), place_of_performance(*), period_of_performance(start_date,current_end_date) | | `IDVS_MINIMAL` | `list_idvs`, `list_vehicle_awardees` | key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated, idv_type | | `IDVS_COMPREHENSIVE` | `get_idv` | Full IDV with offices, place_of_performance, competition, transactions, etc. | | `VEHICLES_MINIMAL` | `list_vehicles` | uuid, solicitation_identifier, is_synthetic_solicitation, program_acronym, organization_id, organization, vehicle_type, description, idv_count, holder_count, order_winner_count, awardee_count, order_count, total_obligated, vehicle_obligations, vehicle_contracts_value, latest_award_date, solicitation_title, solicitation_date | diff --git a/scripts/check_filter_shape_conformance.py b/scripts/check_filter_shape_conformance.py index a440414..dfcfdc9 100644 --- a/scripts/check_filter_shape_conformance.py +++ b/scripts/check_filter_shape_conformance.py @@ -80,6 +80,10 @@ "contract_appeals": "list_contract_appeals", "federal_register": "list_federal_register_documents", "ebuy/requests": "list_ebuy_requests", + "assistance/grants": "list_assistance_grants", + "assistance/loans": "list_assistance_loans", + "assistance/direct_payments": "list_assistance_direct_payments", + "assistance/other": "list_assistance_other", "offices": "list_offices", "psc": "list_psc", "mas_sins": "list_mas_sins", @@ -162,6 +166,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]: IDV, OTA, OTIDV, + AssistanceAward, BudgetAccount, Contract, Entity, @@ -212,6 +217,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]: ITDashboardInvestment, ), ("BUDGET_ACCOUNTS_MINIMAL", ShapeConfig.BUDGET_ACCOUNTS_MINIMAL, BudgetAccount), + ("ASSISTANCE_AWARDS_MINIMAL", ShapeConfig.ASSISTANCE_AWARDS_MINIMAL, AssistanceAward), ] for name, shape_str, model_cls in configs: entries.append((name, shape_str, model_cls)) diff --git a/scripts/check_shape_coverage.py b/scripts/check_shape_coverage.py index 6c0e891..3b03ed6 100644 --- a/scripts/check_shape_coverage.py +++ b/scripts/check_shape_coverage.py @@ -71,6 +71,10 @@ "contract_appeals": "ContractAppeal", "federal_register": "FederalRegisterDocument", "ebuy/requests": "EbuyRequest", + "assistance/grants": "AssistanceAward", + "assistance/loans": "AssistanceAward", + "assistance/direct_payments": "AssistanceAward", + "assistance/other": "AssistanceAward", "offices": "Office", "assistance_listings": "AssistanceListing", "business_types": "BusinessType", diff --git a/tango/__init__.py b/tango/__init__.py index 62cd274..ef37945 100644 --- a/tango/__init__.py +++ b/tango/__init__.py @@ -10,6 +10,10 @@ TangoValidationError, ) from .models import ( + AssistanceAward, + AssistanceAwardListing, + AssistanceRecipient, + AssistanceTransaction, BudgetAccount, BudgetAccountAnomalySource, BudgetAccountAnomalySourceRow, @@ -74,6 +78,10 @@ "RateLimitInfo", "ResolveCandidate", "ResolveResult", + "AssistanceAward", + "AssistanceAwardListing", + "AssistanceRecipient", + "AssistanceTransaction", "BudgetAccount", "BudgetAccountAnomalySource", "BudgetAccountAnomalySourceRow", diff --git a/tango/client.py b/tango/client.py index bb576fc..ec01e97 100644 --- a/tango/client.py +++ b/tango/client.py @@ -22,6 +22,7 @@ OTA, OTIDV, Agency, + AssistanceAward, BudgetAccount, BusinessType, Contract, @@ -1577,6 +1578,660 @@ def get_subaward( data, shape, Subaward, flat, flat_lists, joiner=joiner ) + # ============================================================================ + # Financial-assistance awards (grants, loans, direct payments, other) + # ============================================================================ + + def _list_assistance_awards( + self, + segment: str, + *, + cursor: str | None, + limit: int, + shape: str | None, + flat: bool, + flat_lists: bool, + joiner: str, + exact: bool, + **filters: Any, + ) -> PaginatedResponse: + """Shared body of the four `list_assistance_*` methods, which differ only in the route.""" + params: dict[str, Any] = {"limit": min(limit, 100)} + if cursor: + params["cursor"] = cursor + if shape is None: + shape = ShapeConfig.ASSISTANCE_AWARDS_MINIMAL + if shape: + params["shape"] = shape + if flat: + params["flat"] = "true" + if joiner: + params["joiner"] = joiner + if flat_lists: + params["flat_lists"] = "true" + params.update({k: v for k, v in filters.items() if v is not None}) + if exact: + params["exact"] = "true" + + data = self._get(f"/api/assistance/{segment}/", params) + results = [ + self._parse_response_with_shape( + obj, shape, AssistanceAward, flat, flat_lists, joiner=joiner + ) + for obj in (data.get("results") or []) + ] + return PaginatedResponse( + count=int(data.get("count") or len(results)), + next=data.get("next"), + count_type=data.get(_COUNT_TYPE_KEY), + previous=data.get("previous"), + results=results, + cursor=data.get("cursor"), + meta=data.get("meta"), + ) + + def _get_assistance_award( + self, + segment: str, + key: str, + *, + shape: str | None, + flat: bool, + flat_lists: bool, + joiner: str, + ) -> Any: + if not key: + raise TangoValidationError("Award key is required") + params: dict[str, Any] = {} + if shape is None: + shape = ShapeConfig.ASSISTANCE_AWARDS_MINIMAL + if shape: + params["shape"] = shape + if flat: + params["flat"] = "true" + if joiner: + params["joiner"] = joiner + if flat_lists: + params["flat_lists"] = "true" + data = self._get(f"/api/assistance/{segment}/{quote(key, safe='')}/", params) + return self._parse_response_with_shape( + data, shape, AssistanceAward, flat, flat_lists, joiner=joiner + ) + + def _list_assistance_award_rows( + self, segment: str, key: str, sub_route: str, *, page: int, limit: int + ) -> PaginatedResponse[dict[str, Any]]: + """An award's `/transactions/` or `/listings/` page. These sub-routes take no shape, so rows come back as plain dicts.""" + if not key: + raise TangoValidationError("Award key is required") + data = self._get( + f"/api/assistance/{segment}/{quote(key, safe='')}/{sub_route}/", + {"page": page, "limit": limit}, + ) + results = data.get("results") or [] + return PaginatedResponse( + count=int(data.get("count") or len(results)), + next=data.get("next"), + count_type=data.get(_COUNT_TYPE_KEY), + previous=data.get("previous"), + results=results, + meta=data.get("meta"), + ) + + def list_assistance_grants( + self, + cursor: str | None = None, + limit: int = 25, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + key: str | None = None, + fain: str | None = None, + uri: str | None = None, + uei: str | None = None, + recipient: str | None = None, + search: str | None = None, + awarding_agency: str | None = None, + funding_agency: str | None = None, + cfda_number: str | None = None, + assistance_type: str | None = None, + record_type: str | None = None, + business_funds_indicator: str | None = None, + disaster_emergency_fund_code: str | None = None, + funding_opportunity_number: str | None = None, + award_date: str | None = None, + award_date_gte: str | None = None, + award_date_lte: str | None = None, + fiscal_year: int | None = None, + fiscal_year_gte: int | None = None, + fiscal_year_lte: int | None = None, + pop_start_date_gte: str | None = None, + pop_start_date_lte: str | None = None, + pop_end_date_gte: str | None = None, + pop_end_date_lte: str | None = None, + pop_state: str | None = None, + pop_country: str | None = None, + pop_zip: str | None = None, + obligated_gte: float | str | None = None, + obligated_lte: float | str | None = None, + ordering: str | None = None, + *, + exact: bool = False, + ) -> PaginatedResponse: + """ + List grant and cooperative-agreement awards (`/api/assistance/grants/`). + + Holds every award whose latest action's GSDM assistance type is ``F001`` or ``F002``. + Pagination is cursor-based: pass ``response.cursor`` back as ``cursor`` for the next page. + + Args: + cursor: Cursor token from a previous response's ``cursor``; omit for the first page + limit: Results per page (max 100) + shape: Response shape string (defaults to ``ShapeConfig.ASSISTANCE_AWARDS_MINIMAL``, the API's own default) + flat: If True, flatten nested objects in shaped response + flat_lists: If True, flatten arrays using indexed keys + joiner: Separator for flattened keys when ``flat`` is True + key: Award key (exact). OR several with ``|`` + fain: Federal Award Identification Number (exact). OR several with ``|`` + uri: Unique Record Identifier, carried by aggregate records (exact). OR several with ``|`` + uei: Recipient UEI (exact). OR several with ``|`` + recipient: Recipient name or UEI. Each term matches the recipients closest to it by name; a term under three characters returns 400. OR several with ``|`` + search: Full-text search over identifiers, recipient names, assistance listing titles and descriptions, web-search style + awarding_agency: Awarding agency (code, name or abbreviation). OR several with ``|`` + funding_agency: Funding agency or department, matched like ``awarding_agency``. OR several with ``|`` + cfda_number: The award's primary assistance listing (CFDA) number, e.g. ``93.778``. OR several with ``|`` + assistance_type: GSDM assistance type code (``F001``-``F010``), case-insensitive. OR several with ``|`` + record_type: ``1`` (aggregate), ``2`` (non-aggregate) or ``3`` (non-aggregate to an individual, PII-redacted). OR several with ``|`` + business_funds_indicator: ``NON`` (not Recovery Act) or ``REC`` (Recovery Act), case-insensitive. OR several with ``|`` + disaster_emergency_fund_code: Disaster Emergency Fund Code, e.g. ``L``, matched as a whole code within the award's list. OR several with ``|`` + funding_opportunity_number: The Grants.gov funding opportunity number the award was made under (exact). OR several with ``|`` + award_date: Award date (exact, YYYY-MM-DD) + award_date_gte: Award date on or after (YYYY-MM-DD) + award_date_lte: Award date on or before (YYYY-MM-DD) + fiscal_year: Federal fiscal year of the award (exact) + fiscal_year_gte: Fiscal year >= + fiscal_year_lte: Fiscal year <= + pop_start_date_gte: Period of performance start date on or after (YYYY-MM-DD) + pop_start_date_lte: Period of performance start date on or before (YYYY-MM-DD) + pop_end_date_gte: Period of performance current end date on or after (YYYY-MM-DD) + pop_end_date_lte: Period of performance current end date on or before (YYYY-MM-DD) + pop_state: Place of performance state code, e.g. ``VA``. OR several with ``|`` + pop_country: Place of performance country code, ISO 3166 alpha-3, e.g. ``USA``. OR several with ``|`` + pop_zip: Place of performance five-digit ZIP code. OR several with ``|`` + obligated_gte: Obligated amount >= (USD) + obligated_lte: Obligated amount <= (USD) + ordering: ``award_date``, ``obligated`` or ``pragmatic_obligations``; prefix ``-`` for descending. Defaults to ``-award_date`` + exact: If True, ask the API for an exact count. Past 10,000 matches a filtered list otherwise reports an estimate (``count_type == "approximate"``) + + Examples: + >>> grants = client.list_assistance_grants(cfda_number="93.778", ordering="-obligated") + >>> grants = client.list_assistance_grants(search="wildfire resilience", fiscal_year=2025) + """ + return self._list_assistance_awards( + "grants", + cursor=cursor, + limit=limit, + shape=shape, + flat=flat, + flat_lists=flat_lists, + joiner=joiner, + exact=exact, + key=key, + fain=fain, + uri=uri, + uei=uei, + recipient=recipient, + search=search, + awarding_agency=awarding_agency, + funding_agency=funding_agency, + cfda_number=cfda_number, + assistance_type=assistance_type, + record_type=record_type, + business_funds_indicator=business_funds_indicator, + disaster_emergency_fund_code=disaster_emergency_fund_code, + funding_opportunity_number=funding_opportunity_number, + award_date=award_date, + award_date_gte=award_date_gte, + award_date_lte=award_date_lte, + fiscal_year=fiscal_year, + fiscal_year_gte=fiscal_year_gte, + fiscal_year_lte=fiscal_year_lte, + pop_start_date_gte=pop_start_date_gte, + pop_start_date_lte=pop_start_date_lte, + pop_end_date_gte=pop_end_date_gte, + pop_end_date_lte=pop_end_date_lte, + pop_state=pop_state, + pop_country=pop_country, + pop_zip=pop_zip, + obligated_gte=obligated_gte, + obligated_lte=obligated_lte, + ordering=ordering, + ) + + def get_assistance_grant( + self, + key: str, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + ) -> Any: + """Get one grant or cooperative-agreement award by key (`/api/assistance/grants/{key}/`). + + A retired key (one in an award's ``former_award_keys``) returns the award that carries it now, with its current ``key``. + A key that names no award on this endpoint raises ``TangoNotFoundError``. + """ + return self._get_assistance_award( + "grants", key, shape=shape, flat=flat, flat_lists=flat_lists, joiner=joiner + ) + + def list_assistance_grant_transactions( + self, key: str, page: int = 1, limit: int = 100 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's transactions, latest first (`/api/assistance/grants/{key}/transactions/`). + + Page-number pagination, max ``limit`` 500. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "grants", key, "transactions", page=page, limit=min(limit, 500) + ) + + def list_assistance_grant_listings( + self, key: str, page: int = 1, limit: int = 10 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's money by assistance listing (CFDA), primary listing first (`/api/assistance/grants/{key}/listings/`). + + Page-number pagination, max ``limit`` 100. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "grants", key, "listings", page=page, limit=min(limit, 100) + ) + + def list_assistance_loans( + self, + cursor: str | None = None, + limit: int = 25, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + key: str | None = None, + fain: str | None = None, + uri: str | None = None, + uei: str | None = None, + recipient: str | None = None, + search: str | None = None, + awarding_agency: str | None = None, + funding_agency: str | None = None, + cfda_number: str | None = None, + assistance_type: str | None = None, + record_type: str | None = None, + business_funds_indicator: str | None = None, + disaster_emergency_fund_code: str | None = None, + funding_opportunity_number: str | None = None, + award_date: str | None = None, + award_date_gte: str | None = None, + award_date_lte: str | None = None, + fiscal_year: int | None = None, + fiscal_year_gte: int | None = None, + fiscal_year_lte: int | None = None, + pop_start_date_gte: str | None = None, + pop_start_date_lte: str | None = None, + pop_end_date_gte: str | None = None, + pop_end_date_lte: str | None = None, + pop_state: str | None = None, + pop_country: str | None = None, + pop_zip: str | None = None, + obligated_gte: float | str | None = None, + obligated_lte: float | str | None = None, + ordering: str | None = None, + *, + exact: bool = False, + ) -> PaginatedResponse: + """ + List direct loan and loan guarantee awards (`/api/assistance/loans/`). + + Holds every award whose latest action's GSDM assistance type is ``F003`` or ``F004``. + + Takes the same filters, shapes and cursor pagination as :meth:`list_assistance_grants`. + """ + return self._list_assistance_awards( + "loans", + cursor=cursor, + limit=limit, + shape=shape, + flat=flat, + flat_lists=flat_lists, + joiner=joiner, + exact=exact, + key=key, + fain=fain, + uri=uri, + uei=uei, + recipient=recipient, + search=search, + awarding_agency=awarding_agency, + funding_agency=funding_agency, + cfda_number=cfda_number, + assistance_type=assistance_type, + record_type=record_type, + business_funds_indicator=business_funds_indicator, + disaster_emergency_fund_code=disaster_emergency_fund_code, + funding_opportunity_number=funding_opportunity_number, + award_date=award_date, + award_date_gte=award_date_gte, + award_date_lte=award_date_lte, + fiscal_year=fiscal_year, + fiscal_year_gte=fiscal_year_gte, + fiscal_year_lte=fiscal_year_lte, + pop_start_date_gte=pop_start_date_gte, + pop_start_date_lte=pop_start_date_lte, + pop_end_date_gte=pop_end_date_gte, + pop_end_date_lte=pop_end_date_lte, + pop_state=pop_state, + pop_country=pop_country, + pop_zip=pop_zip, + obligated_gte=obligated_gte, + obligated_lte=obligated_lte, + ordering=ordering, + ) + + def get_assistance_loan( + self, + key: str, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + ) -> Any: + """Get one direct loan or loan guarantee award by key (`/api/assistance/loans/{key}/`). + + A retired key (one in an award's ``former_award_keys``) returns the award that carries it now, with its current ``key``. + A key that names no award on this endpoint raises ``TangoNotFoundError``. + """ + return self._get_assistance_award( + "loans", key, shape=shape, flat=flat, flat_lists=flat_lists, joiner=joiner + ) + + def list_assistance_loan_transactions( + self, key: str, page: int = 1, limit: int = 100 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's transactions, latest first (`/api/assistance/loans/{key}/transactions/`). + + Page-number pagination, max ``limit`` 500. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "loans", key, "transactions", page=page, limit=min(limit, 500) + ) + + def list_assistance_loan_listings( + self, key: str, page: int = 1, limit: int = 10 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's money by assistance listing (CFDA), primary listing first (`/api/assistance/loans/{key}/listings/`). + + Page-number pagination, max ``limit`` 100. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "loans", key, "listings", page=page, limit=min(limit, 100) + ) + + def list_assistance_direct_payments( + self, + cursor: str | None = None, + limit: int = 25, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + key: str | None = None, + fain: str | None = None, + uri: str | None = None, + uei: str | None = None, + recipient: str | None = None, + search: str | None = None, + awarding_agency: str | None = None, + funding_agency: str | None = None, + cfda_number: str | None = None, + assistance_type: str | None = None, + record_type: str | None = None, + business_funds_indicator: str | None = None, + disaster_emergency_fund_code: str | None = None, + funding_opportunity_number: str | None = None, + award_date: str | None = None, + award_date_gte: str | None = None, + award_date_lte: str | None = None, + fiscal_year: int | None = None, + fiscal_year_gte: int | None = None, + fiscal_year_lte: int | None = None, + pop_start_date_gte: str | None = None, + pop_start_date_lte: str | None = None, + pop_end_date_gte: str | None = None, + pop_end_date_lte: str | None = None, + pop_state: str | None = None, + pop_country: str | None = None, + pop_zip: str | None = None, + obligated_gte: float | str | None = None, + obligated_lte: float | str | None = None, + ordering: str | None = None, + *, + exact: bool = False, + ) -> PaginatedResponse: + """ + List direct payment awards, for specified or unrestricted use (`/api/assistance/direct_payments/`). + + Holds every award whose latest action's GSDM assistance type is ``F006`` or ``F007``. + + Takes the same filters, shapes and cursor pagination as :meth:`list_assistance_grants`. + """ + return self._list_assistance_awards( + "direct_payments", + cursor=cursor, + limit=limit, + shape=shape, + flat=flat, + flat_lists=flat_lists, + joiner=joiner, + exact=exact, + key=key, + fain=fain, + uri=uri, + uei=uei, + recipient=recipient, + search=search, + awarding_agency=awarding_agency, + funding_agency=funding_agency, + cfda_number=cfda_number, + assistance_type=assistance_type, + record_type=record_type, + business_funds_indicator=business_funds_indicator, + disaster_emergency_fund_code=disaster_emergency_fund_code, + funding_opportunity_number=funding_opportunity_number, + award_date=award_date, + award_date_gte=award_date_gte, + award_date_lte=award_date_lte, + fiscal_year=fiscal_year, + fiscal_year_gte=fiscal_year_gte, + fiscal_year_lte=fiscal_year_lte, + pop_start_date_gte=pop_start_date_gte, + pop_start_date_lte=pop_start_date_lte, + pop_end_date_gte=pop_end_date_gte, + pop_end_date_lte=pop_end_date_lte, + pop_state=pop_state, + pop_country=pop_country, + pop_zip=pop_zip, + obligated_gte=obligated_gte, + obligated_lte=obligated_lte, + ordering=ordering, + ) + + def get_assistance_direct_payment( + self, + key: str, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + ) -> Any: + """Get one direct payment award by key (`/api/assistance/direct_payments/{key}/`). + + A retired key (one in an award's ``former_award_keys``) returns the award that carries it now, with its current ``key``. + A key that names no award on this endpoint raises ``TangoNotFoundError``. + """ + return self._get_assistance_award( + "direct_payments", key, shape=shape, flat=flat, flat_lists=flat_lists, joiner=joiner + ) + + def list_assistance_direct_payment_transactions( + self, key: str, page: int = 1, limit: int = 100 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's transactions, latest first (`/api/assistance/direct_payments/{key}/transactions/`). + + Page-number pagination, max ``limit`` 500. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "direct_payments", key, "transactions", page=page, limit=min(limit, 500) + ) + + def list_assistance_direct_payment_listings( + self, key: str, page: int = 1, limit: int = 10 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's money by assistance listing (CFDA), primary listing first (`/api/assistance/direct_payments/{key}/listings/`). + + Page-number pagination, max ``limit`` 100. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "direct_payments", key, "listings", page=page, limit=min(limit, 100) + ) + + def list_assistance_other( + self, + cursor: str | None = None, + limit: int = 25, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + key: str | None = None, + fain: str | None = None, + uri: str | None = None, + uei: str | None = None, + recipient: str | None = None, + search: str | None = None, + awarding_agency: str | None = None, + funding_agency: str | None = None, + cfda_number: str | None = None, + assistance_type: str | None = None, + record_type: str | None = None, + business_funds_indicator: str | None = None, + disaster_emergency_fund_code: str | None = None, + funding_opportunity_number: str | None = None, + award_date: str | None = None, + award_date_gte: str | None = None, + award_date_lte: str | None = None, + fiscal_year: int | None = None, + fiscal_year_gte: int | None = None, + fiscal_year_lte: int | None = None, + pop_start_date_gte: str | None = None, + pop_start_date_lte: str | None = None, + pop_end_date_gte: str | None = None, + pop_end_date_lte: str | None = None, + pop_state: str | None = None, + pop_country: str | None = None, + pop_zip: str | None = None, + obligated_gte: float | str | None = None, + obligated_lte: float | str | None = None, + ordering: str | None = None, + *, + exact: bool = False, + ) -> PaginatedResponse: + """ + List insurance, asset forfeiture, property sale and other financial-assistance awards (`/api/assistance/other/`). + + Holds every award whose latest action's GSDM assistance type is ``F005`` or ``F008``-``F010``. + + Takes the same filters, shapes and cursor pagination as :meth:`list_assistance_grants`. + """ + return self._list_assistance_awards( + "other", + cursor=cursor, + limit=limit, + shape=shape, + flat=flat, + flat_lists=flat_lists, + joiner=joiner, + exact=exact, + key=key, + fain=fain, + uri=uri, + uei=uei, + recipient=recipient, + search=search, + awarding_agency=awarding_agency, + funding_agency=funding_agency, + cfda_number=cfda_number, + assistance_type=assistance_type, + record_type=record_type, + business_funds_indicator=business_funds_indicator, + disaster_emergency_fund_code=disaster_emergency_fund_code, + funding_opportunity_number=funding_opportunity_number, + award_date=award_date, + award_date_gte=award_date_gte, + award_date_lte=award_date_lte, + fiscal_year=fiscal_year, + fiscal_year_gte=fiscal_year_gte, + fiscal_year_lte=fiscal_year_lte, + pop_start_date_gte=pop_start_date_gte, + pop_start_date_lte=pop_start_date_lte, + pop_end_date_gte=pop_end_date_gte, + pop_end_date_lte=pop_end_date_lte, + pop_state=pop_state, + pop_country=pop_country, + pop_zip=pop_zip, + obligated_gte=obligated_gte, + obligated_lte=obligated_lte, + ordering=ordering, + ) + + def get_assistance_other( + self, + key: str, + shape: str | None = None, + flat: bool = False, + flat_lists: bool = False, + joiner: str = ".", + ) -> Any: + """Get one award of other financial assistance by key (`/api/assistance/other/{key}/`). + + A retired key (one in an award's ``former_award_keys``) returns the award that carries it now, with its current ``key``. + A key that names no award on this endpoint raises ``TangoNotFoundError``. + """ + return self._get_assistance_award( + "other", key, shape=shape, flat=flat, flat_lists=flat_lists, joiner=joiner + ) + + def list_assistance_other_transactions( + self, key: str, page: int = 1, limit: int = 100 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's transactions, latest first (`/api/assistance/other/{key}/transactions/`). + + Page-number pagination, max ``limit`` 500. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "other", key, "transactions", page=page, limit=min(limit, 500) + ) + + def list_assistance_other_listings( + self, key: str, page: int = 1, limit: int = 10 + ) -> PaginatedResponse[dict[str, Any]]: + """List an award's money by assistance listing (CFDA), primary listing first (`/api/assistance/other/{key}/listings/`). + + Page-number pagination, max ``limit`` 100. Accepts a retired key; a key that names no award returns an empty page. + """ + return self._list_assistance_award_rows( + "other", key, "listings", page=page, limit=min(limit, 100) + ) + # ============================================================================ # GSA eLibrary Contracts # ============================================================================ diff --git a/tango/models.py b/tango/models.py index 47ea496..8a37cc4 100644 --- a/tango/models.py +++ b/tango/models.py @@ -1372,6 +1372,140 @@ class FederalRegisterDocument: full_text: str | None = None +class AssistanceRecipient: + """Schema definition for AssistanceRecipient (not used for instances). + + An assistance award's ``recipient``: Tango's shared recipient record, shaped like an entity. + Every assistance recipient has one, including the many with no UEI; for a recipient with a UEI, ``uuid`` is the entity's own UUID. + ``business_types`` is a list of ``{code, description}`` in USAspending's recipient-type codes, not SAM.gov's. + """ + + uuid: str | None = None + uei: str | None = None + duns: str | None = None + legal_business_name: str | None = None + legal_business_name_raw: str | None = None + county: str | None = None + congressional_district: str | None = None + congressional_district_original: str | None = None + highest_owner: dict[str, Any] | None = None + business_types: list[dict[str, Any]] | None = None + + +class AssistanceTransaction: + """Schema definition for AssistanceTransaction (not used for instances). + + One action on an assistance award: an entry of the ``transactions`` expansion, or a row of the ``/transactions/`` sub-route. + """ + + key: str | None = None + modification_number: str | None = None + transaction_date: date | None = None + action_type: str | None = None + assistance_type_code: str | None = None + correction_delete_indicator: str | None = None + cfda_number: str | None = None + obligated: Decimal | None = None + pragmatic_obligations: Decimal | None = None + non_federal_funding_amount: Decimal | None = None + indirect_cost_federal_share_amount: Decimal | None = None + face_value_of_loan: Decimal | None = None + loan_subsidy_cost: Decimal | None = None + award_amount_basis: str | None = None + award_recipient_basis: str | None = None + action_date_fiscal_year: int | None = None + last_modified_date: datetime | None = None + initial_report_date: datetime | None = None + + +class AssistanceAwardListing: + """Schema definition for AssistanceAwardListing (not used for instances). + + An assistance award's money under one assistance listing (CFDA) program: an entry of the ``listings`` expansion, or a row of the ``/listings/`` sub-route. + """ + + cfda_number: str | None = None + is_primary: bool | None = None + obligated: Decimal | None = None + pragmatic_obligations: Decimal | None = None + non_federal_funding_amount: Decimal | None = None + indirect_cost_federal_share_amount: Decimal | None = None + face_value_of_loan: Decimal | None = None + loan_subsidy_cost: Decimal | None = None + transactions_count: int | None = None + first_action_date: date | None = None + last_action_date: date | None = None + + +class AssistanceAward: + """Schema definition for AssistanceAward (not used for instances). + + Financial-assistance awards from USAspending at ``/api/assistance/grants/``, ``/api/assistance/loans/``, ``/api/assistance/direct_payments/`` and ``/api/assistance/other/``. + The four endpoints serve the same fields; an award lives under the one its latest action's assistance type belongs to. + Shape-on-demand, so every field is optional. + + ``key`` is the award's identifier. A retired key (listed in ``former_award_keys``) still resolves to the award that carries it now. + To compare dollars across the four endpoints, read ``pragmatic_obligations``: the obligation for grants and payments, the subsidy cost for loans. + ``recipient`` is ``None`` on aggregate and PII-redacted records (``record_type`` ``1`` and ``3``). + """ + + key: str | None = None + fain: str | None = None + uri: str | None = None + award_date: date | None = None + last_action_date: date | None = None + last_modified_date: datetime | None = None + fiscal_year: int | None = None + assistance_type: dict[str, Any] | None = None + record_type: str | None = None + cfda_number: str | None = None + assistance_listing: dict[str, Any] | None = None + title: str | None = None + summary: str | None = None + purposes_differ: bool | None = None + obligated: Decimal | None = None + pragmatic_obligations: Decimal | None = None + non_federal_funding_amount: Decimal | None = None + indirect_cost_federal_share_amount: Decimal | None = None + face_value_of_loan: Decimal | None = None + loan_subsidy_cost: Decimal | None = None + insurance_coverage_amount: Decimal | None = None + award_total_obligated: Decimal | None = None + award_total_outlayed: Decimal | None = None + award_total_non_federal_funding: Decimal | None = None + award_total_face_value_of_loan: Decimal | None = None + award_total_loan_subsidy_cost: Decimal | None = None + covid19_obligated: Decimal | None = None + covid19_outlayed: Decimal | None = None + iija_obligated: Decimal | None = None + iija_outlayed: Decimal | None = None + transactions_count: int | None = None + listings_count: int | None = None + assistance_types_count: int | None = None + award_amount_basis: str | None = None + award_recipient_basis: str | None = None + business_funds_indicator: str | None = None + disaster_emergency_fund_codes: str | None = None + funding_opportunity_number: str | None = None + funding_opportunity_goals_text: str | None = None + sai_number: str | None = None + former_award_keys: list[str] | None = None + treasury_accounts_funding_this_award: str | None = None + federal_accounts_funding_this_award: str | None = None + object_classes_funding_this_award: str | None = None + program_activities_funding_this_award: str | None = None + awarding_office: OrganizationOfficePayload | None = None + funding_office: OrganizationOfficePayload | None = None + recipient: AssistanceRecipient | None = None + recipient_address: dict[str, Any] | None = None + place_of_performance: dict[str, Any] | None = None + period_of_performance: dict[str, Any] | None = None + officers: dict[str, Any] | None = None + obligations_by_fiscal_year: list[FiscalYearObligation] | None = None + listings: list[AssistanceAwardListing] | None = None + transactions: list[AssistanceTransaction] | None = None + + class EbuyAttachment: """Schema definition for one entry in an eBuy request's ``attachments`` (not used for instances). @@ -1829,6 +1963,15 @@ class ShapeConfig: # Default for list_organizations() ORGANIZATIONS_MINIMAL: Final = "key,fh_key,name,level,type,short_name" + # Default for the list_assistance_* and get_assistance_* methods. Mirrors the API's own default, which list and detail share. + ASSISTANCE_AWARDS_MINIMAL: Final = ( + "key,fain,uri,award_date,fiscal_year,assistance_type(code,description)," + "record_type,cfda_number,assistance_listing(number,title),title," + "obligated,pragmatic_obligations,face_value_of_loan,loan_subsidy_cost," + "awarding_office(*),funding_office(*),recipient(*),place_of_performance(*)," + "period_of_performance(start_date,current_end_date)" + ) + # Default for list_otas() OTAS_MINIMAL: Final = ( "key,piid,award_date,recipient(display_name,uei),description,total_contract_value,obligated" diff --git a/tango/shapes/explicit_schemas.py b/tango/shapes/explicit_schemas.py index 56a79d7..c0ee8fd 100644 --- a/tango/shapes/explicit_schemas.py +++ b/tango/shapes/explicit_schemas.py @@ -1722,6 +1722,353 @@ ), } +# ============================================================================ +# FINANCIAL ASSISTANCE AWARDS +# ============================================================================ + +ASSISTANCE_RECIPIENT_SCHEMA: dict[str, FieldSchema] = { + "uuid": FieldSchema(name="uuid", type=str, is_optional=True, is_list=False), + "uei": FieldSchema(name="uei", type=str, is_optional=True, is_list=False), + "duns": FieldSchema(name="duns", type=str, is_optional=True, is_list=False), + "legal_business_name": FieldSchema( + name="legal_business_name", type=str, is_optional=True, is_list=False + ), + "legal_business_name_raw": FieldSchema( + name="legal_business_name_raw", type=str, is_optional=True, is_list=False + ), + "county": FieldSchema(name="county", type=str, is_optional=True, is_list=False), + "congressional_district": FieldSchema( + name="congressional_district", type=str, is_optional=True, is_list=False + ), + "congressional_district_original": FieldSchema( + name="congressional_district_original", type=str, is_optional=True, is_list=False + ), + "highest_owner": FieldSchema(name="highest_owner", type=dict, is_optional=True, is_list=False), + "business_types": FieldSchema(name="business_types", type=dict, is_optional=True, is_list=True), +} + +ASSISTANCE_RECIPIENT_ADDRESS_SCHEMA: dict[str, FieldSchema] = { + "address_line1": FieldSchema(name="address_line1", type=str, is_optional=True, is_list=False), + "address_line2": FieldSchema(name="address_line2", type=str, is_optional=True, is_list=False), + "city": FieldSchema(name="city", type=str, is_optional=True, is_list=False), + "state_or_province_code": FieldSchema( + name="state_or_province_code", type=str, is_optional=True, is_list=False + ), + "zip_code": FieldSchema(name="zip_code", type=str, is_optional=True, is_list=False), + "zip_code_plus4": FieldSchema(name="zip_code_plus4", type=str, is_optional=True, is_list=False), + "country_code": FieldSchema(name="country_code", type=str, is_optional=True, is_list=False), + "county_fips": FieldSchema(name="county_fips", type=str, is_optional=True, is_list=False), + "state_fips": FieldSchema(name="state_fips", type=str, is_optional=True, is_list=False), + "foreign_city_name": FieldSchema( + name="foreign_city_name", type=str, is_optional=True, is_list=False + ), + "foreign_province_name": FieldSchema( + name="foreign_province_name", type=str, is_optional=True, is_list=False + ), + "foreign_postal_code": FieldSchema( + name="foreign_postal_code", type=str, is_optional=True, is_list=False + ), + "congressional_district_original": FieldSchema( + name="congressional_district_original", type=str, is_optional=True, is_list=False + ), + "congressional_district_current": FieldSchema( + name="congressional_district_current", type=str, is_optional=True, is_list=False + ), +} + +ASSISTANCE_PLACE_OF_PERFORMANCE_SCHEMA: dict[str, FieldSchema] = { + "country": FieldSchema(name="country", type=str, is_optional=True, is_list=False), + "state_code": FieldSchema(name="state_code", type=str, is_optional=True, is_list=False), + "state_name": FieldSchema(name="state_name", type=str, is_optional=True, is_list=False), + "state_fips": FieldSchema(name="state_fips", type=str, is_optional=True, is_list=False), + "county_fips": FieldSchema(name="county_fips", type=str, is_optional=True, is_list=False), + "county_name": FieldSchema(name="county_name", type=str, is_optional=True, is_list=False), + "city_name": FieldSchema(name="city_name", type=str, is_optional=True, is_list=False), + "zip_code": FieldSchema(name="zip_code", type=str, is_optional=True, is_list=False), + "zip_plus_four": FieldSchema(name="zip_plus_four", type=str, is_optional=True, is_list=False), + "code": FieldSchema(name="code", type=str, is_optional=True, is_list=False), + "scope": FieldSchema(name="scope", type=str, is_optional=True, is_list=False), + "foreign_location": FieldSchema( + name="foreign_location", type=str, is_optional=True, is_list=False + ), +} + +ASSISTANCE_PERIOD_OF_PERFORMANCE_SCHEMA: dict[str, FieldSchema] = { + "start_date": FieldSchema(name="start_date", type=date, is_optional=True, is_list=False), + "current_end_date": FieldSchema( + name="current_end_date", type=date, is_optional=True, is_list=False + ), +} + +ASSISTANCE_AWARD_LISTING_SCHEMA: dict[str, FieldSchema] = { + "cfda_number": FieldSchema(name="cfda_number", type=str, is_optional=True, is_list=False), + "is_primary": FieldSchema(name="is_primary", type=bool, is_optional=True, is_list=False), + "obligated": FieldSchema(name="obligated", type=Decimal, is_optional=True, is_list=False), + "pragmatic_obligations": FieldSchema( + name="pragmatic_obligations", type=Decimal, is_optional=True, is_list=False + ), + "non_federal_funding_amount": FieldSchema( + name="non_federal_funding_amount", type=Decimal, is_optional=True, is_list=False + ), + "indirect_cost_federal_share_amount": FieldSchema( + name="indirect_cost_federal_share_amount", type=Decimal, is_optional=True, is_list=False + ), + "face_value_of_loan": FieldSchema( + name="face_value_of_loan", type=Decimal, is_optional=True, is_list=False + ), + "loan_subsidy_cost": FieldSchema( + name="loan_subsidy_cost", type=Decimal, is_optional=True, is_list=False + ), + "transactions_count": FieldSchema( + name="transactions_count", type=int, is_optional=True, is_list=False + ), + "first_action_date": FieldSchema( + name="first_action_date", type=date, is_optional=True, is_list=False + ), + "last_action_date": FieldSchema( + name="last_action_date", type=date, is_optional=True, is_list=False + ), +} + +ASSISTANCE_TRANSACTION_SCHEMA: dict[str, FieldSchema] = { + "key": FieldSchema(name="key", type=str, is_optional=True, is_list=False), + "modification_number": FieldSchema( + name="modification_number", type=str, is_optional=True, is_list=False + ), + "transaction_date": FieldSchema( + name="transaction_date", type=date, is_optional=True, is_list=False + ), + "action_type": FieldSchema(name="action_type", type=str, is_optional=True, is_list=False), + "assistance_type_code": FieldSchema( + name="assistance_type_code", type=str, is_optional=True, is_list=False + ), + "correction_delete_indicator": FieldSchema( + name="correction_delete_indicator", type=str, is_optional=True, is_list=False + ), + "cfda_number": FieldSchema(name="cfda_number", type=str, is_optional=True, is_list=False), + "obligated": FieldSchema(name="obligated", type=Decimal, is_optional=True, is_list=False), + "pragmatic_obligations": FieldSchema( + name="pragmatic_obligations", type=Decimal, is_optional=True, is_list=False + ), + "non_federal_funding_amount": FieldSchema( + name="non_federal_funding_amount", type=Decimal, is_optional=True, is_list=False + ), + "indirect_cost_federal_share_amount": FieldSchema( + name="indirect_cost_federal_share_amount", type=Decimal, is_optional=True, is_list=False + ), + "face_value_of_loan": FieldSchema( + name="face_value_of_loan", type=Decimal, is_optional=True, is_list=False + ), + "loan_subsidy_cost": FieldSchema( + name="loan_subsidy_cost", type=Decimal, is_optional=True, is_list=False + ), + "award_amount_basis": FieldSchema( + name="award_amount_basis", type=str, is_optional=True, is_list=False + ), + "award_recipient_basis": FieldSchema( + name="award_recipient_basis", type=str, is_optional=True, is_list=False + ), + "action_date_fiscal_year": FieldSchema( + name="action_date_fiscal_year", type=int, is_optional=True, is_list=False + ), + "last_modified_date": FieldSchema( + name="last_modified_date", type=datetime, is_optional=True, is_list=False + ), + "initial_report_date": FieldSchema( + name="initial_report_date", type=datetime, is_optional=True, is_list=False + ), +} + +# One schema for all four `/api/assistance/{grants,loans,direct_payments,other}/` endpoints, which share their fields and shapes. +ASSISTANCE_AWARD_SCHEMA: dict[str, FieldSchema] = { + "key": FieldSchema(name="key", type=str, is_optional=True, is_list=False), + "fain": FieldSchema(name="fain", type=str, is_optional=True, is_list=False), + "uri": FieldSchema(name="uri", type=str, is_optional=True, is_list=False), + "award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False), + "last_action_date": FieldSchema( + name="last_action_date", type=date, is_optional=True, is_list=False + ), + "last_modified_date": FieldSchema( + name="last_modified_date", type=datetime, is_optional=True, is_list=False + ), + "fiscal_year": FieldSchema(name="fiscal_year", type=int, is_optional=True, is_list=False), + "assistance_type": FieldSchema( + name="assistance_type", + type=dict, + is_optional=True, + is_list=False, + nested_model="CodeDescription", + ), + "record_type": FieldSchema(name="record_type", type=str, is_optional=True, is_list=False), + "cfda_number": FieldSchema(name="cfda_number", type=str, is_optional=True, is_list=False), + "title": FieldSchema(name="title", type=str, is_optional=True, is_list=False), + "summary": FieldSchema(name="summary", type=str, is_optional=True, is_list=False), + "purposes_differ": FieldSchema( + name="purposes_differ", type=bool, is_optional=True, is_list=False + ), + "obligated": FieldSchema(name="obligated", type=Decimal, is_optional=True, is_list=False), + "pragmatic_obligations": FieldSchema( + name="pragmatic_obligations", type=Decimal, is_optional=True, is_list=False + ), + "non_federal_funding_amount": FieldSchema( + name="non_federal_funding_amount", type=Decimal, is_optional=True, is_list=False + ), + "indirect_cost_federal_share_amount": FieldSchema( + name="indirect_cost_federal_share_amount", type=Decimal, is_optional=True, is_list=False + ), + "face_value_of_loan": FieldSchema( + name="face_value_of_loan", type=Decimal, is_optional=True, is_list=False + ), + "loan_subsidy_cost": FieldSchema( + name="loan_subsidy_cost", type=Decimal, is_optional=True, is_list=False + ), + "insurance_coverage_amount": FieldSchema( + name="insurance_coverage_amount", type=Decimal, is_optional=True, is_list=False + ), + "award_total_obligated": FieldSchema( + name="award_total_obligated", type=Decimal, is_optional=True, is_list=False + ), + "award_total_outlayed": FieldSchema( + name="award_total_outlayed", type=Decimal, is_optional=True, is_list=False + ), + "award_total_non_federal_funding": FieldSchema( + name="award_total_non_federal_funding", type=Decimal, is_optional=True, is_list=False + ), + "award_total_face_value_of_loan": FieldSchema( + name="award_total_face_value_of_loan", type=Decimal, is_optional=True, is_list=False + ), + "award_total_loan_subsidy_cost": FieldSchema( + name="award_total_loan_subsidy_cost", type=Decimal, is_optional=True, is_list=False + ), + "covid19_obligated": FieldSchema( + name="covid19_obligated", type=Decimal, is_optional=True, is_list=False + ), + "covid19_outlayed": FieldSchema( + name="covid19_outlayed", type=Decimal, is_optional=True, is_list=False + ), + "iija_obligated": FieldSchema( + name="iija_obligated", type=Decimal, is_optional=True, is_list=False + ), + "iija_outlayed": FieldSchema( + name="iija_outlayed", type=Decimal, is_optional=True, is_list=False + ), + "transactions_count": FieldSchema( + name="transactions_count", type=int, is_optional=True, is_list=False + ), + "listings_count": FieldSchema(name="listings_count", type=int, is_optional=True, is_list=False), + "assistance_types_count": FieldSchema( + name="assistance_types_count", type=int, is_optional=True, is_list=False + ), + "award_amount_basis": FieldSchema( + name="award_amount_basis", type=str, is_optional=True, is_list=False + ), + "award_recipient_basis": FieldSchema( + name="award_recipient_basis", type=str, is_optional=True, is_list=False + ), + "business_funds_indicator": FieldSchema( + name="business_funds_indicator", type=str, is_optional=True, is_list=False + ), + "disaster_emergency_fund_codes": FieldSchema( + name="disaster_emergency_fund_codes", type=str, is_optional=True, is_list=False + ), + "funding_opportunity_number": FieldSchema( + name="funding_opportunity_number", type=str, is_optional=True, is_list=False + ), + "funding_opportunity_goals_text": FieldSchema( + name="funding_opportunity_goals_text", type=str, is_optional=True, is_list=False + ), + "sai_number": FieldSchema(name="sai_number", type=str, is_optional=True, is_list=False), + "former_award_keys": FieldSchema( + name="former_award_keys", type=str, is_optional=True, is_list=True + ), + "treasury_accounts_funding_this_award": FieldSchema( + name="treasury_accounts_funding_this_award", type=str, is_optional=True, is_list=False + ), + "federal_accounts_funding_this_award": FieldSchema( + name="federal_accounts_funding_this_award", type=str, is_optional=True, is_list=False + ), + "object_classes_funding_this_award": FieldSchema( + name="object_classes_funding_this_award", type=str, is_optional=True, is_list=False + ), + "program_activities_funding_this_award": FieldSchema( + name="program_activities_funding_this_award", type=str, is_optional=True, is_list=False + ), + "transactions": FieldSchema( + name="transactions", + type=dict, + is_optional=True, + is_list=True, + nested_model="AssistanceTransaction", + ), + "recipient": FieldSchema( + name="recipient", + type=dict, + is_optional=True, + is_list=False, + nested_model="AssistanceRecipient", + ), + "recipient_address": FieldSchema( + name="recipient_address", + type=dict, + is_optional=True, + is_list=False, + nested_model="AssistanceRecipientAddress", + ), + "place_of_performance": FieldSchema( + name="place_of_performance", + type=dict, + is_optional=True, + is_list=False, + nested_model="AssistancePlaceOfPerformance", + ), + "awarding_office": FieldSchema( + name="awarding_office", + type=dict, + is_optional=True, + is_list=False, + nested_model="OrganizationOffice", + ), + "funding_office": FieldSchema( + name="funding_office", + type=dict, + is_optional=True, + is_list=False, + nested_model="OrganizationOffice", + ), + "assistance_listing": FieldSchema( + name="assistance_listing", + type=dict, + is_optional=True, + is_list=False, + nested_model="CFDANumber", + ), + "period_of_performance": FieldSchema( + name="period_of_performance", + type=dict, + is_optional=True, + is_list=False, + nested_model="AssistancePeriodOfPerformance", + ), + "officers": FieldSchema( + name="officers", type=dict, is_optional=True, is_list=False, nested_model="Officers" + ), + "obligations_by_fiscal_year": FieldSchema( + name="obligations_by_fiscal_year", + type=dict, + is_optional=True, + is_list=True, + nested_model="FiscalYearObligation", + ), + "listings": FieldSchema( + name="listings", + type=dict, + is_optional=True, + is_list=True, + nested_model="AssistanceAwardListing", + ), +} + + # ============================================================================ # SCHEMA REGISTRY MAPPING # ============================================================================ @@ -1783,6 +2130,14 @@ "GsaElibraryIdvRef": GSA_ELIBRARY_IDV_REF_SCHEMA, # IT Dashboard "ITDashboardInvestment": ITDASHBOARD_INVESTMENT_SCHEMA, + # Financial assistance awards + "AssistanceAward": ASSISTANCE_AWARD_SCHEMA, + "AssistanceRecipient": ASSISTANCE_RECIPIENT_SCHEMA, + "AssistanceRecipientAddress": ASSISTANCE_RECIPIENT_ADDRESS_SCHEMA, + "AssistancePlaceOfPerformance": ASSISTANCE_PLACE_OF_PERFORMANCE_SCHEMA, + "AssistancePeriodOfPerformance": ASSISTANCE_PERIOD_OF_PERFORMANCE_SCHEMA, + "AssistanceAwardListing": ASSISTANCE_AWARD_LISTING_SCHEMA, + "AssistanceTransaction": ASSISTANCE_TRANSACTION_SCHEMA, } diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_filters_narrow_the_list b/tests/cassettes/TestAssistanceAwardsIntegration.test_filters_narrow_the_list new file mode 100644 index 0000000..11e2663 --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_filters_narrow_the_list @@ -0,0 +1,82 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/?limit=3&shape=key%2Ccfda_number%2Cfiscal_year%2Cobligated&cfda_number=93.778&fiscal_year=2026&ordering=-obligated + response: + body: + string: '{"count":185,"next":"https://tango.makegov.com/api/assistance/grants/?limit=3&shape=key%2Ccfda_number%2Cfiscal_year%2Cobligated&cfda_number=93.778&fiscal_year=2026&ordering=-obligated&cursor=WzM2OTE5MzA4NzcwLjAsICIxZGY0NzU2MC1lN2I3LTVmODEtODNjMS02NTBjNzEzNjNhNjYiXQ%3D%3D","previous":null,"cursor":"WzM2OTE5MzA4NzcwLjAsICIxZGY0NzU2MC1lN2I3LTVmODEtODNjMS02NTBjNzEzNjNhNjYiXQ==","previous_cursor":null,"results":[{"cfda_number":"93.778","fiscal_year":2026,"key":"ASST_NON_2605CA5MAP_075","obligated":110435264806.0},{"cfda_number":"93.778","fiscal_year":2026,"key":"ASST_NON_2605NY5MAP_075","obligated":60489792298.0},{"cfda_number":"93.778","fiscal_year":2026,"key":"ASST_NON_2605TX5MAP_075","obligated":36919308770.0}]}' + headers: + CF-RAY: + - a511b2841d0a7d7a-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:52 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=J6h2vCVldnlLrWzZIP%2F5zZaZ90awtXQcZW1t74%2Fx22xQcZhQ%2BMLA60SoiTelzfD4tvwaCeYL0t76pINbnxYTZYm3DSB9RVPyLdJbRNmFhWpOBmhonmyxKC%2FPj3%2BAWZ62Ehw4"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '719' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.032s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '950' + x-ratelimit-burst-reset: + - '28' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999630' + x-ratelimit-daily-reset: + - '41527' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '950' + x-ratelimit-reset: + - '28' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[direct_payments] b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[direct_payments] new file mode 100644 index 0000000..ecde0cb --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[direct_payments] @@ -0,0 +1,331 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/direct_payments/?limit=1&shape=key + response: + body: + string: '{"count":56739924,"next":"https://tango.makegov.com/api/assistance/direct_payments/?limit=1&shape=key&cursor=WyIyMDI2LTEwLTIyIiwgImQxMjI3ZmYzLWY4ZDEtNWRjMy05MDE0LTI1NzJmMzhjY2MyNiJd","previous":null,"cursor":"WyIyMDI2LTEwLTIyIiwgImQxMjI3ZmYzLWY4ZDEtNWRjMy05MDE0LTI1NzJmMzhjY2MyNiJd","previous_cursor":null,"results":[{"key":"ASST_NON_12CCCEAUCEU5253218_012"}]}' + headers: + CF-RAY: + - a511b280496f5cb9-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=V7UNcge%2B9YeqILrZ75SrUsNp9H%2BIwm5QpRmTCL6kbLGX4XyDTOFUO9AiFtWCwT5AJWQE6iHgk0NyztgRnSd65mqPq58Iq5TV60ZQRuZiuOavW4ZcUvx5X2emo8N3rSPNNBsI"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '360' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.025s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '958' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999638' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '958' + x-ratelimit-reset: + - '29' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/direct_payments/ASST_NON_12CCCEAUCEU5253218_012/?shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"assistance_listing":{"number":"10.051","title":"Commodity Loans and + Loan Deficiency Payments"},"assistance_type":{"code":"F006","description":"Direct + Payment for Specified Use"},"award_date":"2026-10-22","awarding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"cfda_number":"10.051","face_value_of_loan":0.0,"fain":"12CCCEAUCEU5253218","fiscal_year":2027,"funding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"key":"ASST_NON_12CCCEAUCEU5253218_012","loan_subsidy_cost":0.0,"obligated":45412.85,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"HICKORY","code":"NC31065","country":"USA","county_fips":"37035","county_name":"CATAWBA","foreign_location":null,"scope":"SINGLE + ZIP CODE","state_code":"NC","state_fips":"37","state_name":"NORTH CAROLINA","zip_code":"28602","zip_plus_four":"1433"},"pragmatic_obligations":45412.85,"recipient":{"uuid":"b2aca74d-a067-53df-b21e-fed4492021ab","uei":"NBY3ULCLKC27","duns":null,"legal_business_name":"SHUFORD + YARNS LLC","legal_business_name_raw":"SHUFORD YARNS LLC","county":"CATAWBA","congressional_district":"NC-10","congressional_district_original":"NC-10","highest_owner":null,"business_types":[{"code":"X","description":"Other"}]},"record_type":"2","title":"ECONOMIC + ADJUSTMENT ASSISTANCE TO USERS OF UPLAND COTTON","uri":null}' + headers: + CF-RAY: + - a511b280cc810ed5-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=JPqPR3XidYNo6a7WFDNR3TNIWxkII3dzRzbf06CCTypqll14F6Bt%2F8VuYoUBbX4%2BpkFPX%2FjPoLdOw02bG0YkjE28VYVz%2B6uIcB8N42%2BlG3uZtV3zI8KD0%2B2iRRp6pfhbGNm%2F"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '1774' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.021s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '957' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999637' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '957' + x-ratelimit-reset: + - '29' + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/direct_payments/ASST_NON_12CCCEAUCEU5253218_012/transactions/?page=1&limit=5 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"key":"12D2_12CCCEAUCEU5253218_-NONE-_10.051_-NONE-","modification_number":null,"transaction_date":"2026-10-22","action_type":"A1","assistance_type_code":"F006","correction_delete_indicator":null,"cfda_number":"10.051","obligated":45412.85,"pragmatic_obligations":45412.85,"non_federal_funding_amount":null,"indirect_cost_federal_share_amount":0.0,"face_value_of_loan":0.0,"loan_subsidy_cost":0.0,"award_amount_basis":null,"award_recipient_basis":null,"action_date_fiscal_year":2027,"last_modified_date":"2026-10-23T14:19:37Z","initial_report_date":"2026-10-23T14:19:37Z"}]}' + headers: + CF-RAY: + - a511b2813fcff793-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=LxhHkf9B2ytuaALnBSfEnoXaqiRoUziJXmJtM77k0AZGkaZOle0EWnbaM7FAtKFsDdA%2F33i%2Fggwy24yk7sGIp1y4hjOejlR3Tth5phijpZ5KOVX%2BFBbvcti7Pb9quCxYaz22"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '625' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.020s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '956' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999636' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '956' + x-ratelimit-reset: + - '29' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/direct_payments/ASST_NON_12CCCEAUCEU5253218_012/listings/?page=1&limit=10 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"cfda_number":"10.051","is_primary":true,"obligated":45412.85,"pragmatic_obligations":45412.85,"non_federal_funding_amount":null,"indirect_cost_federal_share_amount":0.0,"face_value_of_loan":0.0,"loan_subsidy_cost":0.0,"transactions_count":1,"first_action_date":"2026-10-22","last_action_date":"2026-10-22"}]}' + headers: + CF-RAY: + - a511b281b88840ae-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:52 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=NoDHn2UCorIZgCvRGJnVgLHrZ8AhUqxwjDyrNkrTNsjIMqvKMHjiL0pFIkg3%2FY7VQU%2FrJqC536ZVLMaXaOECGKwFEXifXF%2FcdhMw2AH4gp8aDBY9Vcn1kM6HyZQ3Prt7kf4d"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '360' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.020s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '955' + x-ratelimit-burst-reset: + - '28' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999635' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '955' + x-ratelimit-reset: + - '28' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[grants] b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[grants] new file mode 100644 index 0000000..a05b3ac --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[grants] @@ -0,0 +1,329 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/?limit=1&shape=key + response: + body: + string: '{"count":5759909,"next":"https://tango.makegov.com/api/assistance/grants/?limit=1&shape=key&cursor=WyIyMDI2LTEwLTE5IiwgIjhjNGZlOWNkLTliNDgtNWEwYS05OTYzLTFlZWIxYzQyYzMyMCJd","previous":null,"cursor":"WyIyMDI2LTEwLTE5IiwgIjhjNGZlOWNkLTliNDgtNWEwYS05OTYzLTFlZWIxYzQyYzMyMCJd","previous_cursor":null,"results":[{"key":"ASST_NON_693JF72640002_069"}]}' + headers: + CF-RAY: + - a511b27c098143b6-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=8P1ptuwcBa6THxyLD%2BtNzVNDvY913uI2qe5tDD7zfXX0R5R1UqsGXyU1%2Fj563dKVcoCFaDNQLkvZBl4vrpWnt%2BoCrIR%2F0sHSf5ErtMGKNwDQib4R52YpKE%2B519u%2F5cusxa48"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '345' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.022s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '966' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999646' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '966' + x-ratelimit-reset: + - '29' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/ASST_NON_693JF72640002_069/?shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: "{\"assistance_listing\":{\"number\":\"20.823\",\"title\":\"Port Infrastructure + Development Program\"},\"assistance_type\":{\"code\":\"F001\",\"description\":\"Grant\"},\"award_date\":\"2026-10-19\",\"awarding_office\":{\"organization_id\":\"261f885d-c8b6-5e0d-b2f8-e0799a4e1de2\",\"office_code\":\"69A334\",\"office_name\":\"MARITIME + ADMINISTRATION\",\"agency_code\":\"6938\",\"agency_name\":\"MARITIME ADMINISTRATION\",\"department_code\":\"069\",\"department_name\":\"TRANSPORTATION, + DEPARTMENT OF\"},\"cfda_number\":\"20.823\",\"face_value_of_loan\":0.0,\"fain\":\"693JF72640002\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"261f885d-c8b6-5e0d-b2f8-e0799a4e1de2\",\"office_code\":\"69A334\",\"office_name\":\"MARITIME + ADMINISTRATION\",\"agency_code\":\"6938\",\"agency_name\":\"MARITIME ADMINISTRATION\",\"department_code\":\"069\",\"department_name\":\"TRANSPORTATION, + DEPARTMENT OF\"},\"key\":\"ASST_NON_693JF72640002_069\",\"loan_subsidy_cost\":0.0,\"obligated\":2000000.0,\"period_of_performance\":{\"start_date\":\"2026-10-19\",\"current_end_date\":\"2029-04-30\"},\"place_of_performance\":{\"city_name\":\"MOBILE\",\"code\":\"AL36633\",\"country\":\"USA\",\"county_fips\":\"01097\",\"county_name\":\"MOBILE\",\"foreign_location\":null,\"scope\":\"SINGLE + ZIP CODE\",\"state_code\":\"AL\",\"state_fips\":\"01\",\"state_name\":\"ALABAMA\",\"zip_code\":\"36633\",\"zip_plus_four\":\"1588\"},\"pragmatic_obligations\":2000000.0,\"recipient\":{\"uuid\":\"88f47661-874f-5298-8b0b-0971be0c746d\",\"uei\":\"HS97F2C9NME3\",\"duns\":null,\"legal_business_name\":\"ALABAMA + PORT AUTHORITY\",\"legal_business_name_raw\":\"ALABAMA PORT AUTHORITY\",\"county\":\"MOBILE\",\"congressional_district\":\"AL-02\",\"congressional_district_original\":\"AL-02\",\"highest_owner\":null,\"business_types\":[{\"code\":\"A\",\"description\":\"State + Government\"}]},\"record_type\":\"2\",\"title\":\"AWARD PURPOSE THE PROJECT + INCLUDES ELECTRICAL\u2026\",\"uri\":null}" + headers: + CF-RAY: + - a511b27c9db45f15-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=V%2FJ%2FzcxYLCdlcbN0zUaItTTIh76TC%2BM8j5xKTiOxtEg3Su%2FG18ncsjpfPpHf5fEGX0hBGALsOTtISdaX5MwmGXuoSy6FGPNKY%2Fif6M6BneuHXJQrwtQmcxQ4c61sgWRh88Oj"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '1745' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.026s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '965' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999645' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '965' + x-ratelimit-reset: + - '29' + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/ASST_NON_693JF72640002_069/transactions/?page=1&limit=5 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"key":"6938_693JF72640002_-NONE-_20.823_-NONE-","modification_number":null,"transaction_date":"2026-10-19","action_type":"A1","assistance_type_code":"F001","correction_delete_indicator":null,"cfda_number":"20.823","obligated":2000000.0,"pragmatic_obligations":2000000.0,"non_federal_funding_amount":500000.0,"indirect_cost_federal_share_amount":0.0,"face_value_of_loan":0.0,"loan_subsidy_cost":0.0,"award_amount_basis":"A01","award_recipient_basis":"R01","action_date_fiscal_year":2027,"last_modified_date":"2026-10-20T13:59:38Z","initial_report_date":"2026-10-20T13:59:38Z"}]}' + headers: + CF-RAY: + - a511b27d1c2c72a7-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ZcYqFbFUMx638sdRJsc2vxGPNSirKLOkCfdLBWGsCuDzy9E%2BMsPFu2lemx4ETz1vsj9gnInUUMMBhQvPGPXQJd7zi6%2BgEjZ82OEUqP2zeivR8KURNSbOVCKzC1KnALVlz19U"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '628' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.016s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '964' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999644' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '964' + x-ratelimit-reset: + - '29' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/ASST_NON_693JF72640002_069/listings/?page=1&limit=10 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"cfda_number":"20.823","is_primary":true,"obligated":2000000.0,"pragmatic_obligations":2000000.0,"non_federal_funding_amount":500000.0,"indirect_cost_federal_share_amount":0.0,"face_value_of_loan":0.0,"loan_subsidy_cost":0.0,"transactions_count":1,"first_action_date":"2026-10-19","last_action_date":"2026-10-19"}]}' + headers: + CF-RAY: + - a511b27db8fb433f-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=7OBnRnjXuvaJNw88w4N416saQ2S4ESv88iYVinHfygT4frLBfG5jPtk9Cr9xz72viGkjCqIael5uAt9cjlyvJp%2FhK49%2FBX9qq0rwONHT1KEz6RIsfppLVR5Anztpa8IzJBNo"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '366' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.020s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '963' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999643' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '963' + x-ratelimit-reset: + - '29' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[loans] b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[loans] new file mode 100644 index 0000000..3b00a34 --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[loans] @@ -0,0 +1,328 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/loans/?limit=1&shape=key + response: + body: + string: '{"count":29866842,"next":"https://tango.makegov.com/api/assistance/loans/?limit=1&shape=key&cursor=WyIyMDI2LTEwLTE1IiwgImZmZWEwYzQ3LThjN2QtNTlkNC05MzY4LTdjYzA0NjIzMzA3ZCJd","previous":null,"cursor":"WyIyMDI2LTEwLTE1IiwgImZmZWEwYzQ3LThjN2QtNTlkNC05MzY4LTdjYzA0NjIzMzA3ZCJd","previous_cursor":null,"results":[{"key":"ASST_NON_8606059100_073"}]}' + headers: + CF-RAY: + - a511b27e4ad87c10-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ImQ5nNEvCsY%2B9Rk6aAz6qPVuI%2FtUCcm4Gcn7wgiLJXPoONHSOCc22ZsFF6EVKWeWKmckIYK61hBwf09fPhu7ZxI6jwtfu8CI%2FOETin6zszpSwV6twCg7LvBokHfoK9t%2B3uRF"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '342' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.025s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '962' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999642' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '962' + x-ratelimit-reset: + - '29' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/loans/ASST_NON_8606059100_073/?shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"assistance_listing":{"number":"59.012","title":"7(a) Loan Guarantees"},"assistance_type":{"code":"F004","description":"Loan + Guarantee"},"award_date":"2026-10-15","awarding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"cfda_number":"59.012","face_value_of_loan":425000.0,"fain":"8606059100","fiscal_year":2027,"funding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"key":"ASST_NON_8606059100_073","loan_subsidy_cost":0.0,"obligated":0.0,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"","code":"GA**067","country":"USA","county_fips":"13067","county_name":"COBB","foreign_location":null,"scope":"COUNTY-WIDE","state_code":"GA","state_fips":"13","state_name":"GEORGIA","zip_code":"","zip_plus_four":""},"pragmatic_obligations":0.0,"recipient":{"uuid":"536c50dc-3456-5ff2-979d-0f898b8e85c9","uei":null,"duns":null,"legal_business_name":"SLEDGE + HAMMER CONSULTING CORP.","legal_business_name_raw":"SLEDGE HAMMER CONSULTING + CORP.","county":"COBB","congressional_district":"GA-11","congressional_district_original":"GA-11","highest_owner":null,"business_types":[{"code":"R","description":"Small + Business"}]},"record_type":"2","title":"SBA 7(a) Program Loans","uri":null}' + headers: + CF-RAY: + - a511b27ec9fa255d-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=MuLXenbZGeOHCPLLd0cKL%2Bn%2BQML7BN57emmgdaLbmr%2BgBbB7oeQMgl%2FG5Yedzd6Bu4d%2FZeZdS%2FzeU0tqNWUhxy6KIPCpvTKz5TDkYBs5Qq3lBFRL3N3NjAoYoaGOf7XrAlmk"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '1669' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.021s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '961' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999641' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '961' + x-ratelimit-reset: + - '29' + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/loans/ASST_NON_8606059100_073/transactions/?page=1&limit=5 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"key":"7300_8606059100_-NONE-_59.012_1","modification_number":"1","transaction_date":"2026-10-15","action_type":"A1","assistance_type_code":"F004","correction_delete_indicator":null,"cfda_number":"59.012","obligated":0.0,"pragmatic_obligations":0.0,"non_federal_funding_amount":null,"indirect_cost_federal_share_amount":null,"face_value_of_loan":425000.0,"loan_subsidy_cost":0.0,"award_amount_basis":null,"award_recipient_basis":null,"action_date_fiscal_year":2027,"last_modified_date":"2026-10-20T17:59:24Z","initial_report_date":"2026-10-20T17:59:24Z"}]}' + headers: + CF-RAY: + - a511b27f5915af77-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=f%2FJigXYTbGNbsmCJLTsDZgwmWN6DBt53BDf85ONAKqqN%2FG0oXulB5cEzDVmc%2B2Bsal7RUYocEnDQf4RKAfX2xEg0XlXJFwK1sLlkeYjsDr7LQeH%2BfElWUB04U4vCsYxY0cLK"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '607' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.019s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '960' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999640' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '960' + x-ratelimit-reset: + - '29' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/loans/ASST_NON_8606059100_073/listings/?page=1&limit=10 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"cfda_number":"59.012","is_primary":true,"obligated":0.0,"pragmatic_obligations":0.0,"non_federal_funding_amount":null,"indirect_cost_federal_share_amount":null,"face_value_of_loan":425000.0,"loan_subsidy_cost":0.0,"transactions_count":1,"first_action_date":"2026-10-15","last_action_date":"2026-10-15"}]}' + headers: + CF-RAY: + - a511b27fdfff6a59-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=mEUCXSR9VLf4G6u2UiLP0KYGMqnm6zNOrb6h7ko%2BO6w8TwKfDMD4W7RdX8ENIwnFGARWfROGbRaqe4bfoLf%2Bx4qM4YgxkLrBmvOqGebWNFf8vCD%2Bba%2BZ4vsMSUHieueKc2ZO"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '356' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.020s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '959' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999639' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '959' + x-ratelimit-reset: + - '29' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[other] b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[other] new file mode 100644 index 0000000..9e8c439 --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_get_and_sub_routes[other] @@ -0,0 +1,330 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/other/?limit=1&shape=key + response: + body: + string: '{"count":7637560,"next":"https://tango.makegov.com/api/assistance/other/?limit=1&shape=key&cursor=WyIyMDI2LTEwLTE1IiwgImVmYzZkZTcyLWU5ZGQtNTEwOC04ZDgzLTZkZTFjOWMyYzEzMiJd","previous":null,"cursor":"WyIyMDI2LTEwLTE1IiwgImVmYzZkZTcyLWU5ZGQtNTEwOC04ZDgzLTZkZTFjOWMyYzEzMiJd","previous_cursor":null,"results":[{"key":"ASST_NON_25FI11060100219_012"}]}' + headers: + CF-RAY: + - a511b2823c5214a8-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:52 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=w3sxqilYkR0xHEx3X%2BLeOR3sUopnm%2Bq3KlshT4ku90DZLYkbzjj0kXkxXYBfMp6wHrw8usjjofxXUwHNhpDC%2F6HkDB0hqLYoJMlRtMzCSZ5ucXoAeKgTDrooZEk6t0JVLu0P"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '346' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.021s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '954' + x-ratelimit-burst-reset: + - '28' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999634' + x-ratelimit-daily-reset: + - '41527' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '954' + x-ratelimit-reset: + - '28' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/other/ASST_NON_25FI11060100219_012/?shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: "{\"assistance_listing\":{\"number\":\"10.703\",\"title\":\"Cooperative + Fire Protection Agreement\"},\"assistance_type\":{\"code\":\"F010\",\"description\":\"Other + Financial Assistance\"},\"award_date\":\"2026-10-15\",\"awarding_office\":{\"organization_id\":\"091b577a-bf49-51b2-987e-8509d409bb10\",\"office_code\":\"1204GG\",\"office_name\":\"DESCHUTES + NATIONAL FOREST\",\"agency_code\":\"12C2\",\"agency_name\":\"FOREST SERVICE\",\"department_code\":\"012\",\"department_name\":\"AGRICULTURE, + DEPARTMENT OF\"},\"cfda_number\":\"10.703\",\"face_value_of_loan\":0.0,\"fain\":\"25FI11060100219\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"091b577a-bf49-51b2-987e-8509d409bb10\",\"office_code\":\"04GG\",\"office_name\":\"DESCHUTES + NATIONAL FOREST\",\"agency_code\":\"12C2\",\"agency_name\":\"FOREST SERVICE\",\"department_code\":\"012\",\"department_name\":\"AGRICULTURE, + DEPARTMENT OF\"},\"key\":\"ASST_NON_25FI11060100219_012\",\"loan_subsidy_cost\":0.0,\"obligated\":6949.33,\"period_of_performance\":{\"start_date\":\"2026-07-18\",\"current_end_date\":\"2026-07-19\"},\"place_of_performance\":{\"city_name\":\"\",\"code\":\"00*****\",\"country\":\"USA\",\"county_fips\":null,\"county_name\":null,\"foreign_location\":null,\"scope\":\"MULTI-STATE\",\"state_code\":\"\",\"state_fips\":null,\"state_name\":\"MULTI-STATE\",\"zip_code\":\"\",\"zip_plus_four\":\"\"},\"pragmatic_obligations\":6949.33,\"recipient\":{\"uuid\":\"f0246386-58f3-512a-b943-7c3c2f81572d\",\"uei\":\"K9B4A4D37AX7\",\"duns\":null,\"legal_business_name\":\"SISTERS-CAMP + SHERMAN RURAL FIRE PROTECTION DISTRICT\",\"legal_business_name_raw\":\"SISTERS-CAMP + SHERMAN RURAL FIRE PROTECTION DISTRICT\",\"county\":\"DESCHUTES\",\"congressional_district\":\"OR-04\",\"congressional_district_original\":\"OR-04\",\"highest_owner\":null,\"business_types\":[{\"code\":\"D\",\"description\":\"Special + District Government\"}]},\"record_type\":\"2\",\"title\":\"COOPERATIVE/ MUTUAL + AID FIRE ASSISTANCE PAYMET / FMMI DOC\u2026\",\"uri\":null}" + headers: + CF-RAY: + - a511b282bdec86bd-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:52 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=U1NM1BTZktnmX2oLAtTllhpulRHdSghBAj%2Fq5z%2BYtQ%2FlfIbhuub9DyBN0TMyJ6OVYWnjsByUz8kyOrmlVyECVOuRN65mlRFNsSzUGkwpTQNvaRsk7oQzWD3eB17C%2BqkspJ0h"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '1802' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.022s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '953' + x-ratelimit-burst-reset: + - '28' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999633' + x-ratelimit-daily-reset: + - '41527' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '953' + x-ratelimit-reset: + - '28' + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/other/ASST_NON_25FI11060100219_012/transactions/?page=1&limit=5 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"key":"12C2_25FI11060100219_-NONE-_10.703_0001","modification_number":"0001","transaction_date":"2026-10-15","action_type":"A1","assistance_type_code":"F010","correction_delete_indicator":null,"cfda_number":"10.703","obligated":6949.33,"pragmatic_obligations":6949.33,"non_federal_funding_amount":0.0,"indirect_cost_federal_share_amount":null,"face_value_of_loan":0.0,"loan_subsidy_cost":0.0,"award_amount_basis":null,"award_recipient_basis":null,"action_date_fiscal_year":2027,"last_modified_date":"2026-10-16T19:24:19Z","initial_report_date":"2026-10-16T19:24:19Z"}]}' + headers: + CF-RAY: + - a511b2833ef26e18-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:52 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=p0k%2Bk1X7pK3nDsWwvbKI94EUYZJmgJaiXEe4HwvJNbeyHGfrw0lliXX3lERTw%2FW2xjLC8SXHPofnDymrIpmvDg%2FbFqgxSl2cidds6qKwp0HINcDFXQmP%2Bm9gnyBNHqK74G2v"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '620' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.018s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '952' + x-ratelimit-burst-reset: + - '28' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999632' + x-ratelimit-daily-reset: + - '41527' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '952' + x-ratelimit-reset: + - '28' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/other/ASST_NON_25FI11060100219_012/listings/?page=1&limit=10 + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"cfda_number":"10.703","is_primary":true,"obligated":6949.33,"pragmatic_obligations":6949.33,"non_federal_funding_amount":0.0,"indirect_cost_federal_share_amount":null,"face_value_of_loan":0.0,"loan_subsidy_cost":0.0,"transactions_count":1,"first_action_date":"2026-10-15","last_action_date":"2026-10-15"}]}' + headers: + CF-RAY: + - a511b283affa6e18-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:52 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=mSa2EL%2BmWCDmZSmP5PkF%2BTBThBZC2Hq9%2FA8%2FkbcVmDEJ7EBLjVNot9CyplEH4al9ZfBHvsYD2G3hkYnJj8PyU4nAKCbklGCt9CeBy%2B3G9JBWTL8Hjz0NVAwtCI3tRGhiXEdY"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '358' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.019s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '951' + x-ratelimit-burst-reset: + - '28' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999631' + x-ratelimit-daily-reset: + - '41527' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '951' + x-ratelimit-reset: + - '28' + x-results-counttype: + - exact + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[direct_payments] b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[direct_payments] new file mode 100644 index 0000000..e7664c0 --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[direct_payments] @@ -0,0 +1,209 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/direct_payments/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"count":56739924,"next":"https://tango.makegov.com/api/assistance/direct_payments/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29&cursor=WyIyMDI2LTEwLTE1IiwgImJhYzIyMzUwLWM0MGQtNTMxOC1iNGQ3LTJlMDI4MmVjNjk4NyJd","previous":null,"cursor":"WyIyMDI2LTEwLTE1IiwgImJhYzIyMzUwLWM0MGQtNTMxOC1iNGQ3LTJlMDI4MmVjNjk4NyJd","previous_cursor":null,"results":[{"assistance_listing":{"number":"10.051","title":"Commodity + Loans and Loan Deficiency Payments"},"assistance_type":{"code":"F006","description":"Direct + Payment for Specified Use"},"award_date":"2026-10-22","awarding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"cfda_number":"10.051","face_value_of_loan":0.0,"fain":"12CCCEAUCEU5253218","fiscal_year":2027,"funding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"key":"ASST_NON_12CCCEAUCEU5253218_012","loan_subsidy_cost":0.0,"obligated":45412.85,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"HICKORY","code":"NC31065","country":"USA","county_fips":"37035","county_name":"CATAWBA","foreign_location":null,"scope":"SINGLE + ZIP CODE","state_code":"NC","state_fips":"37","state_name":"NORTH CAROLINA","zip_code":"28602","zip_plus_four":"1433"},"pragmatic_obligations":45412.85,"recipient":{"uuid":"b2aca74d-a067-53df-b21e-fed4492021ab","uei":"NBY3ULCLKC27","duns":null,"legal_business_name":"SHUFORD + YARNS LLC","legal_business_name_raw":"SHUFORD YARNS LLC","county":"CATAWBA","congressional_district":"NC-10","congressional_district_original":"NC-10","highest_owner":null,"business_types":[{"code":"X","description":"Other"}]},"record_type":"2","title":"ECONOMIC + ADJUSTMENT ASSISTANCE TO USERS OF UPLAND COTTON","uri":null},{"assistance_listing":{"number":"10.051","title":"Commodity + Loans and Loan Deficiency Payments"},"assistance_type":{"code":"F006","description":"Direct + Payment for Specified Use"},"award_date":"2026-10-15","awarding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"cfda_number":"10.051","face_value_of_loan":0.0,"fain":"12CCCEAUCEU5500218","fiscal_year":2027,"funding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"key":"ASST_NON_12CCCEAUCEU5500218_012","loan_subsidy_cost":0.0,"obligated":676.46,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"JEFFERSON","code":"GA42016","country":"USA","county_fips":"13157","county_name":"JACKSON","foreign_location":null,"scope":"SINGLE + ZIP CODE","state_code":"GA","state_fips":"13","state_name":"GEORGIA","zip_code":"30549","zip_plus_four":"5836"},"pragmatic_obligations":676.46,"recipient":{"uuid":"3031e4c1-8a26-566c-a96b-9dd6d4d6de27","uei":"KJB1NJBJ2DX1","duns":null,"legal_business_name":"BUHLER + QUALITY YARNS CORP","legal_business_name_raw":"BUHLER QUALITY YARNS CORPORATION","county":"JACKSON","congressional_district":"GA-09","congressional_district_original":"GA-09","highest_owner":{"uei":"ZWQABBDNK4H3","duns":null,"legal_business_name":"SAMIL + SPINNING CO.,LTD","legal_business_name_raw":"SAMIL SPINNING CO.,LTD"},"business_types":[{"code":"X","description":"Other"}]},"record_type":"2","title":"ECONOMIC + ADJUSTMENT ASSISTANCE TO USERS OF UPLAND COTTON","uri":null}]}' + headers: + CF-RAY: + - a511b279cc7c1906-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=IIGzK9u2AsEzDBWBR10HXD%2FD8PEq0ngtL6IAxSonk84USCZ7j%2B3TIAUBR0m9wJpJPtFbraM8%2BlIcXX%2BMlnLwzYVzqj6P5GxdMA1CXj%2Bb36DLrSIZD4bu5KBOZPZv1D%2F5pqoS"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '4411' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.030s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '970' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999650' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '970' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/direct_payments/?limit=2&cursor=WyIyMDI2LTEwLTE1IiwgImJhYzIyMzUwLWM0MGQtNTMxOC1iNGQ3LTJlMDI4MmVjNjk4NyJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"count":56739924,"next":"https://tango.makegov.com/api/assistance/direct_payments/?limit=2&cursor=WyIyMDI2LTEwLTE1IiwgImFiMzFjMGE1LTJkZDctNWU5MS05YmRmLTUwYzhlZjE0NGJkNSJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29","previous":"https://tango.makegov.com/api/assistance/direct_payments/?limit=2&cursor=eyJ2IjogWyIyMDI2LTEwLTE1IiwgImIwOTI2ZjhjLTNjNjItNTg2OS1iMDkxLTczODZjNzdjZjc0MSJdLCAiZCI6ICJwcmV2In0%3D&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29","cursor":"WyIyMDI2LTEwLTE1IiwgImFiMzFjMGE1LTJkZDctNWU5MS05YmRmLTUwYzhlZjE0NGJkNSJd","previous_cursor":"eyJ2IjogWyIyMDI2LTEwLTE1IiwgImIwOTI2ZjhjLTNjNjItNTg2OS1iMDkxLTczODZjNzdjZjc0MSJdLCAiZCI6ICJwcmV2In0=","results":[{"assistance_listing":{"number":"10.051","title":"Commodity + Loans and Loan Deficiency Payments"},"assistance_type":{"code":"F006","description":"Direct + Payment for Specified Use"},"award_date":"2026-10-15","awarding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"cfda_number":"10.051","face_value_of_loan":0.0,"fain":"12CCCEAUCEU5228218","fiscal_year":2027,"funding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"key":"ASST_NON_12CCCEAUCEU5228218_012","loan_subsidy_cost":0.0,"obligated":1461032.0,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"SALISBURY","code":"NC58860","country":"USA","county_fips":"37159","county_name":"ROWAN","foreign_location":null,"scope":"SINGLE + ZIP CODE","state_code":"NC","state_fips":"37","state_name":"NORTH CAROLINA","zip_code":"28146","zip_plus_four":"2316"},"pragmatic_obligations":1461032.0,"recipient":{"uuid":"49e54a48-950b-590a-b24a-c808ac22b1fe","uei":"KSX9F6SNSKJ1","duns":null,"legal_business_name":"GILDAN + YARNS LLC","legal_business_name_raw":"GILDAN YARNS LLC","county":"ROWAN","congressional_district":"NC-06","congressional_district_original":"NC-08","highest_owner":{"uei":"KSX9F6SNSKJ1","duns":null,"legal_business_name":"GILDAN + YARNS LLC","legal_business_name_raw":"GILDAN ACTIVEWEAR INC"},"business_types":[{"code":"X","description":"Other"}]},"record_type":"2","title":"ECONOMIC + ADJUSTMENT ASSISTANCE TO USERS OF UPLAND COTTON","uri":null},{"assistance_listing":{"number":"10.051","title":"Commodity + Loans and Loan Deficiency Payments"},"assistance_type":{"code":"F006","description":"Direct + Payment for Specified Use"},"award_date":"2026-10-15","awarding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"cfda_number":"10.051","face_value_of_loan":0.0,"fain":"12CCCEAUCEU5094218","fiscal_year":2027,"funding_office":{"organization_id":"b04122ee-f3a4-59ff-a81f-95d53416a49a","office_code":"12346T","office_name":"Deputy + Administrator Farm Programs","agency_code":"12D2","agency_name":"FARM SERVICE + AGENCY","department_code":"012","department_name":"AGRICULTURE, DEPARTMENT + OF"},"key":"ASST_NON_12CCCEAUCEU5094218_012","loan_subsidy_cost":0.0,"obligated":12115.85,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"GREENWOOD","code":"SC30895","country":"USA","county_fips":"45047","county_name":"GREENWOOD","foreign_location":null,"scope":"SINGLE + ZIP CODE","state_code":"SC","state_fips":"45","state_name":"SOUTH CAROLINA","zip_code":"29649","zip_plus_four":"9102"},"pragmatic_obligations":12115.85,"recipient":{"uuid":"7f3a1031-8a16-5f87-8a9c-d048fae2af2b","uei":"NDD9TKKJRD15","duns":null,"legal_business_name":"GREENWOOD + MILLS INC","legal_business_name_raw":"GREENWOOD MILLS INC","county":"GREENWOOD","congressional_district":"SC-03","congressional_district_original":"SC-03","highest_owner":{"uei":null,"duns":null,"legal_business_name":null,"legal_business_name_raw":"GMI + HOLDING, INC."},"business_types":[{"code":"X","description":"Other"}]},"record_type":"2","title":"ECONOMIC + ADJUSTMENT ASSISTANCE TO USERS OF UPLAND COTTON","uri":null}]}' + headers: + CF-RAY: + - a511b27a4f490f5b-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ZUGXGIMrj3Il77MNgZMulqwZnMZlRTrXwaKKC%2FJt0LGsXB2A%2BgQvRBQ5rIQI2%2Fwp0UhFvfTEGhJu3IkeHEckSFd8fSdZ4fCfjIQmgt1ExN7eKRvJk71RHgRjkhLu9grmMeFq"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '5173' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.026s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '969' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999649' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '969' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[grants] b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[grants] new file mode 100644 index 0000000..b59aded --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[grants] @@ -0,0 +1,205 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: "{\"count\":5759909,\"next\":\"https://tango.makegov.com/api/assistance/grants/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29&cursor=WyIyMDI2LTEwLTE2IiwgImIxZjg5ZjMwLWNiYjItNTdjOC04MDg2LTc0OGE4OGJmNzU4MSJd\",\"previous\":null,\"cursor\":\"WyIyMDI2LTEwLTE2IiwgImIxZjg5ZjMwLWNiYjItNTdjOC04MDg2LTc0OGE4OGJmNzU4MSJd\",\"previous_cursor\":null,\"results\":[{\"assistance_listing\":{\"number\":\"20.823\",\"title\":\"Port + Infrastructure Development Program\"},\"assistance_type\":{\"code\":\"F001\",\"description\":\"Grant\"},\"award_date\":\"2026-10-19\",\"awarding_office\":{\"organization_id\":\"261f885d-c8b6-5e0d-b2f8-e0799a4e1de2\",\"office_code\":\"69A334\",\"office_name\":\"MARITIME + ADMINISTRATION\",\"agency_code\":\"6938\",\"agency_name\":\"MARITIME ADMINISTRATION\",\"department_code\":\"069\",\"department_name\":\"TRANSPORTATION, + DEPARTMENT OF\"},\"cfda_number\":\"20.823\",\"face_value_of_loan\":0.0,\"fain\":\"693JF72640002\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"261f885d-c8b6-5e0d-b2f8-e0799a4e1de2\",\"office_code\":\"69A334\",\"office_name\":\"MARITIME + ADMINISTRATION\",\"agency_code\":\"6938\",\"agency_name\":\"MARITIME ADMINISTRATION\",\"department_code\":\"069\",\"department_name\":\"TRANSPORTATION, + DEPARTMENT OF\"},\"key\":\"ASST_NON_693JF72640002_069\",\"loan_subsidy_cost\":0.0,\"obligated\":2000000.0,\"period_of_performance\":{\"start_date\":\"2026-10-19\",\"current_end_date\":\"2029-04-30\"},\"place_of_performance\":{\"city_name\":\"MOBILE\",\"code\":\"AL36633\",\"country\":\"USA\",\"county_fips\":\"01097\",\"county_name\":\"MOBILE\",\"foreign_location\":null,\"scope\":\"SINGLE + ZIP CODE\",\"state_code\":\"AL\",\"state_fips\":\"01\",\"state_name\":\"ALABAMA\",\"zip_code\":\"36633\",\"zip_plus_four\":\"1588\"},\"pragmatic_obligations\":2000000.0,\"recipient\":{\"uuid\":\"88f47661-874f-5298-8b0b-0971be0c746d\",\"uei\":\"HS97F2C9NME3\",\"duns\":null,\"legal_business_name\":\"ALABAMA + PORT AUTHORITY\",\"legal_business_name_raw\":\"ALABAMA PORT AUTHORITY\",\"county\":\"MOBILE\",\"congressional_district\":\"AL-02\",\"congressional_district_original\":\"AL-02\",\"highest_owner\":null,\"business_types\":[{\"code\":\"A\",\"description\":\"State + Government\"}]},\"record_type\":\"2\",\"title\":\"AWARD PURPOSE THE PROJECT + INCLUDES ELECTRICAL\u2026\",\"uri\":null},{\"assistance_listing\":{\"number\":\"20.933\",\"title\":\"National + Infrastructure Investments \"},\"assistance_type\":{\"code\":\"F001\",\"description\":\"Grant\"},\"award_date\":\"2026-10-16\",\"awarding_office\":{\"organization_id\":\"261f885d-c8b6-5e0d-b2f8-e0799a4e1de2\",\"office_code\":\"69A334\",\"office_name\":\"MARITIME + ADMINISTRATION\",\"agency_code\":\"6938\",\"agency_name\":\"MARITIME ADMINISTRATION\",\"department_code\":\"069\",\"department_name\":\"TRANSPORTATION, + DEPARTMENT OF\"},\"cfda_number\":\"20.933\",\"face_value_of_loan\":0.0,\"fain\":\"693JF72440048\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"261f885d-c8b6-5e0d-b2f8-e0799a4e1de2\",\"office_code\":\"69A334\",\"office_name\":\"MARITIME + ADMINISTRATION\",\"agency_code\":\"6938\",\"agency_name\":\"MARITIME ADMINISTRATION\",\"department_code\":\"069\",\"department_name\":\"TRANSPORTATION, + DEPARTMENT OF\"},\"key\":\"ASST_NON_693JF72440048_069\",\"loan_subsidy_cost\":0.0,\"obligated\":2003532.0,\"period_of_performance\":{\"start_date\":\"2026-10-16\",\"current_end_date\":\"2028-08-31\"},\"place_of_performance\":{\"city_name\":\"CRISFIELD\",\"code\":\"MD21817\",\"country\":\"USA\",\"county_fips\":\"24039\",\"county_name\":\"SOMERSET\",\"foreign_location\":null,\"scope\":\"SINGLE + ZIP CODE\",\"state_code\":\"MD\",\"state_fips\":\"24\",\"state_name\":\"MARYLAND\",\"zip_code\":\"21817\",\"zip_plus_four\":\"1328\"},\"pragmatic_obligations\":2003532.0,\"recipient\":{\"uuid\":\"510bd6cc-9840-5adb-98d1-d8e5f74dff6b\",\"uei\":\"EKPQAJKKV549\",\"duns\":null,\"legal_business_name\":\"CITY + OF CRISFIELD\",\"legal_business_name_raw\":\"CITY OF CRISFIELD\",\"county\":\"SOMERSET\",\"congressional_district\":\"MD-01\",\"congressional_district_original\":\"MD-01\",\"highest_owner\":{\"uei\":null,\"duns\":null,\"legal_business_name\":null,\"legal_business_name_raw\":\"CITY + OF CRISFIELD\"},\"business_types\":[{\"code\":\"A\",\"description\":\"State + Government\"}]},\"record_type\":\"2\",\"title\":\"AWARD PURPOSE THE PROJECT + FUNDS THE FULL ENGINEERING\u2026\",\"uri\":null}]}" + headers: + CF-RAY: + - a511b2779e881d1a-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=eMi8OVBrJxcQyIl0X8v8aNvdCfECQPCsDsyD6%2BA0G%2BDcSSOS6TU42XsmT%2B%2FzJWqfge6L9DmGrjRkJBE2M8m3j80ExNBPy9pMrfzhWBphhFSScYjRaxnP0zn%2BfN3ZPZNqBaD7"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '4296' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.031s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '974' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999654' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '974' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/grants/?limit=2&cursor=WyIyMDI2LTEwLTE2IiwgImIxZjg5ZjMwLWNiYjItNTdjOC04MDg2LTc0OGE4OGJmNzU4MSJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"count":5759909,"next":"https://tango.makegov.com/api/assistance/grants/?limit=2&cursor=WyIyMDI2LTEwLTE1IiwgImZhYjlkZDIxLTlmZWEtNTBjNS04NjBlLWMxMjg4ZTJlNGVlOCJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29","previous":"https://tango.makegov.com/api/assistance/grants/?limit=2&cursor=eyJ2IjogWyIyMDI2LTEwLTE2IiwgIjJhMGQ1NjViLTNlYzktNTRlZS1hZDE1LWUwMzA0ZjEwMmViZCJdLCAiZCI6ICJwcmV2In0%3D&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29","cursor":"WyIyMDI2LTEwLTE1IiwgImZhYjlkZDIxLTlmZWEtNTBjNS04NjBlLWMxMjg4ZTJlNGVlOCJd","previous_cursor":"eyJ2IjogWyIyMDI2LTEwLTE2IiwgIjJhMGQ1NjViLTNlYzktNTRlZS1hZDE1LWUwMzA0ZjEwMmViZCJdLCAiZCI6ICJwcmV2In0=","results":[{"assistance_listing":{"number":"93.242","title":"Mental + Health Research Grants"},"assistance_type":{"code":"F001","description":"Grant"},"award_date":"2026-10-16","awarding_office":{"organization_id":"c430a9b0-56f5-5b13-8007-a73aeb1a801b","office_code":"75N700","office_name":"NIH + National Institute of Mental Health","agency_code":"7529","agency_name":"NATIONAL + INSTITUTES OF HEALTH","department_code":"075","department_name":"HEALTH AND + HUMAN SERVICES, DEPARTMENT OF"},"cfda_number":"93.242","face_value_of_loan":0.0,"fain":"KMH146453UPSM35","fiscal_year":2027,"funding_office":{"organization_id":"c430a9b0-56f5-5b13-8007-a73aeb1a801b","office_code":"75N700","office_name":"NIH + National Institute of Mental Health","agency_code":"7529","agency_name":"NATIONAL + INSTITUTES OF HEALTH","department_code":"075","department_name":"HEALTH AND + HUMAN SERVICES, DEPARTMENT OF"},"key":"ASST_NON_KMH146453UPSM35_075","loan_subsidy_cost":0.0,"obligated":0.0,"period_of_performance":{"start_date":"2026-09-04","current_end_date":"2030-08-31"},"place_of_performance":{"city_name":"INDIANAPOLIS","code":"IN36000","country":"USA","county_fips":"18097","county_name":"MARION","foreign_location":null,"scope":"SINGLE + ZIP CODE","state_code":"IN","state_fips":"18","state_name":"INDIANA","zip_code":"46202","zip_plus_four":"3012"},"pragmatic_obligations":0.0,"recipient":{"uuid":"b0011714-8095-51e3-b8de-795cc31043f3","uei":"SHHBRBAPSM35","duns":null,"legal_business_name":"TRUSTEES + OF INDIANA UNIVERSITY","legal_business_name_raw":"TRUSTEES OF INDIANA UNIVERSITY","county":"MARION","congressional_district":"IN-07","congressional_district_original":"IN-07","highest_owner":{"uei":"SHHBRBAPSM35","duns":null,"legal_business_name":"TRUSTEES + OF INDIANA UNIVERSITY","legal_business_name_raw":"TRUSTEES OF INDIANA UNIVERSITY"},"business_types":[{"code":"H","description":"Public/State + Controlled Institution of Higher Education"}]},"record_type":"2","title":"Suicide + Interventions for Youth in Primary Care","uri":"KMH146453UPSM35-328102780"},{"assistance_listing":{"number":"15.605","title":"Sport + Fish Restoration "},"assistance_type":{"code":"F001","description":"Grant"},"award_date":"2026-10-15","awarding_office":{"organization_id":"625cae1e-7830-5b81-ac8e-30283dd6570c","office_code":"140F04","office_name":"FWS + SOUTHEAST REGIONAL OFFICE","agency_code":"1448","agency_name":"US FISH AND + WILDLIFE SERVICE","department_code":"014","department_name":"INTERIOR, DEPARTMENT + OF THE"},"cfda_number":"15.605","face_value_of_loan":0.0,"fain":"F27AF00030","fiscal_year":2027,"funding_office":{"organization_id":"625cae1e-7830-5b81-ac8e-30283dd6570c","office_code":"00004","office_name":"FWS + SOUTHEAST REGIONAL OFFICE","agency_code":"1448","agency_name":"US FISH AND + WILDLIFE SERVICE","department_code":"014","department_name":"INTERIOR, DEPARTMENT + OF THE"},"key":"ASST_NON_F27AF00030_014","loan_subsidy_cost":0.0,"obligated":90000.0,"period_of_performance":{"start_date":"2026-11-01","current_end_date":"2027-10-31"},"place_of_performance":{"city_name":"","code":"KY*****","country":"USA","county_fips":null,"county_name":null,"foreign_location":null,"scope":"STATE-WIDE","state_code":"KY","state_fips":"21","state_name":"KENTUCKY","zip_code":"","zip_plus_four":""},"pragmatic_obligations":90000.0,"recipient":{"uuid":"ff5c7a37-a149-5ecb-b641-73d9546e6577","uei":"Y6PKBKPDCSL3","duns":null,"legal_business_name":"KENTUCKY + DEPARTMENT OF FISH & WIL","legal_business_name_raw":"KENTUCKY DEPARTMENT OF + FISH & WIL","county":"FRANKLIN","congressional_district":"KY-01","congressional_district_original":"KY-01","highest_owner":{"uei":null,"duns":null,"legal_business_name":"COMMONWEALTH + OF KENTUCKY","legal_business_name_raw":"COMMONWEALTH OF KENTUCKY"},"business_types":[{"code":"A","description":"State + Government"}]},"record_type":"2","title":"KY - CAMP NELSON BOAT RAMP RECONSTRUCTION + 2026-27","uri":null}]}' + headers: + CF-RAY: + - a511b278189dc330-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=iyiGQ7jUCfGfq4VA9Vn9h6xjflkA%2Faxduha%2BbFVT63cljSSVtiOKW502054%2Fc9Br79MggPIqf59bTsrg5x%2FIJwmKIqx0cBU%2FUoDQ6jw%2FOc1bokCKTXSpSBkFgLrzaw4Wv72L"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '5261' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.032s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '973' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999653' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '973' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[loans] b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[loans] new file mode 100644 index 0000000..2561046 --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[loans] @@ -0,0 +1,193 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/loans/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"count":29866842,"next":"https://tango.makegov.com/api/assistance/loans/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29&cursor=WyIyMDI2LTEwLTE1IiwgImZlNWM4ZDcyLWNkZjEtNTFhMi1hMDNkLThiZDlhMzI1ZjUxOCJd","previous":null,"cursor":"WyIyMDI2LTEwLTE1IiwgImZlNWM4ZDcyLWNkZjEtNTFhMi1hMDNkLThiZDlhMzI1ZjUxOCJd","previous_cursor":null,"results":[{"assistance_listing":{"number":"59.012","title":"7(a) + Loan Guarantees"},"assistance_type":{"code":"F004","description":"Loan Guarantee"},"award_date":"2026-10-15","awarding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"cfda_number":"59.012","face_value_of_loan":425000.0,"fain":"8606059100","fiscal_year":2027,"funding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"key":"ASST_NON_8606059100_073","loan_subsidy_cost":0.0,"obligated":0.0,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"","code":"GA**067","country":"USA","county_fips":"13067","county_name":"COBB","foreign_location":null,"scope":"COUNTY-WIDE","state_code":"GA","state_fips":"13","state_name":"GEORGIA","zip_code":"","zip_plus_four":""},"pragmatic_obligations":0.0,"recipient":{"uuid":"536c50dc-3456-5ff2-979d-0f898b8e85c9","uei":null,"duns":null,"legal_business_name":"SLEDGE + HAMMER CONSULTING CORP.","legal_business_name_raw":"SLEDGE HAMMER CONSULTING + CORP.","county":"COBB","congressional_district":"GA-11","congressional_district_original":"GA-11","highest_owner":null,"business_types":[{"code":"R","description":"Small + Business"}]},"record_type":"2","title":"SBA 7(a) Program Loans","uri":null},{"assistance_listing":{"number":"59.012","title":"7(a) + Loan Guarantees"},"assistance_type":{"code":"F004","description":"Loan Guarantee"},"award_date":"2026-10-15","awarding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"cfda_number":"59.012","face_value_of_loan":2886000.0,"fain":"8605109100","fiscal_year":2027,"funding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"key":"ASST_NON_8605109100_073","loan_subsidy_cost":0.0,"obligated":0.0,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"","code":"VA**179","country":"USA","county_fips":"51179","county_name":"STAFFORD","foreign_location":null,"scope":"COUNTY-WIDE","state_code":"VA","state_fips":"51","state_name":"VIRGINIA","zip_code":"","zip_plus_four":""},"pragmatic_obligations":0.0,"recipient":{"uuid":"a2ecf274-1b3b-5e9c-aa15-2724284adf4b","uei":null,"duns":null,"legal_business_name":"KEVIN + PARKER ROOFING CONTRACTOR, INC.","legal_business_name_raw":"KEVIN PARKER ROOFING + CONTRACTOR, INC.","county":"STAFFORD","congressional_district":"VA-07","congressional_district_original":"VA-07","highest_owner":null,"business_types":[{"code":"R","description":"Small + Business"}]},"record_type":"2","title":"SBA 7(a) Program Loans","uri":null}]}' + headers: + CF-RAY: + - a511b278b9d7590b-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=14m8Show5eD657nVBxgx9hQ9fig6Cc%2F5zJEGxI3ewwlG4DawVQtDLtbiG4CJhXHhdlVic%2FItEpNmkvzAhoppcsBCAVUkOqr0BWRnKhLxJAIaIHYZh6c4XIkVCPy2Q5wegDe3"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '4073' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.026s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '972' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999652' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '972' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/loans/?limit=2&cursor=WyIyMDI2LTEwLTE1IiwgImZlNWM4ZDcyLWNkZjEtNTFhMi1hMDNkLThiZDlhMzI1ZjUxOCJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: '{"count":29866842,"next":"https://tango.makegov.com/api/assistance/loans/?limit=2&cursor=WyIyMDI2LTEwLTE1IiwgImZkYmVkODk4LTIwZTQtNWZkNC05MDE5LWIwMjE2ZjBkOWQ4MiJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29","previous":"https://tango.makegov.com/api/assistance/loans/?limit=2&cursor=eyJ2IjogWyIyMDI2LTEwLTE1IiwgImZkZGFiODVkLWVmYzctNWJhYi04ZDJmLWJjYWYzMjE3NmJhYSJdLCAiZCI6ICJwcmV2In0%3D&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29","cursor":"WyIyMDI2LTEwLTE1IiwgImZkYmVkODk4LTIwZTQtNWZkNC05MDE5LWIwMjE2ZjBkOWQ4MiJd","previous_cursor":"eyJ2IjogWyIyMDI2LTEwLTE1IiwgImZkZGFiODVkLWVmYzctNWJhYi04ZDJmLWJjYWYzMjE3NmJhYSJdLCAiZCI6ICJwcmV2In0=","results":[{"assistance_listing":{"number":"59.012","title":"7(a) + Loan Guarantees"},"assistance_type":{"code":"F004","description":"Loan Guarantee"},"award_date":"2026-10-15","awarding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"cfda_number":"59.012","face_value_of_loan":24500.0,"fain":"8604959106","fiscal_year":2027,"funding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"key":"ASST_NON_8604959106_073","loan_subsidy_cost":0.0,"obligated":0.0,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"","code":"CA**037","country":"USA","county_fips":"06037","county_name":"LOS + ANGELES","foreign_location":null,"scope":"COUNTY-WIDE","state_code":"CA","state_fips":"06","state_name":"CALIFORNIA","zip_code":"","zip_plus_four":""},"pragmatic_obligations":0.0,"recipient":{"uuid":"0e48f14d-c9df-5662-98c9-42d00f068905","uei":null,"duns":null,"legal_business_name":"MOONA + STAR, INC.","legal_business_name_raw":"MOONA STAR, INC.","county":"LOS ANGELES","congressional_district":"CA-32","congressional_district_original":"CA-32","highest_owner":null,"business_types":[{"code":"R","description":"Small + Business"}]},"record_type":"2","title":"SBA 7(a) Program Loans","uri":null},{"assistance_listing":{"number":"59.012","title":"7(a) + Loan Guarantees"},"assistance_type":{"code":"F004","description":"Loan Guarantee"},"award_date":"2026-10-15","awarding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"cfda_number":"59.012","face_value_of_loan":306000.0,"fain":"8603699109","fiscal_year":2027,"funding_office":{"organization_id":"7d23b0cc-0292-51fc-ac50-6ba0f26d0f2d","office_code":"737010","office_name":"OFC + OF CAPITAL ACCESS","agency_code":"7300","agency_name":"SMALL BUSINESS ADMINISTRATION","department_code":"073","department_name":"SMALL + BUSINESS ADMINISTRATION"},"key":"ASST_NON_8603699109_073","loan_subsidy_cost":0.0,"obligated":0.0,"period_of_performance":{"start_date":null,"current_end_date":null},"place_of_performance":{"city_name":"","code":"FL**071","country":"USA","county_fips":"12071","county_name":"LEE","foreign_location":null,"scope":"COUNTY-WIDE","state_code":"FL","state_fips":"12","state_name":"FLORIDA","zip_code":"","zip_plus_four":""},"pragmatic_obligations":0.0,"recipient":{"uuid":"bca64c94-f6d1-58c0-bdce-2074e393ffb2","uei":null,"duns":null,"legal_business_name":"DOCKTALING + LLC","legal_business_name_raw":"DOCKTALING LLC","county":"LEE","congressional_district":"FL-17","congressional_district_original":"FL-17","highest_owner":null,"business_types":[{"code":"R","description":"Small + Business"}]},"record_type":"2","title":"SBA 7(a) Program Loans","uri":null}]}' + headers: + CF-RAY: + - a511b2794ab50f64-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=YMqRDFIi1lyfdTZlxBw8m9SWYv2wJjeAylcuns3CXVc12760X1uRTti3IqgAJTumlMjy9pBDsjTVTthlFjg5wFZUhB5o8MnSerXwaUMly1Rqqfpbs8YR2j4l1S0qxnk0PATF"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '4675' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.032s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '971' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999651' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '971' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[other] b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[other] new file mode 100644 index 0000000..4aa021d --- /dev/null +++ b/tests/cassettes/TestAssistanceAwardsIntegration.test_list_pages_by_cursor[other] @@ -0,0 +1,213 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/other/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: "{\"count\":7637560,\"next\":\"https://tango.makegov.com/api/assistance/other/?limit=2&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29&cursor=WyIyMDI2LTEwLTEyIiwgImZmY2ZiNzU4LTlmZGQtNTBmZS05YWE0LWQ0ODJmM2U3NjY2OSJd\",\"previous\":null,\"cursor\":\"WyIyMDI2LTEwLTEyIiwgImZmY2ZiNzU4LTlmZGQtNTBmZS05YWE0LWQ0ODJmM2U3NjY2OSJd\",\"previous_cursor\":null,\"results\":[{\"assistance_listing\":{\"number\":\"10.703\",\"title\":\"Cooperative + Fire Protection Agreement\"},\"assistance_type\":{\"code\":\"F010\",\"description\":\"Other + Financial Assistance\"},\"award_date\":\"2026-10-15\",\"awarding_office\":{\"organization_id\":\"091b577a-bf49-51b2-987e-8509d409bb10\",\"office_code\":\"1204GG\",\"office_name\":\"DESCHUTES + NATIONAL FOREST\",\"agency_code\":\"12C2\",\"agency_name\":\"FOREST SERVICE\",\"department_code\":\"012\",\"department_name\":\"AGRICULTURE, + DEPARTMENT OF\"},\"cfda_number\":\"10.703\",\"face_value_of_loan\":0.0,\"fain\":\"25FI11060100219\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"091b577a-bf49-51b2-987e-8509d409bb10\",\"office_code\":\"04GG\",\"office_name\":\"DESCHUTES + NATIONAL FOREST\",\"agency_code\":\"12C2\",\"agency_name\":\"FOREST SERVICE\",\"department_code\":\"012\",\"department_name\":\"AGRICULTURE, + DEPARTMENT OF\"},\"key\":\"ASST_NON_25FI11060100219_012\",\"loan_subsidy_cost\":0.0,\"obligated\":6949.33,\"period_of_performance\":{\"start_date\":\"2026-07-18\",\"current_end_date\":\"2026-07-19\"},\"place_of_performance\":{\"city_name\":\"\",\"code\":\"00*****\",\"country\":\"USA\",\"county_fips\":null,\"county_name\":null,\"foreign_location\":null,\"scope\":\"MULTI-STATE\",\"state_code\":\"\",\"state_fips\":null,\"state_name\":\"MULTI-STATE\",\"zip_code\":\"\",\"zip_plus_four\":\"\"},\"pragmatic_obligations\":6949.33,\"recipient\":{\"uuid\":\"f0246386-58f3-512a-b943-7c3c2f81572d\",\"uei\":\"K9B4A4D37AX7\",\"duns\":null,\"legal_business_name\":\"SISTERS-CAMP + SHERMAN RURAL FIRE PROTECTION DISTRICT\",\"legal_business_name_raw\":\"SISTERS-CAMP + SHERMAN RURAL FIRE PROTECTION DISTRICT\",\"county\":\"DESCHUTES\",\"congressional_district\":\"OR-04\",\"congressional_district_original\":\"OR-04\",\"highest_owner\":null,\"business_types\":[{\"code\":\"D\",\"description\":\"Special + District Government\"}]},\"record_type\":\"2\",\"title\":\"COOPERATIVE/ MUTUAL + AID FIRE ASSISTANCE PAYMET / FMMI DOC\u2026\",\"uri\":null},{\"assistance_listing\":{\"number\":\"97.022\",\"title\":\"Flood + Insurance\"},\"assistance_type\":{\"code\":\"F009\",\"description\":\"Sale, + Exchange, or Donation of Property and Goods\"},\"award_date\":\"2026-10-12\",\"awarding_office\":{\"organization_id\":\"5b0fc9d4-aad8-5fff-b442-241ee9f027f3\",\"office_code\":\"70FGRT\",\"office_name\":\"FEMA + Headquarters: Financial Assistance Awards\",\"agency_code\":\"7022\",\"agency_name\":\"FEDERAL + EMERGENCY MANAGEMENT AGENCY\",\"department_code\":\"070\",\"department_name\":\"HOMELAND + SECURITY, DEPARTMENT OF\"},\"cfda_number\":\"97.022\",\"face_value_of_loan\":0.0,\"fain\":\"C99999500003785920260803000000\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"5b0fc9d4-aad8-5fff-b442-241ee9f027f3\",\"office_code\":\"70FGRT\",\"office_name\":\"FEMA + Headquarters: Financial Assistance Awards\",\"agency_code\":\"7022\",\"agency_name\":\"FEDERAL + EMERGENCY MANAGEMENT AGENCY\",\"department_code\":\"070\",\"department_name\":\"HOMELAND + SECURITY, DEPARTMENT OF\"},\"key\":\"ASST_NON_C99999500003785920260803000000_070\",\"loan_subsidy_cost\":0.0,\"obligated\":null,\"period_of_performance\":{\"start_date\":null,\"current_end_date\":null},\"place_of_performance\":{\"city_name\":\"BROOKLYN\",\"code\":\"NY00000\",\"country\":\"USA\",\"county_fips\":\"36047\",\"county_name\":\"KINGS\",\"foreign_location\":null,\"scope\":\"SINGLE + ZIP CODE\",\"state_code\":\"NY\",\"state_fips\":\"36\",\"state_name\":\"NEW + YORK\",\"zip_code\":\"11231\",\"zip_plus_four\":\"4911\"},\"pragmatic_obligations\":null,\"recipient\":{\"uuid\":\"99ba65ea-be2a-5c6d-b101-86ff3eafb31a\",\"uei\":\"W69TCJPDDLZ7\",\"duns\":null,\"legal_business_name\":\"TORRENT + TECHNOLOGIES, INC.\",\"legal_business_name_raw\":\"NFIP DIRECT SERVICING AGENT\",\"county\":\"FLATHEAD\",\"congressional_district\":\"MT-01\",\"congressional_district_original\":\"MT-01\",\"highest_owner\":{\"uei\":\"W69TCJPDDLZ7\",\"duns\":null,\"legal_business_name\":\"TORRENT + TECHNOLOGIES, INC.\",\"legal_business_name_raw\":null},\"business_types\":[{\"code\":\"Q\",\"description\":\"For-Profit + Organization (Other than Small Business)\"}]},\"record_type\":\"2\",\"title\":\"NATIONAL + FLOOD INSURANCE PROGRAM CLAIM COVERED OBLIGATION\u2026\",\"uri\":null}]}" + headers: + CF-RAY: + - a511b27adc5043cb-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:50 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=rggJngL9jyzgdGYA0o2u2nKe2UaPF8X9DRX0yAFNHDRG2inXV8iwBKtQev57Udc9Vdu0bt4gwLa0zqkkf7TckMaEH5i2wcbIv2GYv%2FvHcEsafUt%2B1uFjtX5kVOxITHXBcu5u"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '4533' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.031s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '968' + x-ratelimit-burst-reset: + - '30' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999648' + x-ratelimit-daily-reset: + - '41529' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '968' + x-ratelimit-reset: + - '30' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Host: + - tango.makegov.com + User-Agent: + - python-httpx/0.28.1 + method: GET + uri: https://tango.makegov.com/api/assistance/other/?limit=2&cursor=WyIyMDI2LTEwLTEyIiwgImZmY2ZiNzU4LTlmZGQtNTBmZS05YWE0LWQ0ODJmM2U3NjY2OSJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29 + response: + body: + string: "{\"count\":7637560,\"next\":\"https://tango.makegov.com/api/assistance/other/?limit=2&cursor=WyIyMDI2LTEwLTEyIiwgImZlZGZjZjlmLTU0YzctNTAzMC1hZmRkLTY4OGVhMDRiYmEyMiJd&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29\",\"previous\":\"https://tango.makegov.com/api/assistance/other/?limit=2&cursor=eyJ2IjogWyIyMDI2LTEwLTEyIiwgImZmYmU2Y2VmLTM0YWYtNTRhNC1iYjEwLTcyMmZkMzg5YzQ3MyJdLCAiZCI6ICJwcmV2In0%3D&shape=key%2Cfain%2Curi%2Caward_date%2Cfiscal_year%2Cassistance_type%28code%2Cdescription%29%2Crecord_type%2Ccfda_number%2Cassistance_listing%28number%2Ctitle%29%2Ctitle%2Cobligated%2Cpragmatic_obligations%2Cface_value_of_loan%2Cloan_subsidy_cost%2Cawarding_office%28%2A%29%2Cfunding_office%28%2A%29%2Crecipient%28%2A%29%2Cplace_of_performance%28%2A%29%2Cperiod_of_performance%28start_date%2Ccurrent_end_date%29\",\"cursor\":\"WyIyMDI2LTEwLTEyIiwgImZlZGZjZjlmLTU0YzctNTAzMC1hZmRkLTY4OGVhMDRiYmEyMiJd\",\"previous_cursor\":\"eyJ2IjogWyIyMDI2LTEwLTEyIiwgImZmYmU2Y2VmLTM0YWYtNTRhNC1iYjEwLTcyMmZkMzg5YzQ3MyJdLCAiZCI6ICJwcmV2In0=\",\"results\":[{\"assistance_listing\":{\"number\":\"97.022\",\"title\":\"Flood + Insurance\"},\"assistance_type\":{\"code\":\"F009\",\"description\":\"Sale, + Exchange, or Donation of Property and Goods\"},\"award_date\":\"2026-10-12\",\"awarding_office\":{\"organization_id\":\"5b0fc9d4-aad8-5fff-b442-241ee9f027f3\",\"office_code\":\"70FGRT\",\"office_name\":\"FEMA + Headquarters: Financial Assistance Awards\",\"agency_code\":\"7022\",\"agency_name\":\"FEDERAL + EMERGENCY MANAGEMENT AGENCY\",\"department_code\":\"070\",\"department_name\":\"HOMELAND + SECURITY, DEPARTMENT OF\"},\"cfda_number\":\"97.022\",\"face_value_of_loan\":0.0,\"fain\":\"C18988300217701920260812000000\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"5b0fc9d4-aad8-5fff-b442-241ee9f027f3\",\"office_code\":\"70FGRT\",\"office_name\":\"FEMA + Headquarters: Financial Assistance Awards\",\"agency_code\":\"7022\",\"agency_name\":\"FEDERAL + EMERGENCY MANAGEMENT AGENCY\",\"department_code\":\"070\",\"department_name\":\"HOMELAND + SECURITY, DEPARTMENT OF\"},\"key\":\"ASST_NON_C18988300217701920260812000000_070\",\"loan_subsidy_cost\":0.0,\"obligated\":null,\"period_of_performance\":{\"start_date\":null,\"current_end_date\":null},\"place_of_performance\":{\"city_name\":\"CAMBRIDGE + CITY\",\"code\":\"IN09874\",\"country\":\"USA\",\"county_fips\":\"18177\",\"county_name\":\"WAYNE\",\"foreign_location\":null,\"scope\":\"SINGLE + ZIP CODE\",\"state_code\":\"IN\",\"state_fips\":\"18\",\"state_name\":\"INDIANA\",\"zip_code\":\"47327\",\"zip_plus_four\":\"9724\"},\"pragmatic_obligations\":null,\"recipient\":{\"uuid\":\"cf878ea8-c5ec-5cf5-9dd8-dc80631db0f5\",\"uei\":\"W7D3DJUSN9W7\",\"duns\":null,\"legal_business_name\":\"AUTO-OWNERS + INSURANCE CO\",\"legal_business_name_raw\":\"AUTO-OWNERS INSURANCE COMPANY\",\"county\":\"EATON\",\"congressional_district\":\"MI-07\",\"congressional_district_original\":\"MI-07\",\"highest_owner\":{\"uei\":null,\"duns\":null,\"legal_business_name\":\"AUTO-OWNERS + INSURANCE COMPANY\",\"legal_business_name_raw\":\"AUTO-OWNERS INSURANCE COMPANY\"},\"business_types\":[{\"code\":\"Q\",\"description\":\"For-Profit + Organization (Other than Small Business)\"}]},\"record_type\":\"2\",\"title\":\"NATIONAL + FLOOD INSURANCE PROGRAM CLAIM COVERED OBLIGATION\u2026\",\"uri\":null},{\"assistance_listing\":{\"number\":\"97.022\",\"title\":\"Flood + Insurance\"},\"assistance_type\":{\"code\":\"F009\",\"description\":\"Sale, + Exchange, or Donation of Property and Goods\"},\"award_date\":\"2026-10-12\",\"awarding_office\":{\"organization_id\":\"5b0fc9d4-aad8-5fff-b442-241ee9f027f3\",\"office_code\":\"70FGRT\",\"office_name\":\"FEMA + Headquarters: Financial Assistance Awards\",\"agency_code\":\"7022\",\"agency_name\":\"FEDERAL + EMERGENCY MANAGEMENT AGENCY\",\"department_code\":\"070\",\"department_name\":\"HOMELAND + SECURITY, DEPARTMENT OF\"},\"cfda_number\":\"97.022\",\"face_value_of_loan\":0.0,\"fain\":\"C99999440096803520260813000000\",\"fiscal_year\":2027,\"funding_office\":{\"organization_id\":\"5b0fc9d4-aad8-5fff-b442-241ee9f027f3\",\"office_code\":\"70FGRT\",\"office_name\":\"FEMA + Headquarters: Financial Assistance Awards\",\"agency_code\":\"7022\",\"agency_name\":\"FEDERAL + EMERGENCY MANAGEMENT AGENCY\",\"department_code\":\"070\",\"department_name\":\"HOMELAND + SECURITY, DEPARTMENT OF\"},\"key\":\"ASST_NON_C99999440096803520260813000000_070\",\"loan_subsidy_cost\":0.0,\"obligated\":null,\"period_of_performance\":{\"start_date\":null,\"current_end_date\":null},\"place_of_performance\":{\"city_name\":\"RIVERTON\",\"code\":\"UT00000\",\"country\":\"USA\",\"county_fips\":\"49035\",\"county_name\":\"SALT + LAKE\",\"foreign_location\":null,\"scope\":\"SINGLE ZIP CODE\",\"state_code\":\"UT\",\"state_fips\":\"49\",\"state_name\":\"UTAH\",\"zip_code\":\"84065\",\"zip_plus_four\":\"7579\"},\"pragmatic_obligations\":null,\"recipient\":{\"uuid\":\"99ba65ea-be2a-5c6d-b101-86ff3eafb31a\",\"uei\":\"W69TCJPDDLZ7\",\"duns\":null,\"legal_business_name\":\"TORRENT + TECHNOLOGIES, INC.\",\"legal_business_name_raw\":\"NFIP DIRECT SERVICING AGENT\",\"county\":\"FLATHEAD\",\"congressional_district\":\"MT-01\",\"congressional_district_original\":\"MT-01\",\"highest_owner\":{\"uei\":\"W69TCJPDDLZ7\",\"duns\":null,\"legal_business_name\":\"TORRENT + TECHNOLOGIES, INC.\",\"legal_business_name_raw\":null},\"business_types\":[{\"code\":\"Q\",\"description\":\"For-Profit + Organization (Other than Small Business)\"}]},\"record_type\":\"2\",\"title\":\"NATIONAL + FLOOD INSURANCE PROGRAM CLAIM COVERED OBLIGATION\u2026\",\"uri\":null}]}" + headers: + CF-RAY: + - a511b27b6d3c6e53-EWR + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Tue, 27 Oct 2026 12:27:51 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=aX%2FOeQIapxHd1MjRRrbF6GQkP52P4TMC52nO5zKGiUw8FItQI11mXcGLL26uuplq6LD4t3t3sktK6fVpbcXsZs8A9C7kUzpFsxe4k8XM7E2%2B94rAWLhHR3tULp628OrPmSSb"}]}' + Server: + - cloudflare + Transfer-Encoding: + - chunked + allow: + - GET, HEAD, OPTIONS + cf-cache-status: + - DYNAMIC + content-length: + - '5446' + cross-origin-opener-policy: + - same-origin + referrer-policy: + - same-origin + strict-transport-security: + - max-age=3600 + vary: + - Accept, Cookie + x-content-type-options: + - nosniff + x-execution-time: + - 0.042s + x-frame-options: + - DENY + x-ratelimit-burst-limit: + - '1000' + x-ratelimit-burst-remaining: + - '967' + x-ratelimit-burst-reset: + - '29' + x-ratelimit-daily-limit: + - '2000000' + x-ratelimit-daily-remaining: + - '1999647' + x-ratelimit-daily-reset: + - '41528' + x-ratelimit-limit: + - '1000' + x-ratelimit-remaining: + - '967' + x-ratelimit-reset: + - '29' + x-results-counttype: + - approximate + x-tango-api-version: + - 5.12.1 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/integration/test_assistance_integration.py b/tests/integration/test_assistance_integration.py new file mode 100644 index 0000000..c8b2c5e --- /dev/null +++ b/tests/integration/test_assistance_integration.py @@ -0,0 +1,117 @@ +"""Integration tests for the financial-assistance award endpoints + +Pytest Markers: + @pytest.mark.integration: Marks tests as integration tests that may hit external APIs + @pytest.mark.vcr(): Enables VCR recording/playback for HTTP interactions + +Usage: + pytest tests/integration/test_assistance_integration.py +""" + +import pytest + +from tests.integration.conftest import handle_api_exceptions +from tests.integration.validation import validate_no_parsing_errors, validate_pagination + +# (route segment, list method, get method, transactions method, listings method, GSDM codes) +GROUPS = [ + ( + "grants", + "list_assistance_grants", + "get_assistance_grant", + "list_assistance_grant_transactions", + "list_assistance_grant_listings", + {"F001", "F002"}, + ), + ( + "loans", + "list_assistance_loans", + "get_assistance_loan", + "list_assistance_loan_transactions", + "list_assistance_loan_listings", + {"F003", "F004"}, + ), + ( + "direct_payments", + "list_assistance_direct_payments", + "get_assistance_direct_payment", + "list_assistance_direct_payment_transactions", + "list_assistance_direct_payment_listings", + {"F006", "F007"}, + ), + ( + "other", + "list_assistance_other", + "get_assistance_other", + "list_assistance_other_transactions", + "list_assistance_other_listings", + {"F005", "F008", "F009", "F010"}, + ), +] +SEGMENTS = [group[0] for group in GROUPS] + + +@pytest.mark.vcr() +@pytest.mark.integration +class TestAssistanceAwardsIntegration: + @pytest.mark.parametrize( + ("segment", "list_method", "codes"), + [(g[0], g[1], g[5]) for g in GROUPS], + ids=SEGMENTS, + ) + @handle_api_exceptions("assistance") + def test_list_pages_by_cursor(self, tango_client, segment, list_method, codes): + list_awards = getattr(tango_client, list_method) + first = list_awards(limit=2) + + validate_pagination(first) + assert len(first.results) == 2 + assert first.cursor + for award in first.results: + validate_no_parsing_errors(award) + assert award["key"] + assert award["assistance_type"]["code"] in codes + + second = list_awards(limit=2, cursor=first.cursor) + assert {a["key"] for a in first.results}.isdisjoint(a["key"] for a in second.results) + + @pytest.mark.parametrize( + ("segment", "list_method", "get_method", "transactions_method", "listings_method"), + [g[:5] for g in GROUPS], + ids=SEGMENTS, + ) + @handle_api_exceptions("assistance") + def test_get_and_sub_routes( + self, tango_client, segment, list_method, get_method, transactions_method, listings_method + ): + key = getattr(tango_client, list_method)(limit=1, shape="key").results[0]["key"] + + award = getattr(tango_client, get_method)(key) + assert award["key"] == key + validate_no_parsing_errors(award) + + transactions = getattr(tango_client, transactions_method)(key, limit=5) + assert transactions.count >= 1 + assert all(row["key"] for row in transactions.results) + + listings = getattr(tango_client, listings_method)(key) + assert listings.count >= 1 + assert listings.results[0]["is_primary"] is True + + @handle_api_exceptions("assistance") + def test_filters_narrow_the_list(self, tango_client): + response = tango_client.list_assistance_grants( + cfda_number="93.778", + fiscal_year=2026, + shape="key,cfda_number,fiscal_year,obligated", + ordering="-obligated", + limit=3, + ) + + validate_pagination(response) + assert response.results + for award in response.results: + assert award["cfda_number"] == "93.778" + assert award["fiscal_year"] == 2026 + amounts = [award["obligated"] for award in response.results] + assert amounts == sorted(amounts, reverse=True) diff --git a/tests/test_assistance.py b/tests/test_assistance.py new file mode 100644 index 0000000..3394e44 --- /dev/null +++ b/tests/test_assistance.py @@ -0,0 +1,436 @@ +"""Tests for the financial-assistance award endpoints (`/api/assistance/{grants,loans,direct_payments,other}/`). + +Covers the request contract for every method (path, filters under the API's own param names, the API's default shape, both pagination styles) and the shape schema behind them. Requests are mocked. +""" + +import inspect +import json +from datetime import UTC, date, datetime +from decimal import Decimal +from pathlib import Path +from unittest.mock import Mock, patch + +import httpx +import pytest + +from tango import TangoClient +from tango.exceptions import ShapeValidationError, TangoValidationError +from tango.models import AssistanceAward, ShapeConfig +from tango.shapes.parser import ShapeParser + +CONTRACT_PATH = Path(__file__).resolve().parents[1] / "contracts" / "filter_shape_contract.json" + +AWARD_KEY = "ASST_NON_693JF72640002_069" + +# (route segment, list method, get method, transactions method, listings method) +GROUPS = [ + ( + "grants", + "list_assistance_grants", + "get_assistance_grant", + "list_assistance_grant_transactions", + "list_assistance_grant_listings", + ), + ( + "loans", + "list_assistance_loans", + "get_assistance_loan", + "list_assistance_loan_transactions", + "list_assistance_loan_listings", + ), + ( + "direct_payments", + "list_assistance_direct_payments", + "get_assistance_direct_payment", + "list_assistance_direct_payment_transactions", + "list_assistance_direct_payment_listings", + ), + ( + "other", + "list_assistance_other", + "get_assistance_other", + "list_assistance_other_transactions", + "list_assistance_other_listings", + ), +] +SEGMENTS = [group[0] for group in GROUPS] + +ALL_FILTERS = { + "key": "ASST_NON_A|ASST_NON_B", + "fain": "693JF72640002", + "uri": "27QQCACCDD-4106687094", + "uei": "HS97F2C9NME3", + "recipient": "port authority", + "search": "wildfire resilience", + "awarding_agency": "DOT", + "funding_agency": "069", + "cfda_number": "20.823|93.778", + "assistance_type": "F001", + "record_type": "2", + "business_funds_indicator": "NON", + "disaster_emergency_fund_code": "L", + "funding_opportunity_number": "USDA-FS-2024-CWDG-CWSF", + "award_date": "2026-10-19", + "award_date_gte": "2026-01-01", + "award_date_lte": "2026-12-31", + "fiscal_year": 2027, + "fiscal_year_gte": 2020, + "fiscal_year_lte": 2027, + "pop_start_date_gte": "2026-01-01", + "pop_start_date_lte": "2026-12-31", + "pop_end_date_gte": "2027-01-01", + "pop_end_date_lte": "2029-12-31", + "pop_state": "AL|VA", + "pop_country": "USA", + "pop_zip": "36633", + "obligated_gte": 1000, + "obligated_lte": "2500000.50", + "ordering": "-pragmatic_obligations", +} + + +def _mock(mock_request, payload=None, headers=None): + response = Mock() + response.is_success = True + response.status_code = 200 + response.headers = httpx.Headers(headers or {}) + response.json.return_value = ( + payload + if payload is not None + else {"count": 0, "next": None, "previous": None, "results": []} + ) + response.content = b"{}" + mock_request.return_value = response + return response + + +def _call_params(mock_request) -> dict: + return mock_request.call_args.kwargs.get("params") or {} + + +def _call_url(mock_request) -> str: + return str(mock_request.call_args.kwargs.get("url", "")) + + +def _award(**overrides) -> dict: + row = { + "key": AWARD_KEY, + "fain": "693JF72640002", + "uri": None, + "award_date": "2026-10-19", + "fiscal_year": 2027, + "assistance_type": {"code": "F001", "description": "Grant"}, + "record_type": "2", + "cfda_number": "20.823", + "assistance_listing": { + "number": "20.823", + "title": "Port Infrastructure Development Program", + }, + "title": "AWARD PURPOSE THE PROJECT INCLUDES ELECTRICAL…", + "obligated": 2000000.0, + "pragmatic_obligations": 2000000.0, + "face_value_of_loan": 0.0, + "loan_subsidy_cost": 0.0, + "awarding_office": { + "organization_id": "261f885d-c8b6-5e0d-b2f8-e0799a4e1de2", + "office_code": "69A334", + "office_name": "MARITIME ADMINISTRATION", + "agency_code": "6938", + "agency_name": "MARITIME ADMINISTRATION", + "department_code": "069", + "department_name": "TRANSPORTATION, DEPARTMENT OF", + }, + "funding_office": None, + "recipient": { + "uuid": "88f47661-874f-5298-8b0b-0971be0c746d", + "uei": "HS97F2C9NME3", + "duns": None, + "legal_business_name": "ALABAMA PORT AUTHORITY", + "legal_business_name_raw": "ALABAMA PORT AUTHORITY", + "county": "MOBILE", + "congressional_district": "AL-02", + "congressional_district_original": "AL-02", + "highest_owner": None, + "business_types": [{"code": "A", "description": "State Government"}], + }, + "place_of_performance": { + "city_name": "MOBILE", + "code": "AL36633", + "country": "USA", + "county_fips": "01097", + "county_name": "MOBILE", + "foreign_location": None, + "scope": "SINGLE ZIP CODE", + "state_code": "AL", + "state_fips": "01", + "state_name": "ALABAMA", + "zip_code": "36633", + "zip_plus_four": "1588", + }, + "period_of_performance": {"start_date": "2026-10-19", "current_end_date": "2029-04-30"}, + } + row.update(overrides) + return row + + +def _contract_filter_params(segment: str) -> set[str]: + contract = json.loads(CONTRACT_PATH.read_text(encoding="utf-8")) + return set(contract["resources"][f"assistance/{segment}"]["runtime"]["filter_params"]) + + +def _filter_args(method) -> set[str]: + non_filter = {"self", "cursor", "limit", "shape", "flat", "flat_lists", "joiner", "exact"} + return set(inspect.signature(method).parameters) - non_filter + + +class TestListAssistanceAwards: + @pytest.mark.parametrize(("segment", "list_method"), [(g[0], g[1]) for g in GROUPS]) + def test_filters_go_out_under_the_api_names(self, segment, list_method): + with patch("tango.client.httpx.Client.request") as mock_request: + _mock(mock_request) + getattr(TangoClient(api_key="k"), list_method)(limit=10, **ALL_FILTERS) + + assert _call_url(mock_request).endswith(f"/api/assistance/{segment}/") + assert _call_params(mock_request) == { + "limit": 10, + "shape": ShapeConfig.ASSISTANCE_AWARDS_MINIMAL, + **ALL_FILTERS, + } + + @pytest.mark.parametrize(("segment", "list_method"), [(g[0], g[1]) for g in GROUPS]) + def test_signature_exposes_exactly_the_contract_filters(self, segment, list_method): + assert _filter_args(getattr(TangoClient, list_method)) == _contract_filter_params(segment) + + def test_contract_lists_every_assistance_resource(self): + contract = json.loads(CONTRACT_PATH.read_text(encoding="utf-8")) + assert {f"assistance/{s}" for s in SEGMENTS} <= set(contract["resources"]) + assert {"fain", "cfda_number", "pop_state", "obligated_gte"} <= _contract_filter_params( + "grants" + ) + + @patch("tango.client.httpx.Client.request") + def test_no_filters_are_synthesized(self, mock_request): + _mock(mock_request) + TangoClient(api_key="k").list_assistance_loans() + assert _call_params(mock_request) == { + "limit": 25, + "shape": ShapeConfig.ASSISTANCE_AWARDS_MINIMAL, + } + + @patch("tango.client.httpx.Client.request") + def test_cursor_exact_and_flattening_are_passed(self, mock_request): + _mock(mock_request) + TangoClient(api_key="k").list_assistance_direct_payments( + cursor="WyIyMDI2LTEwLTE2Il0", + limit=500, + shape="key,recipient(uei)", + flat=True, + joiner="__", + flat_lists=True, + exact=True, + ) + assert _call_params(mock_request) == { + "limit": 100, + "cursor": "WyIyMDI2LTEwLTE2Il0", + "shape": "key,recipient(uei)", + "flat": "true", + "joiner": "__", + "flat_lists": "true", + "exact": "true", + } + + @patch("tango.client.httpx.Client.request") + def test_empty_shape_sends_no_shape(self, mock_request): + _mock(mock_request) + TangoClient(api_key="k").list_assistance_other(shape="", flat=True) + assert _call_params(mock_request) == {"limit": 25} + + @patch("tango.client.httpx.Client.request") + def test_results_and_paging_are_parsed(self, mock_request): + _mock( + mock_request, + { + "count": 151, + "next": "https://tango.makegov.com/api/assistance/grants/?cursor=abc", + "previous": None, + "cursor": "abc", + "previous_cursor": None, + "results": [_award()], + }, + headers={"X-Results-CountType": "Approximate"}, + ) + page = TangoClient(api_key="k").list_assistance_grants(cfda_number="20.823") + + assert page.count == 151 + assert page.cursor == "abc" + assert page.count_type == "approximate" + row = page.results[0] + assert row["key"] == AWARD_KEY + assert row["award_date"] == date(2026, 10, 19) + assert row["obligated"] == Decimal("2000000.0") + assert isinstance(row["obligated"], Decimal) + assert row["assistance_type"]["code"] == "F001" + assert row["assistance_listing"]["title"] == "Port Infrastructure Development Program" + assert row["recipient"]["legal_business_name"] == "ALABAMA PORT AUTHORITY" + assert row["recipient"]["business_types"] == [ + {"code": "A", "description": "State Government"} + ] + assert row["awarding_office"]["office_code"] == "69A334" + assert row["funding_office"] is None + assert row["place_of_performance"]["state_code"] == "AL" + assert row["period_of_performance"]["current_end_date"] == date(2029, 4, 30) + + +class TestGetAssistanceAward: + @pytest.mark.parametrize(("segment", "get_method"), [(g[0], g[2]) for g in GROUPS]) + def test_get_uses_the_key_route_and_default_shape(self, segment, get_method): + with patch("tango.client.httpx.Client.request") as mock_request: + _mock(mock_request, _award()) + row = getattr(TangoClient(api_key="k"), get_method)(AWARD_KEY) + + assert _call_url(mock_request).endswith(f"/api/assistance/{segment}/{AWARD_KEY}/") + assert _call_params(mock_request) == {"shape": ShapeConfig.ASSISTANCE_AWARDS_MINIMAL} + assert row["key"] == AWARD_KEY + + @patch("tango.client.httpx.Client.request") + def test_key_is_escaped_as_one_path_segment(self, mock_request): + _mock(mock_request, _award()) + TangoClient(api_key="k").get_assistance_grant("ASST_NON_A/B#1") + assert _call_url(mock_request).endswith("/api/assistance/grants/ASST_NON_A%2FB%231/") + + def test_empty_key_is_refused_before_any_request(self): + with patch("tango.client.httpx.Client.request") as mock_request: + with pytest.raises(TangoValidationError): + TangoClient(api_key="k").get_assistance_loan("") + mock_request.assert_not_called() + + @patch("tango.client.httpx.Client.request") + def test_detail_expansions_parse(self, mock_request): + _mock( + mock_request, + { + "key": AWARD_KEY, + "last_modified_date": "2026-10-20T13:59:38+00:00", + "former_award_keys": ["ASST_NON_8606059100_7300"], + "officers": {"highly_compensated_officer_1_name": "JANE DOE"}, + "obligations_by_fiscal_year": [{"fiscal_year": 2027, "obligated": 2000000.0}], + "listings": [ + { + "cfda_number": "20.823", + "is_primary": True, + "obligated": 2000000.0, + "transactions_count": 1, + "first_action_date": "2026-10-19", + } + ], + "transactions": [ + {"key": "6938_693JF72640002", "transaction_date": "2026-10-19"}, + ], + }, + ) + row = TangoClient(api_key="k").get_assistance_grant( + AWARD_KEY, + shape=( + "key,last_modified_date,former_award_keys,officers(*)," + "obligations_by_fiscal_year(fiscal_year,obligated)," + "listings(cfda_number,is_primary,first_action_date),transactions(key,transaction_date)" + ), + ) + + assert row["last_modified_date"] == datetime(2026, 10, 20, 13, 59, 38, tzinfo=UTC) + assert row["former_award_keys"] == ["ASST_NON_8606059100_7300"] + assert row["officers"]["highly_compensated_officer_1_name"] == "JANE DOE" + assert isinstance(row["obligations_by_fiscal_year"][0]["obligated"], Decimal) + assert row["listings"][0]["is_primary"] is True + assert row["listings"][0]["first_action_date"] == date(2026, 10, 19) + assert row["transactions"][0]["transaction_date"] == date(2026, 10, 19) + + +class TestAssistanceAwardSubRoutes: + @pytest.mark.parametrize( + ("segment", "method", "sub_route", "default_limit"), + [(g[0], g[3], "transactions", 100) for g in GROUPS] + + [(g[0], g[4], "listings", 10) for g in GROUPS], + ) + def test_route_and_page_number_paging(self, segment, method, sub_route, default_limit): + with patch("tango.client.httpx.Client.request") as mock_request: + _mock(mock_request) + getattr(TangoClient(api_key="k"), method)(AWARD_KEY) + + assert _call_url(mock_request).endswith( + f"/api/assistance/{segment}/{AWARD_KEY}/{sub_route}/" + ) + assert _call_params(mock_request) == {"page": 1, "limit": default_limit} + + @pytest.mark.parametrize( + ("method", "cap"), + [("list_assistance_grant_transactions", 500), ("list_assistance_grant_listings", 100)], + ) + def test_limit_is_capped_at_the_route_maximum(self, method, cap): + with patch("tango.client.httpx.Client.request") as mock_request: + _mock(mock_request) + getattr(TangoClient(api_key="k"), method)(AWARD_KEY, page=3, limit=10_000) + assert _call_params(mock_request) == {"page": 3, "limit": cap} + + @patch("tango.client.httpx.Client.request") + def test_rows_come_back_as_dicts(self, mock_request): + transaction = { + "key": "6938_693JF72640002_-NONE-_20.823_-NONE-", + "transaction_date": "2026-10-19", + "action_type": "A1", + "obligated": 2000000.0, + "action_date_fiscal_year": 2027, + } + _mock( + mock_request, + { + "count": 2, + "next": "https://tango.makegov.com/api/assistance/loans/K/transactions/?page=2", + "previous": None, + "results": [transaction], + }, + ) + page = TangoClient(api_key="k").list_assistance_loan_transactions("K", limit=1) + + assert page.count == 2 + assert page.next is not None + assert page.results == [transaction] + + def test_empty_key_is_refused_before_any_request(self): + with patch("tango.client.httpx.Client.request") as mock_request: + with pytest.raises(TangoValidationError): + TangoClient(api_key="k").list_assistance_other_listings("") + mock_request.assert_not_called() + + +class TestAssistanceAwardShapes: + def test_default_shape_mirrors_the_api_default(self): + assert ShapeConfig.ASSISTANCE_AWARDS_MINIMAL == ( + "key,fain,uri,award_date,fiscal_year,assistance_type(code,description)," + "record_type,cfda_number,assistance_listing(number,title),title," + "obligated,pragmatic_obligations,face_value_of_loan,loan_subsidy_cost," + "awarding_office(*),funding_office(*),recipient(*),place_of_performance(*)," + "period_of_performance(start_date,current_end_date)" + ) + + @pytest.mark.parametrize("segment", SEGMENTS) + def test_every_field_and_expansion_the_api_allows_validates(self, segment): + contract = json.loads(CONTRACT_PATH.read_text(encoding="utf-8")) + tree = contract["resources"][f"assistance/{segment}"]["runtime"]["shape"] + leaves = [field for field in tree["fields"] if field not in tree["expands"]] + expands = [f"{name}({','.join(node['fields'])})" for name, node in tree["expands"].items()] + assert len(leaves) > 40 and len(expands) == 12 + + parser = ShapeParser(cache_enabled=True) + parser.validate(parser.parse(",".join(leaves + expands)), AssistanceAward) + + @pytest.mark.parametrize("shape", ["key,transactions", "key,assistance_type"]) + def test_leaf_forms_of_expansions_validate(self, shape): + parser = ShapeParser(cache_enabled=True) + parser.validate(parser.parse(shape), AssistanceAward) + + @pytest.mark.parametrize("field", ["piid", "naics_code", "description", "recipient_name"]) + def test_procurement_only_fields_are_rejected(self, field): + parser = ShapeParser(cache_enabled=True) + with pytest.raises(ShapeValidationError): + parser.validate(parser.parse(f"key,{field}"), AssistanceAward)