usaspending-orm 0.8.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/CHANGELOG.md +90 -7
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/PKG-INFO +68 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/README.md +67 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/api-docs-links.md +19 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/pyproject.toml +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/__init__.py +2 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/award.py +5 -5
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/award_types.py +24 -1
- usaspending_orm-0.9.0/src/usaspending/models/transaction.py +582 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/__init__.py +2 -0
- usaspending_orm-0.8.0/src/usaspending/queries/transactions_search.py → usaspending_orm-0.9.0/src/usaspending/queries/award_transactions_search.py +24 -12
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/awards_search.py +6 -31
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/query_builder.py +107 -15
- usaspending_orm-0.9.0/src/usaspending/queries/transactions_search.py +239 -0
- usaspending_orm-0.9.0/src/usaspending/resources/transactions_resource.py +63 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_contract.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_grant.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_idv.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_loan.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_parent_of_contract.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/idv_child_award_first_result.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/search_contract_first_result.json +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/public_api_surface.json +83 -5
- usaspending_orm-0.9.0/tests/fixtures/spending_by_transaction.json +214 -0
- usaspending_orm-0.9.0/tests/fixtures/spending_by_transaction_count.json +10 -0
- usaspending_orm-0.9.0/tests/fixtures/spending_by_transaction_mixed.json +210 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_transaction.py +42 -1
- usaspending_orm-0.9.0/tests/models/test_transaction_search_projection.py +456 -0
- usaspending_orm-0.8.0/tests/queries/test_transactions_search.py → usaspending_orm-0.9.0/tests/queries/test_award_transactions_search.py +54 -37
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_count_respects_limits.py +5 -5
- usaspending_orm-0.9.0/tests/queries/test_transactions_search.py +504 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_transactions_resource.py +22 -3
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_client_transactions.py +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_integration.py +52 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_readme_examples.py +1 -1
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/uv.lock +1 -1
- usaspending_orm-0.8.0/src/usaspending/models/transaction.py +0 -150
- usaspending_orm-0.8.0/src/usaspending/resources/transactions_resource.py +0 -39
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/.gitignore +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/LICENSE +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/cli/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/cli/download_award.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/client.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/config.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/download/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/download/job.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/download/manager.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/exceptions.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/logging_config.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/agency.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/award_account.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/award_factory.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/award_identifier.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/base_model.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/contract.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/district_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/download.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/federal_account.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/funding.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/grant.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/idv.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/lazy_record.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/loan.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/location.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/period_of_performance.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/procurement_award.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/recipient.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/recipient_business_categories.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/recipient_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/state_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/subaward.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/subtier_agency.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/models/treasury_account_symbol.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/agencies_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/agency_award_summary.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/agency_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/award_accounts_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/award_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/awarding_agencies_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/base_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/client_side_query_builder.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/federal_accounts_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/filter_tree_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/filters.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/funding_agencies_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/funding_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/idv_child_awards.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/mixins.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/recipient_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/recipients_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/single_resource_base.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/spending_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/sub_agency_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/subawards_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/tas_agencies_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/queries/tas_codes_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/agency_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/award_accounts_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/award_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/base_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/download_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/funding_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/recipients_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/spending_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/subawards_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/resources/tas_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/dates.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/numbers.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/rate_limit.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/retry.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/special_cases.yaml +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/textcase.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/src/usaspending/utils/validations.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/conftest.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/agency.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/agency_autocomplete.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/agency_award_summary.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/agency_subagencies.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/accounts.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/award_funding_grant.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/contract.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/grant.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/idv.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/idv_awards.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/loan.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/search_results_contracts.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/search_results_grants.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/search_results_idvs.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/search_results_subawards.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/awards/transactions.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/download_assistance.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/download_search.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/download_status.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/agency.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_account_first_result.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_contract_awarding_agency.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_contract_period_of_performance.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_contract_place_of_performance.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/award_contract_recipient.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/funding_first_result.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/recipient.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/search_subaward_first_result.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/spending_by_recipient_first_result.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/spending_by_state_first_result.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/golden/transaction_first_result.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/recipient_university.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/recipients_search.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/spending_by_award_contracts.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/spending_by_district.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/spending_by_recipient.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/spending_by_state.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/tas_agencies.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/tas_codes.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/tas_federal_accounts.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/top_recipients_params.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/fixtures/top_recipients_response.json +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/mocks/README.md +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/mocks/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/mocks/mock_client.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/mocks/response_builder.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/mocks/test_mock_client.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_agency.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_agency_subagencies.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_agency_tas_integration.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_account.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_agency_integration.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_category_registry.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_common.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_decimal_precision.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_factory.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_identifier.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_lazy_loading.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_office_integration.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_type_constants.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_award_types.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_base_model.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_district_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_federal_account.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_funding.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_lazy_load.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_location.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_period_of_performance.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_recipient.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_recipient_business_categories.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_recipient_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_search_projection.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_state_spending.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_subaward.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_subtier_agency.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_treasury_account_symbol.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/models/test_volatile_field_mapping.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_agencies_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_agency_award_summary.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_agency_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_award_accounts_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_awards_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_client_side_query_builder.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_federal_accounts_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_filters.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_funding_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_idv_child_awards.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_query_builder.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_query_builder_list_behavior.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_query_builder_pagination.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_recipient_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_recipients_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_search_query_builder.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_single_resource_base.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_spending_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_spending_search_count.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_sub_agency_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_subawards_search.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_tas_agencies_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/queries/test_tas_codes_query.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_agency_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_award_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_funding_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_recipients_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_spending_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_subawards_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/resources/test_tas_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/snapshot_support.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_award_accounts_integration.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_cache_fallback.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_characterization.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_client.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_client_caching.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_client_funding.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_client_rate_limit.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_client_security.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_config.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_download_resource.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_exceptions.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_golden_master_integration.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_logging_config.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_mock_client_example.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_public_api_surface.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/test_spending_integration.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/__init__.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/decimal_helpers.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_dates.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_doctests.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_numbers.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_rate_limit.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_retry.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_retry_session_reset.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_special_cases_data.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_textcase.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/utils/test_validations.py +0 -0
- {usaspending_orm-0.8.0 → usaspending_orm-0.9.0}/tests/volatile_fields.py +0 -0
|
@@ -4,6 +4,90 @@ All notable changes to the USASpending ORM library are documented here.
|
|
|
4
4
|
The format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and the project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.9.0] - 2026-07-30
|
|
8
|
+
|
|
9
|
+
Adds the global transaction search: `client.transactions.search()` covers the
|
|
10
|
+
`/search/spending_by_transaction/` endpoint with the full fluent filter set,
|
|
11
|
+
mixed award-type categories, exact bucketed counts, and a `Transaction` model
|
|
12
|
+
that reads both of the API's row shapes.
|
|
13
|
+
|
|
14
|
+
**Breaking changes**
|
|
15
|
+
|
|
16
|
+
- `TransactionsSearch` is renamed to `AwardTransactionsSearch`. The freed name is
|
|
17
|
+
reused in this release for the new global `/search/spending_by_transaction/`
|
|
18
|
+
builder, so code importing `TransactionsSearch` directly keeps importing
|
|
19
|
+
cleanly but gets a different class rather than an `ImportError`. No
|
|
20
|
+
deprecation alias is provided. `client.transactions.award_id()` and
|
|
21
|
+
`award.transactions` are unchanged in both behavior and return value; only the
|
|
22
|
+
class and module names moved.
|
|
23
|
+
|
|
24
|
+
- `Transaction.transaction_amount` and its alias `amt` now return the first
|
|
25
|
+
nonzero of `federal_action_obligation`, `face_value_loan_guarantee` and
|
|
26
|
+
`original_loan_subsidy_cost`, and return the zero when every figure the row
|
|
27
|
+
carries is zero. Previously a zero obligation read as no value at all, so a
|
|
28
|
+
genuine zero-dollar transaction was indistinguishable from a row carrying no
|
|
29
|
+
amount, and a loan row, which reports a zero obligation with the real figure
|
|
30
|
+
in the loan fields, reported nothing. Negative figures such as deobligations
|
|
31
|
+
are preserved rather than skipped over. `amt is None` used to be true for
|
|
32
|
+
every zero-dollar transaction and is now true only for a row carrying none of
|
|
33
|
+
the three fields, so an `is None` check standing in for "no money moved"
|
|
34
|
+
needs `not amt` instead, and loan rows now report their face value, which
|
|
35
|
+
changes any total taken over a loan result set. Note that USAspending.gov's
|
|
36
|
+
own internal "pragmatic obligation" convention uses the subsidy cost, not
|
|
37
|
+
the face value, as a loan transaction's budgetary figure; this library
|
|
38
|
+
reports the face value the website displays, and
|
|
39
|
+
`original_loan_subsidy_cost` remains directly readable for budgetary totals.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- `client.transactions.search()`, returning the new `TransactionsSearch` query
|
|
44
|
+
builder over `/search/spending_by_transaction/`. It searches every
|
|
45
|
+
transaction the API holds rather than the transactions of one award, and
|
|
46
|
+
takes the same fluent filters the award search takes: `keywords()`,
|
|
47
|
+
`agency()`, `time_period()` and `fiscal_year()`, the location, code and
|
|
48
|
+
amount filters, plus `order_by()` over the endpoint's sort fields. An
|
|
49
|
+
`award_type_codes` filter is required, and unlike the award search this
|
|
50
|
+
endpoint accepts codes from more than one category at once, so
|
|
51
|
+
`.contracts().grants()` is a single valid query where the same chain on
|
|
52
|
+
`client.awards.search()` raises `ValidationError`. `count()` and `len()` read
|
|
53
|
+
`/search/spending_by_transaction_count/`, summing the buckets of the
|
|
54
|
+
categories the query selected. `TransactionsSearch` is exported from
|
|
55
|
+
`usaspending` and `usaspending.queries`.
|
|
56
|
+
|
|
57
|
+
- `Transaction` reads both row shapes the API sends: the snake_case keys of the
|
|
58
|
+
award-scoped `/transactions/` listing and the display-name keys of the global
|
|
59
|
+
search (`"Action Date"`, `"Transaction Amount"` and the rest). The existing
|
|
60
|
+
properties gained the second spelling, and the search rows carry fields the
|
|
61
|
+
award-scoped listing never had: `award_internal_id`,
|
|
62
|
+
`generated_unique_award_id`, `award_identifier`, `recipient_name`,
|
|
63
|
+
`recipient_uei`, `recipient_id`, the four agency-name properties,
|
|
64
|
+
`issued_date`, `last_date_to_order`, `naics_code`, `naics_description`,
|
|
65
|
+
`psc_code`, `psc_description`, `cfda_title`, `def_codes`,
|
|
66
|
+
`recipient_location` and `place_of_performance`. `id` is `None` for a global
|
|
67
|
+
search row, which identifies only the parent award, and
|
|
68
|
+
`transaction_description` is the preferred name for what `award_description`
|
|
69
|
+
has always returned. Every raw row key also has a getter under its
|
|
70
|
+
snake_cased name (`award_id`, `mod`, `award_type`, `loan_value`,
|
|
71
|
+
`subsidy_cost`, `description`, `primary_place_of_performance`, `amount`),
|
|
72
|
+
aliasing the normalized helpers, with the exceptions documented in the
|
|
73
|
+
model: agency names live under `*_agency_name`, dict-valued classifications
|
|
74
|
+
under their scalar helpers, and the server's award identifiers under
|
|
75
|
+
`award_internal_id` and `generated_unique_award_id`.
|
|
76
|
+
|
|
77
|
+
### Known limitations
|
|
78
|
+
|
|
79
|
+
- `TransactionsSearch` is bounded by the API's 50,000-row result window.
|
|
80
|
+
Iterating past that point raises `APIError` rather than truncating silently.
|
|
81
|
+
Narrow the filters, set `limit()`, or use `client.downloads` for larger
|
|
82
|
+
result sets; `count()` is not subject to the window.
|
|
83
|
+
|
|
84
|
+
- With a `program_activities()` filter set, `TransactionsSearch.count()` and
|
|
85
|
+
`len()` raise `ValidationError`: the count endpoint silently ignores that
|
|
86
|
+
filter while the search endpoint honors it, so passing it through would
|
|
87
|
+
report a confidently wrong count. `list(query)`, indexing and slicing
|
|
88
|
+
consult the count and so raise too; iteration, `all()`, `first()` and
|
|
89
|
+
`bool()` need no count and work regardless.
|
|
90
|
+
|
|
7
91
|
## [0.8.0] - 2026-07-28
|
|
8
92
|
|
|
9
93
|
Mostly refactoring internals to DRY the codebase and standardize the query interface.
|
|
@@ -262,7 +346,7 @@ truthiness agree under the bounds you set. Adding CI tests to verify compatabili
|
|
|
262
346
|
`q1 = search().keywords("alpha")` followed by `q2 = q1.keywords("beta")`
|
|
263
347
|
left `q1` sending `["alpha", "beta", "beta"]`. Affected chained `keywords`,
|
|
264
348
|
`award_type_codes`, simple `psc_codes` and `treasury_account_components`
|
|
265
|
-
filters.
|
|
349
|
+
filters.
|
|
266
350
|
|
|
267
351
|
- `client.recipients.find_by_recipient_id()` no longer addresses the wrong
|
|
268
352
|
record for a recipient ID carrying several levels, such as
|
|
@@ -274,7 +358,7 @@ truthiness agree under the bounds you set. Adding CI tests to verify compatabili
|
|
|
274
358
|
pass the suffix yourself (see `Recipient.recipient_level` under Added).
|
|
275
359
|
`client.spending.search().recipient_id()` also normalizes its argument now,
|
|
276
360
|
so a multi-level ID copied out of `raw` produces a filter the API can
|
|
277
|
-
match.
|
|
361
|
+
match.
|
|
278
362
|
|
|
279
363
|
- `award.transactions` no longer reports a count that disagrees with what it
|
|
280
364
|
yields. `since()` and `until()` filter in memory, but the count came from
|
|
@@ -282,7 +366,7 @@ truthiness agree under the bounds you set. Adding CI tests to verify compatabili
|
|
|
282
366
|
and `len()` said three where iteration yielded two, slices disagreed with
|
|
283
367
|
`len()`, and `query[-1]` raised `IndexError`. All of them now agree, and
|
|
284
368
|
`limit(1)` means one matching row rather than one row that may then be
|
|
285
|
-
discarded.
|
|
369
|
+
discarded.
|
|
286
370
|
|
|
287
371
|
- A `Recipient` built directly from an award search result now reports its
|
|
288
372
|
`uei` and `location`, which read only the nested spellings a detail
|
|
@@ -291,7 +375,7 @@ truthiness agree under the bounds you set. Adding CI tests to verify compatabili
|
|
|
291
375
|
not. `PeriodOfPerformance` had the same gap for `"Base Obligation Date"`.
|
|
292
376
|
Both models now read every spelling of their own fields, and `Award` hands
|
|
293
377
|
its payload over rather than reproducing the mapping; nested spellings
|
|
294
|
-
still take precedence where a payload carries both.
|
|
378
|
+
still take precedence where a payload carries both.
|
|
295
379
|
|
|
296
380
|
- Indexing and slicing an in-memory query now read the limited collection:
|
|
297
381
|
`agency.federal_accounts.limit(3)[:]` returned every account and
|
|
@@ -303,10 +387,10 @@ truthiness agree under the bounds you set. Adding CI tests to verify compatabili
|
|
|
303
387
|
fieldless `@dataclass` declaration generated an `__eq__` comparing empty
|
|
304
388
|
tuples, so any two transactions were equal and none was hashable. They now
|
|
305
389
|
compare by identity, like every other model, and can be used in sets and as
|
|
306
|
-
dict keys.
|
|
390
|
+
dict keys.
|
|
307
391
|
|
|
308
392
|
- `Recipient.parents` returns a fresh list on each read, so sorting or
|
|
309
|
-
popping the result no longer corrupts the model.
|
|
393
|
+
popping the result no longer corrupts the model.
|
|
310
394
|
|
|
311
395
|
- Iterating `idv.child_awards` no longer raises
|
|
312
396
|
`AttributeError: 'str' object has no attribute 'get'` when reading the four
|
|
@@ -344,7 +428,6 @@ truthiness agree under the bounds you set. Adding CI tests to verify compatabili
|
|
|
344
428
|
- `FederalAccount.count` no longer fires an API request when the count is
|
|
345
429
|
already in the response; the fallback was an eagerly evaluated default
|
|
346
430
|
argument, so every access paid for a request whose result was discarded.
|
|
347
|
-
|
|
348
431
|
|
|
349
432
|
- `query.limit(0).first()` returned a row, contradicting `.all()` and
|
|
350
433
|
`len()`. It now respects the zero, which is also what makes `__bool__`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usaspending-orm
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: A Python ORM wrapper for the USAspending.gov API with intuitive queries, automatic pagination, and smart caching
|
|
5
5
|
Project-URL: Homepage, https://github.com/planetary-society/usaspending-orm
|
|
6
6
|
Project-URL: Repository, https://github.com/planetary-society/usaspending-orm
|
|
@@ -168,6 +168,73 @@ with USASpendingClient() as client:
|
|
|
168
168
|
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
+
### Searching Transactions Across Awards
|
|
172
|
+
|
|
173
|
+
`award.transactions` lists the modifications of a single award. `client.transactions.search()` searches every transaction the API holds, takes the same fluent filters the award search takes, and returns one row per transaction rather than one row per award:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
with USASpendingClient() as client:
|
|
177
|
+
|
|
178
|
+
# Award-level keyword search: one row per matching award
|
|
179
|
+
awards = client.awards.search() \
|
|
180
|
+
.contracts() \
|
|
181
|
+
.agency("National Aeronautics and Space Administration") \
|
|
182
|
+
.keywords("Mars") \
|
|
183
|
+
.fiscal_year(2024)
|
|
184
|
+
|
|
185
|
+
print(awards.count()) # -> 106
|
|
186
|
+
|
|
187
|
+
# Transaction-level keyword search: one row per matching transaction,
|
|
188
|
+
# across every award rather than within one
|
|
189
|
+
transactions = client.transactions.search() \
|
|
190
|
+
.contracts() \
|
|
191
|
+
.agency("National Aeronautics and Space Administration") \
|
|
192
|
+
.keywords("Mars") \
|
|
193
|
+
.fiscal_year(2024) \
|
|
194
|
+
.order_by("transaction_amount", "desc")
|
|
195
|
+
|
|
196
|
+
print(transactions.count()) # -> 264
|
|
197
|
+
|
|
198
|
+
for transaction in transactions.limit(3):
|
|
199
|
+
print(f"{transaction.action_date} {transaction.award_identifier}: ${transaction.amount or 0:,.2f}")
|
|
200
|
+
|
|
201
|
+
# 2023-12-15 80NM0021F0008: $45,874,344.00
|
|
202
|
+
# 2023-11-01 80NM0021F0008: $39,011,361.00
|
|
203
|
+
# 2024-04-12 80NM0021F0008: $36,306,819.00
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
An award type filter is required. Unlike the award search, this endpoint accepts codes from more than one category at once, so contracts and grants can be searched in a single query:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
with USASpendingClient() as client:
|
|
210
|
+
|
|
211
|
+
mixed = client.transactions.search() \
|
|
212
|
+
.contracts() \
|
|
213
|
+
.grants() \
|
|
214
|
+
.agency("National Aeronautics and Space Administration") \
|
|
215
|
+
.keywords("asteroid") \
|
|
216
|
+
.fiscal_year(2024) \
|
|
217
|
+
.order_by("action_date", "desc")
|
|
218
|
+
|
|
219
|
+
print(mixed.count()) # -> 115
|
|
220
|
+
|
|
221
|
+
for transaction in mixed.limit(5):
|
|
222
|
+
print(f"{transaction.action_date} {transaction.type_description}: ${transaction.amount or 0:,.2f}")
|
|
223
|
+
|
|
224
|
+
# 2024-09-25 COOPERATIVE AGREEMENT (B): $319,834.00
|
|
225
|
+
# 2024-09-23 PROJECT GRANT (B): $-0.26
|
|
226
|
+
# 2024-09-19 COOPERATIVE AGREEMENT (B): $0.00
|
|
227
|
+
# 2024-09-18 COOPERATIVE AGREEMENT (B): $0.00
|
|
228
|
+
# 2024-09-18 DEFINITIVE CONTRACT: $3,815,000.00
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The same chain on `client.awards.search()` raises `ValidationError`, because `spending_by_award` offers different fields and filters per category and so accepts only one at a time.
|
|
232
|
+
|
|
233
|
+
Two limits are worth knowing:
|
|
234
|
+
|
|
235
|
+
- The API serves at most the first 50,000 matching rows. Iterating past that point raises `APIError` rather than truncating silently, so narrow the filters, set a `limit()`, or use `client.downloads` for larger result sets. `count()` is not subject to the window.
|
|
236
|
+
- `count()` reads the dedicated transaction count endpoint, which reports one bucket per award-type category, so the figure is the sum of the buckets the query selected. That endpoint does not accept the `program_activities` filter, so with one set `count()` and `len()` raise `ValidationError`; iterating the query and calling `.all()` work regardless, as neither needs a count.
|
|
237
|
+
|
|
171
238
|
### Example: Top National Aeronautics and Space Administration Contractors in Fiscal Year 2024
|
|
172
239
|
|
|
173
240
|
Aggregation endpoints are available through `client.spending`. The same fluent filters
|
|
@@ -139,6 +139,73 @@ with USASpendingClient() as client:
|
|
|
139
139
|
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### Searching Transactions Across Awards
|
|
143
|
+
|
|
144
|
+
`award.transactions` lists the modifications of a single award. `client.transactions.search()` searches every transaction the API holds, takes the same fluent filters the award search takes, and returns one row per transaction rather than one row per award:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
with USASpendingClient() as client:
|
|
148
|
+
|
|
149
|
+
# Award-level keyword search: one row per matching award
|
|
150
|
+
awards = client.awards.search() \
|
|
151
|
+
.contracts() \
|
|
152
|
+
.agency("National Aeronautics and Space Administration") \
|
|
153
|
+
.keywords("Mars") \
|
|
154
|
+
.fiscal_year(2024)
|
|
155
|
+
|
|
156
|
+
print(awards.count()) # -> 106
|
|
157
|
+
|
|
158
|
+
# Transaction-level keyword search: one row per matching transaction,
|
|
159
|
+
# across every award rather than within one
|
|
160
|
+
transactions = client.transactions.search() \
|
|
161
|
+
.contracts() \
|
|
162
|
+
.agency("National Aeronautics and Space Administration") \
|
|
163
|
+
.keywords("Mars") \
|
|
164
|
+
.fiscal_year(2024) \
|
|
165
|
+
.order_by("transaction_amount", "desc")
|
|
166
|
+
|
|
167
|
+
print(transactions.count()) # -> 264
|
|
168
|
+
|
|
169
|
+
for transaction in transactions.limit(3):
|
|
170
|
+
print(f"{transaction.action_date} {transaction.award_identifier}: ${transaction.amount or 0:,.2f}")
|
|
171
|
+
|
|
172
|
+
# 2023-12-15 80NM0021F0008: $45,874,344.00
|
|
173
|
+
# 2023-11-01 80NM0021F0008: $39,011,361.00
|
|
174
|
+
# 2024-04-12 80NM0021F0008: $36,306,819.00
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
An award type filter is required. Unlike the award search, this endpoint accepts codes from more than one category at once, so contracts and grants can be searched in a single query:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
with USASpendingClient() as client:
|
|
181
|
+
|
|
182
|
+
mixed = client.transactions.search() \
|
|
183
|
+
.contracts() \
|
|
184
|
+
.grants() \
|
|
185
|
+
.agency("National Aeronautics and Space Administration") \
|
|
186
|
+
.keywords("asteroid") \
|
|
187
|
+
.fiscal_year(2024) \
|
|
188
|
+
.order_by("action_date", "desc")
|
|
189
|
+
|
|
190
|
+
print(mixed.count()) # -> 115
|
|
191
|
+
|
|
192
|
+
for transaction in mixed.limit(5):
|
|
193
|
+
print(f"{transaction.action_date} {transaction.type_description}: ${transaction.amount or 0:,.2f}")
|
|
194
|
+
|
|
195
|
+
# 2024-09-25 COOPERATIVE AGREEMENT (B): $319,834.00
|
|
196
|
+
# 2024-09-23 PROJECT GRANT (B): $-0.26
|
|
197
|
+
# 2024-09-19 COOPERATIVE AGREEMENT (B): $0.00
|
|
198
|
+
# 2024-09-18 COOPERATIVE AGREEMENT (B): $0.00
|
|
199
|
+
# 2024-09-18 DEFINITIVE CONTRACT: $3,815,000.00
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The same chain on `client.awards.search()` raises `ValidationError`, because `spending_by_award` offers different fields and filters per category and so accepts only one at a time.
|
|
203
|
+
|
|
204
|
+
Two limits are worth knowing:
|
|
205
|
+
|
|
206
|
+
- The API serves at most the first 50,000 matching rows. Iterating past that point raises `APIError` rather than truncating silently, so narrow the filters, set a `limit()`, or use `client.downloads` for larger result sets. `count()` is not subject to the window.
|
|
207
|
+
- `count()` reads the dedicated transaction count endpoint, which reports one bucket per award-type category, so the figure is the sum of the buckets the query selected. That endpoint does not accept the `program_activities` filter, so with one set `count()` and `len()` raise `ValidationError`; iterating the query and calling `.all()` work regardless, as neither needs a count.
|
|
208
|
+
|
|
142
209
|
### Example: Top National Aeronautics and Space Administration Contractors in Fiscal Year 2024
|
|
143
210
|
|
|
144
211
|
Aggregation endpoints are available through `client.spending`. The same fluent filters
|
|
@@ -83,7 +83,7 @@ These documentation links provide detailed information about request parameters,
|
|
|
83
83
|
- **Method**: POST
|
|
84
84
|
- **Documentation**: https://raw.githubusercontent.com/fedspendingtransparency/usaspending-api/refs/heads/master/usaspending_api/api_contracts/contracts/v2/transactions.md
|
|
85
85
|
- **Purpose**: List obligations and modifications for a given award
|
|
86
|
-
- **Corresponding Query Builder**: `src/usaspending/queries/
|
|
86
|
+
- **Corresponding Query Builder**: `src/usaspending/queries/award_transactions_search.py`
|
|
87
87
|
|
|
88
88
|
### Award Transaction Count
|
|
89
89
|
|
|
@@ -91,7 +91,25 @@ These documentation links provide detailed information about request parameters,
|
|
|
91
91
|
- **Method**: GET
|
|
92
92
|
- **Documentation**: https://raw.githubusercontent.com/fedspendingtransparency/usaspending-api/refs/heads/master/usaspending_api/api_contracts/contracts/v2/awards/count/transaction/award_id.md
|
|
93
93
|
- **Purpose**: Return the number of transactions associated with a given award
|
|
94
|
+
- **Corresponding Query Builder**: `src/usaspending/queries/award_transactions_search.py`
|
|
95
|
+
|
|
96
|
+
### Transaction Search
|
|
97
|
+
|
|
98
|
+
- **Endpoint**: `/api/v2/search/spending_by_transaction/`
|
|
99
|
+
- **Method**: POST
|
|
100
|
+
- **Documentation**: https://raw.githubusercontent.com/fedspendingtransparency/usaspending-api/refs/heads/master/usaspending_api/api_contracts/contracts/v2/search/spending_by_transaction.md
|
|
101
|
+
- **Purpose**: Search transactions across all awards using the standard search filters
|
|
102
|
+
- **Corresponding Query Builder**: `src/usaspending/queries/transactions_search.py`
|
|
103
|
+
- **Note**: `filters.award_type_codes`, `fields`, `sort` and `order` are all required, and the sort field must be one of the requested `fields`. Unlike `/api/v2/search/spending_by_award/`, this endpoint accepts award type codes from more than one category in a single request. The API serves at most the first 50,000 matching rows and returns an error past that point.
|
|
104
|
+
|
|
105
|
+
### Transaction Search Count
|
|
106
|
+
|
|
107
|
+
- **Endpoint**: `/api/v2/search/spending_by_transaction_count/`
|
|
108
|
+
- **Method**: POST
|
|
109
|
+
- **Documentation**: https://raw.githubusercontent.com/fedspendingtransparency/usaspending-api/refs/heads/master/usaspending_api/api_contracts/contracts/v2/search/spending_by_transaction_count.md
|
|
110
|
+
- **Purpose**: Return transaction counts by award-type category for a set of search filters
|
|
94
111
|
- **Corresponding Query Builder**: `src/usaspending/queries/transactions_search.py`
|
|
112
|
+
- **Note**: The response reports one bucket per category (`contracts`, `idvs`, `grants`, `loans`, `direct_payments`, `other`), so a count for a query is the sum of the buckets its award type codes select. This endpoint's validator does not accept the `program_activities` filter, which the search endpoint does accept.
|
|
95
113
|
|
|
96
114
|
### Award funding history
|
|
97
115
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "usaspending-orm"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
description = "A Python ORM wrapper for the USAspending.gov API with intuitive queries, automatic pagination, and smart caching"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Casey Dreier", email = "casey.dreier@planetary.org"}
|
|
@@ -60,6 +60,7 @@ from .queries import (
|
|
|
60
60
|
AgenciesSearch,
|
|
61
61
|
AwardingAgenciesSearch,
|
|
62
62
|
AwardsSearch,
|
|
63
|
+
AwardTransactionsSearch,
|
|
63
64
|
FundingAgenciesSearch,
|
|
64
65
|
FundingSearch,
|
|
65
66
|
RecipientsSearch,
|
|
@@ -98,6 +99,7 @@ __all__ = [
|
|
|
98
99
|
"Award",
|
|
99
100
|
"AwardAmountFilter",
|
|
100
101
|
"AwardDateType",
|
|
102
|
+
"AwardTransactionsSearch",
|
|
101
103
|
"AwardingAgenciesSearch",
|
|
102
104
|
# Query builders
|
|
103
105
|
"AwardsSearch",
|
|
@@ -27,9 +27,9 @@ if TYPE_CHECKING:
|
|
|
27
27
|
from ..client import USASpendingClient
|
|
28
28
|
from ..download.job import DownloadJob
|
|
29
29
|
from ..queries.award_accounts_query import AwardAccountsQuery
|
|
30
|
+
from ..queries.award_transactions_search import AwardTransactionsSearch
|
|
30
31
|
from ..queries.funding_search import FundingSearch
|
|
31
32
|
from ..queries.subawards_search import SubAwardsSearch
|
|
32
|
-
from ..queries.transactions_search import TransactionsSearch
|
|
33
33
|
|
|
34
34
|
logger = USASpendingLogger.get_logger(__name__)
|
|
35
35
|
|
|
@@ -488,7 +488,7 @@ class Award(LazyRecord):
|
|
|
488
488
|
# - place_of_performance (Location object: location where the work is performed)
|
|
489
489
|
#
|
|
490
490
|
# Has Many (one-to-many relationships):
|
|
491
|
-
# - transactions (
|
|
491
|
+
# - transactions (AwardTransactionsSearch object: query builder for transactions associated with the award)
|
|
492
492
|
# - funding (FundingSearch object: query builder for treasury funding records (outlay and obligation) associated with the award)
|
|
493
493
|
# - subawards (SubAwardsSearch object: query builder for subawards associated with the award)
|
|
494
494
|
|
|
@@ -702,10 +702,10 @@ class Award(LazyRecord):
|
|
|
702
702
|
return self._build_subtier_agency("awarding")
|
|
703
703
|
|
|
704
704
|
@property
|
|
705
|
-
def transactions(self) ->
|
|
705
|
+
def transactions(self) -> AwardTransactionsSearch:
|
|
706
706
|
"""Get transactions query builder for this award.
|
|
707
707
|
|
|
708
|
-
Returns
|
|
708
|
+
Returns an AwardTransactionsSearch object that can be further filtered and chained.
|
|
709
709
|
|
|
710
710
|
Examples:
|
|
711
711
|
>>> award.transactions.count() # Get count without loading all data
|
|
@@ -713,7 +713,7 @@ class Award(LazyRecord):
|
|
|
713
713
|
>>> award.transactions.all() # Every transaction, as a list
|
|
714
714
|
|
|
715
715
|
Returns:
|
|
716
|
-
|
|
716
|
+
AwardTransactionsSearch: The query builder for transactions.
|
|
717
717
|
"""
|
|
718
718
|
return self._client.transactions.award_id(self.generated_unique_award_id)
|
|
719
719
|
|
|
@@ -23,11 +23,13 @@ missing, so the omission surfaces immediately.
|
|
|
23
23
|
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
|
-
from collections.abc import Mapping
|
|
26
|
+
from collections.abc import Iterable, Mapping
|
|
27
27
|
from dataclasses import dataclass
|
|
28
28
|
from functools import cached_property
|
|
29
29
|
from types import MappingProxyType
|
|
30
30
|
|
|
31
|
+
from ..exceptions import ValidationError
|
|
32
|
+
|
|
31
33
|
|
|
32
34
|
@dataclass(frozen=True)
|
|
33
35
|
class AwardCategory:
|
|
@@ -308,6 +310,27 @@ def is_valid_award_type(code: str) -> bool:
|
|
|
308
310
|
return code in ALL_AWARD_CODES
|
|
309
311
|
|
|
310
312
|
|
|
313
|
+
def validate_award_type_codes(codes: Iterable[str]) -> None:
|
|
314
|
+
"""Raise if any supplied code is not a valid award type code.
|
|
315
|
+
|
|
316
|
+
The award-search and transaction-search builders both accept caller-supplied
|
|
317
|
+
codes and share this check, so the error message cannot drift between them.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
codes: Award type codes to validate.
|
|
321
|
+
|
|
322
|
+
Raises:
|
|
323
|
+
ValidationError: If any code is not in :data:`ALL_AWARD_CODES`, naming
|
|
324
|
+
the offending codes and listing the valid ones.
|
|
325
|
+
"""
|
|
326
|
+
invalid_codes = [code for code in codes if code not in ALL_AWARD_CODES]
|
|
327
|
+
if invalid_codes:
|
|
328
|
+
raise ValidationError(
|
|
329
|
+
f"Invalid award type code(s): {', '.join(sorted(invalid_codes))}. "
|
|
330
|
+
f"Valid codes are: {', '.join(sorted(ALL_AWARD_CODES))}"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
311
334
|
def get_description(code: str) -> str:
|
|
312
335
|
"""Get the description for a given award type code.
|
|
313
336
|
|