tango-python 1.11.0__tar.gz → 1.12.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.
Files changed (208) hide show
  1. {tango_python-1.11.0 → tango_python-1.12.0}/CHANGELOG.md +16 -0
  2. {tango_python-1.11.0 → tango_python-1.12.0}/PKG-INFO +1 -1
  3. {tango_python-1.11.0 → tango_python-1.12.0}/docs/API_REFERENCE.md +14 -2
  4. {tango_python-1.11.0 → tango_python-1.12.0}/pyproject.toml +1 -1
  5. {tango_python-1.11.0 → tango_python-1.12.0}/tango/__init__.py +7 -1
  6. {tango_python-1.11.0 → tango_python-1.12.0}/tango/client.py +16 -1
  7. {tango_python-1.11.0 → tango_python-1.12.0}/tango/models.py +55 -2
  8. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/generated_overlay.py +6 -0
  9. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_client.py +135 -0
  10. {tango_python-1.11.0 → tango_python-1.12.0}/.gitignore +0 -0
  11. {tango_python-1.11.0 → tango_python-1.12.0}/LICENSE +0 -0
  12. {tango_python-1.11.0 → tango_python-1.12.0}/README.md +0 -0
  13. {tango_python-1.11.0 → tango_python-1.12.0}/docs/CLIENT.md +0 -0
  14. {tango_python-1.11.0 → tango_python-1.12.0}/docs/DYNAMIC_MODELS.md +0 -0
  15. {tango_python-1.11.0 → tango_python-1.12.0}/docs/ERRORS.md +0 -0
  16. {tango_python-1.11.0 → tango_python-1.12.0}/docs/PAGINATION.md +0 -0
  17. {tango_python-1.11.0 → tango_python-1.12.0}/docs/SHAPES.md +0 -0
  18. {tango_python-1.11.0 → tango_python-1.12.0}/docs/WEBHOOKS.md +0 -0
  19. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/README.md +0 -0
  20. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/check_filter_shape_conformance.py +0 -0
  21. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/check_shape_coverage.py +0 -0
  22. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/fetch_api_schema.py +0 -0
  23. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/generate_schemas_from_api.py +0 -0
  24. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/generate_shape_overlay.py +0 -0
  25. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/pr_review.py +0 -0
  26. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/probe_shape_types.py +0 -0
  27. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/refresh_contract.py +0 -0
  28. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/smoke_api_parity.py +0 -0
  29. {tango_python-1.11.0 → tango_python-1.12.0}/scripts/test_production.py +0 -0
  30. {tango_python-1.11.0 → tango_python-1.12.0}/tango/exceptions.py +0 -0
  31. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/__init__.py +0 -0
  32. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/explicit_schemas.py +0 -0
  33. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/factory.py +0 -0
  34. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/generator.py +0 -0
  35. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/models.py +0 -0
  36. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/parser.py +0 -0
  37. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/schema.py +0 -0
  38. {tango_python-1.11.0 → tango_python-1.12.0}/tango/shapes/types.py +0 -0
  39. {tango_python-1.11.0 → tango_python-1.12.0}/tango/webhooks/__init__.py +0 -0
  40. {tango_python-1.11.0 → tango_python-1.12.0}/tango/webhooks/cli.py +0 -0
  41. {tango_python-1.11.0 → tango_python-1.12.0}/tango/webhooks/receiver.py +0 -0
  42. {tango_python-1.11.0 → tango_python-1.12.0}/tango/webhooks/signing.py +0 -0
  43. {tango_python-1.11.0 → tango_python-1.12.0}/tango/webhooks/simulate.py +0 -0
  44. {tango_python-1.11.0 → tango_python-1.12.0}/tests/__init__.py +0 -0
  45. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
  46. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
  47. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
  48. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
  49. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
  50. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
  51. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
  52. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
  53. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
  54. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
  55. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
  56. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
  57. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
  58. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
  59. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
  60. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
  61. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
  62. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[detailed-key,piid,award_date,description,total_contract_value,obligated,fiscal_year,set_aside,recipient(display_name,uei),awarding_office(-),place_of_performa...ce114a3c47e2037aaa3c15d00b7031bd +0 -0
  63. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  64. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
  65. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
  66. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
  67. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
  68. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
  69. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
  70. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
  71. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
  72. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
  73. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
  74. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
  75. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
  76. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
  77. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
  78. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
  79. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
  80. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
  81. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
  82. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
  83. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
  84. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
  85. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
  86. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_budget_flows +0 -0
  87. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
  88. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
  89. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
  90. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[comprehensive-uei,legal_business_name,dba_name,cage_code,business_types,primary_naics,naics_codes,psc_codes,email_address,entity_url,description,capabilities,ke...1603a7d52e211cf2b3bc7d32080238aa +0 -0
  91. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
  92. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  93. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  94. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
  95. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
  96. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
  97. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[detailed-id,source_system,external_id,title,description,anticipated_award_date,fiscal_year,naics_code,status,is_active] +0 -0
  98. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
  99. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
  100. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
  101. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
  102. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
  103. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[detailed-grant_id,opportunity_number,title,status(-),agency_code,description,last_updated,cfda_numbers(number,title),applicant_types(-),funding_categories(-)] +0 -0
  104. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
  105. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
  106. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
  107. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
  108. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
  109. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
  110. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
  111. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
  112. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
  113. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
  114. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
  115. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
  116. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
  117. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
  118. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
  119. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
  120. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
  121. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
  122. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
  123. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[minimal-uii,agency_name,bureau_name,investment_title,type_of_investment,part_of_it_portfolio,updated_time,url] +0 -0
  124. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
  125. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
  126. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
  127. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[detailed-notice_id,title,description,solicitation_number,posted_date,naics_code,set_aside,office(-),place_of_performance(-)] +0 -0
  128. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  129. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
  130. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
  131. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
  132. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
  133. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
  134. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
  135. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
  136. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
  137. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
  138. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
  139. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
  140. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[detailed-opportunity_id,title,description,solicitation_number,response_deadline,first_notice_date,last_notice_date,active,naics_code,psc_code,set_asid...23b6b4502ddd665b7184afcff6c6d8d9 +0 -0
  141. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  142. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
  143. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
  144. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
  145. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
  146. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
  147. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
  148. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
  149. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
  150. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[with_dockets-case_id,case_number,title,outcome,filed_date,dockets(docket_number,filed_date,outcome)] +0 -0
  151. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
  152. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
  153. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
  154. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  155. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
  156. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  157. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  158. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[detailed-notice_id,title,description,solicitation_number,posted_date,naics_code,set_aside,office(-),place_of_performance(-)] +0 -0
  159. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  160. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[detailed-opportunity_id,title,description,solicitation_number,response_deadline,first_notice_date,last_notice_date,active,naics_code,psc_code,set_aside,sam_url,office(-),place_of_performance(-)] +0 -0
  161. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  162. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
  163. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
  164. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
  165. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
  166. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
  167. {tango_python-1.11.0 → tango_python-1.12.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
  168. {tango_python-1.11.0 → tango_python-1.12.0}/tests/conftest.py +0 -0
  169. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/README.md +0 -0
  170. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/__init__.py +0 -0
  171. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/conftest.py +0 -0
  172. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_agencies_integration.py +0 -0
  173. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_contracts_integration.py +0 -0
  174. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_edge_cases_integration.py +0 -0
  175. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_entities_integration.py +0 -0
  176. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_forecasts_integration.py +0 -0
  177. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_grants_integration.py +0 -0
  178. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_itdashboard_integration.py +0 -0
  179. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_naics_integration.py +0 -0
  180. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_notices_integration.py +0 -0
  181. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_offices_integration.py +0 -0
  182. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_opportunities_integration.py +0 -0
  183. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_organizations_integration.py +0 -0
  184. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
  185. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_protests_integration.py +0 -0
  186. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_reference_data_integration.py +0 -0
  187. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_subawards_integration.py +0 -0
  188. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
  189. {tango_python-1.11.0 → tango_python-1.12.0}/tests/integration/validation.py +0 -0
  190. {tango_python-1.11.0 → tango_python-1.12.0}/tests/production/__init__.py +0 -0
  191. {tango_python-1.11.0 → tango_python-1.12.0}/tests/production/conftest.py +0 -0
  192. {tango_python-1.11.0 → tango_python-1.12.0}/tests/production/test_production_smoke.py +0 -0
  193. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_api_parity.py +0 -0
  194. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_contract_appeals.py +0 -0
  195. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_dibbs_exclusions_sbir.py +0 -0
  196. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_ebuy.py +0 -0
  197. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_federal_register.py +0 -0
  198. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_models.py +0 -0
  199. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_opportunity_attachments.py +0 -0
  200. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_opportunity_awards.py +0 -0
  201. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_organization_scope.py +0 -0
  202. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_shape_coverage_gate.py +0 -0
  203. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_shapes.py +0 -0
  204. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_sled.py +0 -0
  205. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_webhooks_cli.py +0 -0
  206. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_webhooks_receiver.py +0 -0
  207. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_webhooks_signing.py +0 -0
  208. {tango_python-1.11.0 → tango_python-1.12.0}/tests/test_webhooks_simulate.py +0 -0
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.12.0] - 2026-10-02
11
+
12
+ ### Added
13
+
14
+ - **`account_category` and `source_anomalies` on budget accounts** (Tango API 5.7.0). Both are in the default `BUDGET_ACCOUNTS_MINIMAL` shape and accepted in custom shapes. `account_category` is `budgetary` or `credit_financing` today. `source_anomalies` lists the problems found in the source data behind an account, and is `[]` when there are none. Each element is typed as the new `BudgetAccountSourceAnomaly` `TypedDict` (with `BudgetAccountAnomalySource` and `BudgetAccountAnomalySourceRow` for its nested `source`), and every key is optional. Treat `account_category` and an anomaly's `code` as open strings, since new values may appear.
15
+ - **`list_budget_accounts(account_category=..., account_category_in=...)`** filters by account category, exactly or against a comma-separated list.
16
+
17
+ ### Changed
18
+
19
+ - **`list_budget_accounts()` documents the API's default ordering**: the latest fiscal year first, then largest `enacted_ba` first, with accounts that have no `enacted_ba` last.
20
+ - Re-vendored `contracts/filter_shape_contract.json` (Tango API 5.7.0) and regenerated `tango/shapes/generated_overlay.py`.
21
+
22
+ ### Fixed
23
+
24
+ - **`list_idvs()`, `list_idv_awards()`, `list_idv_child_idvs()` and `list_idv_transactions()` now return the API's pagination `cursor`.** They dropped it, so `response.cursor` was always `None` and a caller following the cursor stopped after the first page.
25
+
10
26
  ## [1.11.0] - 2026-09-29
11
27
 
12
28
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tango-python
3
- Version: 1.11.0
3
+ Version: 1.12.0
4
4
  Summary: Python SDK for the Tango API
5
5
  Project-URL: Homepage, https://github.com/makegov/tango-python
6
6
  Project-URL: Documentation, https://docs.makegov.com/tango-python
@@ -1770,6 +1770,8 @@ accounts = client.list_budget_accounts(
1770
1770
  bea_category=None,
1771
1771
  on_off_budget=None,
1772
1772
  subfunction_code=None,
1773
+ account_category=None,
1774
+ account_category_in=None,
1773
1775
  search=None,
1774
1776
  ordering=None,
1775
1777
  )
@@ -1785,8 +1787,18 @@ accounts = client.list_budget_accounts(
1785
1787
  - `bea_category` - BEA category (exact)
1786
1788
  - `on_off_budget` - On/off budget flag (exact)
1787
1789
  - `subfunction_code` - Subfunction code (exact)
1790
+ - `account_category` - Account category (exact): `budgetary` or `credit_financing` today; treat it as an open string
1791
+ - `account_category_in` - Comma-separated account categories to match any of (e.g. `"budgetary,credit_financing"`)
1788
1792
  - `search` - Full-text search over `account_title`, `agency_name`, `bureau_name`
1789
- - `ordering` - Sort field; prefix with `-` for descending
1793
+ - `ordering` - Sort field; prefix with `-` for descending. The default is the latest fiscal year first, then largest `enacted_ba` first, with accounts that have no `enacted_ba` last.
1794
+
1795
+ **Source anomalies:** the default shape includes `account_category` and `source_anomalies`. `source_anomalies` is a list of problems found in the source data behind the account, and `[]` when there are none. Each element is a `BudgetAccountSourceAnomaly` (a `TypedDict`; every key is optional and may be `None`, so read keys with `.get()`): `code`, `field`, `bound_field`, `action` (`capped` or `flagged`), `reported_value`, `served_value`, `likely_cause`, `affected_fields`, `message`, and `source` (`dataset`, `fiscal_year`, and `rows` of `fiscal_period`, `piid`, `parent_piid`, `tas`, `reporting_agency_id`, `transaction_obligated_amount`, `file_c_source`). Today `code` is one of `contract_exceeds_obligations`, `assistance_exceeds_obligations` or `contract_without_obligations`; treat it as an open string. There is no filter on anomalies.
1796
+
1797
+ ```python
1798
+ for acct in client.list_budget_accounts(fiscal_year=2025).results:
1799
+ for anomaly in acct["source_anomalies"] or []:
1800
+ print(acct["federal_account_symbol"], anomaly.get("code"), anomaly.get("action"))
1801
+ ```
1790
1802
 
1791
1803
  **Returns:** [PaginatedResponse](#paginatedresponse) of `BudgetAccount` records (see [ShapeConfig](#shapeconfig-predefined-shapes) for the default shape).
1792
1804
 
@@ -2547,7 +2559,7 @@ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)
2547
2559
  | `FEDERAL_REGISTER_COMPREHENSIVE` | `get_federal_register_document` | The list fields plus dates, signing_date, start_page, end_page, volume, docket_ids, dockets, regulation_id_numbers, topics, correction_of, corrections, executive_order_number, presidential_document_number, proclamation_number, comment_url, regulations_dot_gov_url, raw_text_url, body_html_url (omits `full_text`) |
2548
2560
  | `EBUY_REQUESTS_MINIMAL` | `list_ebuy_requests` | rfq_id, request_type, title, schedule, sin, status, buyer_name, buyer_agency, buyer_agency_code, reference_number, issue_date, close_date, attachment_count, link_count, last_seen |
2549
2561
  | `EBUY_REQUESTS_COMPREHENSIVE` | `get_ebuy_request` | Every field, plus the `organization` and `attachments` expands |
2550
- | `BUDGET_ACCOUNTS_MINIMAL` | `list_budget_accounts`, `get_budget_account` | id, federal_account_symbol, fiscal_year, agency_code/name, bureau_name, account_title, bea_category, on_off_budget, subfunction_code, lifecycle (requested/enacted/apportioned/obligated/outlayed/unobligated), contract & assistance rollups, key ratios, next-year growth |
2562
+ | `BUDGET_ACCOUNTS_MINIMAL` | `list_budget_accounts`, `get_budget_account` | id, federal_account_symbol, fiscal_year, agency_code/name, bureau_name, account_title, bea_category, on_off_budget, subfunction_code, account_category, lifecycle (requested/enacted/apportioned/obligated/outlayed/unobligated), contract & assistance rollups, key ratios, next-year growth, source_anomalies |
2551
2563
  | `VEHICLE_ORDERS_MINIMAL` | `list_vehicle_orders` | key, piid, award_date, recipient(display_name,uei), total_contract_value, obligated |
2552
2564
  | `ITDASHBOARD_INVESTMENTS_MINIMAL` | `list_itdashboard_investments` | Minimal IT Dashboard investment fields |
2553
2565
  | `ITDASHBOARD_INVESTMENTS_COMPREHENSIVE` | `get_itdashboard_investment` | Full investment fields: uii, agency_code, agency_name, bureau_code, bureau_name, investment_title, type_of_investment, part_of_it_portfolio, updated_time, url |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tango-python"
7
- version = "1.11.0"
7
+ version = "1.12.0"
8
8
  description = "Python SDK for the Tango API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -11,6 +11,9 @@ from .exceptions import (
11
11
  )
12
12
  from .models import (
13
13
  BudgetAccount,
14
+ BudgetAccountAnomalySource,
15
+ BudgetAccountAnomalySourceRow,
16
+ BudgetAccountSourceAnomaly,
14
17
  ContractAppeal,
15
18
  DibbsAward,
16
19
  DibbsRfp,
@@ -59,7 +62,7 @@ from .webhooks import (
59
62
  )
60
63
  from .webhooks.receiver import Delivery, WebhookReceiver
61
64
 
62
- __version__ = "1.11.0"
65
+ __version__ = "1.12.0"
63
66
  __all__ = [
64
67
  "TangoClient",
65
68
  "TangoAPIError",
@@ -72,6 +75,9 @@ __all__ = [
72
75
  "ResolveCandidate",
73
76
  "ResolveResult",
74
77
  "BudgetAccount",
78
+ "BudgetAccountAnomalySource",
79
+ "BudgetAccountAnomalySourceRow",
80
+ "BudgetAccountSourceAnomaly",
75
81
  "ContractAppeal",
76
82
  "FederalRegisterDocument",
77
83
  "EbuyAccess",
@@ -986,6 +986,7 @@ class TangoClient:
986
986
  next=data.get("next"),
987
987
  previous=data.get("previous"),
988
988
  results=results,
989
+ cursor=data.get("cursor"),
989
990
  page_metadata=data.get("page_metadata"),
990
991
  meta=data.get("meta"),
991
992
  )
@@ -1088,6 +1089,7 @@ class TangoClient:
1088
1089
  next=data.get("next"),
1089
1090
  previous=data.get("previous"),
1090
1091
  results=results,
1092
+ cursor=data.get("cursor"),
1091
1093
  page_metadata=data.get("page_metadata"),
1092
1094
  meta=data.get("meta"),
1093
1095
  )
@@ -1133,6 +1135,7 @@ class TangoClient:
1133
1135
  next=data.get("next"),
1134
1136
  previous=data.get("previous"),
1135
1137
  results=results,
1138
+ cursor=data.get("cursor"),
1136
1139
  page_metadata=data.get("page_metadata"),
1137
1140
  meta=data.get("meta"),
1138
1141
  )
@@ -1150,6 +1153,7 @@ class TangoClient:
1150
1153
  next=data.get("next"),
1151
1154
  previous=data.get("previous"),
1152
1155
  results=data.get("results") or [],
1156
+ cursor=data.get("cursor"),
1153
1157
  page_metadata=data.get("page_metadata"),
1154
1158
  meta=data.get("meta"),
1155
1159
  )
@@ -4444,6 +4448,8 @@ class TangoClient:
4444
4448
  bea_category: str | None = None,
4445
4449
  on_off_budget: str | None = None,
4446
4450
  subfunction_code: str | None = None,
4451
+ account_category: str | None = None,
4452
+ account_category_in: str | None = None,
4447
4453
  # Range-numeric filters: each field also accepts ``__gte`` / ``__lte``.
4448
4454
  requested_ba: float | None = None,
4449
4455
  requested_ba_gte: float | None = None,
@@ -4550,6 +4556,11 @@ class TangoClient:
4550
4556
  bea_category: BEA category (exact).
4551
4557
  on_off_budget: On/off budget flag (exact).
4552
4558
  subfunction_code: Subfunction code (exact).
4559
+ account_category: Account category (exact), e.g. ``budgetary`` or
4560
+ ``credit_financing``. Treat the value as an open string; new
4561
+ categories may be added.
4562
+ account_category_in: Comma-separated account categories to match
4563
+ any of, e.g. ``"budgetary,credit_financing"``.
4553
4564
  requested_ba: President's-budget requested BA (exact). Also
4554
4565
  ``requested_ba_gte`` / ``requested_ba_lte`` for range queries.
4555
4566
  enacted_ba: Enacted budget authority (exact / gte / lte).
@@ -4592,7 +4603,9 @@ class TangoClient:
4592
4603
  ordering: Sort field (prefix with '-' for descending). Any of the
4593
4604
  numeric fields above is a valid ordering target — e.g.
4594
4605
  ``ordering="-unobligated_balance"`` to rank by largest
4595
- headroom first.
4606
+ headroom first. The default ordering is the latest fiscal year
4607
+ first, then largest ``enacted_ba`` first, with accounts that
4608
+ have no ``enacted_ba`` sorted last.
4596
4609
  """
4597
4610
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
4598
4611
  if shape is None:
@@ -4615,6 +4628,8 @@ class TangoClient:
4615
4628
  ("bea_category", bea_category),
4616
4629
  ("on_off_budget", on_off_budget),
4617
4630
  ("subfunction_code", subfunction_code),
4631
+ ("account_category", account_category),
4632
+ ("account_category__in", account_category_in),
4618
4633
  ("search", search),
4619
4634
  ("ordering", ordering),
4620
4635
  )
@@ -786,6 +786,55 @@ class ValidateResult:
786
786
  errors: list[str] | None = None
787
787
 
788
788
 
789
+ class BudgetAccountAnomalySourceRow(TypedDict, total=False):
790
+ """One source record behind a budget-account source anomaly.
791
+
792
+ Every key is optional and any value may be ``None``.
793
+ """
794
+
795
+ fiscal_period: int | None
796
+ piid: str | None
797
+ parent_piid: str | None
798
+ tas: str | None
799
+ reporting_agency_id: str | None
800
+ transaction_obligated_amount: float | str | None
801
+ file_c_source: str | None
802
+
803
+
804
+ class BudgetAccountAnomalySource(TypedDict, total=False):
805
+ """Where a budget-account source anomaly was found.
806
+
807
+ Every key is optional and any value may be ``None``.
808
+ """
809
+
810
+ dataset: str | None
811
+ fiscal_year: int | None
812
+ rows: list[BudgetAccountAnomalySourceRow] | None
813
+
814
+
815
+ class BudgetAccountSourceAnomaly(TypedDict, total=False):
816
+ """One problem found in the source data behind a budget account.
817
+
818
+ An element of ``BudgetAccount.source_anomalies``. Every key is optional and
819
+ any value may be ``None``, so read keys with ``.get()``. ``code`` and
820
+ ``action`` are open strings: today ``code`` is one of
821
+ ``contract_exceeds_obligations``, ``assistance_exceeds_obligations`` or
822
+ ``contract_without_obligations``, and ``action`` is ``capped`` or
823
+ ``flagged``, but new values may appear.
824
+ """
825
+
826
+ code: str | None
827
+ field: str | None
828
+ bound_field: str | None
829
+ action: str | None
830
+ reported_value: float | str | None
831
+ served_value: float | str | None
832
+ likely_cause: str | None
833
+ affected_fields: list[str] | None
834
+ message: str | None
835
+ source: BudgetAccountAnomalySource | None
836
+
837
+
789
838
  @dataclass
790
839
  class BudgetAccount:
791
840
  """Schema definition for BudgetAccount (not used for instances).
@@ -805,6 +854,8 @@ class BudgetAccount:
805
854
  bea_category: str | None = None
806
855
  on_off_budget: str | None = None
807
856
  subfunction_code: str | None = None
857
+ # ``budgetary`` or ``credit_financing`` today; treat as an open string.
858
+ account_category: str | None = None
808
859
  # Lifecycle
809
860
  requested_ba: Decimal | None = None
810
861
  enacted_ba: Decimal | None = None
@@ -828,6 +879,8 @@ class BudgetAccount:
828
879
  assistance_share_of_obligated: Decimal | None = None
829
880
  assistance_share_of_obligated_capped: Decimal | None = None
830
881
  assistance_share_capped_flag: bool | None = None
882
+ # Problems found in the source data; ``[]`` when there are none.
883
+ source_anomalies: list[BudgetAccountSourceAnomaly] | None = None
831
884
  # Forward-look
832
885
  next_year_requested_ba: Decimal | None = None
833
886
  ba_growth_next_year: Decimal | None = None
@@ -1728,12 +1781,12 @@ class ShapeConfig:
1728
1781
  # Mirrors the API's BUDGET_ACCOUNT_DEFAULT_SHAPE.
1729
1782
  BUDGET_ACCOUNTS_MINIMAL: Final = (
1730
1783
  "id,federal_account_symbol,fiscal_year,agency_code,agency_name,bureau_name,"
1731
- "account_title,bea_category,on_off_budget,subfunction_code,"
1784
+ "account_title,bea_category,on_off_budget,subfunction_code,account_category,"
1732
1785
  "requested_ba,enacted_ba,apportioned,obligated_total,outlayed_total,"
1733
1786
  "unobligated_balance,contract_obligated,contract_share_of_obligated_capped,"
1734
1787
  "assistance_obligated,obligated_to_apportioned_pct_capped,"
1735
1788
  "obligated_to_enacted_pct_capped,outlayed_to_obligated_pct_capped,"
1736
- "ba_growth_next_year_pct"
1789
+ "ba_growth_next_year_pct,source_anomalies"
1737
1790
  )
1738
1791
 
1739
1792
  # Default for list_organizations()
@@ -1225,6 +1225,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1225
1225
  ),
1226
1226
  },
1227
1227
  "BudgetAccount": {
1228
+ "account_category": FieldSchema(
1229
+ name="account_category", type=str, is_optional=True, is_list=False
1230
+ ),
1228
1231
  "account_narrative_excerpt": FieldSchema(
1229
1232
  name="account_narrative_excerpt", type=str, is_optional=True, is_list=False
1230
1233
  ),
@@ -1410,6 +1413,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1410
1413
  "requested_personnel_share": FieldSchema(
1411
1414
  name="requested_personnel_share", type=str, is_optional=True, is_list=False
1412
1415
  ),
1416
+ "source_anomalies": FieldSchema(
1417
+ name="source_anomalies", type=dict, is_optional=True, is_list=True
1418
+ ),
1413
1419
  "subfunction_code": FieldSchema(
1414
1420
  name="subfunction_code", type=str, is_optional=True, is_list=False
1415
1421
  ),
@@ -271,6 +271,34 @@ class TestTangoClient:
271
271
  assert otas.results[0]["key"] == "OTA-1"
272
272
  assert otas.cursor == "next-page-token"
273
273
 
274
+ @pytest.mark.parametrize(
275
+ ("method", "args"),
276
+ [
277
+ ("list_idvs", ()),
278
+ ("list_idv_awards", ("IDV-1",)),
279
+ ("list_idv_child_idvs", ("IDV-1",)),
280
+ ("list_idv_transactions", ("IDV-1",)),
281
+ ],
282
+ )
283
+ @patch("tango.client.httpx.Client.request")
284
+ def test_idv_list_methods_return_cursor(self, mock_request, method, args):
285
+ mock_response = Mock()
286
+ mock_response.is_success = True
287
+ mock_response.json.return_value = {
288
+ "count": 250,
289
+ "next": "https://tango.makegov.com/api/idvs/?cursor=next-page-token",
290
+ "previous": None,
291
+ "results": [{"key": "IDV-1"}],
292
+ "cursor": "next-page-token",
293
+ }
294
+ mock_response.content = b'{"count": 250}'
295
+ mock_request.return_value = mock_response
296
+
297
+ client = TangoClient(api_key="test-key")
298
+ page = getattr(client, method)(*args, limit=1)
299
+
300
+ assert page.cursor == "next-page-token"
301
+
274
302
  @patch("tango.client.httpx.Client.request")
275
303
  def test_list_otidvs_with_default_shape(self, mock_request):
276
304
  """Test list_otidvs uses default minimal shape"""
@@ -573,6 +601,113 @@ class TestTangoClient:
573
601
  ]
574
602
  assert leaked == [], f"unexpected filter keys sent: {leaked}"
575
603
 
604
+ @patch("tango.client.httpx.Client.request")
605
+ def test_list_budget_accounts_account_category_filters(self, mock_request):
606
+ mock_response = Mock()
607
+ mock_response.is_success = True
608
+ mock_response.json.return_value = {
609
+ "count": 0,
610
+ "next": None,
611
+ "previous": None,
612
+ "results": [],
613
+ }
614
+ mock_response.content = b'{"count": 0, "results": []}'
615
+ mock_request.return_value = mock_response
616
+
617
+ client = TangoClient(api_key="test-key")
618
+ client.list_budget_accounts(
619
+ account_category="credit_financing",
620
+ account_category_in="budgetary,credit_financing",
621
+ )
622
+
623
+ params = mock_request.call_args[1]["params"]
624
+ assert params["account_category"] == "credit_financing"
625
+ assert params["account_category__in"] == "budgetary,credit_financing"
626
+
627
+ @patch("tango.client.httpx.Client.request")
628
+ def test_list_budget_accounts_default_shape_parses_source_anomalies(self, mock_request):
629
+ anomaly = {
630
+ "code": "contract_exceeds_obligations",
631
+ "field": "contract_obligated",
632
+ "bound_field": "obligated_total",
633
+ "action": "capped",
634
+ "reported_value": 1250.0,
635
+ "served_value": 1000.0,
636
+ "likely_cause": None,
637
+ "affected_fields": ["contract_obligated", "contract_share_of_obligated"],
638
+ "message": "Contract obligations exceed total obligations.",
639
+ "source": {
640
+ "dataset": "file_c",
641
+ "fiscal_year": 2025,
642
+ "rows": [
643
+ {
644
+ "fiscal_period": 12,
645
+ "piid": "ABC123",
646
+ "parent_piid": None,
647
+ "tas": "012-1234",
648
+ "reporting_agency_id": "012",
649
+ "transaction_obligated_amount": 1250.0,
650
+ "file_c_source": "award",
651
+ }
652
+ ],
653
+ },
654
+ }
655
+ mock_response = Mock()
656
+ mock_response.is_success = True
657
+ mock_response.json.return_value = {
658
+ "count": 2,
659
+ "next": None,
660
+ "previous": None,
661
+ "results": [
662
+ {
663
+ "id": 1,
664
+ "fiscal_year": 2025,
665
+ "account_category": "budgetary",
666
+ "source_anomalies": [anomaly],
667
+ },
668
+ {
669
+ "id": 2,
670
+ "fiscal_year": 2025,
671
+ "account_category": "credit_financing",
672
+ "enacted_ba": None,
673
+ "source_anomalies": [],
674
+ },
675
+ ],
676
+ }
677
+ mock_response.content = b"{}"
678
+ mock_request.return_value = mock_response
679
+
680
+ client = TangoClient(api_key="test-key")
681
+ page = client.list_budget_accounts()
682
+
683
+ shape_fields = mock_request.call_args[1]["params"]["shape"].split(",")
684
+ assert "account_category" in shape_fields
685
+ assert "source_anomalies" in shape_fields
686
+ first, second = page.results
687
+ assert first["account_category"] == "budgetary"
688
+ assert first["source_anomalies"] == [anomaly]
689
+ assert first["source_anomalies"][0]["source"]["rows"][0]["piid"] == "ABC123"
690
+ assert second["account_category"] == "credit_financing"
691
+ assert second["source_anomalies"] == []
692
+
693
+ @patch("tango.client.httpx.Client.request")
694
+ def test_get_budget_account_accepts_explicit_anomaly_shape(self, mock_request):
695
+ mock_response = Mock()
696
+ mock_response.is_success = True
697
+ mock_response.json.return_value = {
698
+ "id": 7,
699
+ "account_category": "budgetary",
700
+ "source_anomalies": [{"code": "some_future_code"}],
701
+ }
702
+ mock_response.content = b"{}"
703
+ mock_request.return_value = mock_response
704
+
705
+ client = TangoClient(api_key="test-key")
706
+ account = client.get_budget_account(7, shape="id,account_category,source_anomalies")
707
+
708
+ assert account["source_anomalies"] == [{"code": "some_future_code"}]
709
+ assert account["account_category"] == "budgetary"
710
+
576
711
 
577
712
  class TestShapeConfig:
578
713
  """Test ShapeConfig class"""
File without changes
File without changes
File without changes